Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ obj/

*.binlog

sdk.sln.DotSettings.user
# User specific files
*.user

# Debian and python stuff
*.dsc
Expand Down
4 changes: 4 additions & 0 deletions CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,7 @@
/src/Tests/dotnet-watch.Tests/ @captainsafia, @pranavkm, @mkArtakMSFT
/src/Tests/Microsoft.AspNetCore.Watch.BrowserRefresh.Tests/ @captainsafia, @pranavkm, @mkArtakMSFT
/src/BuiltInTools/ @captainsafia, @pranavkm, @mkArtakMSFT

# Compatibility tools owned by runtime team
/src/Compatibility/ @Anipik, @safern, @ericstj
/src/Tests/Microsoft.DotNet.ApiCompatibility/ @Anipik, @safern, @ericstj
16 changes: 16 additions & 0 deletions sdk.sln
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.DotNet.NativeWrap
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.NET.Sdk.Razor.SourceGenerators.Tests", "src\Tests\Microsoft.NET.Sdk.Razor.SourceGenerators.Tests\Microsoft.NET.Sdk.Razor.SourceGenerators.Tests.csproj", "{A71FC21D-D90A-49B5-9B5A-AD4776287B55}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Compatibility", "Compatibility", "{AF683E5C-421E-4DE0-ADD7-9841E5D12BFA}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.DotNet.ApiCompatibility", "src\Compatibility\Microsoft.DotNet.ApiCompatibility\Microsoft.DotNet.ApiCompatibility.csproj", "{3F5A028C-C51B-434A-8C10-37680CD2635C}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.DotNet.ApiCompatibility.Tests", "src\Tests\Microsoft.DotNet.ApiCompatibility.Tests\Microsoft.DotNet.ApiCompatibility.Tests.csproj", "{24F084ED-35BB-401E-89F5-63E5E22C3B3B}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Win32.Msi", "src\Microsoft.Win32.Msi\Microsoft.Win32.Msi.csproj", "{3D002392-6308-41DF-8BD5-224CCC5B049F}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Win32.Msi.Tests", "src\Tests\Microsoft.Win32.Msi.Tests\Microsoft.Win32.Msi.Tests.csproj", "{80932949-B8B2-4163-B325-76F8FDBE3897}"
Expand Down Expand Up @@ -621,6 +626,14 @@ Global
{A71FC21D-D90A-49B5-9B5A-AD4776287B55}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A71FC21D-D90A-49B5-9B5A-AD4776287B55}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A71FC21D-D90A-49B5-9B5A-AD4776287B55}.Release|Any CPU.Build.0 = Release|Any CPU
{3F5A028C-C51B-434A-8C10-37680CD2635C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3F5A028C-C51B-434A-8C10-37680CD2635C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3F5A028C-C51B-434A-8C10-37680CD2635C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3F5A028C-C51B-434A-8C10-37680CD2635C}.Release|Any CPU.Build.0 = Release|Any CPU
{24F084ED-35BB-401E-89F5-63E5E22C3B3B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{24F084ED-35BB-401E-89F5-63E5E22C3B3B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{24F084ED-35BB-401E-89F5-63E5E22C3B3B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{24F084ED-35BB-401E-89F5-63E5E22C3B3B}.Release|Any CPU.Build.0 = Release|Any CPU
{3D002392-6308-41DF-8BD5-224CCC5B049F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3D002392-6308-41DF-8BD5-224CCC5B049F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3D002392-6308-41DF-8BD5-224CCC5B049F}.Release|Any CPU.ActiveCfg = Release|Any CPU
Expand Down Expand Up @@ -746,6 +759,9 @@ Global
{1BBFA19C-03F0-4D27-9D0D-0F8172642107} = {71A9F549-0EB6-41F9-BC16-4A6C5007FC91}
{E97E9E7F-11B4-42F7-8B55-D0451F5E82A0} = {8F22FBD6-BDC8-431E-8402-B7460D3A9724}
{A71FC21D-D90A-49B5-9B5A-AD4776287B55} = {580D1AE7-AA8F-4912-8B76-105594E00B3B}
{AF683E5C-421E-4DE0-ADD7-9841E5D12BFA} = {22AB674F-ED91-4FBC-BFEE-8A1E82F9F05E}
{3F5A028C-C51B-434A-8C10-37680CD2635C} = {AF683E5C-421E-4DE0-ADD7-9841E5D12BFA}
{24F084ED-35BB-401E-89F5-63E5E22C3B3B} = {580D1AE7-AA8F-4912-8B76-105594E00B3B}
{3D002392-6308-41DF-8BD5-224CCC5B049F} = {22AB674F-ED91-4FBC-BFEE-8A1E82F9F05E}
{80932949-B8B2-4163-B325-76F8FDBE3897} = {580D1AE7-AA8F-4912-8B76-105594E00B3B}
{EEF4C7DD-CDC9-44B6-8B4F-725647D54ED8} = {580D1AE7-AA8F-4912-8B76-105594E00B3B}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using Microsoft.CodeAnalysis;
using System;

namespace Microsoft.DotNet.ApiCompatibility.Abstractions
{
/// <summary>
/// Class representing a difference of compatibility, containing detailed information about it.
/// </summary>
public class CompatDifference : IDiagnostic, IEquatable<CompatDifference>
{
/// <summary>
/// The Diagnostic ID for this difference.
/// </summary>
public string DiagnosticId { get; }

/// <summary>
/// The <see cref="DifferenceType"/>.
/// </summary>
public DifferenceType Type { get; }

/// <summary>
/// A diagnostic message for the difference.
/// </summary>
public virtual string Message { get; }

/// <summary>
/// A unique ID in order to identify the API that the difference was raised for.
/// </summary>
public string ReferenceId { get; }

private CompatDifference() { }

/// <summary>
/// Instantiate a new object representing the compatibility difference.
/// </summary>
/// <param name="id"><see cref="string"/> representing the diagnostic ID.</param>
/// <param name="message"><see cref="string"/> message describing the difference.</param>
/// <param name="type"><see cref="DifferenceType"/> to describe the type of the difference.</param>
/// <param name="member"><see cref="ISymbol"/> for which the difference is associated to.</param>
public CompatDifference(string diagnosticId, string message, DifferenceType type, ISymbol member)
: this(diagnosticId, message, type, member?.GetDocumentationCommentId())
{
}

/// <summary>
/// Instantiate a new object representing the compatibility difference.
/// </summary>
/// <param name="id"><see cref="string"/> representing the diagnostic ID.</param>
/// <param name="message"><see cref="string"/> message describing the difference.</param>
/// <param name="type"><see cref="DifferenceType"/> to describe the type of the difference.</param>
/// <param name="memberId"><see cref="string"/> containing the member ID for which the difference is associated to.</param>
public CompatDifference(string diagnosticId, string message, DifferenceType type, string memberId)
{
DiagnosticId = diagnosticId ?? throw new ArgumentNullException(nameof(diagnosticId));
Message = message ?? throw new ArgumentNullException(nameof(message));
Type = type;
ReferenceId = memberId ?? throw new ArgumentNullException(nameof(memberId));
}

/// <summary>
/// Evaluates whether the current object is equal to another <see cref="CompatDifference"/>.
/// </summary>
/// <param name="other"><see cref="CompatDifference"/> to compare against.</param>
/// <returns>True if equals, False if different.</returns>
public bool Equals(CompatDifference other) =>
other != null &&
Type == other.Type &&
DiagnosticId.Equals(other.DiagnosticId, StringComparison.OrdinalIgnoreCase) &&
ReferenceId.Equals(other.ReferenceId, StringComparison.OrdinalIgnoreCase) &&
Message.Equals(other.Message, StringComparison.OrdinalIgnoreCase);

/// <summary>
/// Gets the hashcode that reperesents this instance.
/// </summary>
/// <returns>Unique <see cref="int"/> based on the properties' values of the instance.</returns>
public override int GetHashCode() =>
HashCode.Combine(ReferenceId, DiagnosticId, Message, Type);

/// <summary>
/// Gets a <see cref="string"/> representation of the difference.
/// </summary>
/// <returns><see cref="string"/> describing the difference.</returns>
public override string ToString() => $"{DiagnosticId} : {Message}";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

namespace Microsoft.DotNet.ApiCompatibility.Abstractions
{
/// <summary>
/// Enum representing the different type of differences available.
/// </summary>
public enum DifferenceType
{
Changed,
Added,
Removed
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using Microsoft.CodeAnalysis;

namespace Microsoft.DotNet.ApiCompatibility.Abstractions
{
/// <summary>
/// Provides a mechanism to filter <see cref="ISymbol"/> when building the <see cref="ElementMapper{T}"/>.
/// </summary>
public interface ISymbolFilter
{
/// <summary>
/// Determines whether the <see cref="ISymbol"/> should be included.
/// </summary>
/// <param name="symbol"><see cref="ISymbol"/> to evaluate.</param>
/// <returns>True to include the <paramref name="symbol"/> or false to filter it out.</returns>
bool Include(ISymbol symbol);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using Microsoft.CodeAnalysis;

namespace Microsoft.DotNet.ApiCompatibility.Abstractions
{
internal class SymbolAccessibilityBasedFilter : ISymbolFilter
{
private readonly bool _includeInternalSymbols;

internal SymbolAccessibilityBasedFilter(bool includeInternalSymbols)
{
_includeInternalSymbols = includeInternalSymbols;
}

public bool Include(ISymbol symbol) =>
symbol.DeclaredAccessibility == Accessibility.Public ||
symbol.DeclaredAccessibility == Accessibility.Protected ||
symbol.DeclaredAccessibility == Accessibility.ProtectedOrInternal ||
(_includeInternalSymbols && symbol.DeclaredAccessibility != Accessibility.Private);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

namespace Microsoft.DotNet.ApiCompatibility.Abstractions
{
/// <summary>
/// Interface that describes a diagnostic.
/// </summary>
public interface IDiagnostic
{
/// <summary>
/// String representing the diagnostic ID.
/// </summary>
string DiagnosticId { get; }

/// <summary>
/// String representing the ID for the object that the diagnostic was created for.
/// </summary>
string ReferenceId { get; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.s

using System.Collections.Generic;

namespace Microsoft.DotNet.ApiCompatibility.Abstractions
{
/// <summary>
/// Interface for rule drivers to implement in order to be used returned by the <see cref="IRuleRunnerFactory"/>
/// </summary>
public interface IRuleRunner
{
/// <summary>
/// Runs the registered rules on the mapper.
/// </summary>
/// <typeparam name="T">The underlying type on the mapper.</typeparam>
/// <param name="mapper">The mapper to run the rules on.</param>
/// <returns></returns>
IEnumerable<CompatDifference> Run<T>(ElementMapper<T> mapper);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.s

namespace Microsoft.DotNet.ApiCompatibility.Abstractions
{
/// <summary>
/// The factory to create the driver that the differ should use based on the <see cref="ComparingSettings"/>.
/// </summary>
public interface IRuleRunnerFactory
{
IRuleRunner GetRuleRunner();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System;

namespace Microsoft.DotNet.ApiCompatibility.Abstractions
{
/// <summary>
/// Class that implements a visitor pattern to visit the tree for a given mapper.
/// </summary>
public class MapperVisitor
{
/// <summary>
/// Visits the tree for the given <see cref="ElementMapper{T}"/>.
/// </summary>
/// <typeparam name="T">Underlying type for the objects that the mapper holds.</typeparam>
/// <param name="mapper"><see cref="ElementMapper{T}"/> to visit.</param>
public void Visit<T>(ElementMapper<T> mapper)
{
if (mapper is AssemblySetMapper assemblySetMapper)
{
Visit(assemblySetMapper);
}
else if (mapper is AssemblyMapper assemblyMapper)
{
Visit(assemblyMapper);
}
else if (mapper is NamespaceMapper nsMapper)
{
Visit(nsMapper);
}
else if (mapper is TypeMapper typeMapper)
{
Visit(typeMapper);
}
else if (mapper is MemberMapper memberMapper)
{
Visit(memberMapper);
}
}

/// <summary>
/// Visits the <see cref="AssemblySetMapper"/> and visits each <see cref="AssemblyMapper"/> in the mapper.
/// </summary>
/// <param name="mapper">The <see cref="AssemblySetMapper"/> to visit.</param>
public virtual void Visit(AssemblySetMapper mapper)
{
if (mapper == null)
{
throw new ArgumentNullException(nameof(mapper));
}

foreach (AssemblyMapper assembly in mapper.GetAssemblies())
{
Visit(assembly);
}
}

/// <summary>
/// Visits the <see cref="AssemblyMapper"/> and visits each <see cref="NamespaceMapper"/> in the mapper.
/// </summary>
/// <param name="mapper">The <see cref="AssemblyMapper"/> to visit.</param>
public virtual void Visit(AssemblyMapper mapper)
{
if (mapper == null)
{
throw new ArgumentNullException(nameof(mapper));
}

foreach (NamespaceMapper nsMapper in mapper.GetNamespaces())
{
Visit(nsMapper);
}
}

/// <summary>
/// Visits the <see cref="NamespaceMapper"/> and visits each <see cref="TypeMapper"/> in the mapper.
/// </summary>
/// <param name="mapper">The <see cref="NamespaceMapper"/> to visit.</param>
public virtual void Visit(NamespaceMapper mapper)
{
if (mapper == null)
{
throw new ArgumentNullException(nameof(mapper));
}

foreach (TypeMapper type in mapper.GetTypes())
{
Visit(type);
}
}

/// <summary>
/// Visits the <see cref="TypeMapper"/> and visits the nested types and members in the mapper.
/// </summary>
/// <param name="mapper">The <see cref="TypeMapper"/> to visit.</param>
public virtual void Visit(TypeMapper mapper)
{
if (mapper == null)
{
throw new ArgumentNullException(nameof(mapper));
}

foreach (TypeMapper type in mapper.GetNestedTypes())
{
Visit(type);
}

foreach (MemberMapper member in mapper.GetMembers())
{
Visit(member);
}
}

/// <summary>
/// Visits the <see cref="MemberMapper"/>.
/// </summary>
/// <param name="mapper">The <see cref="MemberMapper"/> to visit.</param>
public virtual void Visit(MemberMapper mapper) { }
}
}
Loading