3

I am using Roslyn to gather all the invocations of methods in a method and replace them with shims in the corresponding test method for the method. The problem is that in spite of initializing the compilation object appropriately with all the references and syntax trees of the project, it throws an ArgumentException which says "Syntax node is not within Syntax tree".

This is the code that I have used to initialize the semantic model:

   public TestMethodCodeGenerator(string code, Project project = null)
    {
        if (!string.IsNullOrWhiteSpace(code))
        {
            var syntaxTree = CSharpSyntaxTree.ParseText(code);

            if (null != project)
            {
                var syntaxTreesOfDocumentsInProject = new List<SyntaxTree>();
                foreach (var document in project.Documents)
                {
                    syntaxTreesOfDocumentsInProject.Add(CSharpSyntaxTree.ParseFile(document.FilePath));
                }

                var compilation = CSharpCompilation.Create("Demo").AddReferences(project.MetadataReferences).AddSyntaxTrees(syntaxTreesOfDocumentsInProject);
                this.semanticModel = compilation.GetSemanticModel(syntaxTreesOfDocumentsInProject[0]);
                }
            }
            else 
            {                
                var compilation = CSharpCompilation.Create("Demo").AddSyntaxTrees(syntaxTree);
                this.semanticModel = compilation.GetSemanticModel(syntaxTree);
            }
        }
    }

This is how I have used the semanticModel to generate shims for all invocations:

private string PopulateMethodBodyWithShims(MethodDeclarationSyntax methodDeclarationSyntax)
    {
        if (null != methodDeclarationSyntax)
        {
            var stringBuilder = new StringBuilder();
            var methodBlock = methodDeclarationSyntax.Body;
            foreach (var statement in methodBlock.Statements)
            {
                var invocationSyntax = this.ExtractMethodInvocationSyntaxFromStatement(statement);
                if (null != invocationSyntax)
                {
                    var call = invocationSyntax.Expression as MemberAccessExpressionSyntax;
                    if (null != call)
                    {
                        try
                        {
                            IMethodSymbol methodSymbol;
                            **methodSymbol = this.semanticModel.GetSymbolInfo(call).Symbol as IMethodSymbol;**
                            if (null != methodSymbol)
                            {
                                var shimMethod = this.GenerateShimMethod(methodSymbol);
                                stringBuilder.AppendLine(GeneratedTestClassConstants.IndentationSpaceToken + GeneratedTestClassConstants.IndentationSpaceToken + shimMethod);
                            }
                        }
                        catch (ArgumentException ex)
                        {
                            ////This exception can be thrown if the syntax node is not within the syntax tree
                            var message = ex.Message;
                            throw new ArgumentException(message + " : " + call);
                        }                           
                    }
                }
            }
.
.
.

The highlighted region in the second code snippet is where the exception occurs and execution flows to the catch block. What is the issue and what am I missing here?

1 Answer 1

1

Why are you parsing your own files and constructing your own compilation? If you already have a Workspace with a Solution, Projects and Documents, you can just use document.GetSyntaxTreeAsync(), document.GetSemanticModelAsync(), and document.Project.GetCompilationAsync() to get at these.

You seem to be adding in a syntax tree for some new code you have, but you could do that by adding that syntax tree and getting a new Project. e.g.

project = project.AddDocument("generatedfile", code).Project;

Anyway, I suspect that the problem with your code as stated is that the semantic model you store is the one that corresponds to the first syntax tree you in syntaxTreesOfDocuments, but you never add syntaxTree (the tree for the code string you have) to that list, and therefore it's not part of the compilation, and definitely isn't the tree that the SemanticModel operates on.

Sign up to request clarification or add additional context in comments.

2 Comments

I get the first point in your answer and will incorporate it. However, with regards to the third point (where you have mentioned the issue in my code), I have obtained the semantic model for the code that is part of the compilation always. In the if clause, I have obtained the compilation from the project and in the else clause I have generated the compilation from the code string that is supplied. Hence, I am still unable to understand the cause of the exception mentioned in the question.
SemanticModels are bound to a specific syntax tree. If you are requesting semantic information for a different tree, you need to get it's semantic model. The exception means you're passing a node from one tree, but the semantic model was original created from a different tree. In the end of the day, just debug your code -- you're probably just passing the wrong value somewhere.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.