Skip to content
Merged
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
125 changes: 66 additions & 59 deletions src/EFCore.Analyzers/InternalUsageDiagnosticAnalyzer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,68 +31,74 @@ public override void Initialize(AnalysisContext context)
context.EnableConcurrentExecution();
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);

context.RegisterOperationAction(
AnalyzeNode,
OperationKind.FieldReference,
OperationKind.PropertyReference,
OperationKind.MethodReference,
OperationKind.EventReference,
OperationKind.Invocation,
OperationKind.ObjectCreation,
OperationKind.VariableDeclaration,
OperationKind.TypeOf);

context.RegisterSymbolAction(
AnalyzeSymbol,
SymbolKind.NamedType,
SymbolKind.Method,
SymbolKind.Property,
SymbolKind.Field,
SymbolKind.Event);
context.RegisterCompilationStartAction(compilationStartContext =>
{
var internalAttributeSymbol = compilationStartContext.Compilation
.GetTypeByMetadataName("Microsoft.EntityFrameworkCore.Infrastructure.EntityFrameworkInternalAttribute");

compilationStartContext.RegisterOperationAction(
operationContext => AnalyzeNode(operationContext, internalAttributeSymbol),
OperationKind.FieldReference,
OperationKind.PropertyReference,
OperationKind.MethodReference,
OperationKind.EventReference,
OperationKind.Invocation,
OperationKind.ObjectCreation,
OperationKind.VariableDeclaration,
OperationKind.TypeOf);

compilationStartContext.RegisterSymbolAction(
symbolContext => AnalyzeSymbol(symbolContext, internalAttributeSymbol),
SymbolKind.NamedType,
SymbolKind.Method,
SymbolKind.Property,
SymbolKind.Field,
SymbolKind.Event);
});
}

private static void AnalyzeNode(OperationAnalysisContext context)
private static void AnalyzeNode(OperationAnalysisContext context, INamedTypeSymbol? internalAttributeSymbol)
{
switch (context.Operation)
{
case IFieldReferenceOperation fieldReference:
AnalyzeMember(context, fieldReference.Field);
AnalyzeMember(context, fieldReference.Field, internalAttributeSymbol);
break;

case IPropertyReferenceOperation propertyReference:
AnalyzeMember(context, propertyReference.Property);
AnalyzeMember(context, propertyReference.Property, internalAttributeSymbol);
break;

case IEventReferenceOperation eventReference:
AnalyzeMember(context, eventReference.Event);
AnalyzeMember(context, eventReference.Event, internalAttributeSymbol);
break;

case IMethodReferenceOperation methodReference:
AnalyzeMember(context, methodReference.Method);
AnalyzeMember(context, methodReference.Method, internalAttributeSymbol);
break;

case IObjectCreationOperation { Constructor: { } constructor }:
AnalyzeMember(context, constructor);
AnalyzeMember(context, constructor, internalAttributeSymbol);
break;

case IInvocationOperation invocation:
AnalyzeInvocation(context, invocation);
AnalyzeInvocation(context, invocation, internalAttributeSymbol);
break;

case IVariableDeclarationOperation variableDeclaration:
AnalyzeVariableDeclaration(context, variableDeclaration);
AnalyzeVariableDeclaration(context, variableDeclaration, internalAttributeSymbol);
break;

case ITypeOfOperation typeOf:
AnalyzeTypeof(context, typeOf);
AnalyzeTypeof(context, typeOf, internalAttributeSymbol);
break;

default:
throw new ArgumentException($"Unexpected operation: {context.Operation.Kind}");
}
}

private static void AnalyzeMember(OperationAnalysisContext context, ISymbol symbol)
private static void AnalyzeMember(OperationAnalysisContext context, ISymbol symbol, INamedTypeSymbol? internalAttributeSymbol)
{
if (symbol.ContainingAssembly?.Equals(context.Compilation.Assembly, SymbolEqualityComparer.Default) == true)
{
Expand All @@ -102,39 +108,39 @@ private static void AnalyzeMember(OperationAnalysisContext context, ISymbol symb

var containingType = symbol.ContainingType;

if (HasInternalAttribute(symbol))
if (HasInternalAttribute(symbol, internalAttributeSymbol))
{
ReportDiagnostic(
context, symbol.Name == WellKnownMemberNames.InstanceConstructorName ? containingType : $"{containingType}.{symbol.Name}");
return;
}

if (IsInternal(context, containingType))
if (IsInternal(context, containingType, internalAttributeSymbol))
{
ReportDiagnostic(context, containingType);
}
}

private static void AnalyzeInvocation(OperationAnalysisContext context, IInvocationOperation invocation)
private static void AnalyzeInvocation(OperationAnalysisContext context, IInvocationOperation invocation, INamedTypeSymbol? internalAttributeSymbol)
{
// First check for any internal type parameters
foreach (var a in invocation.TargetMethod.TypeArguments)
{
if (IsInternal(context, a))
if (IsInternal(context, a, internalAttributeSymbol))
{
context.ReportDiagnostic(Diagnostic.Create(Descriptor, context.Operation.Syntax.GetLocation(), a));
}
}

// Then check the method being invoked
AnalyzeMember(context, invocation.TargetMethod);
AnalyzeMember(context, invocation.TargetMethod, internalAttributeSymbol);
}

private static void AnalyzeVariableDeclaration(OperationAnalysisContext context, IVariableDeclarationOperation variableDeclaration)
private static void AnalyzeVariableDeclaration(OperationAnalysisContext context, IVariableDeclarationOperation variableDeclaration, INamedTypeSymbol? internalAttributeSymbol)
{
foreach (var declarator in variableDeclaration.Declarators)
{
if (IsInternal(context, declarator.Symbol.Type))
if (IsInternal(context, declarator.Symbol.Type, internalAttributeSymbol))
{
var syntax = context.Operation.Syntax switch
{
Expand All @@ -147,47 +153,47 @@ private static void AnalyzeVariableDeclaration(OperationAnalysisContext context,
}
}

private static void AnalyzeTypeof(OperationAnalysisContext context, ITypeOfOperation typeOf)
private static void AnalyzeTypeof(OperationAnalysisContext context, ITypeOfOperation typeOf, INamedTypeSymbol? internalAttributeSymbol)
{
if (IsInternal(context, typeOf.TypeOperand))
if (IsInternal(context, typeOf.TypeOperand, internalAttributeSymbol))
{
ReportDiagnostic(context, typeOf.TypeOperand);
}
}

private static void AnalyzeSymbol(SymbolAnalysisContext context)
private static void AnalyzeSymbol(SymbolAnalysisContext context, INamedTypeSymbol? internalAttributeSymbol)
{
switch (context.Symbol)
{
case INamedTypeSymbol symbol:
AnalyzeNamedTypeSymbol(context, symbol);
AnalyzeNamedTypeSymbol(context, symbol, internalAttributeSymbol);
break;

case IMethodSymbol symbol:
AnalyzeMethodTypeSymbol(context, symbol);
AnalyzeMethodTypeSymbol(context, symbol, internalAttributeSymbol);
break;

case IFieldSymbol symbol:
AnalyzeMemberDeclarationTypeSymbol(context, symbol, symbol.Type);
AnalyzeMemberDeclarationTypeSymbol(context, symbol, symbol.Type, internalAttributeSymbol);
break;

case IPropertySymbol symbol:
AnalyzeMemberDeclarationTypeSymbol(context, symbol, symbol.Type);
AnalyzeMemberDeclarationTypeSymbol(context, symbol, symbol.Type, internalAttributeSymbol);
break;

case IEventSymbol symbol:
AnalyzeMemberDeclarationTypeSymbol(context, symbol, symbol.Type);
AnalyzeMemberDeclarationTypeSymbol(context, symbol, symbol.Type, internalAttributeSymbol);
break;

default:
throw new ArgumentException($"Unexpected {nameof(ISymbol)}: {context.Symbol.GetType().Name}");
}
}

private static void AnalyzeNamedTypeSymbol(SymbolAnalysisContext context, INamedTypeSymbol symbol)
private static void AnalyzeNamedTypeSymbol(SymbolAnalysisContext context, INamedTypeSymbol symbol, INamedTypeSymbol? internalAttributeSymbol)
{
if (symbol.BaseType is ITypeSymbol baseSymbol
&& IsInternal(context, baseSymbol))
&& IsInternal(context, baseSymbol, internalAttributeSymbol))
{
foreach (var declaringSyntax in symbol.DeclaringSyntaxReferences)
{
Expand All @@ -201,7 +207,7 @@ private static void AnalyzeNamedTypeSymbol(SymbolAnalysisContext context, INamed
}
}

foreach (var @interface in symbol.Interfaces.Where(i => IsInternal(context, i)))
foreach (var @interface in symbol.Interfaces.Where(i => IsInternal(context, i, internalAttributeSymbol)))
{
foreach (var declaringSyntax in symbol.DeclaringSyntaxReferences)
{
Expand All @@ -216,15 +222,15 @@ private static void AnalyzeNamedTypeSymbol(SymbolAnalysisContext context, INamed
}
}

private static void AnalyzeMethodTypeSymbol(SymbolAnalysisContext context, IMethodSymbol symbol)
private static void AnalyzeMethodTypeSymbol(SymbolAnalysisContext context, IMethodSymbol symbol, INamedTypeSymbol? internalAttributeSymbol)
{
if (symbol.MethodKind is MethodKind.PropertyGet or MethodKind.PropertySet)
{
// Property getters/setters are handled via IPropertySymbol
return;
}

if (IsInternal(context, symbol.ReturnType))
if (IsInternal(context, symbol.ReturnType, internalAttributeSymbol))
{
foreach (var declaringSyntax in symbol.DeclaringSyntaxReferences)
{
Expand All @@ -238,7 +244,7 @@ private static void AnalyzeMethodTypeSymbol(SymbolAnalysisContext context, IMeth
}
}

foreach (var paramSymbol in symbol.Parameters.Where(ps => IsInternal(context, ps.Type)))
foreach (var paramSymbol in symbol.Parameters.Where(ps => IsInternal(context, ps.Type, internalAttributeSymbol)))
{
foreach (var declaringSyntax in paramSymbol.DeclaringSyntaxReferences)
{
Expand All @@ -257,9 +263,10 @@ private static void AnalyzeMethodTypeSymbol(SymbolAnalysisContext context, IMeth
private static void AnalyzeMemberDeclarationTypeSymbol(
SymbolAnalysisContext context,
ISymbol declarationSymbol,
ITypeSymbol typeSymbol)
ITypeSymbol typeSymbol,
INamedTypeSymbol? internalAttributeSymbol)
{
if (IsInternal(context, typeSymbol))
if (IsInternal(context, typeSymbol, internalAttributeSymbol))
{
foreach (var declaringSyntax in declarationSymbol.DeclaringSyntaxReferences)
{
Expand Down Expand Up @@ -300,18 +307,18 @@ CSharpSyntax.VariableDeclaratorSyntax declarator
_ => syntax
};

private static bool IsInternal(SymbolAnalysisContext context, ITypeSymbol symbol)
private static bool IsInternal(SymbolAnalysisContext context, ITypeSymbol symbol, INamedTypeSymbol? internalAttributeSymbol)
=> symbol.ContainingAssembly?.Equals(context.Compilation.Assembly, SymbolEqualityComparer.Default) != true
&& (IsInInternalNamespace(symbol) || HasInternalAttribute(symbol));
&& (IsInInternalNamespace(symbol) || HasInternalAttribute(symbol, internalAttributeSymbol));

private static bool IsInternal(OperationAnalysisContext context, ITypeSymbol symbol)
private static bool IsInternal(OperationAnalysisContext context, ITypeSymbol symbol, INamedTypeSymbol? internalAttributeSymbol)
=> symbol.ContainingAssembly?.Equals(context.Compilation.Assembly, SymbolEqualityComparer.Default) != true
&& (IsInInternalNamespace(symbol) || HasInternalAttribute(symbol));
&& (IsInInternalNamespace(symbol) || HasInternalAttribute(symbol, internalAttributeSymbol));

private static bool HasInternalAttribute(ISymbol symbol)
=> symbol.GetAttributes().Any(a =>
a.AttributeClass!.ToDisplayString()
== "Microsoft.EntityFrameworkCore.Infrastructure.EntityFrameworkInternalAttribute");
private static bool HasInternalAttribute(ISymbol symbol, INamedTypeSymbol? internalAttributeSymbol)
=> internalAttributeSymbol is not null
&& symbol.GetAttributes().Any(a =>
SymbolEqualityComparer.Default.Equals(a.AttributeClass, internalAttributeSymbol));

private static bool IsInInternalNamespace(ISymbol symbol)
{
Expand Down
Loading