diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Usage/ImplementGenericMathInterfacesCorrectly.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Usage/ImplementGenericMathInterfacesCorrectly.cs index e581151154..67ed576657 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Usage/ImplementGenericMathInterfacesCorrectly.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Usage/ImplementGenericMathInterfacesCorrectly.cs @@ -64,7 +64,7 @@ private void AnalyzeSymbol(SymbolAnalysisContext context, INamedTypeSymbol symbo { if (IsKnownInterfaceInTheChain(anInterface) && FirstTypeParameterNameIsNotTheSymbolName(symbol, anInterface) && - (!symbol.IsGenericType || NotConstrainedToTheInterface(anInterface, symbol.TypeParameters))) + (!symbol.IsGenericType || NotConstrainedToTheInterfaceOrSelf(anInterface, symbol))) { SyntaxNode? typeParameter = FindTheTypeArgumentOfTheInterfaceFromTypeDeclaration(symbol, anInterface); context.ReportDiagnostic(CreateDiagnostic(GMIRule, typeParameter, anInterface.OriginalDefinition.ToDisplayString( @@ -79,7 +79,7 @@ private void AnalyzeSymbol(SymbolAnalysisContext context, INamedTypeSymbol symbo { if (IsKnownInterfaceInTheChain(anInterface) && FirstTypeParameterNameIsNotTheSymbolName(symbol, anInterface) && - (!symbol.IsGenericType || NotConstrainedToTheInterface(anInterface, symbol.TypeParameters))) + (!symbol.IsGenericType || NotConstrainedToTheInterfaceOrSelf(anInterface, symbol))) { SyntaxNode? typeParameter = FindTheTypeArgumentOfTheInterfaceFromTypeDeclaration(symbol, symbol.BaseType); context.ReportDiagnostic(CreateDiagnostic(GMIRule, typeParameter, symbol.BaseType.OriginalDefinition.ToDisplayString( @@ -113,13 +113,14 @@ bool IsKnownInterfaceInTheChain(INamedTypeSymbol anInterface) return false; } - static bool NotConstrainedToTheInterface(INamedTypeSymbol anInterface, ImmutableArray typeParameters) + static bool NotConstrainedToTheInterfaceOrSelf(INamedTypeSymbol anInterface, INamedTypeSymbol symbol) { - foreach (var typeParameter in typeParameters) + foreach (var typeParameter in symbol.TypeParameters) { foreach (var constraint in typeParameter.ConstraintTypes) { - if (constraint.Equals(anInterface, SymbolEqualityComparer.Default)) + if (constraint.Equals(symbol) || + constraint.Equals(anInterface, SymbolEqualityComparer.Default)) { return false; } diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Usage/ImplementGenericMathInterfacesCorrectlyTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Usage/ImplementGenericMathInterfacesCorrectlyTests.cs index 67c660bd1e..06cfc0c9f1 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Usage/ImplementGenericMathInterfacesCorrectlyTests.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Usage/ImplementGenericMathInterfacesCorrectlyTests.cs @@ -571,6 +571,40 @@ public interface IParsable where TSelf : IParsable }").RunAsync(); } + [Fact] + public async Task SelfConstrainedInterfaceDerivedFromGMInterfaceTest() + { + await PopulateTestCs(@" +using System; + +namespace MyNamespace +{ + public interface IMyInterface : IParsable where TSelf : IMyInterface + { } +}").RunAsync(); + } + + [Fact] + public async Task SelfConstrainedClassDerivedFromGMInterfaceTest() + { + await PopulateTestCs(@" +using System; + +public class MyDate : IParsable where TSelf : MyDate +{ + public static TSelf Parse(string s, IFormatProvider provider) + { + throw new NotImplementedException(); + } + + public static bool TryParse(string s, IFormatProvider provider, out TSelf result) + { + throw new NotImplementedException(); + } +} +").RunAsync(); + } + [Fact] public async Task InterfacesImplementedCorrectlyNotWarn() {