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

1

8

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?)

Misreckon answered 17/9, 2015 at 17:56 Comment(0)
F
8

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.

Fugal answered 17/9, 2015 at 21:36 Comment(5)
How do you do this when you didn't create the syntax node? I have found a syntax node and I need to find it again after a transformation. But when I call WithAdditionalAnnotations on the node before the mutation, the syntax node I get back has no Parent.Rodenhouse
@AndrewArnott I think you need to find your node and its parent first. Then call WithAdditionalAnnotations() and save that node. I think you could then then call parent.ReplaceNode(newNodeWithAnnotations).Fugal
@AndrewArnott It's also possible the DocumentEditor or a CSharpSyntaxRewriter may be better suited for your purposes. I find them easier to use when applying a large number of changes to a syntax tree.Fugal
@Fugal can I somehow create new .csproj and sln programmatically with Roslyn and save it to file?Gilliangilliard
@Gilliangilliard as far as I know you cannot add/save new .csproj or .sln with Roslyn. See: github.com/dotnet/roslyn/issues/6825 From what I recall they would accept community contributions on this issue, but aren't working on it themselves.Fugal

© 2022 - 2024 — McMap. All rights reserved.