From 19a636faa82e72deb856e2361054bfe1e4c1b60d Mon Sep 17 00:00:00 2001 From: Manfred Brands Date: Fri, 21 Nov 2025 09:16:36 +0800 Subject: [PATCH] CA1708: Ignore 'extension' blocks for naming check. --- .../IdentifiersShouldDifferByMoreThanCase.cs | 3 +- .../Extensions/INamedTypeSymbolExtensions.cs | 4 +++ .../Compiler/Extensions/ISymbolExtensions.cs | 5 ++++ ...ntifiersShouldDifferByMoreThanCaseTests.cs | 29 +++++++++++++++++++ 4 files changed, 40 insertions(+), 1 deletion(-) diff --git a/src/Microsoft.CodeAnalysis.NetAnalyzers/src/Microsoft.CodeAnalysis.NetAnalyzers/Microsoft.CodeQuality.Analyzers/ApiDesignGuidelines/IdentifiersShouldDifferByMoreThanCase.cs b/src/Microsoft.CodeAnalysis.NetAnalyzers/src/Microsoft.CodeAnalysis.NetAnalyzers/Microsoft.CodeQuality.Analyzers/ApiDesignGuidelines/IdentifiersShouldDifferByMoreThanCase.cs index 887585aab992..d905b372fc19 100644 --- a/src/Microsoft.CodeAnalysis.NetAnalyzers/src/Microsoft.CodeAnalysis.NetAnalyzers/Microsoft.CodeQuality.Analyzers/ApiDesignGuidelines/IdentifiersShouldDifferByMoreThanCase.cs +++ b/src/Microsoft.CodeAnalysis.NetAnalyzers/src/Microsoft.CodeAnalysis.NetAnalyzers/Microsoft.CodeQuality.Analyzers/ApiDesignGuidelines/IdentifiersShouldDifferByMoreThanCase.cs @@ -142,11 +142,12 @@ private static void CheckTypeMembers(IEnumerable members, Action>.GetInstance(StringComparer.OrdinalIgnoreCase); foreach (var member in members) { - // Ignore constructors, indexers, operators and destructors for name check + // Ignore constructors, indexers, operators, destructors and extension blocks for name check if (member.IsConstructor() || member.IsDestructor() || member.IsIndexer() || member.IsUserDefinedOperator() || + member.IsExtension() || overloadsToSkip.Contains(member)) { continue; diff --git a/src/Microsoft.CodeAnalysis.NetAnalyzers/src/Utilities/Compiler/Extensions/INamedTypeSymbolExtensions.cs b/src/Microsoft.CodeAnalysis.NetAnalyzers/src/Utilities/Compiler/Extensions/INamedTypeSymbolExtensions.cs index 9c395554d9a6..0559da8a4418 100644 --- a/src/Microsoft.CodeAnalysis.NetAnalyzers/src/Utilities/Compiler/Extensions/INamedTypeSymbolExtensions.cs +++ b/src/Microsoft.CodeAnalysis.NetAnalyzers/src/Utilities/Compiler/Extensions/INamedTypeSymbolExtensions.cs @@ -17,8 +17,12 @@ internal static class INamedTypeSymbolExtensions private static readonly Func s_isFileLocal = LightupHelpers.CreateSymbolPropertyAccessor(typeof(INamedTypeSymbol), nameof(IsFileLocal), fallbackResult: false); + private static readonly Func s_isExtension = LightupHelpers.CreateSymbolPropertyAccessor(typeof(INamedTypeSymbol), nameof(IsExtension), fallbackResult: false); + public static bool IsFileLocal(this INamedTypeSymbol symbol) => s_isFileLocal(symbol); + public static bool IsExtension(this INamedTypeSymbol symbol) => s_isExtension(symbol); + public static IEnumerable GetBaseTypesAndThis(this INamedTypeSymbol type) { INamedTypeSymbol current = type; diff --git a/src/Microsoft.CodeAnalysis.NetAnalyzers/src/Utilities/Compiler/Extensions/ISymbolExtensions.cs b/src/Microsoft.CodeAnalysis.NetAnalyzers/src/Utilities/Compiler/Extensions/ISymbolExtensions.cs index 03b9c0e21630..e3a352b3718e 100644 --- a/src/Microsoft.CodeAnalysis.NetAnalyzers/src/Utilities/Compiler/Extensions/ISymbolExtensions.cs +++ b/src/Microsoft.CodeAnalysis.NetAnalyzers/src/Utilities/Compiler/Extensions/ISymbolExtensions.cs @@ -161,6 +161,11 @@ public static bool IsConversionOperator([NotNullWhen(returnValue: true)] this IS return symbol is IMethodSymbol { MethodKind: MethodKind.Conversion }; } + public static bool IsExtension([NotNullWhen(returnValue: true)] this ISymbol? symbol) + { + return symbol is INamedTypeSymbol namedTypeSymbol && namedTypeSymbol.IsExtension(); + } + public static ImmutableArray GetParameters(this ISymbol? symbol) { return symbol switch diff --git a/src/Microsoft.CodeAnalysis.NetAnalyzers/tests/Microsoft.CodeAnalysis.NetAnalyzers.UnitTests/Microsoft.CodeQuality.Analyzers/ApiDesignGuidelines/IdentifiersShouldDifferByMoreThanCaseTests.cs b/src/Microsoft.CodeAnalysis.NetAnalyzers/tests/Microsoft.CodeAnalysis.NetAnalyzers.UnitTests/Microsoft.CodeQuality.Analyzers/ApiDesignGuidelines/IdentifiersShouldDifferByMoreThanCaseTests.cs index 5da69d2d9eda..ecc347e95df9 100644 --- a/src/Microsoft.CodeAnalysis.NetAnalyzers/tests/Microsoft.CodeAnalysis.NetAnalyzers.UnitTests/Microsoft.CodeQuality.Analyzers/ApiDesignGuidelines/IdentifiersShouldDifferByMoreThanCaseTests.cs +++ b/src/Microsoft.CodeAnalysis.NetAnalyzers/tests/Microsoft.CodeAnalysis.NetAnalyzers.UnitTests/Microsoft.CodeQuality.Analyzers/ApiDesignGuidelines/IdentifiersShouldDifferByMoreThanCaseTests.cs @@ -419,6 +419,35 @@ public partial class D GetCA1708CSharpResultAt(30, 30, Parameter, "N.D.SomeDelegate")); } + [Fact] + public async Task TestMultipleExtensionBlocks() + { + string code = @" +public static class StringExtensions +{ + private const string ExtensionString = ""Extension""; + + // Static class extensions + extension(string) + { + public static string CreateExtension() => ExtensionString; + } + + // Instance extensions + extension(string s) + { + public bool IsExtensionString() => s == ExtensionString; + } +} +"; + + await new VerifyCS.Test + { + TestCode = code, + LanguageVersion = CodeAnalysis.CSharp.LanguageVersion.CSharp14, + }.RunAsync(); + } + #endregion #region Helper Methods