Skip to content
Open
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
36 changes: 22 additions & 14 deletions Robust.Analyzers/AccessAnalyzer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,27 @@ public override void Initialize(AnalysisContext context)
{
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
context.EnableConcurrentExecution();
context.RegisterOperationAction(CheckFriendship,
OperationKind.FieldReference,
OperationKind.PropertyReference,
OperationKind.MethodReference,
OperationKind.Invocation);
context.RegisterCompilationStartAction(compilationContext =>
{
var friendAttribute = compilationContext.Compilation.GetTypeByMetadataName(AccessAttributeType);
if (friendAttribute is null)
return;

var pureAttribute = compilationContext.Compilation.GetTypeByMetadataName(PureAttributeType);

compilationContext.RegisterOperationAction(
operationContext => CheckFriendship(operationContext, friendAttribute, pureAttribute),
OperationKind.FieldReference,
OperationKind.PropertyReference,
OperationKind.MethodReference,
OperationKind.Invocation);
});
}

private void CheckFriendship(OperationAnalysisContext context)
private void CheckFriendship(
OperationAnalysisContext context,
INamedTypeSymbol friendAttribute,
INamedTypeSymbol pureAttribute)
{
var operation = context.Operation;

Expand Down Expand Up @@ -71,15 +84,12 @@ private void CheckFriendship(OperationAnalysisContext context)
// Get the info of the type defining the member, so we can check the attributes later...
var accessedType = member.ContainingType;

// Get the attributes
var friendAttribute = context.Compilation.GetTypeByMetadataName(AccessAttributeType);

// Get the type that is containing this expression, or, the type where this is happening.
if (context.ContainingSymbol?.ContainingType is not {} accessingType)
return;

// Determine which type of access is happening here... Read, write or execute?
var accessAttempt = DetermineAccess(context, targetAccess, operation);
var accessAttempt = DetermineAccess(pureAttribute, targetAccess, operation);

// Check whether this is a "self" access, including inheritors.
var selfAccess = InheritsFromOrEquals(accessingType, accessedType);
Expand Down Expand Up @@ -194,7 +204,7 @@ bool CheckAttributeFriendship(AttributeData attribute, bool isMemberAttribute)
}
}

private static AccessPermissions DetermineAccess(OperationAnalysisContext context, IOperation operation, IOperation original)
private static AccessPermissions DetermineAccess(INamedTypeSymbol pureAttribute, IOperation operation, IOperation original)
{
switch (operation)
{
Expand All @@ -205,8 +215,6 @@ private static AccessPermissions DetermineAccess(OperationAnalysisContext contex

case IInvocationOperation invoke:
{
var pureAttribute = context.Compilation.GetTypeByMetadataName(PureAttributeType);

foreach (var attribute in invoke.TargetMethod.GetAttributes())
{
// Pure methods are treated as read accesses.
Expand All @@ -219,7 +227,7 @@ private static AccessPermissions DetermineAccess(OperationAnalysisContext contex

case IMemberReferenceOperation member:
{
return DetermineAccess(context, member.Parent, operation);
return DetermineAccess(pureAttribute, member.Parent, operation);
}

default:
Expand Down
13 changes: 7 additions & 6 deletions Robust.Analyzers/ByRefEventAnalyzer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@ public override void Initialize(AnalysisContext context)
context.EnableConcurrentExecution();
context.RegisterCompilationStartAction(compilationContext =>
{
var byRefAttribute = compilationContext.Compilation.GetTypeByMetadataName(ByRefAttribute);
if (byRefAttribute == null)
return;

var raiseMethods = compilationContext.Compilation
.GetTypeByMetadataName("Robust.Shared.GameObjects.EntitySystem")?
.GetMembers()
Expand All @@ -77,14 +81,15 @@ public override void Initialize(AnalysisContext context)
var raiseMethodsArray = raiseMethods.ToArray();

compilationContext.RegisterOperationAction(
ctx => CheckEventRaise(ctx, raiseMethodsArray),
ctx => CheckEventRaise(ctx, raiseMethodsArray, byRefAttribute),
OperationKind.Invocation);
});
}

private static void CheckEventRaise(
OperationAnalysisContext context,
IReadOnlyCollection<IMethodSymbol> raiseMethods)
IReadOnlyCollection<IMethodSymbol> raiseMethods,
INamedTypeSymbol byRefAttribute)
{
if (context.Operation is not IInvocationOperation operation)
return;
Expand Down Expand Up @@ -126,10 +131,6 @@ private static void CheckEventRaise(
return;
}

var byRefAttribute = context.Compilation.GetTypeByMetadataName(ByRefAttribute);
if (byRefAttribute == null)
return;

var isByRefEventType = eventParameter.Type
.GetAttributes()
.Any(attribute => attribute.AttributeClass?.Equals(byRefAttribute, Default) ?? false);
Expand Down
58 changes: 46 additions & 12 deletions Robust.Analyzers/ExplicitInterfaceAnalyzer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,53 @@ public override void Initialize(AnalysisContext context)
{
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.None);
context.EnableConcurrentExecution();
context.RegisterSyntaxNodeAction(AnalyzeNode, SyntaxKind.MethodDeclaration);
context.RegisterSyntaxNodeAction(AnalyzeNode, SyntaxKind.PropertyDeclaration);
context.RegisterCompilationStartAction(compilationContext =>
{
var attrSymbol = compilationContext.Compilation.GetTypeByMetadataName(RequiresExplicitImplementationAttributeMetadataName);
if (attrSymbol is null)
return;

compilationContext.RegisterSymbolStartAction(symbolContext =>
{
if (symbolContext.Symbol is not INamedTypeSymbol typeSymbol)
return;

var explicitInterfaceImplementations = GetExplicitInterfaceImplementations(typeSymbol, attrSymbol);
if (explicitInterfaceImplementations.Count == 0)
return;

symbolContext.RegisterSyntaxNodeAction(
nodeContext => AnalyzeNode(nodeContext, explicitInterfaceImplementations),
SyntaxKind.MethodDeclaration);
symbolContext.RegisterSyntaxNodeAction(
nodeContext => AnalyzeNode(nodeContext, explicitInterfaceImplementations),
SyntaxKind.PropertyDeclaration);
}, SymbolKind.NamedType);
});
}

private void AnalyzeNode(SyntaxNodeAnalysisContext context)
private static HashSet<ISymbol> GetExplicitInterfaceImplementations(
INamedTypeSymbol typeSymbol,
INamedTypeSymbol attrSymbol)
{
var explicitInterfaceImplementations = new HashSet<ISymbol>(SymbolEqualityComparer.Default);

foreach (var interfaceSymbol in typeSymbol.AllInterfaces)
{
if (!interfaceSymbol.GetAttributes().Any(a => SymbolEqualityComparer.Default.Equals(a.AttributeClass, attrSymbol)))
continue;

foreach (var member in interfaceSymbol.GetMembers())
{
if (typeSymbol.FindImplementationForInterfaceMember(member) is { } implementation)
explicitInterfaceImplementations.Add(implementation);
}
}

return explicitInterfaceImplementations;
}

private void AnalyzeNode(SyntaxNodeAnalysisContext context, HashSet<ISymbol> explicitInterfaceImplementations)
{
ISymbol symbol;
Location location;
Expand All @@ -72,15 +114,7 @@ private void AnalyzeNode(SyntaxNodeAnalysisContext context)
return;
}

var attrSymbol = context.Compilation.GetTypeByMetadataName(RequiresExplicitImplementationAttributeMetadataName);

var isInterfaceMember = symbol?.ContainingType.AllInterfaces.Any(
i =>
i.GetMembers().Any(m => SymbolEqualityComparer.Default.Equals(symbol, symbol.ContainingType.FindImplementationForInterfaceMember(m)))
&& i.GetAttributes().Any(a => SymbolEqualityComparer.Default.Equals(a.AttributeClass, attrSymbol))
) ?? false;

if (isInterfaceMember)
if (symbol != null && explicitInterfaceImplementations.Contains(symbol))
{
//we do not have an explicit interface specified. bad!
var diagnostic = Diagnostic.Create(
Expand Down
17 changes: 11 additions & 6 deletions Robust.Analyzers/MustCallBaseAnalyzer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,18 +31,23 @@ public override void Initialize(AnalysisContext context)
{
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
context.EnableConcurrentExecution();
context.RegisterSymbolAction(AnalyzeSymbol, SymbolKind.Method);
context.RegisterCompilationStartAction(compilationContext =>
{
var attrSymbol = compilationContext.Compilation.GetTypeByMetadataName(Attribute);
if (attrSymbol is null)
return;

compilationContext.RegisterSymbolAction(
symbolContext => AnalyzeSymbol(symbolContext, attrSymbol),
SymbolKind.Method);
});
}

private static void AnalyzeSymbol(SymbolAnalysisContext context)
private static void AnalyzeSymbol(SymbolAnalysisContext context, INamedTypeSymbol attrSymbol)
{
if (context.Symbol is not IMethodSymbol { IsOverride: true } method)
return;

var attrSymbol = context.Compilation.GetTypeByMetadataName(Attribute);
if (attrSymbol == null)
return;

if (DoesMethodOverriderHaveAttribute(method, attrSymbol) is not { } data)
return;

Expand Down
14 changes: 11 additions & 3 deletions Robust.Analyzers/NoUncachedRegexAnalyzer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,19 @@ public override void Initialize(AnalysisContext context)
{
context.EnableConcurrentExecution();
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
context.RegisterOperationAction(CheckInvocation, OperationKind.Invocation);
context.RegisterCompilationStartAction(compilationContext =>
{
var regexType = compilationContext.Compilation.GetTypeByMetadataName(RegexType);
if (regexType is null)
return;

compilationContext.RegisterOperationAction(
operationContext => CheckInvocation(operationContext, regexType),
OperationKind.Invocation);
});
}

private static void CheckInvocation(OperationAnalysisContext context)
private static void CheckInvocation(OperationAnalysisContext context, INamedTypeSymbol regexType)
{
if (context.Operation is not IInvocationOperation invocation)
return;
Expand All @@ -54,7 +63,6 @@ private static void CheckInvocation(OperationAnalysisContext context)
if (targetMethod.ContainingType.Name != "Regex")
return;

var regexType = context.Compilation.GetTypeByMetadataName(RegexType);
if (!SymbolEqualityComparer.Default.Equals(regexType, targetMethod.ContainingType))
return;

Expand Down
18 changes: 13 additions & 5 deletions Robust.Analyzers/NotNullableFlagAnalyzer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,18 @@ public override void Initialize(AnalysisContext context)
{
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics);
context.EnableConcurrentExecution();
context.RegisterOperationAction(CheckNotNullableFlag, OperationKind.Invocation);
context.RegisterCompilationStartAction(compilationContext =>
{
var attribute = compilationContext.Compilation.GetTypeByMetadataName(Attribute);
if (attribute is null)
return;

var @bool = compilationContext.Compilation.GetSpecialType(SpecialType.System_Boolean);

compilationContext.RegisterOperationAction(
operationContext => CheckNotNullableFlag(operationContext, attribute, @bool),
OperationKind.Invocation);
});
}

private bool TryGetTypeArgument(IMethodSymbol methodSymbol, string typeParamName, out ITypeSymbol typeArgument)
Expand All @@ -87,14 +98,11 @@ private bool TryGetTypeArgument(IMethodSymbol methodSymbol, string typeParamName
return false;
}

private void CheckNotNullableFlag(OperationAnalysisContext context)
private void CheckNotNullableFlag(OperationAnalysisContext context, INamedTypeSymbol attribute, INamedTypeSymbol @bool)
{
if (context.Operation is not IInvocationOperation invocationOperation || !invocationOperation.TargetMethod.IsGenericMethod)
return;

var attribute = context.Compilation.GetTypeByMetadataName(Attribute);
var @bool = context.Compilation.GetSpecialType(SpecialType.System_Boolean);

foreach (var argument in invocationOperation.Arguments)
{
if(argument.Parameter == null) continue;
Expand Down
32 changes: 22 additions & 10 deletions Robust.Analyzers/PreferGenericVariantAnalyzer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,15 +55,32 @@ public override void Initialize(AnalysisContext context)
{
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.ReportDiagnostics | GeneratedCodeAnalysisFlags.Analyze);
context.EnableConcurrentExecution();
context.RegisterOperationAction(CheckForGenericVariant, OperationKind.Invocation);
context.RegisterCompilationStartAction(compilationContext =>
{
var preferGenericAttribute = compilationContext.Compilation.GetTypeByMetadataName(AttributeType);
if (preferGenericAttribute is null)
return;

var typeTypeSymbol = compilationContext.Compilation.GetTypeByMetadataName("System.Type");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

'tis a bit silly, but make it a const string, please.

if (typeTypeSymbol is null)
return;

var objType = compilationContext.Compilation.GetSpecialType(SpecialType.System_Object);

compilationContext.RegisterOperationAction(
operationContext => CheckForGenericVariant(operationContext, preferGenericAttribute, typeTypeSymbol, objType),
OperationKind.Invocation);
});
}

private void CheckForGenericVariant(OperationAnalysisContext obj)
private void CheckForGenericVariant(
OperationAnalysisContext obj,
INamedTypeSymbol preferGenericAttribute,
INamedTypeSymbol typeTypeSymbol,
INamedTypeSymbol objType)
{
if(obj.Operation is not IInvocationOperation invocationOperation) return;

var preferGenericAttribute = obj.Compilation.GetTypeByMetadataName(AttributeType);

string genericVariant = null;
AttributeData foundAttribute = null;
foreach (var attribute in invocationOperation.TargetMethod.GetAttributes())
Expand All @@ -79,7 +96,6 @@ private void CheckForGenericVariant(OperationAnalysisContext obj)
if(genericVariant == null) return;

var maxTypeParams = 0;
var typeTypeSymbol = obj.Compilation.GetTypeByMetadataName("System.Type");
foreach (var parameter in invocationOperation.TargetMethod.Parameters)
{
if(!SymbolEqualityComparer.Default.Equals(parameter.Type, typeTypeSymbol)) break;
Expand Down Expand Up @@ -107,7 +123,6 @@ private void CheckForGenericVariant(OperationAnalysisContext obj)

var typeParamCount = methodSymbol.TypeParameters.Length;
var failedParamComparison = false;
var objType = obj.Compilation.GetSpecialType(SpecialType.System_Object);
for (int i = 0; i < methodSymbol.Parameters.Length; i++)
{
if (methodSymbol.Parameters[i].Type is ITypeParameterSymbol && SymbolEqualityComparer.Default.Equals(invocationOperation.TargetMethod.Parameters[i + typeParamCount].Type, objType))
Expand Down Expand Up @@ -152,10 +167,7 @@ private void CheckForGenericVariant(OperationAnalysisContext obj)
obj.ReportDiagnostic(Diagnostic.Create(
UseGenericVariantDescriptor,
invocationOperation.Syntax.GetLocation(),
ImmutableDictionary.CreateRange(new Dictionary<string, string>()
{
{"typeOperands", string.Join(",", typeOperands)}
})));
ImmutableDictionary.Create<string, string>().Add("typeOperands", string.Join(",", typeOperands))));
}
}

Expand Down
Loading
Loading