Skip to content
Merged
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
1 change: 1 addition & 0 deletions ChangeLog.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Fix analyzer [RCS1163](https://josefpihrt.github.io/docs/roslynator/analyzers/RCS1163) ([PR](https://github.com/dotnet/roslynator/pull/1280))
- Fix analyzer [RCS1203](https://josefpihrt.github.io/docs/roslynator/analyzers/RCS1203) ([PR](https://github.com/dotnet/roslynator/pull/1282))
- Fix analyzer [RCS1046](https://josefpihrt.github.io/docs/roslynator/analyzers/RCS1046) ([PR](https://github.com/dotnet/roslynator/pull/1283))
- Fix analyzer [RCS1158](https://josefpihrt.github.io/docs/roslynator/analyzers/RCS1158) ([PR](https://github.com/dotnet/roslynator/pull/1288))

## [4.6.4] - 2023-11-24

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.Diagnostics;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;

namespace Roslynator.CSharp.Analysis;
Expand Down Expand Up @@ -84,8 +85,13 @@ private static void AnalyzeNamedType(SymbolAnalysisContext context)
if (typeParameters.IsDefault)
typeParameters = namedType.TypeParameters;

if (!ContainsAnyTypeParameter(typeParameters, fieldSymbol.Type))
if (!ContainsAnyTypeParameter(typeParameters, fieldSymbol.Type)
&& !IsTypeParameterReferenced(
typeParameters,
(fieldSymbol.GetSyntax(context.CancellationToken) as VariableDeclaratorSyntax)?.Initializer?.Value))
{
ReportDiagnostic(context, fieldSymbol);
}

break;
}
Expand Down Expand Up @@ -116,8 +122,13 @@ private static void AnalyzeNamedType(SymbolAnalysisContext context)
if (typeParameters.IsDefault)
typeParameters = namedType.TypeParameters;

if (!ContainsAnyTypeParameter(typeParameters, propertySymbol.Type))
if (!ContainsAnyTypeParameter(typeParameters, propertySymbol.Type)
&& !IsTypeParameterReferenced(
typeParameters,
(propertySymbol.GetSyntax(context.CancellationToken) as PropertyDeclarationSyntax)?.Initializer?.Value))
{
ReportDiagnostic(context, propertySymbol);
}
}

break;
Expand All @@ -126,6 +137,41 @@ private static void AnalyzeNamedType(SymbolAnalysisContext context)
}
}

private static bool IsTypeParameterReferenced(ImmutableArray<ITypeParameterSymbol> typeParameters, ExpressionSyntax value)
{
if (value.IsKind(SyntaxKind.SimpleMemberAccessExpression))
{
var memberAccess = (MemberAccessExpressionSyntax)value;

if (memberAccess.Expression is IdentifierNameSyntax identifierName)
{
if (IsTypeParameter(identifierName, typeParameters))
return true;
}
else if (memberAccess.Expression is TypeOfExpressionSyntax typeOfExpression)
{
if (typeOfExpression.Type is IdentifierNameSyntax identifierName2
&& IsTypeParameter(identifierName2, typeParameters))
{
return true;
}
}
}

return false;

static bool IsTypeParameter(IdentifierNameSyntax identifierName, ImmutableArray<ITypeParameterSymbol> typeParameters)
{
foreach (ITypeParameterSymbol typeParameter in typeParameters)
{
if (typeParameter.Name == identifierName.Identifier.ValueText)
return true;
}

return false;
}
}

private static bool ContainsAnyTypeParameter(ImmutableArray<ITypeParameterSymbol> typeParameters, ImmutableArray<IParameterSymbol> parameters)
{
foreach (IParameterSymbol parameter in parameters)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// Copyright (c) .NET Foundation and Contributors. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Roslynator.Testing;
using Roslynator.Testing.CSharp;
using Xunit;

namespace Roslynator.CSharp.Analysis.Tests;

public class RCS1158StaticMemberInGenericTypeShouldUseTypeParameterTests : AbstractCSharpDiagnosticVerifier<StaticMemberInGenericTypeShouldUseTypeParameterAnalyzer, EmptyCodeFixProvider>
{
public override DiagnosticDescriptor Descriptor { get; } = DiagnosticRules.StaticMemberInGenericTypeShouldUseTypeParameter;

[Fact, Trait(Traits.Analyzer, DiagnosticIdentifiers.StaticMemberInGenericTypeShouldUseTypeParameter)]
public async Task TestNoDiagnostic_Property()
{
await VerifyNoDiagnosticAsync(@"
public sealed class C<T, T2> where T : IFoo<T2>
{
public static T2 Name { get; } = T.Name;
}

public interface IFoo<T>
{
public static abstract T Name { get; }
}");
}

[Fact, Trait(Traits.Analyzer, DiagnosticIdentifiers.StaticMemberInGenericTypeShouldUseTypeParameter)]
public async Task TestNoDiagnostic_Field()
{
await VerifyNoDiagnosticAsync(@"
public sealed class C<T> where T : IFoo
{
public static string Name = typeof(T).Name;
}

public interface IFoo
{
public static abstract string Name { get; }
}");
}
}