KirillOsenkov / RoslynQuoter

Roslyn tool that for a given C# program shows syntax tree API calls to construct its syntax tree
http://roslynquoter.azurewebsites.net
Apache License 2.0
918 stars 118 forks source link

Out-Discard Identifier issues `CS0103: The name '_' does not exist in the current context` #62

Closed bernd5 closed 3 years ago

bernd5 commented 3 years ago

I try to compile the following minified-snippet with the code produced by roslyn quoter:

using System.Collections.Generic;
var dict = new Dictionary<string, string>();
dict.TryGetValue("x", out _);

It gives the following code - just with Compilation / usings extended:

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

/*
using System.Collections.Generic;
var dict = new Dictionary<string, string>();
dict.TryGetValue("x", out _);
*/

var tree = SyntaxTree(
CompilationUnit()
.WithUsings(
    SingletonList<UsingDirectiveSyntax>(
        UsingDirective(
            QualifiedName(
                QualifiedName(
                    IdentifierName("System"),
                    IdentifierName("Collections")),
                IdentifierName("Generic")))))
.WithMembers(
    List<MemberDeclarationSyntax>(
        new MemberDeclarationSyntax[]{
            GlobalStatement(
                LocalDeclarationStatement(
                    VariableDeclaration(
                        IdentifierName("var"))
                    .WithVariables(
                        SingletonSeparatedList<VariableDeclaratorSyntax>(
                            VariableDeclarator(
                                Identifier("dict"))
                            .WithInitializer(
                                EqualsValueClause(
                                    ObjectCreationExpression(
                                        GenericName(
                                            Identifier("Dictionary"))
                                        .WithTypeArgumentList(
                                            TypeArgumentList(
                                                SeparatedList<TypeSyntax>(
                                                    new SyntaxNodeOrToken[]{
                                                        PredefinedType(
                                                            Token(SyntaxKind.StringKeyword)),
                                                        Token(SyntaxKind.CommaToken),
                                                        PredefinedType(
                                                            Token(SyntaxKind.StringKeyword))}))))
                                    .WithArgumentList(
                                        ArgumentList()))))))),
            GlobalStatement(
                ExpressionStatement(
                    InvocationExpression(
                        MemberAccessExpression(
                            SyntaxKind.SimpleMemberAccessExpression,
                            IdentifierName("dict"),
                            IdentifierName("TryGetValue")))
                    .WithArgumentList(
                        ArgumentList(
                            SeparatedList<ArgumentSyntax>(
                                new SyntaxNodeOrToken[]{
                                    Argument(
                                        LiteralExpression(
                                            SyntaxKind.StringLiteralExpression,
                                            Literal("x"))),
                                    Token(SyntaxKind.CommaToken),
                                    Argument(
                                        IdentifierName("_"))
                                    .WithRefOrOutKeyword(
                                        Token(SyntaxKind.OutKeyword))})))))}))
.NormalizeWhitespace()
);

var refApi = MetadataReference.CreateFromFile(typeof(object).Assembly.Location);
var compilation = CSharpCompilation.Create("something", new []{ tree }, new []{refApi});

var diag =compilation.GetDiagnostics().Where(e => e.Severity == DiagnosticSeverity.Error).ToList();

foreach(var d in diag)
{
  Console.WriteLine(d); 
}

Unfortunately it issues the error (see online):

(4,27): error CS0103: The name '_' does not exist in the current context

What is wrong here?

With this code it emits no errors:

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

/*
using System.Collections.Generic;
var dict = new Dictionary<string, string>();
dict.TryGetValue("x", out _);
*/

var tree = SyntaxTree(
ParseCompilationUnit(@"using System.Collections.Generic;
var dict = new Dictionary<string, string>();
dict.TryGetValue(""x"", out _);").NormalizeWhitespace()
);

var refApi = MetadataReference.CreateFromFile(typeof(object).Assembly.Location);
            var com = CSharpCompilation.Create("something", new []{ tree }, new []{refApi});

var diag =com.GetDiagnostics().Where(e => e.Severity == DiagnosticSeverity.Error).ToList();

foreach(var d in diag)
{
  Console.WriteLine(d); 
}
bernd5 commented 3 years ago

By debugging the CSharp-Parser I found the correct way to create the "" IdentifierName. It is not simply `IdentifierName("")` but:

IdentifierName(Identifier(TriviaList(), SyntaxKind.UnderscoreToken, "_", "_", TriviaList()))

Very ugly - is this a roslyn bug or a roslyn quoter bug?

bernd5 commented 3 years ago

fixed by PR