Added exception for T methods

This commit is contained in:
Thorsten Sommer 2025-04-17 10:13:59 +02:00
parent 71c87ec0d4
commit cb0ed0fcd8
Signed by: tsommer
GPG Key ID: 371BBA77A02C0108

View File

@ -36,7 +36,7 @@ public sealed class ThisUsageAnalyzer : DiagnosticAnalyzer
{ {
var genericNameSyntax = (GenericNameSyntax)context.Node; var genericNameSyntax = (GenericNameSyntax)context.Node;
// Skip if already part of a 'this' expression // Skip if already part of a 'this' expression:
if (IsAccessedThroughThis(genericNameSyntax)) if (IsAccessedThroughThis(genericNameSyntax))
return; return;
@ -46,7 +46,11 @@ public sealed class ThisUsageAnalyzer : DiagnosticAnalyzer
if (IsPartOfMemberAccess(genericNameSyntax)) if (IsPartOfMemberAccess(genericNameSyntax))
return; return;
// Get symbol info for the generic name // Skip if it's the 'T' translation method
if (IsTranslationMethod(genericNameSyntax))
return;
// Get symbol info for the generic name:
var symbolInfo = context.SemanticModel.GetSymbolInfo(genericNameSyntax); var symbolInfo = context.SemanticModel.GetSymbolInfo(genericNameSyntax);
var symbol = symbolInfo.Symbol; var symbol = symbolInfo.Symbol;
@ -83,15 +87,24 @@ public sealed class ThisUsageAnalyzer : DiagnosticAnalyzer
} }
} }
private static bool IsTranslationMethod(SyntaxNode node)
{
// Check if this is a method called 'T' (translation method)
if (node is IdentifierNameSyntax { Identifier.Text: "T" })
return true;
return false;
}
private void AnalyzeIdentifier(SyntaxNodeAnalysisContext context) private void AnalyzeIdentifier(SyntaxNodeAnalysisContext context)
{ {
var identifierNameSyntax = (IdentifierNameSyntax)context.Node; var identifierNameSyntax = (IdentifierNameSyntax)context.Node;
// Skip if this identifier is part of a generic name - we'll handle that separately // Skip if this identifier is part of a generic name - we'll handle that separately:
if (identifierNameSyntax.Parent is GenericNameSyntax) if (identifierNameSyntax.Parent is GenericNameSyntax)
return; return;
// Skip if already part of a 'this' expression // Skip if already part of a 'this' expression:
if (IsAccessedThroughThis(identifierNameSyntax)) if (IsAccessedThroughThis(identifierNameSyntax))
return; return;
@ -101,55 +114,59 @@ public sealed class ThisUsageAnalyzer : DiagnosticAnalyzer
if (IsPartOfMemberAccess(identifierNameSyntax)) if (IsPartOfMemberAccess(identifierNameSyntax))
return; return;
// Also skip if it's part of static import statements // Also skip if it's part of static import statements:
if (IsPartOfUsingStaticDirective(identifierNameSyntax)) if (IsPartOfUsingStaticDirective(identifierNameSyntax))
return; return;
// Skip if it's part of a namespace or type name // Skip if it's part of a namespace or type name:
if (IsPartOfNamespaceOrTypeName(identifierNameSyntax)) if (IsPartOfNamespaceOrTypeName(identifierNameSyntax))
return; return;
// Get symbol info // Skip if it's the 'T' translation method:
if (IsTranslationMethod(identifierNameSyntax))
return;
// Get symbol info:
var symbolInfo = context.SemanticModel.GetSymbolInfo(identifierNameSyntax); var symbolInfo = context.SemanticModel.GetSymbolInfo(identifierNameSyntax);
var symbol = symbolInfo.Symbol; var symbol = symbolInfo.Symbol;
if (symbol == null) if (symbol == null)
return; return;
// Skip local variables, parameters, and range variables // Skip local variables, parameters, and range variables:
if (symbol.Kind is SymbolKind.Local or SymbolKind.Parameter or SymbolKind.RangeVariable or SymbolKind.TypeParameter) if (symbol.Kind is SymbolKind.Local or SymbolKind.Parameter or SymbolKind.RangeVariable or SymbolKind.TypeParameter)
return; return;
// Skip types and namespaces // Skip types and namespaces:
if (symbol.Kind is SymbolKind.NamedType or SymbolKind.Namespace) if (symbol.Kind is SymbolKind.NamedType or SymbolKind.Namespace)
return; return;
// Explicitly check if this is a local function // Explicitly check if this is a local function:
if (symbol is IMethodSymbol methodSymbol && IsLocalFunction(methodSymbol)) if (symbol is IMethodSymbol methodSymbol && IsLocalFunction(methodSymbol))
return; return;
// Get the containing type of the current context // Get the containing type of the current context:
var containingSymbol = context.ContainingSymbol; var containingSymbol = context.ContainingSymbol;
var currentType = containingSymbol?.ContainingType; var currentType = containingSymbol?.ContainingType;
// If we're in a static context, allow accessing members without this // If we're in a static context, allow accessing members without this:
if (IsInStaticContext(containingSymbol)) if (IsInStaticContext(containingSymbol))
return; return;
// Now check if the symbol is an instance member of the current class // Now check if the symbol is an instance member of the current class:
if (symbol is IFieldSymbol or IPropertySymbol or IMethodSymbol or IEventSymbol) if (symbol is IFieldSymbol or IPropertySymbol or IMethodSymbol or IEventSymbol)
{ {
// Skip static members // Skip static members:
if (symbol.IsStatic) if (symbol.IsStatic)
return; return;
// Skip constants // Skip constants:
if (symbol is IFieldSymbol { IsConst: true }) if (symbol is IFieldSymbol { IsConst: true })
return; return;
var containingType = symbol.ContainingType; var containingType = symbol.ContainingType;
// If the symbol is a member of the current type or a base type, then require this // If the symbol is a member of the current type or a base type, then require this:
if (currentType != null && (SymbolEqualityComparer.Default.Equals(containingType, currentType) || if (currentType != null && (SymbolEqualityComparer.Default.Equals(containingType, currentType) ||
IsBaseTypeOf(containingType, currentType))) IsBaseTypeOf(containingType, currentType)))
{ {