tree-sitter / tree-sitter-c

C grammar for tree-sitter
MIT License
225 stars 100 forks source link

Fix preproc alternatives (elif, elifdef, else) #203

Closed touzeauv closed 5 months ago

touzeauv commented 5 months ago

Currently, there are two functions ("elseBlock" and "elifBlock") that are used to generate alternatives (i.e. "#elif", "#elifdef", "#else"). More precisely, "elseBlock" generates "#else" and "#elif" nodes, and elifBlock generates "#elifdef" nodes.

The "#ifdef" is currently the only node offering a choice between elseBlock and elifBlock. Other conditional directives only allow elseBlock and thus do not offer the possiblity to have (for example) a "#if" followed by a "#elifdef".

Concretely, consider the following code:

#if defined(FOO)
#elifdef BAR
#endif

This is currently parsed as a preproc_call

(translation_unit [0, 0] - [3, 0]
  (preproc_if [0, 0] - [2, 6]
    condition: (preproc_defined [0, 4] - [0, 16]
      (identifier [0, 12] - [0, 15]))
    (preproc_call [1, 0] - [2, 0]
      directive: (preproc_directive [1, 0] - [1, 8])
      argument: (preproc_arg [1, 9] - [1, 12]))))

I can't think about any reason to disallow this behavior so I suggest to merge both function into a single one that generate all possible alternatives. For the given example, this would result in

(translation_unit [0, 0] - [3, 0]
  (preproc_if [0, 0] - [2, 6]
    condition: (preproc_defined [0, 4] - [0, 16]
      (identifier [0, 12] - [0, 15]))
    alternative: (preproc_elifdef [1, 0] - [1, 12]
      name: (identifier [1, 9] - [1, 12]))))

This commit also adds a test to check for regression