0

In the implementation of my analyzer I am in the AnalyzeSymbol method:

public override void Initialize(AnalysisContext context)
{
  context.RegisterSymbolAction(AnalyzeSymbol, SymbolKind.Method);
}

private static void AnalyzeSymbol(SymbolAnalysisContext context)
{
  // here I look through the syntax of the method body in order to find invocations.
  var methodSymbol = context.Symbol as IMethodSymbol;
  var location = methodSymbol.Locations.FirstOrDefault();
  var methodNode = location.SourceTree.GetCompilationUnitRoot().FindNode(locati‌​on.SourceSpan);
  var blockNode = methodNode.ChildNodes().FirstOrDefault(s => s is BlockSyntax);
  var invocations = blockNode.DescendantNodes()
    .Select(s => s as InvocationExpressionSyntax)
    .Where(s => s != null)

  foreach(InvocationExpressionSyntax i in invocations i)
  {
    // look at the signature of the method which is invoked
    // in order to figure out if the arguments are attributed
  }
}

For each InvocationExpressionSyntax node I find, I need to check with which attributes the arguments are declared in the signature of the declaring method the invocation calls (namely my [ReadOnly] attribute see here). In my understanding I need to get somehow from the InvocationExpressionSyntax object to the corresponding IMethodSymbol, but I cannot figure out how. Unlike FieldDeclarationSyntax which has a property Declarations by which I get an IFieldSymbol like posted here, InvocationExpressionSyntax doesn't and the following code I tried yields null for the symbol:

var model = context.Compilation.GetSemanticModel(ies.SyntaxTree);
var symbol = model.GetDeclaredSymbol(ies.Expression);

What am I doing wrong?

Example code of what my analyzer should analyze is:

void Foo()
{
  int i = 42;

  // coming from this InvocationExpressionSyntax node I want to find out that i is an parameter for an argument which has an [ReadOnly] attribute
  Bar(i);

  // coming from this InvocationExpressionSyntax node I want to find out that i is an parameter for an argument which has NOT an [ReadOnly] attribute
  Baz(i);
}

void Bar([ReadOnly] int i)
{
}

void Baz(int i)
{
}
David
  • 2,426
  • 3
  • 24
  • 36
  • Can you provide an example of what an `InvocationExpressionSyntax` is? and can you also explain why do you need a symbol, perhaps there is a way to do this with out using a symbol – johnny 5 Jun 29 '17 at 19:25
  • @johnny5 See my edit. I think getting the syntax node for the declaration of the method which is called would be enough, too. – David Jun 29 '17 at 20:12
  • 1
    You are aware that an invocation is that actual invoking of a method, and the attribute will not be on the invocation, but it will be on the method declartion itself – johnny 5 Jun 29 '17 at 21:35
  • That's why I need the syntax node at the location where the method is declared. – David Jun 30 '17 at 03:40
  • So you should be actually looking for a MethodDeclarationSyntax and not and InvocationDeclarationSyntax – johnny 5 Jun 30 '17 at 03:43
  • I am not looking for InvocationDeclarationSyntax anywhere in my code. – David Jun 30 '17 at 03:45
  • Yes you are you are using a foreach InvocationExpressionSyntax in invocations – johnny 5 Jun 30 '17 at 03:47
  • For each of these `InvocationExpressionSyntax` I want to find the respective method declaration. – David Jun 30 '17 at 03:50
  • Why aren't you just iterating over the method declarations instead? – johnny 5 Jun 30 '17 at 03:54
  • It depends on from where the method is called. – David Jun 30 '17 at 03:57
  • The method itself is valid independently of an existing or non-existing attribution of the parameters. The context determines this validity. – David Jun 30 '17 at 03:58
  • I see what you want but you are getting things confused. Iterate over all of the code and map all of the methods and there parameter attributes into a dictionary, then when you you do the invocation you check the map to for the method name with namespace. This may get inefficient so you might what to cache the dictionary and figure out when to update it some how – johnny 5 Jun 30 '17 at 04:11
  • Or alternatively you can use the symbol finder like below but it's up to you – johnny 5 Jun 30 '17 at 04:15

1 Answers1

2

You can get declaration symbol for InvocationExpressionSyntax using SymbolFinder

Workspace workspace;
Workspace.TryGetWorkspace(location.SourceTree.GetText().Container, out workspace);
if (workspace != null)
{
    var invocMethodSymbol = model.GetSymbolInfo(ies.Expression).Symbol;
    // Await this task if you can
    var declarationMethodSymbol = SymbolFinder.FindSourceDefinitionAsync(invocMethodSymbol, workspace.CurrentSolution).Result;
}
George Alexandria
  • 2,841
  • 2
  • 16
  • 24
  • The code doesn't check the ref or out parameters for the presence of `[ReadOnly]` attribution. It checks only if the invocation contains a ref or out parameter. – David Jun 29 '17 at 20:22
  • @David I right understand that you want to know invoke you method that has `[ReadOnly]` parameter in declaration, not that you pass `[ReadOnly]` parameter into some method? – George Alexandria Jun 29 '17 at 20:44
  • @David I did not wait your comment and edited the answer. Hope that I right understood your question. So check please the answer. – George Alexandria Jun 29 '17 at 21:47
  • @GeorgeAlexandria you can get the workspace that way? I have to reflect into the option to grab the solution – johnny 5 Jun 30 '17 at 04:18
  • @johnny5 I know a cases when you cannot get workspace that way, for example, it does not always work for `MSBuildWorkspace`, I don't have ideas why doesn't it work always. However, that way works fine for `RoslynVisualStudioWorkspace` that I tested. So the best way it's grab the solution somewhere above this code and use it – George Alexandria Jun 30 '17 at 06:55
  • @GeorgeAlexandria interesting, I wish I knew that earlier. It took me forever to find a way to get the workspace – johnny 5 Jun 30 '17 at 07:02