Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Parsing C# Conditional Compilation statements in roslyn

How do I parse C# conditional compilation statement using Roslyn.

In the following code, I want Roslyn to give the Conditional compilation statement node.

public abstract class TestClass
{
    public int Get()
    {
    #if DEBUG
        return 1;
    #else
        return 2;
    #endif
    }
}

I don't get conditional compilation node in SyntaxTree and neither it is part of LeadingTrivia of } or TrailingTrivia of {

What I get in LeadingTrivia of } is "\t\t#endif\r\n\t\t" and TrailingTrivia of { is "\r\n" which is not complete conditional compilation statement.

Can someone point me to the right direction?

like image 863
adeel41 Avatar asked Dec 01 '16 13:12

adeel41


1 Answers

If you parse the nodes via visitor (CSharpSyntaxRewriter\Walker) you need to override:

public override SyntaxNode VisitIfDirectiveTrivia(IfDirectiveTriviaSyntax node)
{
}

public override SyntaxNode VisitElseDirectiveTrivia(ElseDirectiveTriviaSyntax node)
{
}

public override SyntaxNode VisitEndIfDirectiveTrivia(EndIfDirectiveTriviaSyntax node)
{
}

If you want to get this not via visitors, you can do this:

node.DescendantNodesAndSelf().OfType<ConditionalDirectiveTriviaSyntax>();

You can take a look here to see what Roslyn generating for your conditional.

Update

I checked it, and indeed its kind of complicated here, because its not Node of course. The node is just return 2 or if you write #define DEBUG its return 1.

So, in case you have that conditional directive inside method you can do something like this:

// First get the relevant method (or any other parent block depending in the case)
var method = root.DescendantNodes().OfType<MethodDeclarationSyntax>().First();

// Then, you can write method similar to this
static SyntaxTrivia GetConditionalDirectiveTrivia(SyntaxNode node, SyntaxKind kind)
{
    foreach (SyntaxNode syntaxNode in node.DescendantNodes())
    {
        var trivia = syntaxNode.GetLeadingTrivia().FirstOrDefault(t => t.Kind() == kind);
        if (trivia != default(SyntaxTrivia))
            return trivia;
        GetConditionalDirectiveTrivia(syntaxNode, kind);
    }
    return default(SyntaxTrivia);
}

And call it like this:

GetConditionalDirectiveTrivia(method, SyntaxKind.IfDirectiveTrivia);

You can find it in more ways without the Kind but I think its good enough.

Note, that its just an example. I return here only the first trivia.

In your real code, you can write more elegant solution (maybe even an extension method) to get for example AllTriviaOfKind or something else that fit your requirements. Also, you can return the token trivia or the parent node, if the trivia itself is useless for you, whatever you want\need.

I hope its help.

like image 93
Dudi Keleti Avatar answered Oct 12 '22 23:10

Dudi Keleti