diff --git a/Arcade.sln b/Arcade.sln index 7527d11bb90..dc66fd32a22 100644 --- a/Arcade.sln +++ b/Arcade.sln @@ -35,8 +35,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.DotNet.SwaggerGen EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.DotNet.GenAPI", "src\Microsoft.DotNet.GenAPI\Microsoft.DotNet.GenAPI.csproj", "{9427265E-C224-4B40-8157-F05529D084D3}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.DotNet.ApiCompat", "src\Microsoft.DotNet.ApiCompat\src\Microsoft.DotNet.ApiCompat.csproj", "{9E406363-CFB1-4851-AEE9-9DEF98A7F814}" -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SignCheck", "SignCheck", "{A704E5B2-86A4-4D4F-902D-C10EA18C12DF}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.DotNet.SignCheck", "src\SignCheck\SignCheck\Microsoft.DotNet.SignCheck.csproj", "{AF298985-511F-476A-8F62-79908BE8DF01}" @@ -99,16 +97,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.DotNet.Build.Task EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.DotNet.RemoteExecutor.Tests", "src\Microsoft.DotNet.RemoteExecutor\tests\Microsoft.DotNet.RemoteExecutor.Tests.csproj", "{D6AC20A4-1719-49FE-B112-B2AB564496F8}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.DotNet.ApiCompat.Tests", "src\Microsoft.DotNet.ApiCompat\tests\Microsoft.DotNet.ApiCompat.Tests.csproj", "{61041759-64FE-425F-8984-BA876428A595}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AttributeDifference.Implementation", "src\Microsoft.DotNet.ApiCompat\tests\TestProjects\AttributeDifference\Implementation\AttributeDifference.Implementation.csproj", "{52E92416-5E5F-4A62-A837-9D8554DD2805}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Microsoft.DotNet.ApiCompat", "Microsoft.DotNet.ApiCompat", "{E41E23C4-5CB0-4C61-9E05-EEFFEC4B356D}" -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "TestProjects", "TestProjects", "{6F517597-E9E2-43B2-B7E2-757132EA525C}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AttributeDifference.Contract", "src\Microsoft.DotNet.ApiCompat\tests\TestProjects\AttributeDifference\Contract\AttributeDifference.Contract.csproj", "{1CC55B23-6212-4120-BF52-8DED9CFF9FBC}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.DotNet.Build.Tasks.Archives", "src\Microsoft.DotNet.Build.Tasks.Archives\Microsoft.DotNet.Build.Tasks.Archives.csproj", "{5579768A-CC07-477C-ACE4-06FE9B0686A7}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.DotNet.SourceBuild.Tasks", "src\Microsoft.DotNet.SourceBuild\tasks\Microsoft.DotNet.SourceBuild.Tasks.csproj", "{F9D72AF5-9320-43C8-A24F-CBE294FCED0A}" @@ -139,8 +129,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.DotNet.Build.Task EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.DotNet.Build.Tasks.Templating.Tests", "src\Microsoft.DotNet.Build.Tasks.Templating\test\Microsoft.DotNet.Build.Tasks.Templating.Tests.csproj", "{FB4168D5-6EA6-4777-AD4F-95758C177FE8}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.DotNet.ApiCompat.Core", "src\Microsoft.DotNet.ApiCompat\src\Microsoft.DotNet.ApiCompat.Core\Microsoft.DotNet.ApiCompat.Core.csproj", "{90FA5F6B-5C96-44EF-BB83-4AFF62A1CF8B}" -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{6DA9F58A-34D5-45A6-998E-5D2B8037C3FE}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Microsoft.DotNet.XUnitAssert", "Microsoft.DotNet.XUnitAssert", "{3C542789-2576-48C8-9772-C9D7575F7E42}" @@ -337,18 +325,6 @@ Global {9427265E-C224-4B40-8157-F05529D084D3}.Release|x64.Build.0 = Release|Any CPU {9427265E-C224-4B40-8157-F05529D084D3}.Release|x86.ActiveCfg = Release|Any CPU {9427265E-C224-4B40-8157-F05529D084D3}.Release|x86.Build.0 = Release|Any CPU - {9E406363-CFB1-4851-AEE9-9DEF98A7F814}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {9E406363-CFB1-4851-AEE9-9DEF98A7F814}.Debug|Any CPU.Build.0 = Debug|Any CPU - {9E406363-CFB1-4851-AEE9-9DEF98A7F814}.Debug|x64.ActiveCfg = Debug|Any CPU - {9E406363-CFB1-4851-AEE9-9DEF98A7F814}.Debug|x64.Build.0 = Debug|Any CPU - {9E406363-CFB1-4851-AEE9-9DEF98A7F814}.Debug|x86.ActiveCfg = Debug|Any CPU - {9E406363-CFB1-4851-AEE9-9DEF98A7F814}.Debug|x86.Build.0 = Debug|Any CPU - {9E406363-CFB1-4851-AEE9-9DEF98A7F814}.Release|Any CPU.ActiveCfg = Release|Any CPU - {9E406363-CFB1-4851-AEE9-9DEF98A7F814}.Release|Any CPU.Build.0 = Release|Any CPU - {9E406363-CFB1-4851-AEE9-9DEF98A7F814}.Release|x64.ActiveCfg = Release|Any CPU - {9E406363-CFB1-4851-AEE9-9DEF98A7F814}.Release|x64.Build.0 = Release|Any CPU - {9E406363-CFB1-4851-AEE9-9DEF98A7F814}.Release|x86.ActiveCfg = Release|Any CPU - {9E406363-CFB1-4851-AEE9-9DEF98A7F814}.Release|x86.Build.0 = Release|Any CPU {AF298985-511F-476A-8F62-79908BE8DF01}.Debug|Any CPU.ActiveCfg = Debug|x86 {AF298985-511F-476A-8F62-79908BE8DF01}.Debug|Any CPU.Build.0 = Debug|x86 {AF298985-511F-476A-8F62-79908BE8DF01}.Debug|x64.ActiveCfg = Debug|x86 @@ -681,42 +657,6 @@ Global {D6AC20A4-1719-49FE-B112-B2AB564496F8}.Release|x64.Build.0 = Release|Any CPU {D6AC20A4-1719-49FE-B112-B2AB564496F8}.Release|x86.ActiveCfg = Release|Any CPU {D6AC20A4-1719-49FE-B112-B2AB564496F8}.Release|x86.Build.0 = Release|Any CPU - {61041759-64FE-425F-8984-BA876428A595}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {61041759-64FE-425F-8984-BA876428A595}.Debug|Any CPU.Build.0 = Debug|Any CPU - {61041759-64FE-425F-8984-BA876428A595}.Debug|x64.ActiveCfg = Debug|Any CPU - {61041759-64FE-425F-8984-BA876428A595}.Debug|x64.Build.0 = Debug|Any CPU - {61041759-64FE-425F-8984-BA876428A595}.Debug|x86.ActiveCfg = Debug|Any CPU - {61041759-64FE-425F-8984-BA876428A595}.Debug|x86.Build.0 = Debug|Any CPU - {61041759-64FE-425F-8984-BA876428A595}.Release|Any CPU.ActiveCfg = Release|Any CPU - {61041759-64FE-425F-8984-BA876428A595}.Release|Any CPU.Build.0 = Release|Any CPU - {61041759-64FE-425F-8984-BA876428A595}.Release|x64.ActiveCfg = Release|Any CPU - {61041759-64FE-425F-8984-BA876428A595}.Release|x64.Build.0 = Release|Any CPU - {61041759-64FE-425F-8984-BA876428A595}.Release|x86.ActiveCfg = Release|Any CPU - {61041759-64FE-425F-8984-BA876428A595}.Release|x86.Build.0 = Release|Any CPU - {52E92416-5E5F-4A62-A837-9D8554DD2805}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {52E92416-5E5F-4A62-A837-9D8554DD2805}.Debug|Any CPU.Build.0 = Debug|Any CPU - {52E92416-5E5F-4A62-A837-9D8554DD2805}.Debug|x64.ActiveCfg = Debug|Any CPU - {52E92416-5E5F-4A62-A837-9D8554DD2805}.Debug|x64.Build.0 = Debug|Any CPU - {52E92416-5E5F-4A62-A837-9D8554DD2805}.Debug|x86.ActiveCfg = Debug|Any CPU - {52E92416-5E5F-4A62-A837-9D8554DD2805}.Debug|x86.Build.0 = Debug|Any CPU - {52E92416-5E5F-4A62-A837-9D8554DD2805}.Release|Any CPU.ActiveCfg = Release|Any CPU - {52E92416-5E5F-4A62-A837-9D8554DD2805}.Release|Any CPU.Build.0 = Release|Any CPU - {52E92416-5E5F-4A62-A837-9D8554DD2805}.Release|x64.ActiveCfg = Release|Any CPU - {52E92416-5E5F-4A62-A837-9D8554DD2805}.Release|x64.Build.0 = Release|Any CPU - {52E92416-5E5F-4A62-A837-9D8554DD2805}.Release|x86.ActiveCfg = Release|Any CPU - {52E92416-5E5F-4A62-A837-9D8554DD2805}.Release|x86.Build.0 = Release|Any CPU - {1CC55B23-6212-4120-BF52-8DED9CFF9FBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {1CC55B23-6212-4120-BF52-8DED9CFF9FBC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {1CC55B23-6212-4120-BF52-8DED9CFF9FBC}.Debug|x64.ActiveCfg = Debug|Any CPU - {1CC55B23-6212-4120-BF52-8DED9CFF9FBC}.Debug|x64.Build.0 = Debug|Any CPU - {1CC55B23-6212-4120-BF52-8DED9CFF9FBC}.Debug|x86.ActiveCfg = Debug|Any CPU - {1CC55B23-6212-4120-BF52-8DED9CFF9FBC}.Debug|x86.Build.0 = Debug|Any CPU - {1CC55B23-6212-4120-BF52-8DED9CFF9FBC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {1CC55B23-6212-4120-BF52-8DED9CFF9FBC}.Release|Any CPU.Build.0 = Release|Any CPU - {1CC55B23-6212-4120-BF52-8DED9CFF9FBC}.Release|x64.ActiveCfg = Release|Any CPU - {1CC55B23-6212-4120-BF52-8DED9CFF9FBC}.Release|x64.Build.0 = Release|Any CPU - {1CC55B23-6212-4120-BF52-8DED9CFF9FBC}.Release|x86.ActiveCfg = Release|Any CPU - {1CC55B23-6212-4120-BF52-8DED9CFF9FBC}.Release|x86.Build.0 = Release|Any CPU {5579768A-CC07-477C-ACE4-06FE9B0686A7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {5579768A-CC07-477C-ACE4-06FE9B0686A7}.Debug|Any CPU.Build.0 = Debug|Any CPU {5579768A-CC07-477C-ACE4-06FE9B0686A7}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -897,18 +837,6 @@ Global {FB4168D5-6EA6-4777-AD4F-95758C177FE8}.Release|x64.Build.0 = Release|Any CPU {FB4168D5-6EA6-4777-AD4F-95758C177FE8}.Release|x86.ActiveCfg = Release|Any CPU {FB4168D5-6EA6-4777-AD4F-95758C177FE8}.Release|x86.Build.0 = Release|Any CPU - {90FA5F6B-5C96-44EF-BB83-4AFF62A1CF8B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {90FA5F6B-5C96-44EF-BB83-4AFF62A1CF8B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {90FA5F6B-5C96-44EF-BB83-4AFF62A1CF8B}.Debug|x64.ActiveCfg = Debug|Any CPU - {90FA5F6B-5C96-44EF-BB83-4AFF62A1CF8B}.Debug|x64.Build.0 = Debug|Any CPU - {90FA5F6B-5C96-44EF-BB83-4AFF62A1CF8B}.Debug|x86.ActiveCfg = Debug|Any CPU - {90FA5F6B-5C96-44EF-BB83-4AFF62A1CF8B}.Debug|x86.Build.0 = Debug|Any CPU - {90FA5F6B-5C96-44EF-BB83-4AFF62A1CF8B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {90FA5F6B-5C96-44EF-BB83-4AFF62A1CF8B}.Release|Any CPU.Build.0 = Release|Any CPU - {90FA5F6B-5C96-44EF-BB83-4AFF62A1CF8B}.Release|x64.ActiveCfg = Release|Any CPU - {90FA5F6B-5C96-44EF-BB83-4AFF62A1CF8B}.Release|x64.Build.0 = Release|Any CPU - {90FA5F6B-5C96-44EF-BB83-4AFF62A1CF8B}.Release|x86.ActiveCfg = Release|Any CPU - {90FA5F6B-5C96-44EF-BB83-4AFF62A1CF8B}.Release|x86.Build.0 = Release|Any CPU {AB8D5F86-60FA-416A-B047-83B1E9118425}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {AB8D5F86-60FA-416A-B047-83B1E9118425}.Debug|Any CPU.Build.0 = Debug|Any CPU {AB8D5F86-60FA-416A-B047-83B1E9118425}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -1004,7 +932,6 @@ Global {FF9DA4D9-3A84-441B-8AAE-B73B6D1EE8B3} = {BB99CE55-AD68-484A-9474-B8BACED247AB} {697EB593-3277-4C43-B8F3-C578E21B051B} = {BB99CE55-AD68-484A-9474-B8BACED247AB} {4FA6B420-53A3-4956-9C02-8649A720A9F5} = {BB99CE55-AD68-484A-9474-B8BACED247AB} - {9E406363-CFB1-4851-AEE9-9DEF98A7F814} = {E41E23C4-5CB0-4C61-9E05-EEFFEC4B356D} {AF298985-511F-476A-8F62-79908BE8DF01} = {A704E5B2-86A4-4D4F-902D-C10EA18C12DF} {39B69A56-43E9-480D-9A60-8E0917795FE8} = {A704E5B2-86A4-4D4F-902D-C10EA18C12DF} {F23ED302-7451-4D40-85C3-FACFCF884661} = {C53DD924-C212-49EA-9BC4-1827421361EF} @@ -1019,10 +946,6 @@ Global {3376C769-211F-4537-A156-5F841FF7840B} = {C53DD924-C212-49EA-9BC4-1827421361EF} {03390E61-9DC1-4893-93A4-193D76C16034} = {C53DD924-C212-49EA-9BC4-1827421361EF} {D6AC20A4-1719-49FE-B112-B2AB564496F8} = {C53DD924-C212-49EA-9BC4-1827421361EF} - {61041759-64FE-425F-8984-BA876428A595} = {C53DD924-C212-49EA-9BC4-1827421361EF} - {52E92416-5E5F-4A62-A837-9D8554DD2805} = {6F517597-E9E2-43B2-B7E2-757132EA525C} - {6F517597-E9E2-43B2-B7E2-757132EA525C} = {E41E23C4-5CB0-4C61-9E05-EEFFEC4B356D} - {1CC55B23-6212-4120-BF52-8DED9CFF9FBC} = {6F517597-E9E2-43B2-B7E2-757132EA525C} {CE5278A3-2442-4309-A543-5BA5C1C76A2A} = {C53DD924-C212-49EA-9BC4-1827421361EF} {E941EDE6-3FFB-4776-A4CE-750755D57817} = {C53DD924-C212-49EA-9BC4-1827421361EF} {6CA09DC9-E654-4906-A977-1279F6EDC109} = {C53DD924-C212-49EA-9BC4-1827421361EF} @@ -1030,7 +953,6 @@ Global {B5E9D9D8-59E0-49F8-9C3C-75138A2D452C} = {C53DD924-C212-49EA-9BC4-1827421361EF} {0B5D3C20-EB58-4A82-A3AA-2E626A17B35D} = {C53DD924-C212-49EA-9BC4-1827421361EF} {FB4168D5-6EA6-4777-AD4F-95758C177FE8} = {C53DD924-C212-49EA-9BC4-1827421361EF} - {90FA5F6B-5C96-44EF-BB83-4AFF62A1CF8B} = {E41E23C4-5CB0-4C61-9E05-EEFFEC4B356D} {3C542789-2576-48C8-9772-C9D7575F7E42} = {6DA9F58A-34D5-45A6-998E-5D2B8037C3FE} {AB8D5F86-60FA-416A-B047-83B1E9118425} = {3C542789-2576-48C8-9772-C9D7575F7E42} {14462553-E4E1-4F67-B954-4BF24B1DAAFE} = {3C542789-2576-48C8-9772-C9D7575F7E42} diff --git a/src/Microsoft.DotNet.ApiCompat/README.md b/src/Microsoft.DotNet.ApiCompat/README.md deleted file mode 100644 index 6cdef64ad81..00000000000 --- a/src/Microsoft.DotNet.ApiCompat/README.md +++ /dev/null @@ -1,38 +0,0 @@ -# Not maintained anymore - -:warning: Microsoft.DotNet.ApiCompat which is CCI based isn't maintained anymore and was never intended to be publicly used. Please switch to the Roslyn based ApiCompat & PackageValidation functionality that is part of the .NET SDK: https://learn.microsoft.com/en-us/dotnet/fundamentals/package-validation/overview. This code base will be deleted in the future. - -## Microsoft.DotNet.ApiCompat - -APICompat is a tool which may be used to test API compatibility between a two .NET assemblies. - -When testing, the tool will compare a *contract* to an *implementation*. - -The *contract* represents the API that's expected : for example a reference assembly or a previous version of an assembly. - -The *implementation* represents the API that's provided : for example the current version of an assembly. - -### Usage - -API Compat can be used by referencing this Microsoft.DotNet.ApiCompat package from the *implementation* project, and providing the path to the *contract* via a single `@(ResolvedMatchingContract)` item. Dependencies of `@(ResolvedMatchingContract)` must be specified in either `DependencyPaths` metadata on the items themselves or via the `$(ContractDependencyPaths)` property. - -When API Compat identifies an error it will log the error and fail the build. If you wish to ignore the error you can copy the error text to a baseline file (see below). Take care when doing this as these errors represent compatibility problems between the *contract* and *implementation*. - -## Required setting - -- `@(ResolvedMatchingContract)` - should point to a single file that represents the contract to validate -- `%(DependencyPaths)` - optional, specifies a semi-colon delimited set of paths that contain the assembly dependencies of this contract -- `$(ContractDependencyPaths)` - optional, specifies a semi-colon delimited set of paths that contain the assembly dependencies of this contract - -## Additional settings - -- `$(RunApiCompat)` - true to run APICompat, defaults to true -- `$(RunApiCompatForSrc)` - true to run APICompat treating project output as *implementation* and `@(ResolvedMatchingContract)` as *contract*, defaults to true. -- `$(RunMatchingRefApiCompat)` - true to run APICompat treating project output as *contract* and `@(ResolvedMatchingContract)` as *implementation*, defaults to false. This is also known as reverse API compat and can help ensure that every public API defined in a project is exposed in `@(ResolvedMatchingContract)`. -- `$(ApiCompatExcludeAttributeList)` - Attributes to exclude from APICompat checks. This is a text file containing types in DocID format, EG: T:Namespace.TypeName. -- `$(ApiCompatEnforceOptionalRules)` - true to enforce optional rules, default is false. An example of an optional rule is parameter naming which can break source compatibility but not binary compatibility. -- `$(ApiCompatBaseline)` - path to baseline file used to suppress errors, defaults to a file in the project directory. -- `$(BaselineAllAPICompatError)` - true to indicate that the baseline file should be rewritten suppressing all API compat errors. You may set this when building the project to conveniently update the baseline when you wish to suppress them, eg: `dotnet msbuild /p:BaselineAllAPICompatError=true` -- `$(MatchingRefApiCompatBaseline)` - same as `$(ApiCompatBaseline)` but for reverse API compat. -- `$(BaselineAllMatchingRefApiCompatError)` - same as `$(BaselineAllAPICompatError)` but for reverse API compat. -- `$(AdditionalApiCompatOptions)` - allows you to pass additional parameters as command line parameters, for example `--exclude-non-browsable`. diff --git a/src/Microsoft.DotNet.ApiCompat/src/ApiCompatRunner.cs b/src/Microsoft.DotNet.ApiCompat/src/ApiCompatRunner.cs deleted file mode 100644 index d072ec28a01..00000000000 --- a/src/Microsoft.DotNet.ApiCompat/src/ApiCompatRunner.cs +++ /dev/null @@ -1,125 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.IO; -using McMaster.Extensions.CommandLineUtils; - -namespace Microsoft.DotNet.ApiCompat -{ - /// - /// The console app's entry point which invokes Executor.Run in Microsoft.DotNet.ApiCompat.Core. - /// The other entry point is an msbuild task, ApiCompatTask. - /// - public class ApiCompatRunner - { - private readonly CommandLineApplication _app; - private TextWriter _outputStream; - - public ApiCompatRunner(TextWriter outputStream = null) - { - _outputStream = outputStream; - _app = CreateApp(); - } - - public int Run(string[] args) => _app.Execute(args); - - private CommandLineApplication CreateApp() - { - var app = new CommandLineApplication - { - Name = "ApiCompat", - FullName = "A command line tool to verify that two sets of APIs are compatible.", - ResponseFileHandling = ResponseFileHandling.ParseArgsAsSpaceSeparated, - Out = _outputStream ?? Console.Out, - Error = _outputStream ?? Console.Error - }; - app.HelpOption("-?|-h|--help"); - app.VersionOption("-v|--version", typeof(Program).Assembly.GetName().Version.ToString()); - - CommandArgument contracts = app.Argument("contracts", "Comma delimited list of assemblies or directories of assemblies for all the contract assemblies."); - contracts.IsRequired(); - CommandOption implDirs = app.Option("-i|--impl-dirs", "Comma delimited list of directories to find the implementation assemblies for each contract assembly.", CommandOptionType.SingleValue); - implDirs.IsRequired(allowEmptyStrings: true); - CommandOption baseline = app.Option("-b|--baseline", "Comma delimited list of files to skip known diffs.", CommandOptionType.SingleValue); - CommandOption validateBaseline = app.Option("--validate-baseline", "Validates that baseline files don't have invalid/unused diffs.", CommandOptionType.NoValue); - CommandOption mdil = app.Option("-m|--mdil", "Enforce MDIL servicing rules in addition to IL rules.", CommandOptionType.NoValue); - CommandOption outFilePath = app.Option("-o|--out", "Output file path. Default is the console.", CommandOptionType.SingleValue); - CommandOption leftOperand = app.Option("-l|--left-operand", "Name for left operand in comparison, default is 'contract'.", CommandOptionType.SingleValue); - CommandOption rightOperand = app.Option("-r|--right-operand", "Name for right operand in comparison, default is 'implementation'.", CommandOptionType.SingleValue); - CommandOption listRules = app.Option("--list-rules", "Outputs all the rules. If this options is supplied all other options are ignored.", CommandOptionType.NoValue); - CommandOption remapFile = app.Option("--remap-file", "File with a list of type and/or namespace remappings to consider apply to names while diffing.", CommandOptionType.SingleValue); - CommandOption skipGroupByAssembly = app.Option("--skip-group-by-assembly", "Skip grouping the differences by assembly instead of flattening the namespaces.", CommandOptionType.NoValue); - CommandOption skipUnifyToLibPath = app.Option("--skip-unify-to-lib-path", "Skip unifying the assembly references to the loaded assemblies and the assemblies found in the given directories (contractDepends and implDirs).", CommandOptionType.NoValue); - CommandOption resolveFx = app.Option("--resolve-fx", "If a contract or implementation dependency cannot be found in the given directories, fallback to try to resolve against the framework directory on the machine.", CommandOptionType.NoValue); - CommandOption contractDepends = app.Option("--contract-depends", "Comma delimited list of directories used to resolve the dependencies of the contract assemblies.", CommandOptionType.SingleValue); - CommandOption contractCoreAssembly = app.Option("--contract-core-assembly", "Simple name for the core assembly to use.", CommandOptionType.SingleValue); - CommandOption ignoreDesignTimeFacades = app.Option("--ignore-design-time-facades", "Ignore design time facades in the contract set while analyzing.", CommandOptionType.NoValue); - CommandOption warnOnIncorrectVersion = app.Option("--warn-on-incorrect-version", "Warn if the contract version number doesn't match the found implementation version number.", CommandOptionType.NoValue); - CommandOption warnOnMissingAssemblies = app.Option("--warn-on-missing-assemblies", "Warn if the contract assembly cannot be found in the implementation directories. Default is to error and not do analysis.", CommandOptionType.NoValue); - CommandOption excludeNonBrowsable = app.Option("--exclude-non-browsable", "When MDIL servicing rules are not being enforced, exclude validation on types that are marked with EditorBrowsable(EditorBrowsableState.Never).", CommandOptionType.NoValue); - CommandOption excludeAttributes = app.Option("--exclude-attributes", "Comma delimited list of files with types in DocId format of which attributes to exclude.", CommandOptionType.SingleValue); - CommandOption enforceOptionalRules = app.Option("--enforce-optional-rules", "Enforce optional rules, in addition to the mandatory set of rules.", CommandOptionType.NoValue); - CommandOption allowDefaultInterfaceMethods = app.Option("--allow-default-interface-methods", "Allow default interface methods additions to not be considered breaks. This flag should only be used if you know your consumers support DIM", CommandOptionType.NoValue); - CommandOption respectInternals = app.Option( - "--respect-internals", - "Include both internal and public APIs if assembly contains an InternalsVisibleTo attribute. Otherwise, include only public APIs.", - CommandOptionType.NoValue); - - // --exclude-compiler-generated is recommended if the same option was passed to GenAPI. - // - // For one thing, comparing compiler-generated attributes, especially `CompilerGeneratedAttribute` itself, - // on members leads to numerous false incompatibilities e.g. { get; set; } properties result in two - // compiler-generated methods but GenAPI produces `{ get { throw null; } set { } }` i.e. explicit methods. - CommandOption excludeCompilerGenerated = app.Option( - "--exclude-compiler-generated", - "Exclude APIs marked with a CompilerGenerated attribute.", - CommandOptionType.NoValue); - - app.OnExecute(() => - { - bool disableAssemblyResolveTraceListener = false; - // Use Console.Out if no output file path is passed in or - // when the file cannot be opened or created. - if (_outputStream == null && (string.IsNullOrWhiteSpace(outFilePath.Value()) || - !OutputHelper.TryGetOutput(outFilePath.Value(), out _outputStream))) - { - _outputStream = Console.Out; - disableAssemblyResolveTraceListener = true; - } - - Executor.Run(usesMSBuildLog: false, - disableAssemblyResolveTraceListener, - SplitPaths(contracts.Value), - SplitPaths(implDirs.Value()), - _outputStream, - rightOperand.Value(), - leftOperand.Value(), - listRules.HasValue(), - SplitPaths(baseline.Value()), - validateBaseline.HasValue(), - resolveFx.HasValue(), - skipUnifyToLibPath.HasValue(), - SplitPaths(contractDepends.Value()), - contractCoreAssembly.Value(), - ignoreDesignTimeFacades.HasValue(), - warnOnMissingAssemblies.HasValue(), - respectInternals.HasValue(), - warnOnIncorrectVersion.HasValue(), - enforceOptionalRules.HasValue(), - mdil.HasValue(), - excludeNonBrowsable.HasValue(), - excludeCompilerGenerated.HasValue(), - remapFile.Value(), - skipGroupByAssembly.HasValue(), - SplitPaths(excludeAttributes.Value()), - allowDefaultInterfaceMethods.HasValue()); - }); - - return app; - } - - private static string[] SplitPaths(string pathSet) => - pathSet?.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries) ?? new string[0]; - } -} diff --git a/src/Microsoft.DotNet.ApiCompat/src/ApiCompatTask.cs b/src/Microsoft.DotNet.ApiCompat/src/ApiCompatTask.cs deleted file mode 100644 index 6b06d56dc3c..00000000000 --- a/src/Microsoft.DotNet.ApiCompat/src/ApiCompatTask.cs +++ /dev/null @@ -1,218 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.IO; -using Microsoft.Build.Framework; -using Microsoft.DotNet.Build.Tasks; - -namespace Microsoft.DotNet.ApiCompat -{ - /// - /// MSBuild task that invokes Executor.Run in Microsoft.DotNet.ApiCompat.Core. - /// The console app's entry point is ApiCompatRunner which invokes Executor.Run as well. - /// - public class ApiCompatTask : BuildTask - { - private TextWriter _outputStream; - - // Keep argument comments in sync with ApiCompatRunner.cs - - /// - /// Comma delimited list of assemblies or directories of assemblies for all the contract assemblies. - /// - [Required] - public string[] Contracts { get; set; } - - /// - /// Comma delimited list of directories to find the implementation assemblies for each contract assembly. - /// - [Required] - public string[] ImplementationDirectories { get; set; } - - /// - /// Name for right operand in comparison, default is 'implementation'. - /// - public string RightOperand { get; set; } - - /// - /// Name for left operand in comparison, default is 'contract'. - /// - public string LeftOperand { get; set; } - - /// - /// Output file path. Default is the console. - /// - public string OutFilePath { get; set; } - - /// - /// Comma delimited list of files to skip known diffs. - /// - public string[] BaselineFiles { get; set; } - - /// - /// Validates that baseline files don't have invalid/unused diffs. - /// - public bool ValidateBaseline { get; set; } - - /// - /// If a contract or implementation dependency cannot be found in the given directories, - /// fallback to try to resolve against the framework directory on the machine. - /// - public bool ResolveFramework { get; set; } - - /// - /// Skip unifying the assembly references to the loaded assemblies and the assemblies - /// found in the given directories (contractDepends and implDirs). - /// - public bool SkipUnifyToLibPath { get; set; } - - /// - /// Comma delimited list of directories used to resolve the dependencies of the contract assemblies. - /// - public string[] ContractDepends { get; set; } - - /// - /// Simple name for the core assembly to use. - /// - public string ContractCoreAssembly { get; set; } - - /// - /// Ignore design time facades in the contract set while analyzing. - /// - public bool IgnoreDesignTimeFacades { get; set; } - - /// - /// Warn if the contract assembly cannot be found in the implementation directories. - /// Default is to error and not do analysis. - /// - public bool WarnOnMissingAssemblies { get; set; } - - /// - /// Include both internal and public APIs if assembly contains an InternalsVisibleTo attribute. - /// Otherwise, include only public APIs. - /// - public bool RespectInternals { get; set; } - - /// - /// Warn if the contract version number doesn't match the found implementation version number. - /// - public bool WarnOnIncorrectVersion { get; set; } - - /// - /// Enforce optional rules, in addition to the mandatory set of rules. - /// - public bool EnforceOptionalRules { get; set; } - - /// - /// Enforce MDIL servicing rules in addition to IL rules. - /// - public bool MDIL { get; set; } - - /// - /// When MDIL servicing rules are not being enforced, exclude validation on types that are marked - /// with EditorBrowsable(EditorBrowsableState.Never). - /// - public bool ExcludeNonBrowsable { get; set; } - - /// - /// Exclude APIs marked with a CompilerGenerated attribute. - /// - public bool ExcludeCompilerGenerated { get; set; } - - /// - /// File with a list of type and/or namespace remappings to consider apply to names while diffing. - /// - public string RemapFile { get; set; } - - /// - /// Skip grouping the differences by assembly instead of flattening the namespaces. - /// - public bool SkipGroupByAssembly { get; private set; } - - /// - /// Comma delimited list of files with types in DocId format of which attributes to exclude. - /// - public string[] ExcludeAttributes { get; set; } - - /// - /// Allow default interface methods additions to not be considered breaks. This flag should only be - /// used if you know your consumers support DIM - /// - public bool AllowDefaultInterfaceMethods { get; set; } - - /// - /// Allows to disable the trace listener that emits messages when an assembly can't be resolved. - /// - public bool DisableAssemblyResolveTraceListener { get; set; } - - /// - /// If true, the task ignores the exit code. Otherwise, the task returns false if the exit code is non-zero. - /// - public bool IgnoreExitCode { get; set; } - - /// - /// The ExitCode of the task for a more detailed analysis. - /// - [Output] - public int ExitCode { get; set; } - - public ApiCompatTask() - { - } - - public ApiCompatTask(TextWriter outputStream) - { - _outputStream = outputStream; - } - - public override bool Execute() - { - bool usesMSBuildLog = false; - - // Use the MSBuildTextWriter if no output file path is passed in or - // when the file cannot be opened or created. - if (_outputStream == null && (string.IsNullOrWhiteSpace(OutFilePath) || - !OutputHelper.TryGetOutput(OutFilePath, out _outputStream))) - { - _outputStream = new MSBuildTextWriter(Log); - usesMSBuildLog = true; - DisableAssemblyResolveTraceListener = true; - } - - ExitCode = Executor.Run(usesMSBuildLog, - DisableAssemblyResolveTraceListener, - Contracts, - ImplementationDirectories, - _outputStream, - RightOperand, - LeftOperand, - listRules: false, - BaselineFiles, - ValidateBaseline, - ResolveFramework, - SkipUnifyToLibPath, - ContractDepends, - ContractCoreAssembly, - IgnoreDesignTimeFacades, - WarnOnMissingAssemblies, - RespectInternals, - WarnOnIncorrectVersion, - EnforceOptionalRules, - MDIL, - ExcludeNonBrowsable, - ExcludeCompilerGenerated, - RemapFile, - SkipGroupByAssembly, - ExcludeAttributes, - AllowDefaultInterfaceMethods); - - // If the tool exited cleanly, but logged errors then assign a failing exit code (-1) - if (ExitCode == 0 && Log.HasLoggedErrors) - { - ExitCode = -1; - } - - return IgnoreExitCode || ExitCode == 0; - } - } -} diff --git a/src/Microsoft.DotNet.ApiCompat/src/MSBuildTextWriter.cs b/src/Microsoft.DotNet.ApiCompat/src/MSBuildTextWriter.cs deleted file mode 100644 index 12718689484..00000000000 --- a/src/Microsoft.DotNet.ApiCompat/src/MSBuildTextWriter.cs +++ /dev/null @@ -1,40 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using Microsoft.DotNet.Build.Tasks; -using System; -using System.IO; -using System.Text; - -namespace Microsoft.DotNet.ApiCompat -{ - /// - /// A text writer that uses MSBuild's logging infrastructure to log errors. - /// - internal class MSBuildTextWriter : TextWriter - { - private readonly Log _log; - - public MSBuildTextWriter(Log log) : base() - { - _log = log ?? throw new ArgumentNullException(nameof(log)); - } - - public override Encoding Encoding => Encoding.Default; - - public override void WriteLine(string value) - { - _log.LogError(value); - } - - public override void WriteLine() - { - _log.LogError(Environment.NewLine); - } - - public override void Write(string value) - { - _log.LogError(value); - } - } -} diff --git a/src/Microsoft.DotNet.ApiCompat/src/Microsoft.DotNet.ApiCompat.Core/DifferenceWriter.cs b/src/Microsoft.DotNet.ApiCompat/src/Microsoft.DotNet.ApiCompat.Core/DifferenceWriter.cs deleted file mode 100644 index 1335b040e83..00000000000 --- a/src/Microsoft.DotNet.ApiCompat/src/Microsoft.DotNet.ApiCompat.Core/DifferenceWriter.cs +++ /dev/null @@ -1,110 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Collections.Generic; -using System.Composition; -using System.Diagnostics; -using System.IO; -using System.Linq; -using Microsoft.Cci.Differs; -using Microsoft.Cci.Filters; -using Microsoft.Cci.Mappings; -using Microsoft.Cci.Traversers; - -namespace Microsoft.Cci.Writers -{ - public class DifferenceWriter : DifferenceTraverser, ICciDifferenceWriter - { - private readonly List _differences; - private readonly TextWriter _writer; - private int _totalDifferences = 0; - private readonly bool _usesMSBuildLog; - - public static int ExitCode { get; set; } - - [Import] - public IDifferenceOperands Operands { get; set; } - - public DifferenceWriter(TextWriter writer, MappingSettings settings, IDifferenceFilter filter, bool usesMSBuildLog = false) - : base(settings, filter) - { - _writer = writer; - _differences = new List(); - _usesMSBuildLog = usesMSBuildLog; - } - - public void Write(string oldAssembliesName, IEnumerable oldAssemblies, string newAssembliesName, IEnumerable newAssemblies) - { - this.Visit(oldAssemblies, newAssemblies); - - if (!this.Settings.GroupByAssembly) - { - if (_differences.Count > 0) - { - string header = $"Compat issues between {Operands.Implementation} set {oldAssembliesName} and {Operands.Contract} set {newAssembliesName}:"; - OutputDifferences(header, _differences); - _totalDifferences += _differences.Count; - _differences.Clear(); - } - } - - if (DifferenceFilter is BaselineDifferenceFilter filter) - { - var unusedBaselineDifferences = filter.GetUnusedBaselineDifferences(); - if (unusedBaselineDifferences.Any()) - { - _writer.WriteLine($"{Environment.NewLine}*** Invalid/Unused baseline differences ***"); - foreach (var diff in unusedBaselineDifferences) - { - _writer.WriteLine(diff); - _totalDifferences++; - } - } - } - - // Don't use the msbuild logging infrastructure to write as that would be promoted as an error. - // We don't want the task to log anything except failures when not writing into a baseline file. - if (!_usesMSBuildLog) - { - _writer.WriteLine("Total Issues: {0}", _totalDifferences); - } - - _totalDifferences = 0; - } - - public override void Visit(AssemblyMapping mapping) - { - Debug.Assert(_differences.Count == 0); - - base.Visit(mapping); - - if (this.Settings.GroupByAssembly) - { - if (_differences.Count > 0) - { - string header = string.Format("Compat issues with assembly {0}:", mapping.Representative.Name.Value); - OutputDifferences(header, _differences); - _totalDifferences += _differences.Count; - _differences.Clear(); - } - } - } - - private void OutputDifferences(string header, IEnumerable differences) - { - _writer.WriteLine(header); - - foreach (var diff in differences) - _writer.WriteLine(diff.ToString()); - } - - public override void Visit(Difference difference) - { - _differences.Add(difference); - - // For now use this to set the ExitCode to 2 if there are any differences - DifferenceWriter.ExitCode = 2; - } - } -} diff --git a/src/Microsoft.DotNet.ApiCompat/src/Microsoft.DotNet.ApiCompat.Core/Executor.cs b/src/Microsoft.DotNet.ApiCompat/src/Microsoft.DotNet.ApiCompat.Core/Executor.cs deleted file mode 100644 index 428fcfd6e2d..00000000000 --- a/src/Microsoft.DotNet.ApiCompat/src/Microsoft.DotNet.ApiCompat.Core/Executor.cs +++ /dev/null @@ -1,334 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Collections.Generic; -using System.Composition; -using System.Composition.Hosting; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Reflection; -using Microsoft.Cci; -using Microsoft.Cci.Comparers; -using Microsoft.Cci.Differs; -using Microsoft.Cci.Differs.Rules; -using Microsoft.Cci.Extensions; -using Microsoft.Cci.Extensions.CSharp; -using Microsoft.Cci.Filters; -using Microsoft.Cci.Mappings; -using Microsoft.Cci.Writers; - -namespace Microsoft.DotNet.ApiCompat -{ - /// - /// The core part of ApiCompat which gets invoked by the ApiCompatRunner console frontend - /// and the ApiCompatTask msbuild task. - /// - public static class Executor - { - /// - /// The core part of ApiCompat which accepts a given set of arguments and - /// performs api compatibility checks. - /// - public static int Run(bool usesMSBuildLog, - bool disableAssemblyResolveTraceListener, - IEnumerable contracts, - IEnumerable implementationDirectories, - TextWriter output, - string rightOperand = "implementation", - string leftOperand = "contract", - bool listRules = false, - IEnumerable baselineFileNames = null, - bool validateBaseline = false, - bool resolveFramework = false, - bool skipUnifyToLibPath = false, - IEnumerable contractDependsFileNames = null, - string contractCoreAssembly = null, - bool ignoreDesignTimeFacades = false, - bool warnOnMissingAssemblies = false, - bool respectInternals = false, - bool warnOnIncorrectVersion = false, - bool enforceOptionalRules = false, - bool mdil = false, - bool excludeNonBrowsable = false, - bool excludeCompilerGenerated = false, - string remapFile = null, - bool skipGroupByAssembly = false, - IEnumerable excludeAttributes = null, - bool allowDefaultInterfaceMethods = false) - { - // Clear exit code from previous runs on the same domain given this is a static property. - DifferenceWriter.ExitCode = 0; - - if (listRules) - { - CompositionHost c = GetCompositionHost(); - ExportCciSettings.StaticSettings = CciComparers.Default.GetEqualityComparer(); - - IEnumerable rules = c.GetExports(); - - foreach (IDifferenceRule rule in rules.OrderBy(r => r.GetType().Name, StringComparer.OrdinalIgnoreCase)) - { - string ruleName = rule.GetType().Name; - - if (IsOptionalRule(rule)) - ruleName += " (optional)"; - - output.WriteLine(ruleName); - } - - return 0; - } - - using (output) - { - if (DifferenceWriter.ExitCode != 0) - return 0; - - if (!disableAssemblyResolveTraceListener) - Trace.Listeners.Add(new TextWriterTraceListener(output) { Filter = new EventTypeFilter(SourceLevels.Error | SourceLevels.Warning) }); - - try - { - BaselineDifferenceFilter filter = GetBaselineDifferenceFilter(baselineFileNames, validateBaseline); - NameTable sharedNameTable = new(); - HostEnvironment contractHost = new(sharedNameTable); - contractHost.UnableToResolve += (sender, e) => Trace.TraceError($"Unable to resolve assembly '{e.Unresolved}' referenced by the {leftOperand} assembly '{e.Referrer}'."); - contractHost.ResolveAgainstRunningFramework = resolveFramework; - contractHost.UnifyToLibPath = !skipUnifyToLibPath; - contractHost.AddLibPaths(contractDependsFileNames); - IEnumerable contractAssemblies = contractHost.LoadAssemblies(contracts, contractCoreAssembly); - - if (ignoreDesignTimeFacades) - contractAssemblies = contractAssemblies.Where(a => !a.IsFacade()); - - HostEnvironment implHost = new(sharedNameTable); - implHost.UnableToResolve += (sender, e) => Trace.TraceError($"Unable to resolve assembly '{e.Unresolved}' referenced by the {rightOperand} assembly '{e.Referrer}'."); - implHost.ResolveAgainstRunningFramework = resolveFramework; - implHost.UnifyToLibPath = !skipUnifyToLibPath; - implHost.AddLibPaths(implementationDirectories); - if (warnOnMissingAssemblies) - implHost.LoadErrorTreatment = ErrorTreatment.TreatAsWarning; - - // The list of contractAssemblies already has the core assembly as the first one (if _contractCoreAssembly was specified). - IEnumerable implAssemblies = implHost.LoadAssemblies(contractAssemblies.Select(a => a.AssemblyIdentity), warnOnIncorrectVersion); - - // Exit after loading if the code is set to non-zero - if (DifferenceWriter.ExitCode != 0) - return 0; - - bool includeInternals = respectInternals && - contractAssemblies.Any(assembly => assembly.Attributes.HasAttributeOfType( - "System.Runtime.CompilerServices.InternalsVisibleToAttribute")); - ICciDifferenceWriter writer = GetDifferenceWriter( - output, - filter, - enforceOptionalRules, - mdil, - excludeNonBrowsable, - includeInternals, - excludeCompilerGenerated, - remapFile, - !skipGroupByAssembly, - leftOperand, - rightOperand, - excludeAttributes, - allowDefaultInterfaceMethods, - usesMSBuildLog); - writer.Write(string.Join(",", implementationDirectories), implAssemblies, string.Join(",", contracts), contractAssemblies); - - return 0; - } - catch (FileNotFoundException) - { - // FileNotFoundException will be thrown by GetBaselineDifferenceFilter if it doesn't find the baseline file - // OR if GetComparers doesn't find the remap file. - return 2; - } - } - } - - private static ICciDifferenceWriter GetDifferenceWriter(TextWriter writer, - IDifferenceFilter filter, - bool enforceOptionalRules, - bool mdil, - bool excludeNonBrowsable, - bool includeInternals, - bool excludeCompilerGenerated, - string remapFile, - bool groupByAssembly, - string leftOperand, - string rightOperand, - IEnumerable excludeAttributes, - bool allowDefaultInterfaceMethods, - bool usesMSBuildLog) - { - CompositionHost container = GetCompositionHost(); - - bool RuleFilter(IDifferenceRuleMetadata ruleMetadata) - { - if (ruleMetadata.OptionalRule && !enforceOptionalRules) - return false; - - if (ruleMetadata.MdilServicingRule && !mdil) - return false; - return true; - } - - if (mdil && excludeNonBrowsable) - { - Trace.TraceWarning("Enforcing MDIL servicing rules and exclusion of non-browsable types are both enabled, but they are not compatible so non-browsable types will not be excluded."); - } - - if (includeInternals && (mdil || excludeNonBrowsable)) - { - Trace.TraceWarning("Enforcing MDIL servicing rules or exclusion of non-browsable types are enabled " + - "along with including internals -- an incompatible combination. Internal members will not be included."); - } - - ICciFilter cciFilter = GetCciFilter(mdil, excludeNonBrowsable, includeInternals, excludeCompilerGenerated); - var settings = new MappingSettings - { - Comparers = GetComparers(remapFile), - DiffFactory = new ElementDifferenceFactory(container, RuleFilter), - DiffFilter = GetDiffFilter(cciFilter), - Filter = cciFilter, - GroupByAssembly = groupByAssembly, - IncludeForwardedTypes = true, - }; - - if (filter == null) - { - filter = new DifferenceFilter(); - } - - var diffWriter = new DifferenceWriter(writer, settings, filter, usesMSBuildLog); - ExportCciSettings.StaticSettings = settings.TypeComparer; - ExportCciSettings.StaticOperands = new DifferenceOperands() - { - Contract = leftOperand, - Implementation = rightOperand - }; - - ExportCciSettings.StaticAttributeFilter = GetAttributeFilter(excludeAttributes); - ExportCciSettings.StaticRuleSettings = new RuleSettings { AllowDefaultInterfaceMethods = allowDefaultInterfaceMethods }; - - // Always compose the diff writer to allow it to import or provide exports - container.SatisfyImports(diffWriter); - - return diffWriter; - } - - private static BaselineDifferenceFilter GetBaselineDifferenceFilter(IEnumerable baselineFileNames, bool validateBaseline) - { - if (baselineFileNames == null) - { - return null; - } - - BaselineDifferenceFilter baselineDifferenceFilter = null; - - AddFiles(baselineFileNames, (file) => - (baselineDifferenceFilter ??= new BaselineDifferenceFilter(new DifferenceFilter(), validateBaseline)).AddBaselineFile(file)); - - return baselineDifferenceFilter; - } - - private static AttributeFilter GetAttributeFilter(IEnumerable ignoreAttributeFileNames) - { - AttributeFilter attributeFilter = new(); - - if (ignoreAttributeFileNames != null) - { - AddFiles(ignoreAttributeFileNames, (file) => attributeFilter.AddIgnoreAttributeFile(file)); - } - - return attributeFilter; - } - - private static void AddFiles(IEnumerable files, System.Action addFile) - { - foreach (string file in files) - { - if (!string.IsNullOrEmpty(file)) - { - if (!File.Exists(file)) - { - throw new FileNotFoundException($"File {file} was not found!", file); - } - - addFile(file); - } - } - } - - private static CompositionHost GetCompositionHost() - { - ContainerConfiguration configuration = new ContainerConfiguration().WithAssembly(typeof(Executor).GetTypeInfo().Assembly); - return configuration.CreateContainer(); - } - - private static ICciComparers GetComparers(string remapFile) - { - if (!string.IsNullOrEmpty(remapFile)) - { - if (!File.Exists(remapFile)) - { - throw new FileNotFoundException("ERROR: RemapFile {0} was not found!", remapFile); - } - return new NamespaceRemappingComparers(remapFile); - } - return CciComparers.Default; - } - - private static ICciFilter GetCciFilter( - bool enforcingMdilRules, - bool excludeNonBrowsable, - bool includeInternals, - bool excludeCompilerGenerated) - { - ICciFilter includeFilter; - if (enforcingMdilRules) - { - includeFilter = new MdilPublicOnlyCciFilter() - { - IncludeForwardedTypes = true - }; - } - else if (excludeNonBrowsable) - { - includeFilter = new PublicEditorBrowsableOnlyCciFilter() - { - IncludeForwardedTypes = true - }; - } - else if (includeInternals) - { - includeFilter = new InternalsAndPublicCciFilter(); - } - else - { - includeFilter = new PublicOnlyCciFilter() - { - IncludeForwardedTypes = true - }; - } - - if (excludeCompilerGenerated) - { - includeFilter = new IntersectionFilter(includeFilter, new ExcludeCompilerGeneratedCciFilter()); - } - - return includeFilter; - } - - private static IMappingDifferenceFilter GetDiffFilter(ICciFilter filter) => - new MappingDifferenceFilter(GetIncludeFilter(), filter); - - private static Func GetIncludeFilter() => (d => d != DifferenceType.Unchanged); - - private static bool IsOptionalRule(IDifferenceRule rule) => - rule.GetType().GetTypeInfo().GetCustomAttribute().OptionalRule; - } -} diff --git a/src/Microsoft.DotNet.ApiCompat/src/Microsoft.DotNet.ApiCompat.Core/ExportCciSettings.cs b/src/Microsoft.DotNet.ApiCompat/src/Microsoft.DotNet.ApiCompat.Core/ExportCciSettings.cs deleted file mode 100644 index bc119a17b65..00000000000 --- a/src/Microsoft.DotNet.ApiCompat/src/Microsoft.DotNet.ApiCompat.Core/ExportCciSettings.cs +++ /dev/null @@ -1,40 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Collections.Generic; -using System.Composition; -using Microsoft.Cci; -using Microsoft.Cci.Differs; -using Microsoft.Cci.Differs.Rules; - -namespace Microsoft.DotNet.ApiCompat -{ - public class ExportCciSettings - { - public static IEqualityComparer StaticSettings { get; set; } - public static IDifferenceOperands StaticOperands { get; set; } - public static IAttributeFilter StaticAttributeFilter { get; set; } - - public static IRuleSettings StaticRuleSettings { get; set; } - - public ExportCciSettings() - { - Settings = StaticSettings; - Operands = StaticOperands; - AttributeFilter = StaticAttributeFilter; - RuleSettings = StaticRuleSettings; - } - - [Export(typeof(IEqualityComparer))] - public IEqualityComparer Settings { get; } - - [Export(typeof(IDifferenceOperands))] - public IDifferenceOperands Operands { get; } - - [Export(typeof(IAttributeFilter))] - public IAttributeFilter AttributeFilter { get; } - - [Export(typeof(IRuleSettings))] - public IRuleSettings RuleSettings { get; } - } -} diff --git a/src/Microsoft.DotNet.ApiCompat/src/Microsoft.DotNet.ApiCompat.Core/MdilPublicOnlyCciFilter.cs b/src/Microsoft.DotNet.ApiCompat/src/Microsoft.DotNet.ApiCompat.Core/MdilPublicOnlyCciFilter.cs deleted file mode 100644 index b50d9a6f4b1..00000000000 --- a/src/Microsoft.DotNet.ApiCompat/src/Microsoft.DotNet.ApiCompat.Core/MdilPublicOnlyCciFilter.cs +++ /dev/null @@ -1,70 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Collections.Generic; -using System.Composition; -using System.Composition.Hosting; -using System.Diagnostics; -using System.IO; -using System.Linq; -using Microsoft.Cci; -using Microsoft.Cci.Comparers; -using Microsoft.Cci.Differs; -using Microsoft.Cci.Extensions; -using Microsoft.Cci.Filters; -using Microsoft.Cci.Mappings; -using Microsoft.Cci.Writers; -using Microsoft.Cci.Writers.Syntax; - -namespace Microsoft.DotNet.ApiCompat -{ - // This filter is activated when ApiCompat is used to detect changes that validate MDIL servicing rules. - // - internal class MdilPublicOnlyCciFilter : PublicOnlyCciFilter - { - public MdilPublicOnlyCciFilter(bool excludeAttributes = true) - : base(excludeAttributes) - { - } - - // How MDIL affects type visibility: - // - // - A type marked [TreatAsPublicSurface] is treated as public. - // - // - If any member of the type is marked [TreatedAsPublicSurface], the type is treated as public by ApiCompat (but its - // non-[TreatAs] public members are not.) - // - // Note that there may be corner cases where this causes ApiCompat to enforce rules that aren't really necessary - e.g. - // a non-public non-versionable struct that has one [TreatAs] static method will be forbidden from changing its - // layout even though this isn't strictly required by Triton. This is a consequence of the fact that ApiCompat - // is not designed to support non-public types exposing public members. - // - // - public override bool Include(ITypeDefinition type) - { - if (type == null || Dummy.Type == type) - return false; - //if (type.IsTreatedAsVisibleOutsideAssembly()) - // return true; - //if (type.Members.Any(m => m.IsTreatedAsVisibleOutsideAssembly())) - // return true; - return base.Include(type); - } - - // How MDIL affects member visibility: - // - // - A member marked [TreatAsPublicSurface] is treated as public regardless of its own visibility or that of its containing type. - // - public override bool Include(ITypeDefinitionMember member) - { - if (member == null) - return false; - - //if (member.IsTreatedAsVisibleOutsideAssembly()) - // return true; - - return base.Include(member); - } - } -} diff --git a/src/Microsoft.DotNet.ApiCompat/src/Microsoft.DotNet.ApiCompat.Core/Microsoft.DotNet.ApiCompat.Core.csproj b/src/Microsoft.DotNet.ApiCompat/src/Microsoft.DotNet.ApiCompat.Core/Microsoft.DotNet.ApiCompat.Core.csproj deleted file mode 100644 index da3ca78b700..00000000000 --- a/src/Microsoft.DotNet.ApiCompat/src/Microsoft.DotNet.ApiCompat.Core/Microsoft.DotNet.ApiCompat.Core.csproj +++ /dev/null @@ -1,19 +0,0 @@ - - - - $(NetCurrent);$(NetFrameworkToolCurrent) - true - - - - - - - - - - - - - diff --git a/src/Microsoft.DotNet.ApiCompat/src/Microsoft.DotNet.ApiCompat.Core/NamespaceRemappingComparer.cs b/src/Microsoft.DotNet.ApiCompat/src/Microsoft.DotNet.ApiCompat.Core/NamespaceRemappingComparer.cs deleted file mode 100644 index 81cc6036f1d..00000000000 --- a/src/Microsoft.DotNet.ApiCompat/src/Microsoft.DotNet.ApiCompat.Core/NamespaceRemappingComparer.cs +++ /dev/null @@ -1,126 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Collections.Generic; -using System.IO; -using System.Diagnostics; - -namespace Microsoft.Cci.Comparers -{ - public class NamespaceRemappingComparers : CciComparers - { - private List> _mappings = new List>(); - - public NamespaceRemappingComparers(string mappingFile) - { - if (!string.IsNullOrEmpty(mappingFile)) - ParseConfig(mappingFile); - Debug.Assert(_mappings.Count > 0, "NamespaceRemappingComparers has no namespace remappings."); - } - - public override string GetKey(INamespaceDefinition ns) - { - return RemapName(base.GetKey(ns)); - } - - public override string GetKey(ITypeReference type) - { - return RemapName(base.GetKey(type)); - } - - public override string GetKey(ITypeDefinitionMember member) - { - return RemapName(base.GetKey(member)); - } - - private string RemapName(string name) - { - Debug.Assert(name != null); - - Debug.Assert(_mappings.Count > 0, "NamespaceRemappingComparers has no namespace remappings."); - - int maxNumTypesLeft = CountNumTypes(name); - string mappedName = name; - foreach (var mapping in _mappings) - { - // Note that we can have partial matches on type names! Do not rename a type like - // RelativeSourceMode to RelativeSource - we have both names. Similarly, if we have - // a rule like DependencyProperty -> IDependencyProperty, then make sure we don't - // consider IDependencyProperty a match, converting it to "IIDependencyProperty" - if (IsBadTypeNameMatch(mappedName, mapping.Item1)) - { - //Debug.WriteLine("NamespaceRemappingComparer: Avoiding name mapping for {0} because {1} is a partial match.", name, mapping.Item1); - continue; - } - - mappedName = mappedName.Replace(mapping.Item1, mapping.Item2); - - // Perf optimization - assume if we've found what we're trying to replace, we're done and can return. - // However this needs to work for strings that contain multiple types, such as method signatures - // or generic types. We'd replace hopefully each of those types. - if (!Object.ReferenceEquals(mappedName, name)) - { - maxNumTypesLeft--; - if (maxNumTypesLeft == 0) - return mappedName; - } - } - - return mappedName; - } - - private static bool IsBadTypeNameMatch(String name, String mappingName) - { - // Check whether we have more characters afterwards - bool moreAfter = (name.StartsWith(mappingName) && !name.Equals(mappingName) && - (name[mappingName.Length] != '.' && name[mappingName.Length] != ')' && name[mappingName.Length] != '<')); - int indexOfName = name.IndexOf(mappingName); - bool moreBefore = indexOfName > 0 && (name[indexOfName - 1] != '.' && name[indexOfName - 1] != '(' && name[indexOfName - 1] != ' ' && name[indexOfName - 1] != '<'); - return moreAfter || moreBefore; - } - - // Maximum number of types in a type name is 1 + the number of commas, :'s, ('s and <'s. Consider Foo(int x, Bar y). 5 types. - // Or consider G : Base, IFoo - // Note name can be a type name or method signature or generic type or delegate. - private int CountNumTypes(String name) - { - Debug.Assert(name != null); - - // For nested types, I think we only need to map the outer type. But in case it comes up, *you* can think about this. - Debug.Assert(name.IndexOf('+') < 0, "Encountered a nested type. Not sure whether to treat that as two types or one, or to only remap the outer type."); - - int numTypes = 1; - foreach (char c in name) - if (c == ',' || c == '(' || c == '<' || c == ':') - numTypes++; - return numTypes; - } - - private void ParseConfig(string mappingFile) - { - Debug.Assert(!String.IsNullOrEmpty(mappingFile)); - - Debug.Assert(File.Exists(mappingFile), String.Format("Excepting namespace mapping file \"{0}\" to exist.", mappingFile)); - foreach (string mapping in File.ReadAllLines(mappingFile)) - { - if (string.IsNullOrWhiteSpace(mapping) || - mapping.StartsWith("#") || - mapping.StartsWith("//")) - continue; - - string[] split = mapping.Split(','); - - if (split.Length != 2) - { - Debug.WriteLine("ApiCompat NamespaceRemappingComparer: unparsable line found in file {0}. Line: \"{1}\"", mappingFile, mapping); - continue; - } - - _mappings.Add(Tuple.Create(split[0], split[1])); - } - - Debug.Assert(_mappings.Count > 0, "Expected to find namespace mappings in our namespace mapping text file. Is this intentional?"); - } - } -} diff --git a/src/Microsoft.DotNet.ApiCompat/src/Microsoft.DotNet.ApiCompat.Core/PublicEditorBrowsableOnlyCciFilter.cs b/src/Microsoft.DotNet.ApiCompat/src/Microsoft.DotNet.ApiCompat.Core/PublicEditorBrowsableOnlyCciFilter.cs deleted file mode 100644 index 9c162bf9f06..00000000000 --- a/src/Microsoft.DotNet.ApiCompat/src/Microsoft.DotNet.ApiCompat.Core/PublicEditorBrowsableOnlyCciFilter.cs +++ /dev/null @@ -1,56 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Linq; -using Microsoft.Cci; -using Microsoft.Cci.Extensions; -using Microsoft.Cci.Filters; - -namespace Microsoft.DotNet.ApiCompat -{ - // This filter is activated when ApiCompat is used to detect changes that validate public types except those that are not EditorBrowsable. - // - internal class PublicEditorBrowsableOnlyCciFilter : PublicOnlyCciFilter - { - private HashSet _typeExclusions = new HashSet(); - - public PublicEditorBrowsableOnlyCciFilter(bool excludeAttributes = true) - : base(excludeAttributes) - { - } - - public override bool Include(ITypeDefinition type) - { - if (!base.Include(type)) - { - return false; - } - - return !Exclude(type, _typeExclusions); - } - - private static bool Exclude(IReference reference, HashSet exclusions, string alternateName = null) - { - string name = reference.FullName(); - bool excluded = exclusions.Contains(name); - - if (!excluded && alternateName != null) - { - excluded = exclusions.Contains(alternateName); - } - - bool exclude = excluded || (reference.Attributes != null - && reference.Attributes.Any(attribute => attribute.IsEditorBrowseableStateNever())); - - if (exclude && !excluded) - { - exclusions.Add(name); - } - - return exclude; - } - } -} diff --git a/src/Microsoft.DotNet.ApiCompat/src/Microsoft.DotNet.ApiCompat.Core/Rules/Compat/AttributeDifference.cs b/src/Microsoft.DotNet.ApiCompat/src/Microsoft.DotNet.ApiCompat.Core/Rules/Compat/AttributeDifference.cs deleted file mode 100644 index 8f5c2668aac..00000000000 --- a/src/Microsoft.DotNet.ApiCompat/src/Microsoft.DotNet.ApiCompat.Core/Rules/Compat/AttributeDifference.cs +++ /dev/null @@ -1,203 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using Microsoft.Cci.Extensions; -using System.Linq; -using System.Collections.Generic; -using Microsoft.Cci.Mappings; -using System.Composition; -using Microsoft.Cci.Comparers; -using Microsoft.Cci.Filters; - -namespace Microsoft.Cci.Differs.Rules -{ - internal class AssemblyAttributeDifferences : AttributeDifference - { - public override DifferenceType Diff(IDifferences differences, IAssembly impl, IAssembly contract) - { - if (impl == null || contract == null) - return DifferenceType.Unknown; - - bool added = false; - - //added |= AnyAttributeAdded(differences, impl, impl.AssemblyAttributes, contract.AssemblyAttributes); - //added |= AnyAttributeAdded(differences, impl, impl.ModuleAttributes, contract.ModuleAttributes); - //added |= AnySecurityAttributeAdded(differences, impl, impl.SecurityAttributes, contract.SecurityAttributes); - - if (added) - return DifferenceType.Changed; - - return DifferenceType.Unknown; - } - } - - [ExportDifferenceRule] - internal class AttributeDifference : CompatDifferenceRule - { - private readonly MappingSettings _settings = new MappingSettings() - { - Filter = new AttributesFilter(includeAttributes: true) - }; - - [Import] - public IAttributeFilter AttributeFilter { get; set; } - - public override DifferenceType Diff(IDifferences differences, IAssembly impl, IAssembly contract) - { - if (impl == null || contract == null) - return DifferenceType.Unknown; - - bool added = false; - - //added |= AnyAttributeAdded(differences, impl, impl.AssemblyAttributes, contract.AssemblyAttributes); - //added |= AnyAttributeAdded(differences, impl, impl.ModuleAttributes, contract.ModuleAttributes); - //added |= AnySecurityAttributeAdded(differences, impl, impl.SecurityAttributes, contract.SecurityAttributes); - - if (added) - return DifferenceType.Changed; - - return DifferenceType.Unknown; - } - - public override DifferenceType Diff(IDifferences differences, ITypeDefinition impl, ITypeDefinition contract) - { - if (impl == null || contract == null) - return DifferenceType.Unknown; - - bool changed = CheckAttributeDifferences(differences, impl, impl.Attributes, contract.Attributes); - if (impl.IsGeneric) - { - IGenericParameter[] method1GenParams = impl.GenericParameters.ToArray(); - IGenericParameter[] method2GenParam = contract.GenericParameters.ToArray(); - for (int i = 0; i < impl.GenericParameterCount; i++) - changed |= CheckAttributeDifferences(differences, method1GenParams[i], method1GenParams[i].Attributes, method2GenParam[i].Attributes, member: contract); - } - - return changed ? DifferenceType.Changed : DifferenceType.Unchanged; ; - } - - public override DifferenceType Diff(IDifferences differences, ITypeDefinitionMember impl, ITypeDefinitionMember contract) - { - if (impl == null || contract == null) - return DifferenceType.Unknown; - - bool changed = CheckAttributeDifferences(differences, impl, impl.Attributes, contract.Attributes); - - var implMethod = impl as IMethodDefinition; - var contractMethod = contract as IMethodDefinition; - if (implMethod != null && contractMethod != null) - { - IParameterDefinition[] method1Params = implMethod.Parameters.ToArray(); - IParameterDefinition[] method2Params = contractMethod.Parameters.ToArray(); - for (int i = 0; i < implMethod.ParameterCount; i++) - changed |= CheckAttributeDifferences(differences, method1Params[i], method1Params[i].Attributes, method2Params[i].Attributes, member: implMethod); - - if (implMethod.IsGeneric) - { - IGenericParameter[] method1GenParams = implMethod.GenericParameters.ToArray(); - IGenericParameter[] method2GenParam = contractMethod.GenericParameters.ToArray(); - for (int i = 0; i < implMethod.GenericParameterCount; i++) - changed |= CheckAttributeDifferences(differences, method1GenParams[i], method1GenParams[i].Attributes, method2GenParam[i].Attributes, member: implMethod); - } - } - - return changed ? DifferenceType.Changed : DifferenceType.Unchanged; - } - - private bool CheckAttributeDifferences(IDifferences differences, IReference target, IEnumerable implAttributes, IEnumerable contractAttributes, IReference member = null) - { - AttributesMapping> attributeMapping = new AttributesMapping>(_settings); - AttributeComparer attributeComparer = new AttributeComparer(); - attributeMapping.AddMapping(0, contractAttributes.OrderBy(a => a, attributeComparer)); - attributeMapping.AddMapping(1, implAttributes.OrderBy(a => a, attributeComparer)); - - string errString = $"'{target.FullName()}'"; - if (target is IParameterDefinition || target is IGenericParameter) - { - errString = target is IGenericParameter ? "generic param" : "parameter"; - errString += $" '{target.FullName()}' on member '{member?.FullName()}'"; - } - - bool changed = false; - foreach (var group in attributeMapping.Attributes) - { - switch (group.Difference) - { - case DifferenceType.Added: - { - ITypeReference type = group.Representative.Attributes.First().Type; - - if (AttributeFilter.ShouldExclude(type.DocId())) - break; - - // Allow for additions - differences.Add(new Difference("AddedAttribute", - $"Attribute '{type.FullName()}' exists on {errString} in the {Implementation} but not the {Contract}.")); - - changed = true; - break; - } - case DifferenceType.Changed: - { - ITypeReference type = group.Representative.Attributes.First().Type; - - if (AttributeFilter.ShouldExclude(type.DocId())) - break; - - string contractKey = attributeComparer.GetKey(group[0].Attributes.First()); - string implementationKey = attributeComparer.GetKey(group[1].Attributes.First()); - - differences.AddIncompatibleDifference("CannotChangeAttribute", - $"Attribute '{type.FullName()}' on {errString} changed from '{contractKey}' in the {Contract} to '{implementationKey}' in the {Implementation}."); - - changed = true; - break; - } - - case DifferenceType.Removed: - { - ITypeReference type = group.Representative.Attributes.First().Type; - - if (AttributeFilter.ShouldExclude(type.DocId())) - break; - - differences.AddIncompatibleDifference("CannotRemoveAttribute", - $"Attribute '{type.FullName()}' exists on {errString} in the {Contract} but not the {Implementation}."); - - - // removals of an attribute are considered a "change" of the type - changed = true; - break; - } - } - } - return changed; - } - } - - public interface IAttributeFilter - { - bool ShouldExclude(string attributeDocID); - } - - public class AttributeFilter : IAttributeFilter - { - private readonly HashSet ignorableAttributes = new HashSet(); - - public AttributeFilter() - { } - - public void AddIgnoreAttributeFile(string filePath) - { - foreach(string id in DocIdExtensions.ReadDocIds(filePath)) - { - ignorableAttributes.Add(id); - } - } - - public bool ShouldExclude(string attributeDocID) - { - return ignorableAttributes.Contains(attributeDocID); - } - } -} diff --git a/src/Microsoft.DotNet.ApiCompat/src/Microsoft.DotNet.ApiCompat.Core/Rules/Compat/CannotAddAbstractMembers.cs b/src/Microsoft.DotNet.ApiCompat/src/Microsoft.DotNet.ApiCompat.Core/Rules/Compat/CannotAddAbstractMembers.cs deleted file mode 100644 index a730581a50c..00000000000 --- a/src/Microsoft.DotNet.ApiCompat/src/Microsoft.DotNet.ApiCompat.Core/Rules/Compat/CannotAddAbstractMembers.cs +++ /dev/null @@ -1,34 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using Microsoft.Cci.Extensions; -using Microsoft.Cci.Extensions.CSharp; - -namespace Microsoft.Cci.Differs.Rules -{ - [ExportDifferenceRule] - internal class CannotAddAbstractMembers : CompatDifferenceRule - { - public override DifferenceType Diff(IDifferences differences, Mappings.MemberMapping mapping) - { - ITypeDefinitionMember impl = mapping[0]; - ITypeDefinitionMember contract = mapping[1]; - - if (impl == null) - return DifferenceType.Unknown; - - if (contract == null && impl.IsAbstract()) - { - // If the type is effectively sealed then it is ok to remove abstract members - ITypeDefinition contractType = mapping.ContainingType[1]; - // We check that interfaces have the same number of members in another rule so there is no need to check that here. - if (contractType != null && (contractType.IsEffectivelySealed() || (contractType.IsInterface && mapping.ContainingType[0].IsInterface))) - return DifferenceType.Unknown; - - differences.AddIncompatibleDifference(this, impl.GetMemberViolationMessage("Member", $"is abstract in the {Implementation}", $"is missing in the {Contract}")); - return DifferenceType.Changed; - } - return DifferenceType.Unknown; - } - } -} diff --git a/src/Microsoft.DotNet.ApiCompat/src/Microsoft.DotNet.ApiCompat.Core/Rules/Compat/CannotAddAttributes.cs b/src/Microsoft.DotNet.ApiCompat/src/Microsoft.DotNet.ApiCompat.Core/Rules/Compat/CannotAddAttributes.cs deleted file mode 100644 index 7bf9156a25c..00000000000 --- a/src/Microsoft.DotNet.ApiCompat/src/Microsoft.DotNet.ApiCompat.Core/Rules/Compat/CannotAddAttributes.cs +++ /dev/null @@ -1,159 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using Microsoft.Cci.Extensions; -using Microsoft.Cci.Writers.CSharp; -using System.Linq; -using System.Collections.Generic; -using System.Diagnostics; -using Microsoft.Cci.Mappings; - -namespace Microsoft.Cci.Differs.Rules -{ - // @todo: This is still a work-in-progress - suppressing it as it's causing repetition wrt to the Mdil rule that checks for specific custom attributes. - // [ExportDifferenceRule] - internal class CannotAddAttributes : CompatDifferenceRule - { - private MappingSettings _settings = new MappingSettings(); - - public override DifferenceType Diff(IDifferences differences, IAssembly impl, IAssembly contract) - { - if (impl == null || contract == null) - return DifferenceType.Unknown; - - bool added = false; - - added |= AnyAttributeAdded(differences, impl, impl.AssemblyAttributes, contract.AssemblyAttributes); - added |= AnyAttributeAdded(differences, impl, impl.ModuleAttributes, contract.ModuleAttributes); - added |= AnySecurityAttributeAdded(differences, impl, impl.SecurityAttributes, contract.SecurityAttributes); - - if (added) - return DifferenceType.Changed; - - return DifferenceType.Unknown; - } - - public override DifferenceType Diff(IDifferences differences, ITypeDefinition impl, ITypeDefinition contract) - { - if (impl == null || contract == null) - return DifferenceType.Unknown; - - if (AnyAttributeAdded(differences, impl, impl.Attributes, contract.Attributes)) - return DifferenceType.Changed; - - return DifferenceType.Unknown; - } - - public override DifferenceType Diff(IDifferences differences, ITypeDefinitionMember impl, ITypeDefinitionMember contract) - { - if (impl == null || contract == null) - return DifferenceType.Unknown; - - bool added = false; - - added |= AnyAttributeAdded(differences, impl, impl.Attributes, contract.Attributes); - added |= AnyMethodSpecificAttributeAdded(differences, impl as IMethodDefinition, contract as IMethodDefinition); - - if (added) - return DifferenceType.Changed; - - return DifferenceType.Unknown; - } - - private bool AnyMethodSpecificAttributeAdded(IDifferences differences, IMethodDefinition implMethod, IMethodDefinition contractMethod) - { - if (implMethod == null || contractMethod == null) - return false; - - bool added = false; - - added |= AnyAttributeAdded(differences, implMethod, implMethod.ReturnValueAttributes, contractMethod.ReturnValueAttributes); - added |= AnySecurityAttributeAdded(differences, implMethod, implMethod.SecurityAttributes, contractMethod.SecurityAttributes); - - Debug.Assert(implMethod.ParameterCount == contractMethod.ParameterCount); - - IParameterDefinition[] method1Params = implMethod.Parameters.ToArray(); - IParameterDefinition[] method2Params = contractMethod.Parameters.ToArray(); - for (int i = 0; i < implMethod.ParameterCount; i++) - added |= AnyAttributeAdded(differences, method1Params[i], method1Params[i].Attributes, method2Params[i].Attributes); - - return added; - } - - private bool AnySecurityAttributeAdded(IDifferences differences, IReference target, IEnumerable attribues1, IEnumerable attributes2) - { - return AnyAttributeAdded(differences, target, attribues1.SelectMany(a => a.Attributes), attributes2.SelectMany(a => a.Attributes)); - } - - private bool AnyAttributeAdded(IDifferences differences, IReference target, IEnumerable implAttributes, IEnumerable contractAttributes) - { - bool added = false; - - AttributesMapping> attributeMapping = new AttributesMapping>(_settings); - attributeMapping.AddMapping(0, implAttributes); - attributeMapping.AddMapping(1, contractAttributes); - - foreach (var group in attributeMapping.Attributes) - { - switch (group.Difference) - { - case DifferenceType.Added: - ITypeReference type = group.Representative.Attributes.First().Type; - string attribName = type.FullName(); - - if (s_IgnorableAttributes.Contains(attribName)) - break; - - differences.AddIncompatibleDifference(this, - $"Attribute '{attribName}' exists in the {Contract} but not the {Implementation}."); - - added = true; - - break; - case DifferenceType.Changed: - - //TODO: Add some more logic to check the two lists of attributes which have the same type. - break; - - case DifferenceType.Removed: - // Removing attributes is OK - break; - } - } - - return added; - } - - // Ignore list copied from ApiConformance tool - private static HashSet s_IgnorableAttributes = new HashSet { - "System.Reflection.AssemblyFileVersionAttribute", - "System.Reflection.AssemblyInformationalVersionAttribute", - "System.Reflection.AssemblyKeyFileAttribute", - "System.Runtime.AssemblyTargetedPatchBandAttribute", - "System.ObsoleteAttribute", - "System.SupportedPlatformsAttribute", - "System.Reflection.AssemblyProductAttribute", - "System.Resources.SatelliteContractVersionAttribute", - "System.Runtime.CompilerServices.TypeForwardedFromAttribute", - "System.Runtime.CompilerServices.CompilerGeneratedAttribute", - "System.Runtime.TargetedPatchingOptOutAttribute", - "System.ComponentModel.EditorBrowsableAttribute", - "System.Diagnostics.DebuggerDisplayAttribute", // Should this really be ignored? What about cross sku? - "System.Diagnostics.DebuggerTypeProxyAttribute", // Should this really be ignored? What about cross sku? - "System.Diagnostics.DebuggerBrowsableAttribute", // Should this really be ignored? What about cross sku? - "System.Runtime.CompilerServices.FriendAccessAllowedAttribute", // Should this really be ignorable when checking the a previous version of the same sku? - "System.Runtime.CompilerServices.InternalsVisibleToAttribute", // Should this really be ignorable when checking the a previous version of the same sku? - "System.Runtime.CompilerServices.ReferenceAssemblyAttribute", - "System.Security.UnverifiableCodeAttribute", // Ignoring this for now because all the refasms are build with the /unsafe switch even if it isn't necessary - "System.Runtime.CompilerServices.ExtensionAttribute", - - // For now we are ignoring security attributes because they can be different in - // the contract assemblies from the implementation assemblies. At some future time we - // should add a rule to verify that the security is the same in the contracts and implementation. - "System.Security.SecuritySafeCriticalAttribute", - "System.Security.SecurityCriticalAttribute", - "System.Security.AllowPartiallyTrustedCallersAttribute", - "System.Security.SecurityRulesAttribute", - }; - } -} diff --git a/src/Microsoft.DotNet.ApiCompat/src/Microsoft.DotNet.ApiCompat.Core/Rules/Compat/CannotMakeAbstract.cs b/src/Microsoft.DotNet.ApiCompat/src/Microsoft.DotNet.ApiCompat.Core/Rules/Compat/CannotMakeAbstract.cs deleted file mode 100644 index 2a68c3b347f..00000000000 --- a/src/Microsoft.DotNet.ApiCompat/src/Microsoft.DotNet.ApiCompat.Core/Rules/Compat/CannotMakeAbstract.cs +++ /dev/null @@ -1,42 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using Microsoft.Cci.Extensions; -using Microsoft.Cci.Extensions.CSharp; - -namespace Microsoft.Cci.Differs.Rules -{ - [ExportDifferenceRule] - internal class CannotMakeAbstract : CompatDifferenceRule - { - public override DifferenceType Diff(IDifferences differences, ITypeDefinitionMember impl, ITypeDefinitionMember contract) - { - if (impl == null || contract == null) - return DifferenceType.Unknown; - - if (impl.IsAbstract() && !contract.IsAbstract()) - { - differences.AddIncompatibleDifference("CannotMakeMemberAbstract", impl.GetMemberViolationMessage("Member", $"is abstract in the {Implementation}", $"is not abstract in the {Contract}")); - return DifferenceType.Changed; - } - - return DifferenceType.Unknown; - } - - public override DifferenceType Diff(IDifferences differences, ITypeDefinition impl, ITypeDefinition contract) - { - if (impl == null || contract == null) - return DifferenceType.Unknown; - - if (impl.IsAbstract && !contract.IsAbstract) - { - differences.AddIncompatibleDifference("CannotMakeTypeAbstract", - $"Type '{impl.FullName()}' is abstract in the {Implementation} but is not abstract in the {Contract}."); - - return DifferenceType.Changed; - } - - return DifferenceType.Unknown; - } - } -} diff --git a/src/Microsoft.DotNet.ApiCompat/src/Microsoft.DotNet.ApiCompat.Core/Rules/Compat/CannotMakeMoreVisible.cs b/src/Microsoft.DotNet.ApiCompat/src/Microsoft.DotNet.ApiCompat.Core/Rules/Compat/CannotMakeMoreVisible.cs deleted file mode 100644 index d014ae0e025..00000000000 --- a/src/Microsoft.DotNet.ApiCompat/src/Microsoft.DotNet.ApiCompat.Core/Rules/Compat/CannotMakeMoreVisible.cs +++ /dev/null @@ -1,60 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using Microsoft.Cci.Extensions; -using Microsoft.Cci.Extensions.CSharp; -using Microsoft.Cci.Writers.CSharp; - -namespace Microsoft.Cci.Differs.Rules -{ - // Removed because it appears the *MustExist rules already supersede these. - [ExportDifferenceRule] - internal class CannotMakeMoreVisible : CompatDifferenceRule - { - public override DifferenceType Diff(IDifferences differences, ITypeDefinitionMember impl, ITypeDefinitionMember contract) - { - if (impl == null || contract == null) - return DifferenceType.Unknown; - - // If implementation is public then contract can be any visibility - if (impl.Visibility == TypeMemberVisibility.Public) - return DifferenceType.Unknown; - - // If implementation is protected or protected internal then contract must be protected or protected internal as well. - if (impl.Visibility == TypeMemberVisibility.Family || impl.Visibility == TypeMemberVisibility.FamilyOrAssembly) - { - if (contract.Visibility != TypeMemberVisibility.Family && contract.Visibility != TypeMemberVisibility.FamilyOrAssembly) - { - differences.AddIncompatibleDifference(this, - $"Visibility of member '{impl.FullName()}' is '{impl.GetVisibilityName()}' in the {Implementation} but '{contract.GetVisibilityName()}' in the {Contract}."); - return DifferenceType.Changed; - } - } - - return DifferenceType.Unknown; - } - - public override DifferenceType Diff(IDifferences differences, ITypeDefinition impl, ITypeDefinition contract) - { - if (impl == null || contract == null) - return DifferenceType.Unknown; - - // If implementation is public then contract can be any visibility - if (impl.GetVisibility() == TypeMemberVisibility.Public) - return DifferenceType.Unknown; - - // If implementation is protected or protected internal then contract must be protected or protected internal as well. - if (impl.GetVisibility() == TypeMemberVisibility.Family || impl.GetVisibility() == TypeMemberVisibility.FamilyOrAssembly) - { - if (contract.GetVisibility() != TypeMemberVisibility.Family && contract.GetVisibility() != TypeMemberVisibility.FamilyOrAssembly) - { - differences.AddIncompatibleDifference(this, - $"Visibility of type '{impl.FullName()}' is '{impl.GetVisibilityName()}' in the {Implementation} but '{contract.GetVisibilityName()}' in the {Contract}."); - return DifferenceType.Changed; - } - } - - return DifferenceType.Unknown; - } - } -} diff --git a/src/Microsoft.DotNet.ApiCompat/src/Microsoft.DotNet.ApiCompat.Core/Rules/Compat/CannotMakeNonVirtual.cs b/src/Microsoft.DotNet.ApiCompat/src/Microsoft.DotNet.ApiCompat.Core/Rules/Compat/CannotMakeNonVirtual.cs deleted file mode 100644 index 5d2dc4e7ac3..00000000000 --- a/src/Microsoft.DotNet.ApiCompat/src/Microsoft.DotNet.ApiCompat.Core/Rules/Compat/CannotMakeNonVirtual.cs +++ /dev/null @@ -1,58 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using Microsoft.Cci.Extensions; -using Microsoft.Cci.Extensions.CSharp; - -namespace Microsoft.Cci.Differs.Rules -{ - [ExportDifferenceRule] - internal class CannotMakeNonVirtual : CompatDifferenceRule - { - public override DifferenceType Diff(IDifferences differences, ITypeDefinitionMember impl, ITypeDefinitionMember contract) - { - if (impl == null || contract == null) - return DifferenceType.Unknown; - - bool isImplOverridable = IsOverridable(impl); - bool isContractOverridable = IsOverridable(contract); - - /* - //@todo: Move to a separate rule that's only run in "strict" mode. - if (isImplInhertiable && !isContractInheritiable) - { - // This is separate because it can be noisy and is generally allowed as long as it is reviewed properly. - differences.AddIncompatibleDifference("CannotMakeMemberVirtual", - "Member '{0}' is virtual in the implementation but non-virtual in the contract.", - impl.FullName()); - - return DifferenceType.Changed; - } - */ - - if (isContractOverridable && !isImplOverridable) - { - differences.AddIncompatibleDifference("CannotMakeMemberNonVirtual", impl.GetMemberViolationMessage("Member", $"is non-virtual in the {Implementation}", $"is virtual in the {Contract}")); - return DifferenceType.Changed; - } - - return DifferenceType.Unknown; - } - - private bool IsOverridable(ITypeDefinitionMember member) - { - if (!member.IsVirtual()) - return false; - - // member virtual final is not overridable - if (member.IsSealed()) - return false; - - // if member type is Effectively sealed and cannot be extended, then the member cannot be inherited - if (member.ContainingTypeDefinition != null && member.ContainingTypeDefinition.IsEffectivelySealed()) - return false; - - return true; - } - } -} diff --git a/src/Microsoft.DotNet.ApiCompat/src/Microsoft.DotNet.ApiCompat.Core/Rules/Compat/CannotRemoveBaseTypeOrInterface.cs b/src/Microsoft.DotNet.ApiCompat/src/Microsoft.DotNet.ApiCompat.Core/Rules/Compat/CannotRemoveBaseTypeOrInterface.cs deleted file mode 100644 index 9e29f5ab85d..00000000000 --- a/src/Microsoft.DotNet.ApiCompat/src/Microsoft.DotNet.ApiCompat.Core/Rules/Compat/CannotRemoveBaseTypeOrInterface.cs +++ /dev/null @@ -1,75 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Collections.Generic; -using System.Composition; -using Microsoft.Cci.Extensions; - -namespace Microsoft.Cci.Differs.Rules -{ - [ExportDifferenceRule] - internal class CannotRemoveBaseTypeOrInterface : CompatDifferenceRule - { - [Import] - public IEqualityComparer _typeComparer { get; set; } = null; - - public override DifferenceType Diff(IDifferences differences, ITypeDefinition impl, ITypeDefinition contract) - { - if (impl == null || contract == null) - return DifferenceType.Unknown; - - if (AddedBaseType(differences, impl, contract) || - AddedInterface(differences, impl, contract)) - return DifferenceType.Changed; - - return DifferenceType.Unknown; - } - - private bool AddedBaseType(IDifferences differences, ITypeDefinition impl, ITypeDefinition contract) - { - // For interfaces we rely only on the AddedInterface check - if (impl.IsInterface || contract.IsInterface) - return false; - - // Base types must be in the same order so we have to compare them in order - List implBaseTypes = new List(impl.GetAllBaseTypes()); - - int lastIndex = 0; - foreach (var contractBaseType in contract.GetAllBaseTypes()) - { - lastIndex = implBaseTypes.FindIndex(lastIndex, item1BaseType => _typeComparer.Equals(item1BaseType, contractBaseType)); - - if (lastIndex < 0) - { - differences.AddIncompatibleDifference(this, - $"Type '{contract.FullName()}' does not inherit from base type '{contractBaseType.FullName()}' in the {Implementation} but it does in the {Contract}."); - return true; - } - } - - return false; - } - - private bool AddedInterface(IDifferences differences, ITypeDefinition impl, ITypeDefinition contract) - { - // Interfaces can be in any order so use a HashSet - HashSet implInterfaces = new HashSet(impl.GetAllInterfaces(), _typeComparer); - - foreach (var contractInterface in contract.GetAllInterfaces()) - { - // Ignore internal interfaces - if (!contractInterface.IsVisibleOutsideAssembly()) - continue; - - if (!implInterfaces.Contains(contractInterface)) - { - differences.AddIncompatibleDifference(this, - $"Type '{contract.FullName()}' does not implement interface '{contractInterface.FullName()}' in the {Implementation} but it does in the {Contract}."); - return true; - } - } - - return false; - } - } -} diff --git a/src/Microsoft.DotNet.ApiCompat/src/Microsoft.DotNet.ApiCompat.Core/Rules/Compat/CannotRemoveGenerics.cs b/src/Microsoft.DotNet.ApiCompat/src/Microsoft.DotNet.ApiCompat.Core/Rules/Compat/CannotRemoveGenerics.cs deleted file mode 100644 index 9b4defc0406..00000000000 --- a/src/Microsoft.DotNet.ApiCompat/src/Microsoft.DotNet.ApiCompat.Core/Rules/Compat/CannotRemoveGenerics.cs +++ /dev/null @@ -1,97 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using Microsoft.Cci.Extensions; -using System.Linq; -using System.Collections.Generic; - -namespace Microsoft.Cci.Differs.Rules -{ - // @todo: More thinking needed to see whether this is really breaking. - //[ExportDifferenceRule] - internal class CannotRemoveGenerics : CompatDifferenceRule - { - public override DifferenceType Diff(IDifferences differences, ITypeDefinition impl, ITypeDefinition contract) - { - if (impl == null || contract == null) - return DifferenceType.Unknown; - - return DiffConstraints(differences, impl, impl.GenericParameters, contract.GenericParameters); - } - - public override DifferenceType Diff(IDifferences differences, ITypeDefinitionMember impl, ITypeDefinitionMember contract) - { - return Diff(differences, impl as IMethodDefinition, contract as IMethodDefinition); - } - - private DifferenceType Diff(IDifferences differences, IMethodDefinition implMethod, IMethodDefinition contractMethod) - { - if (implMethod == null || contractMethod == null) - return DifferenceType.Unknown; - - return DiffConstraints(differences, implMethod, implMethod.GenericParameters, contractMethod.GenericParameters); - } - - private DifferenceType DiffConstraints(IDifferences differences, IReference target, IEnumerable implGenericParams, IEnumerable contractGenericParams) - { - int beforeCount = differences.Count(); - IGenericParameter[] implParams = implGenericParams.ToArray(); - IGenericParameter[] contractParams = contractGenericParams.ToArray(); - - // We shouldn't hit this because the types/members shouldn't be matched up if they have different generic argument lists - if (implParams.Length != contractParams.Length) - return DifferenceType.Changed; - - for (int i = 0; i < implParams.Length; i++) - { - IGenericParameter implParam = implParams[i]; - IGenericParameter contractParam = contractParams[i]; - - if (contractParam.Variance != TypeParameterVariance.NonVariant && - contractParam.Variance != implParam.Variance) - { - differences.AddIncompatibleDifference("CannotChangeVariance", - $"Variance on generic parameter '{implParam.FullName()}' for '{target.FullName()}' is '{implParam.Variance}' in the {Implementation} but '{contractParam.Variance}' in the {Contract}."); - } - - string implConstraints = string.Join(",", GetConstraints(implParam).OrderBy(s => s, StringComparer.OrdinalIgnoreCase)); - string contractConstraints = string.Join(",", GetConstraints(contractParam).OrderBy(s => s, StringComparer.OrdinalIgnoreCase)); - - if (!string.Equals(implConstraints, contractConstraints)) - { - differences.AddIncompatibleDifference("CannotChangeGenericConstraints", - $"Constraints for generic parameter '{implParam.FullName()}' for '{target.FullName()}' is '{implConstraints}' in the {Implementation} but '{contractConstraints}' in the {Contract}."); - } - } - - if (differences.Count() != beforeCount) - return DifferenceType.Changed; - - return DifferenceType.Unknown; - } - - private IEnumerable GetConstraints(IGenericParameter parameter) - { - if (parameter.MustBeValueType) - yield return "struct"; - else - { - if (parameter.MustBeReferenceType) - yield return "class"; - - if (parameter.MustHaveDefaultConstructor) - yield return "new()"; - } - - foreach (var constraint in parameter.Constraints) - { - // Skip valuetype because we should get it above. - if (TypeHelper.TypesAreEquivalent(constraint, constraint.PlatformType.SystemValueType) && parameter.MustBeValueType) - continue; - - yield return constraint.FullName(); - } - } - } -} diff --git a/src/Microsoft.DotNet.ApiCompat/src/Microsoft.DotNet.ApiCompat.Core/Rules/Compat/CannotSealType.cs b/src/Microsoft.DotNet.ApiCompat/src/Microsoft.DotNet.ApiCompat.Core/Rules/Compat/CannotSealType.cs deleted file mode 100644 index c7c5b7d0f73..00000000000 --- a/src/Microsoft.DotNet.ApiCompat/src/Microsoft.DotNet.ApiCompat.Core/Rules/Compat/CannotSealType.cs +++ /dev/null @@ -1,28 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using Microsoft.Cci.Extensions; -using Microsoft.Cci.Extensions.CSharp; - -namespace Microsoft.Cci.Differs.Rules -{ - [ExportDifferenceRule] - internal class CannotSealType : CompatDifferenceRule - { - public override DifferenceType Diff(IDifferences differences, ITypeDefinition impl, ITypeDefinition contract) - { - if (impl == null || contract == null) - return DifferenceType.Unknown; - - if (impl.IsEffectivelySealed() && !contract.IsEffectivelySealed()) - { - differences.AddIncompatibleDifference(this, - $"Type '{impl.FullName()}' is {(impl.IsSealed ? "actually (has the sealed modifier)" : "effectively (has a private constructor)")} sealed in the {Implementation} but not sealed in the {Contract}."); - - return DifferenceType.Changed; - } - - return DifferenceType.Unknown; - } - } -} diff --git a/src/Microsoft.DotNet.ApiCompat/src/Microsoft.DotNet.ApiCompat.Core/Rules/Compat/CompatDifferenceRule.cs b/src/Microsoft.DotNet.ApiCompat/src/Microsoft.DotNet.ApiCompat.Core/Rules/Compat/CompatDifferenceRule.cs deleted file mode 100644 index ac585a64bef..00000000000 --- a/src/Microsoft.DotNet.ApiCompat/src/Microsoft.DotNet.ApiCompat.Core/Rules/Compat/CompatDifferenceRule.cs +++ /dev/null @@ -1,16 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Composition; - -namespace Microsoft.Cci.Differs.Rules -{ - internal abstract class CompatDifferenceRule : DifferenceRule - { - [Import] - public IDifferenceOperands Operands { get; set; } - - public string Contract => Operands?.Contract ?? "contract"; - public string Implementation => Operands?.Implementation ?? "implementation"; - } -} diff --git a/src/Microsoft.DotNet.ApiCompat/src/Microsoft.DotNet.ApiCompat.Core/Rules/Compat/DelegatesMustMatch.cs b/src/Microsoft.DotNet.ApiCompat/src/Microsoft.DotNet.ApiCompat.Core/Rules/Compat/DelegatesMustMatch.cs deleted file mode 100644 index 11f3a2df33b..00000000000 --- a/src/Microsoft.DotNet.ApiCompat/src/Microsoft.DotNet.ApiCompat.Core/Rules/Compat/DelegatesMustMatch.cs +++ /dev/null @@ -1,89 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Collections.Generic; -using System.Composition; -using System.Diagnostics; -using System.Linq; -using Microsoft.Cci.Extensions; -using Microsoft.Cci.Extensions.CSharp; - -namespace Microsoft.Cci.Differs.Rules -{ - [ExportDifferenceRule] - internal class DelegatesMustMatch : CompatDifferenceRule - { - [Import] - public IEqualityComparer _typeComparer { get; set; } = null; - - public override DifferenceType Diff(IDifferences differences, ITypeDefinition impl, ITypeDefinition contract) - { - if (impl == null || contract == null) - return DifferenceType.Unknown; - - if (!impl.IsDelegate || !contract.IsDelegate) - return DifferenceType.Unknown; - - IMethodDefinition implMethod = impl.GetInvokeMethod(); - IMethodDefinition contractMethod = contract.GetInvokeMethod(); - - Debug.Assert(implMethod != null && contractMethod != null); - - if (!ReturnTypesMatch(differences, implMethod, contractMethod) || - !ParamNamesAndTypesMatch(differences, implMethod, contractMethod)) - return DifferenceType.Changed; - - return DifferenceType.Unknown; - } - - private bool ReturnTypesMatch(IDifferences differences, IMethodDefinition implMethod, IMethodDefinition contractMethod) - { - ITypeReference implReturnType = implMethod.GetReturnType(); - ITypeReference contractReturnType = contractMethod.GetReturnType(); - - if (implReturnType == null || contractReturnType == null) - return true; - - if (!_typeComparer.Equals(implReturnType, contractReturnType)) - { - differences.AddTypeMismatchDifference("DelegateReturnTypesMustMatch", implReturnType, contractReturnType, - $"Return type on delegate '{implMethod.ContainingType.FullName()}' is '{implReturnType.FullName()}' in the {Implementation} but '{contractReturnType.FullName()}' in the {Contract}."); - return false; - } - - return true; - } - - private bool ParamNamesAndTypesMatch(IDifferences differences, IMethodDefinition implMethod, IMethodDefinition contractMethod) - { - int paramCount = implMethod.ParameterCount; - - Debug.Assert(paramCount == contractMethod.ParameterCount); - - IParameterDefinition[] implParams = implMethod.Parameters.ToArray(); - IParameterDefinition[] contractParams = contractMethod.Parameters.ToArray(); - - bool match = true; - for (int i = 0; i < paramCount; i++) - { - IParameterDefinition implParam = implParams[i]; - IParameterDefinition contractParam = contractParams[i]; - - if (!implParam.Name.Value.Equals(contractParam.Name.Value)) - { - differences.AddIncompatibleDifference("DelegateParamNameMustMatch", - $"Parameter name on delegate '{implMethod.ContainingType.FullName()}' is '{implParam.Name.Value}' in the {Implementation} but '{contractParam.Name.Value}' in the {Contract}."); - match = false; - } - - if (!_typeComparer.Equals(implParam.Type, contractParam.Type)) - { - differences.AddTypeMismatchDifference("DelegateParamTypeMustMatch", implParam.Type, contractParam.Type, - $"Type for parameter '{implParam.Name.Value}' on delegate '{implMethod.ContainingType.FullName()}' is '{implParam.Type.FullName()}' in the {Implementation} but '{contractParam.Type.FullName()}' in the {Contract}."); - match = false; - } - } - return match; - } - } -} diff --git a/src/Microsoft.DotNet.ApiCompat/src/Microsoft.DotNet.ApiCompat.Core/Rules/Compat/EnumTypesMustMatch.cs b/src/Microsoft.DotNet.ApiCompat/src/Microsoft.DotNet.ApiCompat.Core/Rules/Compat/EnumTypesMustMatch.cs deleted file mode 100644 index d129d78c166..00000000000 --- a/src/Microsoft.DotNet.ApiCompat/src/Microsoft.DotNet.ApiCompat.Core/Rules/Compat/EnumTypesMustMatch.cs +++ /dev/null @@ -1,38 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Collections.Generic; -using System.Composition; -using Microsoft.Cci.Extensions; -using Microsoft.Cci.Extensions.CSharp; - -namespace Microsoft.Cci.Differs.Rules -{ - [ExportDifferenceRule] - internal class EnumTypesMustMatch : CompatDifferenceRule - { - [Import] - public IEqualityComparer _typeComparer { get; set; } = null; - - public override DifferenceType Diff(IDifferences differences, ITypeDefinition impl, ITypeDefinition contract) - { - if (impl == null || contract == null) - return DifferenceType.Unknown; - - if (!impl.IsEnum || !contract.IsEnum) - return DifferenceType.Unknown; - - ITypeReference implType = impl.GetEnumType(); - ITypeReference contractType = contract.GetEnumType(); - - if (!_typeComparer.Equals(implType, contractType)) - { - differences.AddTypeMismatchDifference(this, implType, contractType, - $"Enum type for '{impl.FullName()}' is '{implType.FullName()}' in {Implementation} but '{contractType.FullName()}' in the {Contract}."); - return DifferenceType.Changed; - } - - return DifferenceType.Unknown; - } - } -} diff --git a/src/Microsoft.DotNet.ApiCompat/src/Microsoft.DotNet.ApiCompat.Core/Rules/Compat/EnumValuesMustMatch.cs b/src/Microsoft.DotNet.ApiCompat/src/Microsoft.DotNet.ApiCompat.Core/Rules/Compat/EnumValuesMustMatch.cs deleted file mode 100644 index d25bf0fb44a..00000000000 --- a/src/Microsoft.DotNet.ApiCompat/src/Microsoft.DotNet.ApiCompat.Core/Rules/Compat/EnumValuesMustMatch.cs +++ /dev/null @@ -1,44 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using Microsoft.Cci.Extensions; -using Microsoft.Cci.Extensions.CSharp; -using System; -using System.Diagnostics; - -namespace Microsoft.Cci.Differs.Rules -{ - [ExportDifferenceRule] - internal class EnumValuesMustMatch : CompatDifferenceRule - { - public override DifferenceType Diff(IDifferences differences, ITypeDefinitionMember impl, ITypeDefinitionMember contract) - { - if (impl == null || contract == null) - return DifferenceType.Unknown; - - if (!impl.ContainingTypeDefinition.IsEnum || !contract.ContainingTypeDefinition.IsEnum) - return DifferenceType.Unknown; - - IFieldDefinition implField = impl as IFieldDefinition; - IFieldDefinition contractField = contract as IFieldDefinition; - - Debug.Assert(implField != null || contractField != null); - - string implValue = Convert.ToString(implField.Constant.Value); - string contractValue = Convert.ToString(contractField.Constant.Value); - - // Calling the toString method to compare in since we might have the case where one Enum is type a and the other is type b, but they might still have same value. - if (implValue != contractValue) - { - ITypeReference implValType = impl.ContainingTypeDefinition.GetEnumType(); - ITypeReference contractValType = contract.ContainingTypeDefinition.GetEnumType(); - - differences.AddIncompatibleDifference(this, - $"Enum value '{implField.FullName()}' is ({implValType.FullName()}){implField.Constant.Value} in the {Implementation} but ({contractValType.FullName()}){contractField.Constant.Value} in the {Contract}."); - return DifferenceType.Changed; - } - - return DifferenceType.Unknown; - } - } -} diff --git a/src/Microsoft.DotNet.ApiCompat/src/Microsoft.DotNet.ApiCompat.Core/Rules/Compat/IDifferenceOperands.cs b/src/Microsoft.DotNet.ApiCompat/src/Microsoft.DotNet.ApiCompat.Core/Rules/Compat/IDifferenceOperands.cs deleted file mode 100644 index b09ff3b955b..00000000000 --- a/src/Microsoft.DotNet.ApiCompat/src/Microsoft.DotNet.ApiCompat.Core/Rules/Compat/IDifferenceOperands.cs +++ /dev/null @@ -1,33 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Microsoft.Cci.Differs -{ - /// - /// Names for the left and right operands of a difference - /// - public interface IDifferenceOperands - { - /// - /// Name of left operand of a difference operation. Typically called a contract or reference. - /// - string Contract { get; } - /// - /// Name of right operand of a difference operation. Typically called an implementation. - /// - string Implementation { get; } - } - - public class DifferenceOperands : IDifferenceOperands - { - public string Contract { get; set; } - - public string Implementation { get; set; } - } -} diff --git a/src/Microsoft.DotNet.ApiCompat/src/Microsoft.DotNet.ApiCompat.Core/Rules/Compat/IRuleSettings.cs b/src/Microsoft.DotNet.ApiCompat/src/Microsoft.DotNet.ApiCompat.Core/Rules/Compat/IRuleSettings.cs deleted file mode 100644 index 4b16919b0ea..00000000000 --- a/src/Microsoft.DotNet.ApiCompat/src/Microsoft.DotNet.ApiCompat.Core/Rules/Compat/IRuleSettings.cs +++ /dev/null @@ -1,27 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Microsoft.Cci.Differs -{ - /// - /// Rule Settings - /// - public interface IRuleSettings - { - /// - /// Gets a value indicating whether DIM rules should be applied. - /// - bool AllowDefaultInterfaceMethods { get; } - } - - public class RuleSettings : IRuleSettings - { - public bool AllowDefaultInterfaceMethods { get; set; } - } -} diff --git a/src/Microsoft.DotNet.ApiCompat/src/Microsoft.DotNet.ApiCompat.Core/Rules/Compat/InterfacesShouldHaveSameMembers.cs b/src/Microsoft.DotNet.ApiCompat/src/Microsoft.DotNet.ApiCompat.Core/Rules/Compat/InterfacesShouldHaveSameMembers.cs deleted file mode 100644 index c68222102b1..00000000000 --- a/src/Microsoft.DotNet.ApiCompat/src/Microsoft.DotNet.ApiCompat.Core/Rules/Compat/InterfacesShouldHaveSameMembers.cs +++ /dev/null @@ -1,98 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using Microsoft.Cci.Extensions; -using Microsoft.Cci.Extensions.CSharp; -using System.Linq; -using System.Collections.Generic; -using System.Composition; -using Microsoft.Cci.Comparers; -using System; - -namespace Microsoft.Cci.Differs.Rules -{ - [ExportDifferenceRule] - internal class InterfacesShouldHaveSameMembers : CompatDifferenceRule - { - [Import] - public IRuleSettings RuleSettings { get; set; } - - public override DifferenceType Diff(IDifferences differences, ITypeDefinitionMember impl, ITypeDefinitionMember contract) - { - if (contract != null && impl == null) - { - if (contract.ContainingTypeDefinition.IsInterface) - { - differences.AddIncompatibleDifference(this, contract.GetMemberViolationMessage($"{GetNameOfInterfaceMemberType(contract)}", $"is present in the {Contract}", $"not in the {Implementation}")); - return DifferenceType.Changed; - } - } - - if (impl != null && contract == null) - { - if (impl.ContainingTypeDefinition.IsInterface && !CanIgnoreAddedInterfaceMember(impl)) - { - differences.AddIncompatibleDifference(this, impl.GetMemberViolationMessage($"{GetNameOfInterfaceMemberType(impl)}", $"is present in the {Implementation}", $"not in the {Contract}")); - return DifferenceType.Changed; - } - } - - return base.Diff(differences, impl, contract); - } - - private string GetNameOfInterfaceMemberType(ITypeDefinitionMember member) - { - return $"{(IsDefaultImplementationMethod(member) ? "Default interface" : "Interface")} member"; - } - - private bool CanIgnoreAddedInterfaceMember(ITypeDefinitionMember member) - { - if (!RuleSettings.AllowDefaultInterfaceMethods) - { - return false; - } - - return IsDefaultImplementationMethod(member); - } - - private bool IsDefaultImplementationMethod(ITypeDefinitionMember member) - { - // Default Implementation Method (DIM) scenario. - // On DIM, static fields or methods that are not abstract, - // have conditional implementation and should not be considered a break - if (member is IFieldDefinition field) - { - return field.IsStatic; - } - - if (member is IMethodDefinition method) - { - return !method.IsAbstract; - } - - // If Getter or Setter exist, verify it is not Abstract - if (member is IPropertyDefinition prop) - { - if ((prop.Getter != null && ((IMethodDefinition)prop.Getter).IsAbstract) || (prop.Setter != null && ((IMethodDefinition)prop.Setter).IsAbstract)) - { - return false; - } - - return true; - } - - // If Adder or Remover exist, verify it is not Abstract - if (member is IEventDefinition evt) - { - if ((evt.Adder != null && ((IMethodDefinition)evt.Adder).IsAbstract) || (evt.Remover != null && ((IMethodDefinition)evt.Remover).IsAbstract)) - { - return false; - } - - return true; - } - - return false; - } - } -} diff --git a/src/Microsoft.DotNet.ApiCompat/src/Microsoft.DotNet.ApiCompat.Core/Rules/Compat/MembersMustExist.cs b/src/Microsoft.DotNet.ApiCompat/src/Microsoft.DotNet.ApiCompat.Core/Rules/Compat/MembersMustExist.cs deleted file mode 100644 index bfee851e55b..00000000000 --- a/src/Microsoft.DotNet.ApiCompat/src/Microsoft.DotNet.ApiCompat.Core/Rules/Compat/MembersMustExist.cs +++ /dev/null @@ -1,140 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Collections.Generic; -using System.Composition; -using System.Linq; -using Microsoft.Cci.Extensions; -using Microsoft.Cci.Extensions.CSharp; -using Microsoft.Cci.Mappings; - -namespace Microsoft.Cci.Differs.Rules -{ - [ExportDifferenceRule] - internal class MembersMustExist : CompatDifferenceRule - { - [Import] - public IEqualityComparer _typeComparer { get; set; } = null; - - public override DifferenceType Diff(IDifferences differences, MemberMapping mapping) - { - ITypeDefinitionMember implMember = mapping[0]; - ITypeDefinitionMember contractMember = mapping[1]; - - if (!(implMember == null && contractMember != null)) - return DifferenceType.Unknown; - - // Nested types are handled separately. - // @TODO: Events and Properties - should we consider these too (or rely on the fact that dropping one of these will also drop their accessors.) - if (!(contractMember is IMethodDefinition || contractMember is IFieldDefinition)) - return DifferenceType.Unknown; - - string incompatibleDifferenceMessage = contractMember.GetMemberViolationMessage("Member", $"does not exist in the {Implementation}", $"it does exist in the {Contract}"); - - ITypeDefinition contractType = mapping.ContainingType[0]; - if (contractType != null) - { - if (contractMember is IMethodDefinition contractMethod) - { - // If the contract is a Explicit Interface method, we don't need to check if the method is in implementation since that will be caught by different rule. - if (contractMethod.IsExplicitInterfaceMethod()) - return DifferenceType.Unknown; - - // It is valid to promote a member from a base type up so check to see if it member exits on a base type. - if (FindMatchingMethodOnBase(contractType, contractMethod)) - { - return DifferenceType.Unknown; - } - } - } - - differences.AddIncompatibleDifference(this, incompatibleDifferenceMessage); - return DifferenceType.Added; - } - - private bool FindMatchingMethodOnBase(ITypeDefinition type, IMethodDefinition method) - { - if (type == null || method.IsConstructor) - { - return false; - } - - foreach (var baseType in type.GetAllBaseTypes()) - { - if (FindMethodInCollection(method, baseType.Methods)) - { - return true; - } - } - - return false; - } - - private bool FindMethodInCollection(IMethodDefinition targetMethod, IEnumerable collectionOfMethods) - { - string targetMethodName = targetMethod.Name.Value; - - foreach (IMethodDefinition potentialMatch in collectionOfMethods) - { - if (targetMethodName == potentialMatch.Name.Value) - { - if (ParameterTypesAreEqual(potentialMatch, targetMethod)) - { - if (!ReturnTypesMatch(potentialMatch, targetMethod)) - { - return false; - } - - if (!targetMethod.IsGeneric && !potentialMatch.IsGeneric) - { - return true; - } - - if (targetMethod.GenericParameterCount == potentialMatch.GenericParameterCount) - { - return true; - } - } - } - } - - return false; - } - - private bool ParameterTypesAreEqual(IMethodDefinition implMethod, IMethodDefinition contractMethod) - { - IParameterDefinition[] params1 = implMethod.Parameters.ToArray(); - IParameterDefinition[] params2 = contractMethod.Parameters.ToArray(); - - if (params1.Length != params2.Length) - return false; - - for (int i = 0; i < params1.Length; i++) - { - IParameterDefinition param1 = params1[i]; - IParameterDefinition param2 = params2[i]; - - if (!_typeComparer.Equals(param1.Type, param2.Type)) - return false; - } - - return true; - } - - public bool ReturnTypesMatch(IMethodDefinition implMethod, IMethodDefinition contractMethod) - { - ITypeReference implType = implMethod.GetReturnType(); - ITypeReference contractType = contractMethod.GetReturnType(); - - if (implType == null || contractType == null) - return true; - - if (!_typeComparer.Equals(implType, contractType)) - { - return false; - } - - return true; - } - } -} diff --git a/src/Microsoft.DotNet.ApiCompat/src/Microsoft.DotNet.ApiCompat.Core/Rules/Compat/ParameterModifiersCannotChange.cs b/src/Microsoft.DotNet.ApiCompat/src/Microsoft.DotNet.ApiCompat.Core/Rules/Compat/ParameterModifiersCannotChange.cs deleted file mode 100644 index bcda21f01c0..00000000000 --- a/src/Microsoft.DotNet.ApiCompat/src/Microsoft.DotNet.ApiCompat.Core/Rules/Compat/ParameterModifiersCannotChange.cs +++ /dev/null @@ -1,161 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using Microsoft.Cci.Comparers; -using Microsoft.Cci.Extensions; -using Microsoft.Cci.Extensions.CSharp; - -namespace Microsoft.Cci.Differs.Rules -{ - // Look for differences in a parameter's marshaling attributes like in, out, & ref, as well as - // potentially custom modifiers like const & volatile. - [ExportDifferenceRule] - internal class ParameterModifiersCannotChange : CompatDifferenceRule - { - public override DifferenceType Diff(IDifferences differences, ITypeDefinitionMember impl, ITypeDefinitionMember contract) - { - if (impl == null || contract == null) - return DifferenceType.Unknown; - - IMethodDefinition method1 = impl as IMethodDefinition; - IMethodDefinition method2 = contract as IMethodDefinition; - - if (method1 == null || method2 == null) - return DifferenceType.Unknown; - - if (!CheckModifiersOnParametersAndReturnValue(differences, method1, method2)) - return DifferenceType.Changed; - - return DifferenceType.Unknown; - } - - private bool CheckModifiersOnParametersAndReturnValue(IDifferences differences, IMethodDefinition implMethod, IMethodDefinition contractMethod) - { - int paramCount = implMethod.ParameterCount; - - Debug.Assert(paramCount == contractMethod.ParameterCount); - - if (paramCount == 0) - return true; - - IParameterDefinition[] implParams = implMethod.Parameters.ToArray(); - IParameterDefinition[] contractParams = contractMethod.Parameters.ToArray(); - - bool match = true; - for (int i = 0; i < paramCount; i++) - { - IParameterDefinition implParam = implParams[i]; - IParameterDefinition contractParam = contractParams[i]; - - //TODO: Do we care about the compatibility with marshalling attributes Out\In? They don't seem to be set consistently. - - if (GetModifier(implParam) != GetModifier(contractParam)) - { - differences.AddIncompatibleDifference(this, - $"Modifiers on parameter '{implParam.Name.Value}' on method '{implMethod.FullName()}' are '{GetModifier(implParam)}' in the {Implementation} but '{GetModifier(contractParam)}' in the {Contract}."); - match = false; - } - - // Now check custom modifiers, primarily focused on const & volatile - if (implParam.IsModified || contractParam.IsModified) - { - var union = implParam.CustomModifiers - .Union(contractParam.CustomModifiers, ModifierComparer.Default); - if (implParam.CustomModifiers.Count() != union.Count()) - { - differences.AddIncompatibleDifference(this, - $"Custom modifiers on parameter '{implParam.Name.Value}' on method '{implMethod.FullName()}' are '{PrintCustomModifiers(implParam.CustomModifiers)}' in the {Implementation} but '{PrintCustomModifiers(contractParam.CustomModifiers)}' in the {Contract}."); - match = false; - } - } - } - - string implReturnModifier = GetReturnValueModifier(implMethod); - string contractReturnModifier = GetReturnValueModifier(contractMethod); - - if (implReturnModifier != contractReturnModifier) - { - differences.AddIncompatibleDifference(this, - $"Modifiers on return type of method '{implMethod.FullName()}' are '{implReturnModifier}' in the {Implementation} but '{contractReturnModifier}' in the {Contract}."); - match = false; - } - - return match; - } - - private string GetModifier(IParameterDefinition parameter) - { - if (parameter.IsOut && !parameter.IsIn && parameter.IsByReference) - { - return "out"; - } - else if (parameter.IsByReference) - { - if (parameter.Attributes.HasIsReadOnlyAttribute()) - { - return "in"; - } - - return "ref"; - } - - return ""; - } - - private String PrintCustomModifiers(IEnumerable modifiers) - { - String s = String.Join(", ", modifiers.Select(m => m.Modifier.FullName())); - if (String.IsNullOrEmpty(s)) - return ""; - return s; - } - - private string GetReturnValueModifier(IMethodDefinition method) - { - string modifier = ""; - if (method.ReturnValueIsByRef) - { - modifier = "ref"; - - if (method.ReturnValueAttributes.HasIsReadOnlyAttribute()) - modifier += " readonly"; - } - return modifier; - } - - private class ModifierComparer : IEqualityComparer - { - private readonly static IEqualityComparer TypeComparer = - CciComparers.Default.GetEqualityComparer(); - - private ModifierComparer() { } - - public static ModifierComparer Default = new ModifierComparer(); - - public bool Equals(ICustomModifier x, ICustomModifier y) - { - if (x == null || y == null) - { - return x == null && y == null; - } - - return x.IsOptional == y.IsOptional && - TypeComparer.Equals(x.Modifier, y.Modifier); - } - - public int GetHashCode(ICustomModifier obj) - { - if (obj == null) - { - return 0; - } - - return (obj.IsOptional ? 1 : 0) | TypeComparer.GetHashCode(obj.Modifier) << 1; - } - } - } -} diff --git a/src/Microsoft.DotNet.ApiCompat/src/Microsoft.DotNet.ApiCompat.Core/Rules/Compat/ParameterNamesCannotChange.cs b/src/Microsoft.DotNet.ApiCompat/src/Microsoft.DotNet.ApiCompat.Core/Rules/Compat/ParameterNamesCannotChange.cs deleted file mode 100644 index 247cd01e26f..00000000000 --- a/src/Microsoft.DotNet.ApiCompat/src/Microsoft.DotNet.ApiCompat.Core/Rules/Compat/ParameterNamesCannotChange.cs +++ /dev/null @@ -1,55 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Diagnostics; -using System.Linq; -using Microsoft.Cci.Extensions; - -namespace Microsoft.Cci.Differs.Rules -{ - [ExportDifferenceRule(OptionalRule = true)] - internal class ParameterNamesCannotChange : CompatDifferenceRule - { - public override DifferenceType Diff(IDifferences differences, ITypeDefinitionMember impl, ITypeDefinitionMember contract) - { - if (impl == null || contract == null) - return DifferenceType.Unknown; - - IMethodDefinition implMethod = impl as IMethodDefinition; - IMethodDefinition contractMethod = contract as IMethodDefinition; - - if (implMethod == null || contractMethod == null) - return DifferenceType.Unknown; - - if (!ParamNamesMatch(differences, implMethod, contractMethod)) - return DifferenceType.Changed; - - return DifferenceType.Unknown; - } - - private bool ParamNamesMatch(IDifferences differences, IMethodDefinition implMethod, IMethodDefinition contractMethod) - { - int paramCount = implMethod.ParameterCount; - - Debug.Assert(paramCount == contractMethod.ParameterCount); - - IParameterDefinition[] implParams = implMethod.Parameters.ToArray(); - IParameterDefinition[] contractParams = contractMethod.Parameters.ToArray(); - - bool match = true; - for (int i = 0; i < paramCount; i++) - { - IParameterDefinition implParam = implParams[i]; - IParameterDefinition contractParam = contractParams[i]; - - if (!implParam.Name.Value.Equals(contractParam.Name.Value)) - { - differences.AddIncompatibleDifference(this, - $"Parameter name on member '{implMethod.FullName()}' is '{implParam.Name.Value}' in the {Implementation} but '{contractParam.Name.Value}' in the {Contract}."); - match = false; - } - } - return match; - } - } -} diff --git a/src/Microsoft.DotNet.ApiCompat/src/Microsoft.DotNet.ApiCompat.Core/Rules/Compat/TypeCannotChangeClassification.cs b/src/Microsoft.DotNet.ApiCompat/src/Microsoft.DotNet.ApiCompat.Core/Rules/Compat/TypeCannotChangeClassification.cs deleted file mode 100644 index 4a3b01b98ad..00000000000 --- a/src/Microsoft.DotNet.ApiCompat/src/Microsoft.DotNet.ApiCompat.Core/Rules/Compat/TypeCannotChangeClassification.cs +++ /dev/null @@ -1,61 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using Microsoft.Cci.Extensions; -using Microsoft.Cci.Extensions.CSharp; - -namespace Microsoft.Cci.Differs.Rules -{ - [ExportDifferenceRule] - internal class TypeCannotChangeClassification : CompatDifferenceRule - { - public override DifferenceType Diff(IDifferences differences, ITypeDefinition impl, ITypeDefinition contract) - { - if (impl == null || contract == null) - return DifferenceType.Unknown; - - string implObjType = GetObjectType(impl); - string contractObjType = GetObjectType(contract); - - if (implObjType != contractObjType) - { - differences.AddIncompatibleDifference(this, - $"Type '{impl.FullName()}' is a '{implObjType}' in the {Implementation} but is a '{contractObjType}' in the {Contract}."); - - return DifferenceType.Changed; - } - - if (contract.Attributes.HasIsReadOnlyAttribute() && !impl.Attributes.HasIsReadOnlyAttribute()) - { - differences.AddIncompatibleDifference(this, - $"Type '{impl.FullName()}' is marked as readonly in the {Contract} so it must also be marked readonly in the {Implementation}."); - - return DifferenceType.Changed; - } - - return DifferenceType.Unknown; - } - - private string GetObjectType(ITypeDefinition type) - { - if (type.IsClass) - return "class"; - - if (type.IsValueType) - { - if (type.Attributes.HasIsByRefLikeAttribute()) - return "ref struct"; - - return "struct"; - } - - if (type.IsInterface) - return "interface"; - - if (type.IsDelegate) - return "delegate"; - - throw new System.NotSupportedException(string.Format("Only support types that are class, struct, or interface. {0}", type.GetType())); - } - } -} diff --git a/src/Microsoft.DotNet.ApiCompat/src/Microsoft.DotNet.ApiCompat.Core/Rules/Compat/TypesMustAlwaysImplementIDisposable.cs b/src/Microsoft.DotNet.ApiCompat/src/Microsoft.DotNet.ApiCompat.Core/Rules/Compat/TypesMustAlwaysImplementIDisposable.cs deleted file mode 100644 index c3c016bdb15..00000000000 --- a/src/Microsoft.DotNet.ApiCompat/src/Microsoft.DotNet.ApiCompat.Core/Rules/Compat/TypesMustAlwaysImplementIDisposable.cs +++ /dev/null @@ -1,37 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using Microsoft.Cci.Extensions; - -namespace Microsoft.Cci.Differs.Rules -{ - // Candidate for strict mode - //[ExportDifferenceRule] - internal class TypesMustAlwaysImplementIDisposable : CompatDifferenceRule - { - public override DifferenceType Diff(IDifferences differences, ITypeDefinition impl, ITypeDefinition contract) - { - if (impl == null || contract == null) - return DifferenceType.Unknown; - - if (ImplementsIDisposable(impl) && !ImplementsIDisposable(contract)) - { - differences.AddIncompatibleDifference(this, - $"Type '{impl.FullName()}' implements IDisposable in the {Implementation} but not the {Contract}."); - return DifferenceType.Changed; - } - - return DifferenceType.Unknown; - } - - private bool ImplementsIDisposable(ITypeDefinition type) - { - foreach (ITypeReference iface in type.Interfaces) - { - if (iface.AreEquivalent("System.IDisposable")) - return true; - } - return false; - } - } -} diff --git a/src/Microsoft.DotNet.ApiCompat/src/Microsoft.DotNet.ApiCompat.Core/Rules/Compat/TypesMustExist.cs b/src/Microsoft.DotNet.ApiCompat/src/Microsoft.DotNet.ApiCompat.Core/Rules/Compat/TypesMustExist.cs deleted file mode 100644 index 03402eb2543..00000000000 --- a/src/Microsoft.DotNet.ApiCompat/src/Microsoft.DotNet.ApiCompat.Core/Rules/Compat/TypesMustExist.cs +++ /dev/null @@ -1,50 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using Microsoft.Cci.Extensions; - -namespace Microsoft.Cci.Differs.Rules -{ - [ExportDifferenceRule] - internal class TypesMustExist : CompatDifferenceRule - { - public override DifferenceType Diff(IDifferences differences, ITypeDefinition impl, ITypeDefinition contract) - { - if (impl == null && contract != null) - { - if (!ReportAsMembersMustExist(contract, differences)) - { - differences.AddIncompatibleDifference(this, - $"Type '{contract.FullName()}' does not exist in the {Implementation} but it does exist in the {Contract}."); - } - - return DifferenceType.Added; - } - - return DifferenceType.Unknown; - } - - // Usability hack: Ordinarily, removing a type does not trigger secondary messages about removing its members. Normally, - // this is very useful behavior. However, this backfires in the case where a type "vanishes" solely because someone removed - // its last [TreatAsPublicSurface] member. [TreatAsPublicSurface] is the only known case where one can "remove" a type merely - // by changing something about one of its members - something that has no precedent in the minds of most developers. - // Reporting the violation as a removal of the type would be quite unhelpful in this case. Thus, if the "removed" type - // had one or more [TreatAsPublicSurface] members, we counterfeit a MembersMustExist message. - private bool ReportAsMembersMustExist(ITypeDefinition contract, IDifferences differences) - { - bool specialCasedViolation = false; - //foreach (ITypeDefinitionMember member in contract.Members) - //{ - // if (member.MarkedAsPublicSurface()) - // { - // differences.AddIncompatibleDifference( - // "MembersMustExist", - // $"Member '{member.FullName()}' does not exist in the {Implementation} but it does exist in the {Contract}."); - // specialCasedViolation = true; - // } - //} - return specialCasedViolation; - } - } -} - diff --git a/src/Microsoft.DotNet.ApiCompat/src/Microsoft.DotNet.ApiCompat.Core/Rules/InheritanceHierarchyChangeTracker.cs b/src/Microsoft.DotNet.ApiCompat/src/Microsoft.DotNet.ApiCompat.Core/Rules/InheritanceHierarchyChangeTracker.cs deleted file mode 100644 index e1b6aba1e48..00000000000 --- a/src/Microsoft.DotNet.ApiCompat/src/Microsoft.DotNet.ApiCompat.Core/Rules/InheritanceHierarchyChangeTracker.cs +++ /dev/null @@ -1,108 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Collections.Generic; -using System.Composition; -using System.Diagnostics; -using System.Linq; -using Microsoft.Cci; -using Microsoft.Cci.Differs; -using Microsoft.Cci.Extensions; - -namespace Microsoft.DotNet.ApiCompat.Rules -{ - // This rule ensures the inheritance hierarchy didn't change in drastic ways. - // Our goal is to create a subset of two Frameworks that essentially looks like an older - // version of both. So all the normal versioning rules must apply from the subset to - // both Frameworks. For types, this means we can from the subset we can add new base classes - // to represent either Framework, but the changes we make must both be compatible with each - // Framework. Essentially, we can add base classes and we can remove base classes, - // but we can't do both at the same time. (In reality, we can both add and remove base classes - // at the same time, but one of those must inherit from the other.) - //[ExportDifferenceRule(NonAPIConformanceRule = true)] // does not enforce that one set is a subset of the other - not appropriate for version-to-version API conformance. - internal class InheritanceHierarchyChangeTracker : DifferenceRule - { - [Import] - public IEqualityComparer _typeComparer { get; set; } = null; - - // Consider the following object hierarchy. Remember we are not enforcing a subset relationship - // on both types. Our goal is to build a third API that is a subset of both, with versioning rules - // that will produce a consistent universe with both new frameworks. - // Object - // Fruit Shape - // Apple - // Red Delicious - // - // 1) A type is not reparented across the hierarchy. (ie, Apple may subclass Object, but may not subclass Shape) - // 2) Removing a type like Apple from the inheritance hierarchy is legal, BUT all new methods on Apple must be - // duplicated on Red Delicious. (Methods will be tracked in a separate rule) - // 3) If you removed a type like Apple, then all overridden methods from Fruit & higher should be - // re-overridden by Red Delicious to ensure behavior is compatible. (methods will be tracked in a separate rule) - // 4) It is legal to add a type like Fruit into the hierarchy - consider this the reverse of #2. - // 5) If one Framework had Object -> NewType -> Apple, that is only legal if NewType is in the other - // Framework's inheritance hierarchy somewhere between Object and Apple. IE, NewType could be - // "Food" or "RoundFruit", but could not be "Shape" - public override DifferenceType Diff(IDifferences differences, ITypeDefinition item1, ITypeDefinition item2) - { - if (item1 == null || item2 == null) - return DifferenceType.Unknown; - - Debug.Assert(_typeComparer != null); - - IEnumerable item1BaseClassChain = GetBaseClassChain(item1); - IEnumerable item2BaseClassChain = GetBaseClassChain(item2); - - bool added = item2BaseClassChain.Except(item1BaseClassChain, _typeComparer).Any(t => true); - bool removed = item1BaseClassChain.Except(item2BaseClassChain, _typeComparer).Any(t => true); - - // To a first approximation, we cannot both add base types and remove base types. - // IE, we cannot take an Apple, remove Fruit and add Shape. - // However there are more pathologically complicated inheritance hierarchies that are linear but where we - // add one type & remove another. If both additions & removals occur, they're only legal if one of those - // added or removed types subclasses the other one. We do not currently check for that. - if (added && removed) - { - // Special case for DependencyObject and its derived types - if (item1BaseClassChain.Any((type) => TypeHelper.GetTypeName(type) == "System.Windows.DependencyObject") && - item2BaseClassChain.Any((type) => TypeHelper.GetTypeName(type) == "Windows.UI.DirectUI.DependencyObject")) - { - // If the new type name is the same as the old type name in a new namespace, let's consider that a known issue. - String oldBaseTypeName = TypeHelper.GetTypeName(item1BaseClassChain.First()); - String newBaseTypeName = TypeHelper.GetTypeName(item2BaseClassChain.First()); - oldBaseTypeName = oldBaseTypeName.Replace("System.Windows", "Windows.UI.DirectUI"); - if (oldBaseTypeName == newBaseTypeName) - return DifferenceType.Unknown; - } - - differences.AddIncompatibleDifference(this, - "Type {0} or one of its base classes was reparented in an incompatible way. Old base classes: {1} New Base Classes: {2}", - item1.FullName(), PrintClassHierarchy(item1BaseClassChain), PrintClassHierarchy(item2BaseClassChain)); - return DifferenceType.Changed; - } - - return DifferenceType.Unknown; - } - - // Does not include this type. - private IList GetBaseClassChain(ITypeDefinition type) - { - List bases = new List(); - ITypeDefinition t = type; - while (t != null) - { - t = t.BaseClasses.SingleOrDefault().GetDefinitionOrNull(); // If there are multiple base classes, update rule to handle multiple inheritance. - if (t != null) - bases.Add(t); - } - return bases.ToArray(); - } - - private String PrintClassHierarchy(IEnumerable baseClasses) - { - Debug.Assert(baseClasses != null); - - return String.Join(", ", baseClasses.Reverse().Select(t => t.FullName())); - } - } -} diff --git a/src/Microsoft.DotNet.ApiCompat/src/Microsoft.DotNet.ApiCompat.Core/Rules/ParameterTypeCannotChange.cs b/src/Microsoft.DotNet.ApiCompat/src/Microsoft.DotNet.ApiCompat.Core/Rules/ParameterTypeCannotChange.cs deleted file mode 100644 index f0e0570e48f..00000000000 --- a/src/Microsoft.DotNet.ApiCompat/src/Microsoft.DotNet.ApiCompat.Core/Rules/ParameterTypeCannotChange.cs +++ /dev/null @@ -1,81 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using Microsoft.Cci.Extensions; -using Microsoft.Cci.Mappings; - -namespace Microsoft.Cci.Differs.Rules -{ - // This rule only helps try to match up methods where a return type changed on one or the other. - //[ExportDifferenceRule(NonAPIConformanceRule=true)] - internal class ParameterTypeCannotChange : DifferenceRule - { - // TODO: Add support for property parameter lists - public override DifferenceType Diff(IDifferences differences, MemberMapping mapping) - { - IMethodDefinition method1 = mapping[0] as IMethodDefinition; - IMethodDefinition method2 = mapping[1] as IMethodDefinition; - - if (method1 == null && method2 == null) - return DifferenceType.Unknown; - - if (method1 != null && method2 != null) - return DifferenceType.Unknown; - - if (method1 != null) - { - IMethodDefinition match = FindBestMatch(method1, mapping.ContainingType, 1, 0); - - if (match != null) - { - differences.AddIncompatibleDifference(this, - "Cannot change parameter types for method {0} and {1}", method1.GetMethodSignature(), match.GetMethodSignature()); - return DifferenceType.Changed; - } - } - - if (method2 != null) - { - IMethodDefinition match = FindBestMatch(method2, mapping.ContainingType, 0, 1); - - if (match != null) - { - differences.AddIncompatibleDifference(this, - "Cannot change parameter types for method {0} and {1}", method2.GetMethodSignature(), match.GetMethodSignature()); - return DifferenceType.Changed; - } - } - - return DifferenceType.Unknown; - } - - private IMethodDefinition FindBestMatch(IMethodDefinition matchMethod, TypeMapping mapping, int typeIndex, int memberIndex) - { - // No matches if we don't have a matching type. - if (mapping[typeIndex] == null) - return null; - - foreach (IMethodDefinition method in mapping[typeIndex].Methods) - { - if (method.Name.Value != matchMethod.Name.Value) continue; - - if (method.ParameterCount != matchMethod.ParameterCount) continue; - - if (method.IsGeneric && matchMethod.IsGeneric && - method.GenericParameterCount != matchMethod.GenericParameterCount) - continue; - - MemberMapping member = mapping.FindMember(method); - - // It is possible to find a match that was filtered at the mapping layer - if (member == null) continue; - - // If the other member also doesn't have a match then this is our best match - if (member[memberIndex] == null) - return method; - } - - return null; - } - } -} diff --git a/src/Microsoft.DotNet.ApiCompat/src/Microsoft.DotNet.ApiCompat.csproj b/src/Microsoft.DotNet.ApiCompat/src/Microsoft.DotNet.ApiCompat.csproj deleted file mode 100644 index e9da26b2d67..00000000000 --- a/src/Microsoft.DotNet.ApiCompat/src/Microsoft.DotNet.ApiCompat.csproj +++ /dev/null @@ -1,35 +0,0 @@ - - - - $(NetCurrent);$(NetFrameworkToolCurrent) - true - true - $(NoWarn);0436 - Major - Microsoft.DotNet.ApiCompat.Core\**\* - - Exe - true - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/Microsoft.DotNet.ApiCompat/src/OutputHelper.cs b/src/Microsoft.DotNet.ApiCompat/src/OutputHelper.cs deleted file mode 100644 index efdec71497b..00000000000 --- a/src/Microsoft.DotNet.ApiCompat/src/OutputHelper.cs +++ /dev/null @@ -1,44 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Diagnostics; -using System.IO; - -namespace Microsoft.DotNet.ApiCompat -{ - internal class OutputHelper - { - /// - /// Opens a stream to a given file path and returns it. - /// - /// The file path to open a stream to. - /// The opened TextWriter that points to the passed in file. - /// Returns true when the output was opened or created successfully. - public static bool TryGetOutput(string outFilePath, out TextWriter writer) - { - if (string.IsNullOrWhiteSpace(outFilePath)) - throw new ArgumentNullException(nameof(outFilePath)); - - const int NumRetries = 10; - string exceptionMessage = null; - for (int retries = 0; retries < NumRetries; retries++) - { - try - { - writer = new StreamWriter(File.OpenWrite(outFilePath)); - return true; - } - catch (Exception e) - { - exceptionMessage = e.Message; - System.Threading.Thread.Sleep(100); - } - } - - Trace.TraceError("Cannot open output file '{0}': {1}", outFilePath, exceptionMessage); - writer = null; - return false; - } - } -} diff --git a/src/Microsoft.DotNet.ApiCompat/src/Program.cs b/src/Microsoft.DotNet.ApiCompat/src/Program.cs deleted file mode 100644 index 99a9eaa0fdb..00000000000 --- a/src/Microsoft.DotNet.ApiCompat/src/Program.cs +++ /dev/null @@ -1,13 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace Microsoft.DotNet.ApiCompat -{ - public class Program - { - public static int Main(string[] args) - { - return new ApiCompatRunner().Run(args); - } - } -} diff --git a/src/Microsoft.DotNet.ApiCompat/src/build/Microsoft.DotNet.ApiCompat.targets b/src/Microsoft.DotNet.ApiCompat/src/build/Microsoft.DotNet.ApiCompat.targets deleted file mode 100644 index 1ace45dfe49..00000000000 --- a/src/Microsoft.DotNet.ApiCompat/src/build/Microsoft.DotNet.ApiCompat.targets +++ /dev/null @@ -1,116 +0,0 @@ - - - - - $(MSBuildThisFileDirectory)..\tools\net9.0\Microsoft.DotNet.ApiCompat.dll - $(MSBuildThisFileDirectory)..\tools\net472\Microsoft.DotNet.ApiCompat.exe - - - true - $(RunApiCompat) - false - - <_ApiCompatSemaphoreFile>$(MSBuildThisFileName).semaphore - - - - $(TargetsTriggeredByCompilation);ValidateApiCompatForSrc - $(TargetsTriggeredByCompilation);RunMatchingRefApiCompat - - - - - - - - - - - - - - - - - - <_DependencyDirectoriesTemp Include="@(ReferencePath -> '%(RootDir)%(Directory)')" /> - - - <_DependencyDirectories Condition="'%(_DependencyDirectoriesTemp.ReferenceSourceTarget)' == 'ProjectReference'" Include="%(_DependencyDirectoriesTemp.Identity)" /> - <_DependencyDirectories Condition="'%(_DependencyDirectoriesTemp.ReferenceSourceTarget)' != 'ProjectReference'" Include="%(_DependencyDirectoriesTemp.Identity)" /> - <_ContractDependencyDirectories Include="@(ResolvedMatchingContract -> '%(RootDir)%(Directory)')" /> - <_ContractDependencyDirectories Include="@(ResolvedMatchingContract -> '%(DependencyPaths)')" /> - <_ContractDependencyDirectories Include="$(ContractDependencyPaths)" /> - - - - - - - - - - - - - - - - - - - - - - - - <_ContractDependencyDirectoriesTemp Include="@(ReferencePath -> '%(RootDir)%(Directory)')" /> - - - <_ContractDependencyDirectories Remove="@(_ContractDependencyDirectories)" /> - <_ContractDependencyDirectories Condition="'%(_ContractDependencyDirectoriesTemp.ReferenceSourceTarget)' == 'ProjectReference'" Include="%(_ContractDependencyDirectoriesTemp.Identity)" /> - <_ContractDependencyDirectories Condition="'%(_ContractDependencyDirectoriesTemp.ReferenceSourceTarget)' != 'ProjectReference'" Include="%(_ContractDependencyDirectoriesTemp.Identity)" /> - <_ImplementationDependencyDirectories Include="@(ResolvedMatchingContract -> '%(RootDir)%(Directory)')" /> - <_ImplementationDependencyDirectories Include="@(ResolvedMatchingContract -> '%(DependencyPaths)')" /> - <_ImplementationDependencyDirectories Include="$(ContractDependencyPaths)" /> - - - - - - - - - - - - - - - - - diff --git a/src/Microsoft.DotNet.ApiCompat/tests/ApiCompatFrontend.cs b/src/Microsoft.DotNet.ApiCompat/tests/ApiCompatFrontend.cs deleted file mode 100644 index 8e8dbaaf18a..00000000000 --- a/src/Microsoft.DotNet.ApiCompat/tests/ApiCompatFrontend.cs +++ /dev/null @@ -1,11 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace Microsoft.DotNet.ApiCompat.Tests -{ - public enum ApiCompatFrontend - { - Console, - MSBuildTask - } -} diff --git a/src/Microsoft.DotNet.ApiCompat/tests/AttributeDifferenceTests.cs b/src/Microsoft.DotNet.ApiCompat/tests/AttributeDifferenceTests.cs deleted file mode 100644 index dc9ca6a4faa..00000000000 --- a/src/Microsoft.DotNet.ApiCompat/tests/AttributeDifferenceTests.cs +++ /dev/null @@ -1,62 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.IO; -using Xunit; - -namespace Microsoft.DotNet.ApiCompat.Tests -{ - public class AttributeDifferenceTests - { - private readonly string _implementationPath = Path.Combine(AppContext.BaseDirectory, "Implementation", "AttributeDifference.dll"); - private readonly string _contractPath = Path.Combine(AppContext.BaseDirectory, "Contract"); - - [Theory] - [InlineData(ApiCompatFrontend.Console)] - [InlineData(ApiCompatFrontend.MSBuildTask)] - public void AttributeDifferenceIsFound(ApiCompatFrontend frontend) - { - string runOutput = Helpers.RunApiCompat(_implementationPath, _contractPath, "implementation", "contract", frontend); - - Assert.Contains("CannotRemoveAttribute : Attribute 'System.ComponentModel.DesignerAttribute' exists on 'AttributeDifference.AttributeDifferenceClass1' in the implementation but not the contract.", runOutput); - Assert.Contains("CannotRemoveAttribute : Attribute 'AttributeDifference.FooAttribute' exists on 'AttributeDifference.AttributeDifferenceClass1' in the implementation but not the contract.", runOutput); - Assert.Contains("CannotRemoveAttribute : Attribute 'AttributeDifference.FooAttribute' exists on 'AttributeDifference.AttributeDifferenceClass1.PropertyWithAttribute' in the implementation but not the contract.", runOutput); - Assert.Contains("CannotRemoveAttribute : Attribute 'AttributeDifference.FooAttribute' exists on 'AttributeDifference.AttributeDifferenceClass1.EventWithAttribute' in the implementation but not the contract.", runOutput); - Assert.Contains("CannotRemoveAttribute : Attribute 'System.ComponentModel.DefaultValueAttribute' exists on generic param 'T' on member 'AttributeDifference.AttributeDifferenceClass1.GenericMethodWithAttribute()' in the implementation but not the contract.", runOutput); - Assert.Contains("CannotRemoveAttribute : Attribute 'System.ComponentModel.DefaultValueAttribute' exists on generic param 'T' on member 'AttributeDifference.AttributeDifferenceClass1.GenericMethodWithAttribute()' in the implementation but not the contract.", runOutput); - Assert.Contains("CannotRemoveAttribute : Attribute 'AttributeDifference.FooAttribute' exists on 'AttributeDifference.AttributeDifferenceClass1.MethodWithAttribute()' in the implementation but not the contract.", runOutput); - Assert.Contains("CannotRemoveAttribute : Attribute 'AttributeDifference.FooAttribute' exists on parameter 'myParameter' on member 'AttributeDifference.AttributeDifferenceClass1.MethodWithAttribute(System.String, System.Object)' in the implementation but not the contract.", runOutput); - Assert.Contains("CannotRemoveAttribute : Attribute 'System.ComponentModel.DefaultValueAttribute' exists on generic param 'TOne' on member 'AttributeDifference.AttributeDifferenceGenericCLass' in the implementation but not the contract.", runOutput); - Assert.Contains("Total Issues: 8", runOutput); - } - - [Theory] - [InlineData(ApiCompatFrontend.Console)] - [InlineData(ApiCompatFrontend.MSBuildTask)] - public void AttributeDifferenceIsFoundWithExcludeAttributesFile(ApiCompatFrontend frontend) - { - using TempFile excludeAttributesFile = TempFile.Create(); - - File.WriteAllLines(excludeAttributesFile.Path, new string[] { "T:System.ComponentModel.DisplayNameAttribute", "T:AttributeDifference.FooAttribute" }); - - string runOutput = Helpers.RunApiCompat(_implementationPath, new string[] { _contractPath }, new string[] { excludeAttributesFile.Path }, "implementation", "contract", frontend); - Assert.Contains("CannotRemoveAttribute : Attribute 'System.ComponentModel.DesignerAttribute' exists on 'AttributeDifference.AttributeDifferenceClass1' in the implementation but not the contract.", runOutput); - Assert.Contains("Total Issues: 3", runOutput); - } - - [Theory] - [InlineData(ApiCompatFrontend.Console)] - [InlineData(ApiCompatFrontend.MSBuildTask)] - public void NoIssuesWithExcludeAttributesFile(ApiCompatFrontend frontend) - { - using TempFile excludeAttributesFile = TempFile.Create(); - - File.WriteAllLines(excludeAttributesFile.Path, new string[] { "T:System.ComponentModel.DesignerAttribute", "T:AttributeDifference.FooAttribute", "T:System.ComponentModel.DefaultValueAttribute" }); - - string runOutput = Helpers.RunApiCompat(_implementationPath, new string[] { _contractPath }, new string[] { excludeAttributesFile.Path }, null, null, frontend); - - Assert.Contains("Total Issues: 0", runOutput); - } - } -} diff --git a/src/Microsoft.DotNet.ApiCompat/tests/Helpers.cs b/src/Microsoft.DotNet.ApiCompat/tests/Helpers.cs deleted file mode 100644 index d2280218b76..00000000000 --- a/src/Microsoft.DotNet.ApiCompat/tests/Helpers.cs +++ /dev/null @@ -1,75 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Collections.Generic; -using System.IO; -using System.Linq; - -namespace Microsoft.DotNet.ApiCompat.Tests -{ - public static class Helpers - { - public static string RunApiCompat(string left, string rightDirs, ApiCompatFrontend frontend) => RunApiCompat(left, rightDirs, null, null, frontend); - - public static string RunApiCompat(string left, string rightDirs, string leftName, string rightName, ApiCompatFrontend frontend) => RunApiCompat(left, new string[] { rightDirs }, Enumerable.Empty(), leftName, rightName, frontend); - - public static string RunApiCompat(string left, IEnumerable rightDirs, IEnumerable excludeAttributesFiles, string leftName, string rightName, ApiCompatFrontend frontend) - { - using var writer = new StringWriter(); - string frameworkRuntimePath = Path.GetDirectoryName(typeof(object).Assembly.Location); - - if (frontend == ApiCompatFrontend.Console) - { - string[] args = GetApiCompatArgs(left, rightDirs, excludeAttributesFiles, leftName, rightName, frameworkRuntimePath); - new ApiCompatRunner(writer).Run(args); - } - else if (frontend == ApiCompatFrontend.MSBuildTask) - { - new ApiCompatTask(writer) - { - Contracts = new string[] { left }, - ImplementationDirectories = rightDirs - .Concat(new string[] { frameworkRuntimePath }).ToArray(), - ContractDepends = new string[] { frameworkRuntimePath }, - LeftOperand = leftName, - RightOperand = rightName, - ExcludeAttributes = excludeAttributesFiles?.ToArray() - }.Execute(); - } - - return writer.ToString(); - } - - private static string[] GetApiCompatArgs(string left, IEnumerable rightDirs, IEnumerable excludeAttributesFiles, string leftName, string rightName, string frameworkRuntimePath) - { - List args = new() - { - left, - "-i", - $"{string.Join(",", rightDirs.ToArray())},{frameworkRuntimePath}", - "--contract-depends", - frameworkRuntimePath - }; - - if (!string.IsNullOrEmpty(leftName)) - { - args.Add("-l"); - args.Add(leftName); - } - - if (!string.IsNullOrEmpty(rightName)) - { - args.Add("-r"); - args.Add(rightName); - } - - if (excludeAttributesFiles.Count() > 0) - { - args.Add("--exclude-attributes"); - args.Add(string.Join(",", excludeAttributesFiles)); - } - - return args.ToArray(); - } - } -} diff --git a/src/Microsoft.DotNet.ApiCompat/tests/Microsoft.DotNet.ApiCompat.Tests.csproj b/src/Microsoft.DotNet.ApiCompat/tests/Microsoft.DotNet.ApiCompat.Tests.csproj deleted file mode 100644 index 9ffc5ab968c..00000000000 --- a/src/Microsoft.DotNet.ApiCompat/tests/Microsoft.DotNet.ApiCompat.Tests.csproj +++ /dev/null @@ -1,27 +0,0 @@ - - - - $(NetCurrent);$(NetFrameworkToolCurrent) - false - - - - - - - - - <_testProjectHelper Include="TestProjects\**\*.csproj" /> - <_testProjectHelper Update="@(_testProjectHelper)"> - Implementation\ - Contract\ - - - - - - - - - - diff --git a/src/Microsoft.DotNet.ApiCompat/tests/TempFile.cs b/src/Microsoft.DotNet.ApiCompat/tests/TempFile.cs deleted file mode 100644 index 6908b94864b..00000000000 --- a/src/Microsoft.DotNet.ApiCompat/tests/TempFile.cs +++ /dev/null @@ -1,48 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Runtime.CompilerServices; - -namespace System.IO -{ - /// - /// Represents a temporary file. Creating an instance creates a file at the specified path, - /// and disposing the instance deletes the file. - /// - public sealed class TempFile : IDisposable - { - /// Gets the created file's path. - public string Path { get; } - - public TempFile(string path) - { - Path = path; - } - - ~TempFile() => DeleteFile(); - - public static TempFile Create([CallerMemberName] string memberName = null, [CallerLineNumber] int lineNumber = 0) - { - return new TempFile(GetFilePath($"{IO.Path.GetRandomFileName()}_{memberName}_{lineNumber}")); - } - - public void Dispose() - { - GC.SuppressFinalize(this); - DeleteFile(); - } - - private void DeleteFile() - { - try { File.Delete(Path); } - catch { /* Ignore exceptions on disposal paths */ } - } - - private static string GetFilePath(string fileName) - { - string directory = IO.Path.Combine(IO.Path.GetTempPath(), IO.Path.GetRandomFileName()); - Directory.CreateDirectory(directory); - return IO.Path.Combine(directory, fileName); - } - } -} diff --git a/src/Microsoft.DotNet.ApiCompat/tests/TestProjects/AttributeDifference/Contract/AttributeDifference.Contract.csproj b/src/Microsoft.DotNet.ApiCompat/tests/TestProjects/AttributeDifference/Contract/AttributeDifference.Contract.csproj deleted file mode 100644 index 82f57cb7ace..00000000000 --- a/src/Microsoft.DotNet.ApiCompat/tests/TestProjects/AttributeDifference/Contract/AttributeDifference.Contract.csproj +++ /dev/null @@ -1,9 +0,0 @@ - - - - netstandard2.0 - false - AttributeDifference - - - diff --git a/src/Microsoft.DotNet.ApiCompat/tests/TestProjects/AttributeDifference/Contract/AttributeDifference.cs b/src/Microsoft.DotNet.ApiCompat/tests/TestProjects/AttributeDifference/Contract/AttributeDifference.cs deleted file mode 100644 index 0f0a4af3ac6..00000000000 --- a/src/Microsoft.DotNet.ApiCompat/tests/TestProjects/AttributeDifference/Contract/AttributeDifference.cs +++ /dev/null @@ -1,20 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.ComponentModel; - -namespace AttributeDifference -{ - [DisplayName("Attribute difference class1")] - public class AttributeDifferenceClass1 - { - public string MethodWithAttribute(string myParameter, [DefaultValue("myObject")] object myObject) => throw null; - public T GenericMethodWithAttribute() => throw null; - public void MethodWithAttribute() { } - public string PropertyWithAttribute { get; set; } - public event System.EventHandler EventWithAttribute { add { } remove { } } - } - public class AttributeDifferenceGenericCLass - { - } -} diff --git a/src/Microsoft.DotNet.ApiCompat/tests/TestProjects/AttributeDifference/Implementation/AttributeDifference.Implementation.csproj b/src/Microsoft.DotNet.ApiCompat/tests/TestProjects/AttributeDifference/Implementation/AttributeDifference.Implementation.csproj deleted file mode 100644 index 82f57cb7ace..00000000000 --- a/src/Microsoft.DotNet.ApiCompat/tests/TestProjects/AttributeDifference/Implementation/AttributeDifference.Implementation.csproj +++ /dev/null @@ -1,9 +0,0 @@ - - - - netstandard2.0 - false - AttributeDifference - - - diff --git a/src/Microsoft.DotNet.ApiCompat/tests/TestProjects/AttributeDifference/Implementation/AttributeDifferenceClass1.cs b/src/Microsoft.DotNet.ApiCompat/tests/TestProjects/AttributeDifference/Implementation/AttributeDifferenceClass1.cs deleted file mode 100644 index 3000785e406..00000000000 --- a/src/Microsoft.DotNet.ApiCompat/tests/TestProjects/AttributeDifference/Implementation/AttributeDifferenceClass1.cs +++ /dev/null @@ -1,29 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.ComponentModel; - -namespace AttributeDifference -{ - [Designer("Foo")] - [DisplayName("Attribute difference class1")] - [Foo] - public class AttributeDifferenceClass1 - { - public string MethodWithAttribute([Foo] string myParameter, [DefaultValue("myObject")] object myObject) => myParameter; - public T GenericMethodWithAttribute<[DefaultValue("T")] T>() => default(T); - [Foo] - public void MethodWithAttribute() { } - [Foo] - public string PropertyWithAttribute { get; set; } - [Foo] - public event System.EventHandler EventWithAttribute { add { } remove { } } - } - - public class AttributeDifferenceGenericCLass<[DefaultValue("TOne")] TOne, [DefaultValue("TTwo")] TTwo> - { - } - - internal class FooAttribute : Attribute { } -} diff --git a/src/Microsoft.DotNet.GenFacades/build/Microsoft.DotNet.GenFacades.NotSupported.targets b/src/Microsoft.DotNet.GenFacades/build/Microsoft.DotNet.GenFacades.NotSupported.targets index 2de7a52184d..df2b75b89db 100644 --- a/src/Microsoft.DotNet.GenFacades/build/Microsoft.DotNet.GenFacades.NotSupported.targets +++ b/src/Microsoft.DotNet.GenFacades/build/Microsoft.DotNet.GenFacades.NotSupported.targets @@ -6,7 +6,7 @@ AddGenFacadeNotSupportedCompileItem;$(CoreCompileDependsOn) - false + false $(NoWarn);CA1823;CA1821;CS0169