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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added .nuget/nuget.exe
Binary file not shown.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,7 @@
<ExcludeFromSourceOnlyBuild>true</ExcludeFromSourceOnlyBuild>
</PropertyGroup>

<ItemGroup>
<None Update="AttributesToExclude.txt">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>


<ItemGroup>
<ProjectReference Include="..\..\Microsoft.DotNet.ApiSymbolExtensions\Microsoft.DotNet.ApiSymbolExtensions.csproj" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ namespace Microsoft.DotNet.ApiDiff.Tool;
/// </summary>
public static class Program
{
private static readonly string AttributesToExcludeDefaultFileName = "AttributesToExclude.txt";

public static async Task Main(string[] args)
{
Expand Down Expand Up @@ -87,10 +86,10 @@ public static async Task Main(string[] args)

Option<FileInfo[]?> 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<FileInfo[]?> optionFilesWithApisToExclude = new("--apisToExclude", "-eapis")
Expand Down
Original file line number Diff line number Diff line change
@@ -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;

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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<string> 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()];
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ internal MemoryOutputDiffGenerator(
_afterAssemblySymbols = new ConcurrentDictionary<string, IAssemblySymbol>(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);
Expand Down Expand Up @@ -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<KeyValuePair<string, MemberDeclarationSyntax>>
{
public int Compare(KeyValuePair<string, MemberDeclarationSyntax> first, KeyValuePair<string, MemberDeclarationSyntax> second)
Expand Down
69 changes: 60 additions & 9 deletions test/Microsoft.DotNet.ApiDiff.Tests/Diff.Attribute.Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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: """
Expand Down Expand Up @@ -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: """
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,6 @@
<Compile Include="..\Microsoft.DotNet.GenAPI.Tests\TestAssemblyLoaderFactory.cs" />
<Compile Include="..\Microsoft.DotNet.ApiSymbolExtensions.Tests\SymbolFactory.cs" />
<Compile Include="..\Microsoft.DotNet.ApiSymbolExtensions.Tests\TempDirectory.cs" />
<None
Include="$(RepoRoot)src\Compatibility\ApiDiff\Microsoft.DotNet.ApiDiff.Tool\AttributesToExclude.txt"
Link="AttributesToExclude.txt">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<Project>
<PropertyGroup>
<PackageJsonName Condition="$(PackageJsonName) == ''">lib</PackageJsonName>
<PackageJsonVersion Condition="$(PackageJsonVersion) == ''">1.0.0</PackageJsonVersion>
<PackageJsonDescription Condition="$(PackageJsonDescription) == ''">A test project to verify .NET SDK compatibility for workload restore and non-Managed projects</PackageJsonDescription>
<PackageJsonMain Condition="$(PackageJsonMain) == ''">index.js</PackageJsonMain>
<PackageJsonScriptsTest Condition="$(PackageJsonScriptsTest) == ''">echo "Error: no test specified" &amp;&amp; exit 1</PackageJsonScriptsTest>
<PackageJsonAuthor Condition="$(PackageJsonAuthor) == ''"></PackageJsonAuthor>
<PackageJsonLicense Condition="$(PackageJsonLicense) == ''">MIT</PackageJsonLicense>
</PropertyGroup>
</Project>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Revert this file as well as .DefaultPatchVersionForAspNetCoreApp2_1 and .BundledAspNetCoreVersion

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in commit c7a4ef9. Reverted all unrelated test asset files (.BundledAspNetCoreVersion, .DefaultPatchVersionForAspNetCoreApp2_1, package.g.props, and nuget.exe) by checking them out from the commit before my changes.

Empty file.
Loading