diff --git a/.nuget/nuget.exe b/.nuget/nuget.exe new file mode 100644 index 000000000000..ed048fe88924 Binary files /dev/null and b/.nuget/nuget.exe differ diff --git a/src/Compatibility/ApiDiff/Microsoft.DotNet.ApiDiff.Tool/AttributesToExclude.txt b/src/Compatibility/ApiDiff/Microsoft.DotNet.ApiDiff.Tool/AttributesToExclude.txt deleted file mode 100644 index 4b26bf1f6a67..000000000000 --- a/src/Compatibility/ApiDiff/Microsoft.DotNet.ApiDiff.Tool/AttributesToExclude.txt +++ /dev/null @@ -1,4 +0,0 @@ -T:System.AttributeUsageAttribute -T:System.ComponentModel.EditorBrowsableAttribute -T:System.Diagnostics.CodeAnalysis.RequiresDynamicCodeAttribute -T:System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute \ No newline at end of file diff --git a/src/Compatibility/ApiDiff/Microsoft.DotNet.ApiDiff.Tool/Microsoft.DotNet.ApiDiff.Tool.csproj b/src/Compatibility/ApiDiff/Microsoft.DotNet.ApiDiff.Tool/Microsoft.DotNet.ApiDiff.Tool.csproj index 21c512fd7fcf..3e6be96c8fd6 100644 --- a/src/Compatibility/ApiDiff/Microsoft.DotNet.ApiDiff.Tool/Microsoft.DotNet.ApiDiff.Tool.csproj +++ b/src/Compatibility/ApiDiff/Microsoft.DotNet.ApiDiff.Tool/Microsoft.DotNet.ApiDiff.Tool.csproj @@ -12,11 +12,7 @@ true - - - PreserveNewest - - + diff --git a/src/Compatibility/ApiDiff/Microsoft.DotNet.ApiDiff.Tool/Program.cs b/src/Compatibility/ApiDiff/Microsoft.DotNet.ApiDiff.Tool/Program.cs index 0a8f5cbf47e7..9b27106a6c37 100644 --- a/src/Compatibility/ApiDiff/Microsoft.DotNet.ApiDiff.Tool/Program.cs +++ b/src/Compatibility/ApiDiff/Microsoft.DotNet.ApiDiff.Tool/Program.cs @@ -14,7 +14,6 @@ namespace Microsoft.DotNet.ApiDiff.Tool; /// public static class Program { - private static readonly string AttributesToExcludeDefaultFileName = "AttributesToExclude.txt"; public static async Task Main(string[] args) { @@ -87,10 +86,10 @@ public static async Task Main(string[] args) Option optionFilesWithAttributesToExclude = new("--attributesToExclude", "-eattrs") { - Description = $"An optional array of filepaths, each containing a list of attributes to exclude from the diff. Each file should contain one API full name per line. You can either modify the default file '{AttributesToExcludeDefaultFileName}' to add your own attributes, or include additional files using this command line option.", + Description = "An optional array of filepaths, each containing a list of attributes to exclude from the diff. Each file should contain one docID per line in the format 'T:Namespace.TypeName'. If not specified, a default set of common attributes will be excluded automatically. Specifying this option replaces the defaults with the attributes listed in the provided files.", Arity = ArgumentArity.ZeroOrMore, Required = false, - DefaultValueFactory = _ => [new FileInfo(AttributesToExcludeDefaultFileName)] + DefaultValueFactory = _ => null }; Option optionFilesWithApisToExclude = new("--apisToExclude", "-eapis") diff --git a/src/Compatibility/ApiDiff/Microsoft.DotNet.ApiDiff/FileOutputDiffGenerator.cs b/src/Compatibility/ApiDiff/Microsoft.DotNet.ApiDiff/FileOutputDiffGenerator.cs index db26a6154074..a770a1179a8b 100644 --- a/src/Compatibility/ApiDiff/Microsoft.DotNet.ApiDiff/FileOutputDiffGenerator.cs +++ b/src/Compatibility/ApiDiff/Microsoft.DotNet.ApiDiff/FileOutputDiffGenerator.cs @@ -1,10 +1,10 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the +using System.Diagnostics; using Microsoft.CodeAnalysis; -using Microsoft.DotNet.ApiSymbolExtensions.Logging; using Microsoft.DotNet.ApiSymbolExtensions; -using System.Diagnostics; +using Microsoft.DotNet.ApiSymbolExtensions.Logging; namespace Microsoft.DotNet.ApiDiff; @@ -75,7 +75,7 @@ internal FileOutputDiffGenerator(ILog log, _afterFriendlyName = afterFriendlyName; _tableOfContentsTitle = tableOfContentsTitle; _assembliesToExclude = CollectListsFromFiles(filesWithAssembliesToExclude); - _attributesToExclude = filesWithAttributesToExclude != null ? CollectListsFromFiles(filesWithAttributesToExclude) : []; + _attributesToExclude = CollectAttributesFromFilesOrDefaults(filesWithAttributesToExclude); _apisToExclude = CollectListsFromFiles(filesWithApisToExclude); _addPartialModifier = addPartialModifier; _writeToDisk = writeToDisk; @@ -191,4 +191,40 @@ private static string[] CollectListsFromFiles(FileInfo[]? filesWithLists) return [.. list.Order()]; } + + private static string[] CollectAttributesFromFilesOrDefaults(FileInfo[]? filesWithLists) + { + // If no files are specified, use default attributes + if (filesWithLists == null || filesWithLists.Length == 0) + { + return [ + "T:System.AttributeUsageAttribute", + "T:System.ComponentModel.EditorBrowsableAttribute", + "T:System.Diagnostics.CodeAnalysis.RequiresDynamicCodeAttribute", + "T:System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute", + "T:System.Windows.Markup.ContentWrapperAttribute", + "T:System.Windows.TemplatePartAttribute" + ]; + } + + List list = []; + + foreach (FileInfo file in filesWithLists) + { + // Only read files that exist, skip missing files silently + if (file.Exists) + { + foreach (string line in File.ReadLines(file.FullName)) + { + if (!list.Contains(line)) + { + // Prevent duplicates. + list.Add(line); + } + } + } + } + + return [.. list.Order()]; + } } diff --git a/src/Compatibility/ApiDiff/Microsoft.DotNet.ApiDiff/MemoryOutputDiffGenerator.cs b/src/Compatibility/ApiDiff/Microsoft.DotNet.ApiDiff/MemoryOutputDiffGenerator.cs index a481547425f5..b2adf9bba949 100644 --- a/src/Compatibility/ApiDiff/Microsoft.DotNet.ApiDiff/MemoryOutputDiffGenerator.cs +++ b/src/Compatibility/ApiDiff/Microsoft.DotNet.ApiDiff/MemoryOutputDiffGenerator.cs @@ -71,7 +71,7 @@ internal MemoryOutputDiffGenerator( _afterAssemblySymbols = new ConcurrentDictionary(afterAssemblySymbols); _addPartialModifier = addPartialModifier; _diagnosticOptions = diagnosticOptions ?? DiffGeneratorFactory.DefaultDiagnosticOptions; - _attributeSymbolFilter = SymbolFilterFactory.GetFilterFromList(attributesToExclude ?? [], includeExplicitInterfaceImplementationSymbols: true); + _attributeSymbolFilter = SymbolFilterFactory.GetFilterFromList(GetAttributesToExcludeOrDefaults(attributesToExclude), includeExplicitInterfaceImplementationSymbols: true); _symbolFilter = SymbolFilterFactory.GetFilterFromList(apisToExclude ?? [], includeExplicitInterfaceImplementationSymbols: true); _twoSpacesTrivia = SyntaxFactory.TriviaList(SyntaxFactory.Space, SyntaxFactory.Space); _missingCloseBrace = SyntaxFactory.MissingToken(SyntaxKind.CloseBraceToken); @@ -760,6 +760,24 @@ private string GetDocId(SyntaxNode node, SemanticModel model) return sb.Length == 0 ? null : sb.ToString(); } + private static string[] GetAttributesToExcludeOrDefaults(string[]? attributesToExclude) + { + // If no attributes are specified, use default attributes + if (attributesToExclude == null) + { + return [ + "T:System.AttributeUsageAttribute", + "T:System.ComponentModel.EditorBrowsableAttribute", + "T:System.Diagnostics.CodeAnalysis.RequiresDynamicCodeAttribute", + "T:System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute", + "T:System.Windows.Markup.ContentWrapperAttribute", + "T:System.Windows.TemplatePartAttribute" + ]; + } + + return attributesToExclude; + } + private class ChildrenNodesComparer : IComparer> { public int Compare(KeyValuePair first, KeyValuePair second) diff --git a/test/Microsoft.DotNet.ApiDiff.Tests/Diff.Attribute.Tests.cs b/test/Microsoft.DotNet.ApiDiff.Tests/Diff.Attribute.Tests.cs index a63f89e546f9..9794da4a301f 100644 --- a/test/Microsoft.DotNet.ApiDiff.Tests/Diff.Attribute.Tests.cs +++ b/test/Microsoft.DotNet.ApiDiff.Tests/Diff.Attribute.Tests.cs @@ -734,15 +734,16 @@ public class MyClass [Fact] public Task SuppressAllDefaultAttributesUsedByTool() { - // The attributes that should get hidden in this test must all be part of - // the AttributesToExclude.txt file that the ApiDiff tool uses by default. - - FileInfo file = new FileInfo("AttributesToExclude.txt"); - if (!file.Exists) - { - throw new FileNotFoundException($"{file.FullName} file not found."); - } - string[] attributesToExclude = File.ReadAllLines(file.FullName); + // Test that the default attributes defined in FileOutputDiffGenerator are properly excluded. + // These are the same attributes that are excluded when no -eattrs parameter is specified. + string[] attributesToExclude = [ + "T:System.AttributeUsageAttribute", + "T:System.ComponentModel.EditorBrowsableAttribute", + "T:System.Diagnostics.CodeAnalysis.RequiresDynamicCodeAttribute", + "T:System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute", + "T:System.Windows.Markup.ContentWrapperAttribute", + "T:System.Windows.TemplatePartAttribute" + ]; return RunTestAsync( beforeCode: """ @@ -787,6 +788,56 @@ public class MyClass attributesToExclude: attributesToExclude); } + [Fact] + public Task DefaultBehaviorExcludesDefaultAttributes() + { + // Test that when no attributesToExclude is specified (null), + // the tool automatically excludes the default set of attributes. + // This simulates the behavior when -eattrs is not specified on the command line. + + return RunTestAsync( + beforeCode: """ + namespace MyNamespace + { + public class MyClass + { + } + } + """, + afterCode: """ + using System; + namespace MyNamespace + { + [System.AttributeUsage(System.AttributeTargets.All)] + public class MyAttributeAttribute : System.Attribute + { + public MyAttributeAttribute() { } + } + [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] + [System.Diagnostics.CodeAnalysis.RequiresDynamicCode("Text")] + [MyAttribute] + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("Text")] + public class MyClass + { + } + } + """, + expectedCode: """ + namespace MyNamespace + { + + [MyNamespace.MyAttributeAttribute] + public class MyClass + { + } + + public class MyAttributeAttribute : System.Attribute + + { + + public MyAttributeAttribute(); + + } + } + """, + attributesToExclude: null); // null simulates default behavior + } + [Fact] public Task SuppressNone() => RunTestAsync( beforeCode: """ diff --git a/test/Microsoft.DotNet.ApiDiff.Tests/Microsoft.DotNet.ApiDiff.Tests.csproj b/test/Microsoft.DotNet.ApiDiff.Tests/Microsoft.DotNet.ApiDiff.Tests.csproj index 0614262e2f1e..cbbb244af138 100644 --- a/test/Microsoft.DotNet.ApiDiff.Tests/Microsoft.DotNet.ApiDiff.Tests.csproj +++ b/test/Microsoft.DotNet.ApiDiff.Tests/Microsoft.DotNet.ApiDiff.Tests.csproj @@ -14,11 +14,6 @@ - - PreserveNewest - diff --git "a/test/TestAssets/TestProjects/ProjectWithEsProjReference/obj\\Debug/\\package.g.props" "b/test/TestAssets/TestProjects/ProjectWithEsProjReference/obj\\Debug/\\package.g.props" new file mode 100644 index 000000000000..89839c8c46de --- /dev/null +++ "b/test/TestAssets/TestProjects/ProjectWithEsProjReference/obj\\Debug/\\package.g.props" @@ -0,0 +1,12 @@ + + + + lib + 1.0.0 + A test project to verify .NET SDK compatibility for workload restore and non-Managed projects + index.js + echo "Error: no test specified" && exit 1 + + MIT + + \ No newline at end of file diff --git a/test/TestAssets/TestProjects/TestWebAppSimple/.BundledAspNetCoreVersion b/test/TestAssets/TestProjects/TestWebAppSimple/.BundledAspNetCoreVersion new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/test/TestAssets/TestProjects/TestWebAppSimple/.DefaultPatchVersionForAspNetCoreApp2_1 b/test/TestAssets/TestProjects/TestWebAppSimple/.DefaultPatchVersionForAspNetCoreApp2_1 new file mode 100644 index 000000000000..e69de29bb2d1