-
Notifications
You must be signed in to change notification settings - Fork 480
New Analyzer: Implement Generic Math Interfaces Correctly #6126
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 6 commits
Commits
Show all changes
13 commits
Select commit
Hold shift + click to select a range
435e0ed
Check with predefined interface list
buyaa-n c4365f0
Add more interfaces
buyaa-n d869fa7
Apply feedbacks
buyaa-n 8a1f037
Merge conflicts
buyaa-n 6945836
Instead mock APIs use preview 7 for testing
buyaa-n 8709bae
Check base type, check intefaces recursively, add more test scenarios…
buyaa-n 3f243fa
Add more scenarios for interface/record/class,improve messaging, add …
buyaa-n a0026a1
Update generated files
buyaa-n b4361b8
Update messaging, add tes scenario for alias
buyaa-n cfe7be0
Fix RS1033 warnings
buyaa-n 17e24a0
Merge branch 'main' of github.com:dotnet/roslyn-analyzers into CRTP-a…
buyaa-n e4f98ec
Apply more feedbacks
buyaa-n ca74d7b
Support partial class with multiple source scenario
buyaa-n File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
46 changes: 46 additions & 0 deletions
46
...CSharp/Microsoft.NetCore.Analyzers/Usage/CSharpImplementGenericMathInterfacesCorrectly.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,46 @@ | ||
| // Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information. | ||
|
|
||
| using Microsoft.CodeAnalysis; | ||
| using Microsoft.CodeAnalysis.CSharp.Syntax; | ||
| using Microsoft.CodeAnalysis.Diagnostics; | ||
| using Microsoft.NetCore.Analyzers.Usage; | ||
|
|
||
| namespace Microsoft.NetCore.CSharp.Analyzers.Usage | ||
| { | ||
| [DiagnosticAnalyzer(LanguageNames.CSharp)] | ||
| public sealed class CSharpImplementGenericMathInterfacesCorrectly : ImplementGenericMathInterfacesCorrectly | ||
| { | ||
| protected override SyntaxNode? FindTheTypeArgumentOfTheInterfaceFromTypeDeclaration(ISymbol typeSymbol, ISymbol anInterfaceSymbol) | ||
| { | ||
| foreach (SyntaxReference syntaxReference in typeSymbol.DeclaringSyntaxReferences) | ||
| { | ||
| SyntaxNode typeDefinition = syntaxReference.GetSyntax(); | ||
| if (typeDefinition is ClassDeclarationSyntax classDeclaration) | ||
| { | ||
| return FindTypeArgumentFromBaseInterfaceList(classDeclaration.BaseList.Types, anInterfaceSymbol); | ||
| } | ||
| else if (typeDefinition is StructDeclarationSyntax structDeclaration) | ||
| { | ||
| return FindTypeArgumentFromBaseInterfaceList(structDeclaration.BaseList.Types, anInterfaceSymbol); | ||
| } | ||
| } | ||
|
|
||
| return null; | ||
| } | ||
|
|
||
| private static SyntaxNode? FindTypeArgumentFromBaseInterfaceList(SeparatedSyntaxList<BaseTypeSyntax> baseListTypes, ISymbol anInterfaceSymbol) | ||
| { | ||
| foreach (BaseTypeSyntax baseType in baseListTypes) | ||
| { | ||
| if (baseType is SimpleBaseTypeSyntax simpleBaseType && | ||
| simpleBaseType.Type is GenericNameSyntax genericName && | ||
| genericName.Identifier.ValueText == anInterfaceSymbol.Name) | ||
| { | ||
| return genericName.TypeArgumentList.Arguments[0]; | ||
| } | ||
| } | ||
|
|
||
| return null; | ||
| } | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
117 changes: 117 additions & 0 deletions
117
...alyzers/Core/Microsoft.NetCore.Analyzers/Usage/ImplementGenericMathInterfacesCorrectly.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,117 @@ | ||
| // Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information. | ||
|
|
||
| using System.Collections.Immutable; | ||
| using Analyzer.Utilities; | ||
| using Analyzer.Utilities.Extensions; | ||
| using Microsoft.CodeAnalysis; | ||
| using Microsoft.CodeAnalysis.Diagnostics; | ||
|
|
||
| namespace Microsoft.NetCore.Analyzers.Usage | ||
| { | ||
| using static MicrosoftNetCoreAnalyzersResources; | ||
|
|
||
| public abstract class ImplementGenericMathInterfacesCorrectly : DiagnosticAnalyzer | ||
buyaa-n marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| { | ||
| private const string RuleId = "CA2260"; | ||
|
|
||
| internal static readonly DiagnosticDescriptor GMInterfacesRule = DiagnosticDescriptorHelper.Create( | ||
| RuleId, | ||
| CreateLocalizableResourceString(nameof(ImplementGenericMathInterfacesCorrectlyTitle)), | ||
| CreateLocalizableResourceString(nameof(ImplementGenericMathInterfacesCorrectlyMessage)), | ||
| DiagnosticCategory.Usage, | ||
| RuleLevel.BuildWarning, | ||
| description: CreateLocalizableResourceString(nameof(ImplementGenericMathInterfacesCorrectlyDesciption)), | ||
| isPortedFxCopRule: false, | ||
| isDataflowRule: false); | ||
|
|
||
| private static readonly ImmutableArray<string> s_knownInterfaces = ImmutableArray.Create("IParsable`1", "ISpanParsable`1", "IAdditionOperators`3", "IAdditiveIdentity`2", | ||
| "IBinaryFloatingPointIeee754`1", "IBinaryInteger`1", "IBinaryNumber`1", "IBitwiseOperators`3", "IComparisonOperators`2", "IDecrementOperators`1", "IDivisionOperators`3", | ||
buyaa-n marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| "IEqualityOperators`2", "IExponentialFunctions`1", "IFloatingPointIeee754`1", "IFloatingPoint`1", "IHyperbolicFunctions`1", "IIncrementOperators`1", "ILogarithmicFunctions`1", | ||
buyaa-n marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| "IMinMaxValue`1", "IModulusOperators`3", "IMultiplicativeIdentity`2", "IMultiplyOperators`3", "INumberBase`1", "INumber`1", "IPowerFunctions`1", "IRootFunctions`1", "IShiftOperators`2", | ||
buyaa-n marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| "ISignedNumber`1", "ISubtractionOperators`3", "ITrigonometricFunctions`1", "IUnaryNegationOperators`2", "IUnaryPlusOperators`2", "IUnsignedNumber`1", "IFloatingPointConstants`1"); | ||
|
|
||
| public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(GMInterfacesRule); | ||
|
|
||
| public override void Initialize(AnalysisContext context) | ||
| { | ||
| context.EnableConcurrentExecution(); | ||
| context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); | ||
buyaa-n marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| context.RegisterCompilationStartAction(context => | ||
| { | ||
| if (!context.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemIParsable1, out var iParsableInterface) || | ||
| !context.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemNumericsIDecrementOperators1, out var iBinaryInteger1)) | ||
buyaa-n marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| { | ||
| return; | ||
| } | ||
|
|
||
| context.RegisterSymbolAction(context => | ||
| { | ||
| if (context.Symbol is INamedTypeSymbol ntSymbol && !ntSymbol.IsGenericType) | ||
buyaa-n marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
buyaa-n marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| { | ||
| AnalyzeSymbol(context, ntSymbol, iParsableInterface.ContainingNamespace, iBinaryInteger1.ContainingNamespace); | ||
| } | ||
| }, SymbolKind.NamedType); | ||
| }); | ||
| } | ||
|
|
||
| private void AnalyzeSymbol(SymbolAnalysisContext context, INamedTypeSymbol symbol, INamespaceSymbol systemNS, INamespaceSymbol systemNumericsNS) | ||
| { | ||
| if (!CheckInterfacesForViolation(symbol)) | ||
| { | ||
| INamedTypeSymbol? baseType = symbol.BaseType; | ||
| if (baseType != null && baseType.IsGenericType) | ||
buyaa-n marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| { | ||
| CheckInterfacesForViolation(baseType); | ||
| } | ||
| } | ||
|
|
||
| bool CheckInterfacesForViolation(INamedTypeSymbol lookup) | ||
| { | ||
| foreach (INamedTypeSymbol anInterface in lookup.Interfaces) | ||
| { | ||
| if (anInterface.IsGenericType) | ||
| { | ||
| if (IsKnownInterface(anInterface, systemNS, systemNumericsNS) && | ||
| FirstTypeParameterNameIsNotTheSymbolName(symbol, anInterface)) | ||
| { | ||
| SyntaxNode? typeParameter = FindTheTypeArgumentOfTheInterfaceFromTypeDeclaration(symbol, symbol.Equals(lookup, SymbolEqualityComparer.Default) ? anInterface : lookup); | ||
| if (typeParameter != null) | ||
| { | ||
| context.ReportDiagnostic(typeParameter.CreateDiagnostic(GMInterfacesRule, anInterface.Name, symbol.Name, GetTSelfParameterName(lookup, anInterface))); | ||
| } | ||
| else | ||
| { | ||
| context.ReportDiagnostic(symbol.CreateDiagnostic(GMInterfacesRule, anInterface.Name, symbol.Name, GetTSelfParameterName(lookup, anInterface))); | ||
| } | ||
|
|
||
| return true; | ||
| } | ||
| else if (CheckInterfacesForViolation(anInterface)) | ||
| { | ||
| return true; | ||
| } | ||
| } | ||
| } | ||
| return false; | ||
| } | ||
|
|
||
| string GetTSelfParameterName(INamedTypeSymbol symbol, INamedTypeSymbol anInterface) => | ||
| symbol.IsGenericType ? symbol.OriginalDefinition.TypeParameters[0].Name : anInterface.OriginalDefinition.TypeParameters[0].Name; | ||
| } | ||
|
|
||
| protected abstract SyntaxNode? FindTheTypeArgumentOfTheInterfaceFromTypeDeclaration(ISymbol typeSymbol, ISymbol theInterfaceSymbol); | ||
|
|
||
| private bool IsKnownInterface(INamedTypeSymbol anInterface, INamespaceSymbol systemNS, INamespaceSymbol systemNumericsNS) | ||
| { | ||
| var iNamespace = anInterface.ContainingNamespace; | ||
|
|
||
| return s_knownInterfaces.Contains(anInterface.MetadataName) && | ||
| (iNamespace.Equals(systemNS, SymbolEqualityComparer.Default) || | ||
| iNamespace.Equals(systemNumericsNS, SymbolEqualityComparer.Default)); | ||
buyaa-n marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| private bool FirstTypeParameterNameIsNotTheSymbolName(INamedTypeSymbol symbol, INamedTypeSymbol anInterface) => | ||
| anInterface.TypeArguments[0].Name != symbol.Name; | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.