Constructing NameOf expression via SyntaxFactory (Roslyn)

761 views Asked by At

Trying to construct nameof expression from scratch using C# SyntaxFactory. Roslyn fails to recognize my InvocationExpressionSyntax as a contextual nameof keyword and throws error diagnostics upon Emit command.

Tried to give Roslyn valid code to parse in hope that I'll find differences between my syntax construct and the "correctly parsed one". I was able to track the difference down to "nameof" identifier token, but that's where I got stuck. I can't find any difference between my nameof token and the parsed one, yet they are still somehow different.

When I use the "parsed one" everything works and Emit works as expected, without errors. On the other hand, when I use my own nameof token, it fails to compile and Emit throws error diagnostics.

Code I was testing it on:

var CODE = "class X { void Q() { var q = nameof(Q); } }";

var correctSyntaxTree = CSharpSyntaxTree.ParseText(CODE);
var correctRoot = correctSyntaxTree.GetRoot();
var correctNameOf = correctRoot.DescendantNodes().OfType<InvocationExpressionSyntax>().First();

var correctExpression = (IdentifierNameSyntax)correctNameOf.Expression;

var myNameOf = SyntaxFactory.InvocationExpression (
    correctExpression, //Works
    //SyntaxFactory.IdentifierName("nameof"), //Doesn't work
    //SyntaxFactory.IdentifierName(SyntaxFactory.Token(SyntaxKind.NameOfKeyword)), //System.ArgumentException: 'identifier'

    //SyntaxFactory.IdentifierName(correctExpression.Identifier), //Works
    //SyntaxFactory.IdentifierName(correctExpression.Identifier.ValueText), //Doesn't work

    //correctExpression.WithIdentifier(SyntaxFactory.Identifier("nameof")), //Doesn't work
    //correctExpression.WithIdentifier(SyntaxFactory.Identifier(correctExpression.Identifier.ValueText)), //Doesn't work

    //SyntaxFactory.IdentifierName("nameof").WithTriviaFrom(correctExpression), //Doesn't work
    //correctExpression.CopyAnnotationsTo(SyntaxFactory.IdentifierName("nameof")), //Doesn't work
    //SyntaxFactory.IdentifierName(correctExpression.Identifier.WithoutAnnotations().WithoutTrivia()), //Works

    SyntaxFactory.ArgumentList (
        SyntaxFactory.SingletonSeparatedList (
            SyntaxFactory.Argument (
                SyntaxFactory.IdentifierName("Q")
            )
        )
    )
);

var newRoot = correctRoot.ReplaceNode(correctNameOf, myNameOf);
var newTree = CSharpSyntaxTree.Create((CSharpSyntaxNode)newRoot);

var compilation = CSharpCompilation.Create (
    "Compilation",
    new[] { newTree },
    references: MsCorLib,
    options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)
);

var compilationResult = compilation.Emit(new MemoryStream());

Could anyone point out any difference between the parsed token and my token, which is causing the Roslyn to (correctly) not be able to bind the nameof expression to any "in-code" symbol, yet not treat it as contextual keyword?

Compilation error: (1,30): error CS0103: The name 'nameof' does not exist in the current context

Compilation#LanguageVersion: CSharp7

Roslyn version: (Nuget newest) Microsoft.CodeAnalysis.CSharp v2.3.2

1

There are 1 answers

2
nejcs On BEST ANSWER

If you look into non-private members of Identifier you will see that there is property RawContextualKind. When you are parsing nameof from code, you will get this contextual kind: enter image description here

But if you create identifier with SyntaxFactory.IdentifierName("nameof") you get this: enter image description here

In second case, nameof is treated as normal identifier and that is why you receive an error. You should use SyntaxFactory.Identifier() overload which allows you to specify a contextual kind:

SyntaxFactory.IdentifierName(
    SyntaxFactory.Identifier(
        SyntaxFactory.TriviaList(),
        SyntaxKind.NameOfKeyword,
        "nameof",
        "nameof",
        SyntaxFactory.TriviaList()))