Home > Net >  Why is SyntaxFactory.ParseStatement Only Running the First Line of Code?
Why is SyntaxFactory.ParseStatement Only Running the First Line of Code?

Time:08-19

I'm trying to support C# code snippets with Roslyn but I can't seem to get it to work. It all compiles and runs but it only seems to run the first line??? Here's an example I put together to show the behavior:

using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using RoslynTest;
using System.Reflection;

string code = @"
Console.WriteLine(""Line 1"");
Console.WriteLine(""Line 2"");
Console.WriteLine(""Line 3"");
";

var compileUnit = SyntaxFactory.CompilationUnit();
var codeNamespace = SyntaxFactory.NamespaceDeclaration(SyntaxFactory.ParseName("RoslynTest"));
compileUnit = compileUnit.AddUsings(
    SyntaxFactory.UsingDirective(SyntaxFactory.ParseName("System"))
);

var testClass = SyntaxFactory.ClassDeclaration("TestClass");
testClass = testClass.AddModifiers(SyntaxFactory.Token(SyntaxKind.PublicKeyword));
testClass = testClass.AddBaseListTypes(SyntaxFactory.SimpleBaseType(SyntaxFactory.ParseTypeName("ITest")));

var testMethod = SyntaxFactory.MethodDeclaration(
         SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.VoidKeyword)), "Test")
        .AddModifiers(SyntaxFactory.Token(SyntaxKind.PublicKeyword))
        .WithBody(SyntaxFactory.Block(SyntaxFactory.ParseStatement(code)).NormalizeWhitespace());

testClass = testClass.AddMembers(testMethod);
codeNamespace = codeNamespace.AddMembers(testClass);
compileUnit = compileUnit.AddMembers(codeNamespace);

var assemblyPath = Path.GetDirectoryName(typeof(object).Assembly.Location);

var references = AppDomain.CurrentDomain
    .GetAssemblies()
    .Where(a => !a.IsDynamic)
    .Select(a => a.Location)
    .Where(s => !string.IsNullOrEmpty(s))
    .Select(s => MetadataReference.CreateFromFile(s))
    .ToList();

references.Add(MetadataReference.CreateFromFile(Path.Combine(assemblyPath, "mscorlib.dll")));
references.Add(MetadataReference.CreateFromFile(Path.Combine(assemblyPath, "System.dll")));
references.Add(MetadataReference.CreateFromFile(Path.Combine(assemblyPath, "System.Core.dll")));

var compilation = CSharpCompilation.Create(
    assemblyName: Guid.NewGuid().ToString(),
    syntaxTrees: new[] { compileUnit.SyntaxTree },
    references: references,
    options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary, optimizationLevel: OptimizationLevel.Release)
);

var stream = new MemoryStream();
var emitResult = compilation.Emit(stream);

if (emitResult.Success)
{
    stream.Seek(0, SeekOrigin.Begin);
    Assembly assembly = Assembly.Load(stream.ToArray());

    Type type = assembly.GetType("RoslynTest.TestClass");
    ITest test = Activator.CreateInstance(type) as ITest;
    
    test.Test();
}

Basically, the class I generate implements an Interface called ITest and then runs the Test() method. If you look at the code snippet it should output 3 lines, however the output is always just a single line that says

Line 1.

CodePudding user response:

SyntaxFactory.Block(SyntaxFactory.ParseStatement(code))

ParseStatement is exactly that: it parses the first statement and ignores the rest.

  • Related