3

I'm trying write an analyzer, I need to find all assignments made to a field using Roslyn.

private async static Task<bool> VariableDoesNotMutate(SyntaxNodeAnalysisContext context, VariableDeclaratorSyntax firstVariable)
{
    var variableSymbol = context.SemanticModel.GetDeclaredSymbol(firstVariable);
    var references = await SymbolFinder.FindReferencesAsync(variableSymbol, context.GetSolution());

    foreach (var reference in references)
    {
        //How do I check for assignment?
    }

    //need to filter by assignments
    return references.Count() > 1;
}

I heard using the symbolFinder was correct, but I'm not sure how to do this.
The symbol finder requires a solution, which I only have access to through a hack, so I'm assuming there is another way to do this.

Issues:

  1. When I try to Find all references to a variable only the Declaration is returned an I do not find any other references how can I fix this?

  2. Once I have a reference How can I determine if it's an assignment?

johnny 5
  • 19,893
  • 50
  • 121
  • 195

1 Answers1

0

I Couldn't find any references originally because my document was not in the correct solution. Analyzer's don't provide you a way to get to the Solution and as @SLaks say's for preformance reasons you should not do this:

To Get the Solution You need to reflect into the AnalyzerOptions I've written an answer how to do so here

However, If you need to you can do this Get The Equivalent Symbol in the Solution and work off of that. This is potentially dangerous

static async Task<ISymbol> GetEquivalentSymbol(SyntaxNodeAnalysisContext context, FieldDeclarationSyntax field, CancellationToken cancellationToken)
{
    var solution = context.GetSolution();
    var classDeclaration = field.Ancestors().OfType<ClassDeclarationSyntax>().FirstOrDefault();
    var namespaceDeclaration = field.Ancestors().OfType<NamespaceDeclarationSyntax>().FirstOrDefault();

    var className = classDeclaration?.Identifier.ValueText;

    var initialVariable = field.Declaration.Variables.FirstOrDefault();

    foreach (var project in solution.Projects)
    {
        foreach (var document in project.Documents)
        {
            var semanticModel = await document.GetSemanticModelAsync(cancellationToken);
            var root = await document.GetSyntaxRootAsync(cancellationToken);
            if (null != namespaceDeclaration)
            {
                var namespaceNode = root.DescendantNodes().OfType<NamespaceDeclarationSyntax>()
                    .FirstOrDefault(node => node.Name.ToString() == namespaceDeclaration.Name.ToString());
                if (null == namespaceNode)
                {
                    continue;
                }
            }

            var classNode = root.DescendantNodes()
                .OfType<ClassDeclarationSyntax>()
                .FirstOrDefault(node => node.Identifier.ValueText == className);

            var desiredField = classNode?.DescendantNodes().OfType<FieldDeclarationSyntax>()
                .FirstOrDefault(x => x.Declaration.Variables.First().Identifier.ValueText == initialVariable.Identifier.ValueText);

            if (desiredField == null)
            {
                continue;
            }

            var symbol = semanticModel.GetDeclaredSymbol(desiredField.Declaration.Variables.FirstOrDefault());
            return symbol;
        }
    }

    return null;
}

Then you can get the references like so:

var equivalentSymbol = await GetEquivalentSymbol(context, field, cancellationToken);

var references = await SymbolFinder.FindReferencesAsync(equivalentSymbol, context.GetSolution(), cancellationToken);
johnny 5
  • 19,893
  • 50
  • 121
  • 195