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?
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With