Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get declared symbol from SemanticModel for newly created class?

What is the easiest way to get an ISymbol from a ClassDeclaration that I just created?

Consider the following code:

AdhocWorkspace workspace = new AdhocWorkspace();         
Project project = workspace.AddProject("Test", LanguageNames.CSharp);

ClassDeclarationSyntax classDeclaration = SyntaxFactory.ClassDeclaration("MyClass");
CompilationUnitSyntax compilationUnit = SyntaxFactory.CompilationUnit().AddMembers(classDeclaration);         
Document document = project.AddDocument("Test.cs", compilationUnit);
SemanticModel semanticModel = await document.GetSemanticModelAsync();
ISymbol symbol = semanticModel.GetDeclaredSymbol(classDeclaration); // <-- Throws Exception

The last line throws an exception saying "Syntax node is not within syntax tree".

I assume I need to get the ClassDeclarationSyntax that I just created again from the new SyntaxTree. But what is the easiest way to find it in the new SyntaxTree given that I only have the old ClassDeclarationSyntax?

In the example above the class is the only class in the SyntaxTree and is the first child of the CompilationUnit, so it would be easy to find in this simple case. But imagine a situation where the syntax tree contains a lot of declarations, that may be nested, and the sought after class declaration is nested deep within? Is there some way to use the old ClassDeclarationSyntax to find the new one? (Or am I going about things all wrong here?)

like image 506
DeCaf Avatar asked Sep 17 '15 17:09

DeCaf


1 Answers

You can use a SyntaxAnnotation to keep track of your class nodes:

AdhocWorkspace workspace = new AdhocWorkspace();
Project project = workspace.AddProject("Test", LanguageNames.CSharp);

//Attach a syntax annotation to the class declaration
var syntaxAnnotation = new SyntaxAnnotation("ClassTracker");
var classDeclaration = SyntaxFactory.ClassDeclaration("MyClass")
    .WithAdditionalAnnotations(syntaxAnnotation);

var compilationUnit = SyntaxFactory.CompilationUnit().AddMembers(classDeclaration);

Document document = project.AddDocument("Test.cs", compilationUnit);
SemanticModel semanticModel = document.GetSemanticModelAsync().Result;

//Use the annotation on our original node to find the new class declaration
var changedClass = document.GetSyntaxRootAsync().Result.DescendantNodes().OfType<ClassDeclarationSyntax>()
    .Where(n => n.HasAnnotation(syntaxAnnotation)).Single();
var symbol = semanticModel.GetDeclaredSymbol(changedClass);

This should work no matter what kind of complex documents you end up adding your class to.

like image 200
JoshVarty Avatar answered Nov 13 '22 04:11

JoshVarty