Parsing C# Conditional Compilation statements in roslyn
Asked Answered
N

2

6

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?

Nonconformance answered 1/12, 2016 at 13:23 Comment(2)
If you are willing to consider a non-Roslyn solution, I can describe one where this process is extremely straightforward.Singularity
@ira-baxter it has to be roslyn specifcNonconformance
L
2

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.

Larrigan answered 1/12, 2016 at 13:47 Comment(9)
I am not using visitors so I tried calling DescendantNodesAndSelf on Method node but I don't see ConditionalDirectiveTriviaSyntax in the list instead there is a BlockSyntax (which is too generic)Nonconformance
@Nonconformance BlockSyntax is the parent of all your method body. Within this block you will find statements and then you will find what you need. So if your original node is MethodDeclerationSyntax you need to deep into to find what you want.Larrigan
@Nonconformance I writing from my phone now.. I can update my answer soon and show you exactly how to do that (if you need)Larrigan
This is what I get from thae Block node ` [0]: SyntaxNodeOrToken Block { #if DEBUG return 1; #else return 2; #endif } [1]: SyntaxNodeOrToken OpenBraceToken { [2]: SyntaxNodeOrToken ReturnStatement return 2; [3]: SyntaxNodeOrToken ReturnKeyword return [4]: SyntaxNodeOrToken NumericLiteralExpression 2 [5]: SyntaxNodeOrToken NumericLiteralToken 2 [6]: SyntaxNodeOrToken SemicolonToken ; [7]: SyntaxNodeOrToken CloseBraceToken }`Nonconformance
@Nonconformance When Ill get back to the office Ill update the answerLarrigan
Thanks @dudi-keleti I'll let you know the resultNonconformance
I've fixed my problem by using SyntaxToken ContainsDirectives property. So on } token if ContainsDirectives is true or on { token's GetNextToken().ContainsDirectives is true then it is part of directive.Nonconformance
@Nonconformance Yes true. If you just need to know if it contains directive .Larrigan
I am marking your answer as correct as I didn't provide complete information.Nonconformance
P
2

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

In fact, it's in the LeadingTrivia of the return keyword in return 2;. The lead trivia for return is:

  • IfDirectiveTrivia (#if DEBUG)
  • DisableTextTrivia (return 1;)
  • ElseDirectiveTrivia (#else)
Peacoat answered 1/12, 2016 at 13:48 Comment(1)
Thanks for giving me some information on how to find that information but not sure if I'll do that way because it looks I've to parse a lot.Nonconformance

© 2022 - 2024 — McMap. All rights reserved.