dotnet/roslyn

SyntaxFactory and discards

開放

#35,558 建立於 2019年5月7日

 (3 則留言) (1 個反應) (0 位負責人)C# (4,257 個分叉)batch import
Area-CompilersConcept-APIFeature Requesthelp wanted

倉庫指標

星標
 (20,414 顆星)
PR 合併指標
 (平均合併 6天 17小時) (30 天內合併 256 個 PR)

描述

Version Used: 3.0.0

Consider the following code:

using System;
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;

class Program
{

    static void Main()
    {
        string code = @"
class C
{
    static void Main()
    {
        _ = 0;
    }
}";

        var file = SyntaxFactory.ParseCompilationUnit(code);

        void Compile()
        {
            var tree = SyntaxFactory.SyntaxTree(file);
            var compilation = CSharpCompilation.Create(null)
                .AddSyntaxTrees(tree)
                .AddReferences(MetadataReference.CreateFromFile(typeof(object).Assembly.Location));

            var diagnostics = compilation.GetDiagnostics();

            if (!diagnostics.Any())
                Console.WriteLine("No diagnostics");

            foreach (var diagnostic in diagnostics)
            {
                Console.WriteLine(diagnostic);
            }
        }

        Compile();

        var identifier = file.DescendantNodes().OfType<IdentifierNameSyntax>().Single();
        file = file.ReplaceNode(identifier, SyntaxFactory.IdentifierName("_"));

        Compile();
    }
}

It parses a simple piece of code and compiles it. Then, it replaces the parsed identifier _ with one created using SyntaxFactory.IdentifierName("_") and compiles again.

I would expect both to compile by interpreting the _ as a discard, but they don't; the output of the program above is:

No diagnostics
(6,1): error CS0103: The name '_' does not exist in the current context

As far as I can tell, this happens because the identifier token created by SyntaxFactory does not have its contextual kind set up correctly:

var fromFactory = SyntaxFactory.Identifier("_");
var parsed = ((IdentifierNameSyntax)SyntaxFactory.ParseExpression("_")).Identifier;

SyntaxKind ContextualKind(SyntaxToken token)
{
    var property = typeof(SyntaxToken).GetProperty("RawContextualKind", BindingFlags.NonPublic | BindingFlags.Instance);

    return (SyntaxKind)(int)property.GetValue(token);
}

Console.WriteLine(ContextualKind(fromFactory));
Console.WriteLine(ContextualKind(parsed));

This prints:

IdentifierToken
UnderscoreToken

There is an overload of SyntaxFactory.Identifier which accepts SyntaxKind contextualKind, so it is possible to use SyntaxFactory to create the right identifier token. But shouldn't the simpler overload do this automatically?

貢獻者指南