diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/DoNotCallEnumerableCastOrOfTypeWithIncompatibleTypesAnalyzer.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/DoNotCallEnumerableCastOrOfTypeWithIncompatibleTypesAnalyzer.cs index a1f3ce44e4..bc3dda1329 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/DoNotCallEnumerableCastOrOfTypeWithIncompatibleTypesAnalyzer.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/DoNotCallEnumerableCastOrOfTypeWithIncompatibleTypesAnalyzer.cs @@ -196,6 +196,10 @@ static bool CastWillAlwaysFail(ITypeSymbol castFrom, ITypeSymbol castTo) return false; } + // Most checks are better with OriginalDefinition, but keep the ones passed in around. + ITypeSymbol castFromParam = castFrom; + ITypeSymbol castToParam = castTo; + castFrom = castFrom.OriginalDefinition; castTo = castTo.OriginalDefinition; @@ -269,8 +273,8 @@ static bool IsUnconstrainedTypeParameter(ITypeParameterSymbol typeParameterSymbo return false; case (TypeKind.Class, TypeKind.Class): - return !castFrom.DerivesFrom(castTo) - && !castTo.DerivesFrom(castFrom); + return !castFromParam.DerivesFrom(castToParam) + && !castToParam.DerivesFrom(castFromParam); case (TypeKind.Interface, TypeKind.Class): return castTo.IsSealed && !castTo.DerivesFrom(castFrom); diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/DoNotCallEnumerableCastOrOfTypeWithIncompatibleTypesAnalyzerTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/DoNotCallEnumerableCastOrOfTypeWithIncompatibleTypesAnalyzerTests.cs index 271b683d3e..7e6523ce82 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/DoNotCallEnumerableCastOrOfTypeWithIncompatibleTypesAnalyzerTests.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/DoNotCallEnumerableCastOrOfTypeWithIncompatibleTypesAnalyzerTests.cs @@ -866,6 +866,50 @@ public sealed record class DataNodeUpdate(DataNode Updated) : NodeUpdate() { new Derived() }.Cast().ToList(); + + // same code but generic, fails + // CastTest\Program.cs(7,1,7,77): warning CA2021: Type 'GenericBase' is incompatible with type 'GenericDerived' and cast attempts will throw InvalidCastException at runtime (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2021) + new List>() { new GenericDerived() }.Cast().ToList(); + } +} + +class Base +{ +} + +class Derived : Base +{ +} + +class GenericBase +{ +} + +class GenericDerived : GenericBase +{ +}" + }; + await test.RunAsync(); + } + [Fact] public async Task NonGenericCasesVB() {