Skip to content

C# Parser Fails (Root ERROR Node) with Conditionally Compiled Attribute When Member is Not in Same Conditional Block #376

@pavel-shchelkun-ilogos

Description

@pavel-shchelkun-ilogos

The tree-sitter-csharp parser fails and produces a root ERROR node when encountering a C# attribute that is conditionally compiled using #if/#endif, if the member (e.g., method, class) that the attribute applies to is not also enclosed within the same conditional compilation block.

Environment:

  • py-tree-sitter version: 0.24.0
  • tree-sitter-language-pack version: 0.7.3
  • tree-sitter-csharp grammar version: 0.23.1
  • tree-sitter-embedded-template grammar version: 0.23.2
  • Operating System: macOS Sonoma 14.5

Minimal Reproducible Example:

// test_conditional_attribute.cs
namespace TestNs
{
    public class TestClass
    {
#if SOME_CONDITION // This can be any valid preprocessor symbol
        [System.Obsolete("Conditional attribute")] // Attribute is conditional
#endif
        public void MyMethod() // Method is NOT conditional in the same block
        {
            // Method body
        }
    }
}

Observed Behavior:
When parsing the above code, the tree.root_node.type is ERROR and tree.root_node.has_error is True. The error seems to encompass a large portion of the file, starting very early if this pattern appears near the top of a declaration.

Expected Behavior:
The parser should correctly parse this valid C# construct, associating the conditional attribute with MyMethod if SOME_CONDITION were true (tree-sitter parses structure, not evaluating defines), or parsing MyMethod without the attribute if SOME_CONDITION were false. The root node should be compilation_unit and has_error should be False.

Note:
If the entire method declaration (including the attribute) is wrapped in the #if/#endif block, it parses correctly:

// This parses correctly:
namespace TestNs
{
    public class TestClass
    {
#if SOME_CONDITION
        [System.Obsolete("Conditional attribute")]
        public void MyMethod() 
        {
            // Method body
        }
#endif
    }
}

This suggests an issue with handling preprocessor directives that "interrupt" the expected contiguity between an attribute and the member it decorates. The grammar.js for tree-sitter-csharp often includes preprocessor directives in extras, which should theoretically allow them in many places, but this specific pattern seems to expose a limitation.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions