Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Analyzer doesn't change the syntax tree, so we should use the Visitor #10

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
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
19 changes: 6 additions & 13 deletions src/Augurk.CSharpAnalyzer.Core/Analyzers/EntryPointAnalyzer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ public EntryPointAnalyzer(AnalyzeContext context, SemanticModel model)
/// Called when a class declaration is discovered in the source code.
/// </summary>
/// <param name="node">A <see cref="ClassDeclarationSyntax"/> describing the declared class.</param>
public override SyntaxNode VisitClassDeclaration(ClassDeclarationSyntax node)
public override void VisitClassDeclaration(ClassDeclarationSyntax node)
{
// Get attributes for the class declaration
var symbolInfo = model.GetDeclaredSymbol(node);
Expand All @@ -56,18 +56,17 @@ public override SyntaxNode VisitClassDeclaration(ClassDeclarationSyntax node)
if (attributes.Any(attribute => attribute.AttributeClass.Name == "BindingAttribute" && attribute.AttributeClass.ContainingNamespace.Name == "SpecFlow"))
{
// A class with a [BindingAttribute] is of interest, so dig deeper
return base.VisitClassDeclaration(node);
base.VisitClassDeclaration(node);
}

// Do not analyze classes without a BindingAttribute any further
return node;
}

/// <summary>
/// Called when a method declaration is discovered in the source code.
/// </summary>
/// <param name="node">A <see cref="MethodDeclarationSyntax"/> describing the declared method.</param>
public override SyntaxNode VisitMethodDeclaration(MethodDeclarationSyntax node)
public override void VisitMethodDeclaration(MethodDeclarationSyntax node)
{
// Get attributes for the method declaration
var symbolInfo = model.GetDeclaredSymbol(node);
Expand All @@ -79,29 +78,23 @@ public override SyntaxNode VisitMethodDeclaration(MethodDeclarationSyntax node)
// A method with a [WhenAttribute] is of interest, let's dig deeper
this.insideEntryPoint = true;
context.Collector.StepInto(symbolInfo);
var result = base.VisitMethodDeclaration(node);
base.VisitMethodDeclaration(node);
context.Collector.StepOut();
this.insideEntryPoint = false;
return result;
}

// Do not analyze methods without a WhenAttribute any further
return node;
}

/// <summary>
/// Called when a method invocation is discovered in source code.
/// </summary>
/// <param name="node">An <see cref="InvocationExpressionSyntax"/> describing the method being invoked.</param>
public override SyntaxNode VisitInvocationExpression(InvocationExpressionSyntax node)
public override void VisitInvocationExpression(InvocationExpressionSyntax node)
{
if (this.insideEntryPoint)
{
return base.VisitInvocationExpression(node);
}
else
{
return node;
base.VisitInvocationExpression(node);
}
}
}
Expand Down
74 changes: 38 additions & 36 deletions src/Augurk.CSharpAnalyzer.Core/Analyzers/InvocationTreeAnalyzer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ namespace Augurk.CSharpAnalyzer.Analyzers
/// <summary>
/// Analyzes CSharp code of a method being invoked on a particular type to find other invocations.
/// </summary>
public class InvocationTreeAnalyzer : CSharpSyntaxRewriter
public class InvocationTreeAnalyzer : CSharpSyntaxVisitor
{
private readonly AnalyzeContext context;
private readonly SemanticModel model;
Expand Down Expand Up @@ -59,7 +59,7 @@ public InvocationTreeAnalyzer(AnalyzeContext context, SemanticModel model, Invok
/// Called when a method invocation is discovered in source code.
/// </summary>
/// <param name="node">An <see cref="InvocationExpressionSyntax"/> describing the method being invoked.</param>
public override SyntaxNode VisitInvocationExpression(InvocationExpressionSyntax node)
public override void VisitInvocationExpression(InvocationExpressionSyntax node)
{
// We might have nested invocations thus go one level deeper first if necessary
ExpressionSyntax expression = node.Expression;
Expand Down Expand Up @@ -92,53 +92,55 @@ public override SyntaxNode VisitInvocationExpression(InvocationExpressionSyntax
if (methodInvoked == null)
{
// Nothing to see here
return node;
return;
}

SyntaxReference declaringSyntaxReference = methodInvoked.GetComparableSyntax();
if (declaringSyntaxReference == null)
{
return StepOver(node, methodInvoked);
StepOver(node, methodInvoked);
return;
}

return HandleInvocation(node, methodInvoked, declaringSyntaxReference, this.invokedMethod.TargetType);
HandleInvocation(node, methodInvoked, declaringSyntaxReference, this.invokedMethod.TargetType);
return;
}

return base.VisitInvocationExpression(node);
base.VisitInvocationExpression(node);
}

private SyntaxNode HandleInvocation(InvocationExpressionSyntax node, IMethodSymbol methodInvoked, SyntaxReference declaringSyntaxReference, TypeInfo? targetType)
private void HandleInvocation(InvocationExpressionSyntax node, IMethodSymbol methodInvoked, SyntaxReference declaringSyntaxReference, TypeInfo? targetType)
{
// Determine how the method is being invoked
if (methodInvoked.IsExtensionMethod)
{
// Find target type and step in
return HandleExtensionMethod(node, methodInvoked, declaringSyntaxReference, targetType);
HandleExtensionMethod(node, methodInvoked, declaringSyntaxReference, targetType);
}
else if (methodInvoked.ContainingType.TypeKind == TypeKind.Interface)
{
// Find implementation, or step over
return HandleInterfaceMethod(node, methodInvoked, declaringSyntaxReference, targetType);
HandleInterfaceMethod(node, methodInvoked, declaringSyntaxReference, targetType);
}
else if (methodInvoked.ContainingType.TypeKind == TypeKind.Class && methodInvoked.ContainingType.IsAbstract)
{
if (methodInvoked.IsAbstract)
{
// Find implementation, or step over
return HandleAbstractMethod(node, methodInvoked, declaringSyntaxReference, targetType);
HandleAbstractMethod(node, methodInvoked, declaringSyntaxReference, targetType);
}
else
{
if (methodInvoked.IsVirtual || methodInvoked.IsOverride)
{
// Find implementation and step in, or step into virtual method
return HandleVirtualMethod(node, methodInvoked, declaringSyntaxReference, targetType);
HandleVirtualMethod(node, methodInvoked, declaringSyntaxReference, targetType);
}
else
{
// Step in
targetType = node.GetTargetOfInvocation(methodInvoked, model, this.invokedMethod);
return StepInto(node, new InvokedMethod(methodInvoked, targetType, node.GetArgumentTypes(methodInvoked, model, this.invokedMethod), declaringSyntaxReference));
StepInto(node, new InvokedMethod(methodInvoked, targetType, node.GetArgumentTypes(methodInvoked, model, this.invokedMethod), declaringSyntaxReference));
}
}
}
Expand All @@ -147,44 +149,46 @@ private SyntaxNode HandleInvocation(InvocationExpressionSyntax node, IMethodSymb
if (methodInvoked.IsVirtual || methodInvoked.IsOverride)
{
// Find implementation and step in, or step into virtual method
return HandleVirtualMethod(node, methodInvoked, declaringSyntaxReference, targetType);
HandleVirtualMethod(node, methodInvoked, declaringSyntaxReference, targetType);
}
else
{
// Step in
return StepInto(node, new InvokedMethod(methodInvoked, targetType, node.GetArgumentTypes(methodInvoked, model, this.invokedMethod), declaringSyntaxReference));
StepInto(node, new InvokedMethod(methodInvoked, targetType, node.GetArgumentTypes(methodInvoked, model, this.invokedMethod), declaringSyntaxReference));
}
}
}

private SyntaxNode HandleExtensionMethod(InvocationExpressionSyntax node, IMethodSymbol method, SyntaxReference declaringSyntaxReference, TypeInfo? targetType)
private void HandleExtensionMethod(InvocationExpressionSyntax node, IMethodSymbol method, SyntaxReference declaringSyntaxReference, TypeInfo? targetType)
{
TypeInfo? target = node.GetTargetOfInvocation(method, model, this.invokedMethod);
// Finish by locking in these types through a ToList
IEnumerable<TypeInfo?> argumentTypes = Enumerable.Repeat(target, 1).Concat(node.GetArgumentTypes(method.ReducedFrom, model, this.invokedMethod)).ToList();
return StepIntoExtensionMethod(node, new InvokedMethod(method.ReducedFrom, null, argumentTypes, method.ReducedFrom.GetComparableSyntax()));
StepIntoExtensionMethod(node, new InvokedMethod(method.ReducedFrom, null, argumentTypes, method.ReducedFrom.GetComparableSyntax()));
}

private SyntaxNode HandleInterfaceMethod(InvocationExpressionSyntax node, IMethodSymbol method, SyntaxReference declaringSyntaxReference, TypeInfo? targetType)
private void HandleInterfaceMethod(InvocationExpressionSyntax node, IMethodSymbol method, SyntaxReference declaringSyntaxReference, TypeInfo? targetType)
{
TypeInfo? target = node.GetTargetOfInvocation(method, model, this.invokedMethod);
if (!target.HasValue)
{
return StepOver(node, method);
StepOver(node, method);
return;
}

IMethodSymbol implementingMethod = target.Value.Type.FindImplementationForInterfaceMember(method) as IMethodSymbol;
if (implementingMethod != null)
{
return HandleInvocation(node, implementingMethod, implementingMethod.GetComparableSyntax(), target);
HandleInvocation(node, implementingMethod, implementingMethod.GetComparableSyntax(), target);
return;
}
else
{
return StepOver(node, method);
StepOver(node, method);
}
}

private SyntaxNode HandleAbstractMethod(InvocationExpressionSyntax node, IMethodSymbol method, SyntaxReference declaringSyntaxReference, TypeInfo? targetType)
private void HandleAbstractMethod(InvocationExpressionSyntax node, IMethodSymbol method, SyntaxReference declaringSyntaxReference, TypeInfo? targetType)
{
TypeInfo? target = node.GetTargetOfInvocation(method, model, this.invokedMethod);
if (target.HasValue)
Expand All @@ -195,15 +199,16 @@ private SyntaxNode HandleAbstractMethod(InvocationExpressionSyntax node, IMethod
// Check if the current member is the abstract/virtual method being invoked
if (member.OverriddenMethod.GetComparableSyntax().Equals(declaringSyntaxReference))
{
return HandleInvocation(node, member, member.GetComparableSyntax(), target);
HandleInvocation(node, member, member.GetComparableSyntax(), target);
return;
}
}
}

return StepOver(node, method);
StepOver(node, method);
}

private SyntaxNode HandleVirtualMethod(InvocationExpressionSyntax node, IMethodSymbol method, SyntaxReference declaringSyntaxReference, TypeInfo? targetType)
private void HandleVirtualMethod(InvocationExpressionSyntax node, IMethodSymbol method, SyntaxReference declaringSyntaxReference, TypeInfo? targetType)
{
TypeInfo? target = node.GetTargetOfInvocation(method, model, this.invokedMethod);
if (target.HasValue)
Expand All @@ -221,31 +226,30 @@ private SyntaxNode HandleVirtualMethod(InvocationExpressionSyntax node, IMethodS
// Check if the current member is the abstract/virtual method being invoked
if (overridenMethod.GetComparableSyntax().Equals(declaringSyntaxReference))
{
return HandleInvocation(node, member, member.GetComparableSyntax(), target);
HandleInvocation(node, member, member.GetComparableSyntax(), target);
return;
}
}
}

return StepInto(node, new InvokedMethod(method, this.invokedMethod.TargetType, node.GetArgumentTypes(method, model, this.invokedMethod), declaringSyntaxReference));
StepInto(node, new InvokedMethod(method, this.invokedMethod.TargetType, node.GetArgumentTypes(method, model, this.invokedMethod), declaringSyntaxReference));
}

private SyntaxNode StepOver(InvocationExpressionSyntax node, IMethodSymbol method)
private void StepOver(InvocationExpressionSyntax node, IMethodSymbol method)
{
context.Collector.StepOver(method);
return node;
}

private SyntaxNode StepOverExtensionMethod(InvocationExpressionSyntax node, IMethodSymbol method, ITypeSymbol targetType)
private void StepOverExtensionMethod(InvocationExpressionSyntax node, IMethodSymbol method, ITypeSymbol targetType)
{
context.Collector.StepOverExtensionMethod(method, targetType);
return node;
}

private SyntaxNode StepInto(InvocationExpressionSyntax node, InvokedMethod method)
private void StepInto(InvocationExpressionSyntax node, InvokedMethod method)
{
if (context.Collector.IsAlreadyCollected(method.Method))
{
return StepOver(node, method.Method);
StepOver(node, method.Method);
}
else
{
Expand All @@ -254,15 +258,14 @@ private SyntaxNode StepInto(InvocationExpressionSyntax node, InvokedMethod metho
new InvokedMethod(method.Method, method.TargetType, method.ArgumentTypes, method.DeclaringSyntaxReference));
visitor.Visit(method.DeclaringSyntaxReference.GetSyntax());
context.Collector.StepOut();
return node;
}
}

private SyntaxNode StepIntoExtensionMethod(InvocationExpressionSyntax node, InvokedMethod method)
private void StepIntoExtensionMethod(InvocationExpressionSyntax node, InvokedMethod method)
{
if (context.Collector.IsExtensionMethodAlreadyCollected(method.Method, method.ArgumentTypes.First()?.Type))
{
return StepOverExtensionMethod(node, method.Method, method.ArgumentTypes.First()?.Type);
StepOverExtensionMethod(node, method.Method, method.ArgumentTypes.First()?.Type);
}
else
{
Expand All @@ -271,7 +274,6 @@ private SyntaxNode StepIntoExtensionMethod(InvocationExpressionSyntax node, Invo
new InvokedMethod(method.Method, method.TargetType, method.ArgumentTypes, method.DeclaringSyntaxReference));
visitor.Visit(method.DeclaringSyntaxReference.GetSyntax());
context.Collector.StepOut();
return node;
}
}
}
Expand Down