tree-sitter / tree-sitter/tree-sitter-cpp
bug: anonymous pointer-to-member declarator (`Class::*`) is not parsed in abstract-declarator contexts (`using`, `sizeof`, cast)
Nobody has claimed this yet.
- Dominant language
- JavaScript
- Stars
- 453
- Forks
- 185
- PR merge metrics
- No merged PRs in 30d
Description
Did you check existing issues?
- I have read all the tree-sitter docs if it relates to using the parser
- I have searched the existing issues of tree-sitter-cpp
Tree-Sitter CLI Version, if relevant
tree-sitter-cpp 0.23.4, tree-sitter 0.23.2 (Python bindings)
Describe the bug
The grammar handles the named pointer-to-member declarator (void (C::*p)(int)) but has no production for the anonymous / abstract form (C::*) used in type-ids. As a result, every C++ construct whose syntax requires an abstract type-id containing a pointer-to-member fails to parse:
- alias declarations:
using F = void (C::*)(int); sizeof(void (C::*)(int))- C-style casts:
(void (C::*)(int))p - (presumably) function-pointer parameter types, template arguments,
decltype, etc.
In every case, has_error is True and the parse tree contains either a MISSING "type_identifier" inside qualified_identifier or a stray abstract_pointer_declarator (*) attached as a sibling.
This bug appears to be the underlying parser misparse behind the error-recovery cascade observed in #352 (which frames the same construct as a template-call / statement-ordering recovery issue). It is distinct from #348 (C++‑style static_cast / typeid misparse) and from #102 / #113 (which fixed only the named declarator).
Steps To Reproduce / Bad Parse Tree
The cleanest reproducer is an alias declaration, because the RHS is required by the C++ grammar to be a type-id — there is no cast / call-expression ambiguity to argue about. The snippet compiles cleanly with g++ -c:
struct C;
using F = void (C::*)(int);
Parses as:
(translation_unit
(struct_specifier
(struct)
name: (type_identifier))
(;)
(alias_declaration
(using)
name: (type_identifier)
(=)
type: (type_descriptor
type: (primitive_type)
declarator: (abstract_function_declarator
declarator: (abstract_function_declarator
parameters: (parameter_list
(()
(parameter_declaration
type: (qualified_identifier
scope: (namespace_identifier)
(::)
name: (MISSING "type_identifier"))
declarator: (abstract_pointer_declarator
(*)))
())))
parameters: (parameter_list
(()
(parameter_declaration
type: (primitive_type))
()))))
(;)))
;; has_error = True
The grammar tries to interpret the inner (C::*) as a parameter_list whose sole parameter_declaration has type qualified_identifier (C::) with a MISSING type_identifier, plus an orphan abstract_pointer_declarator (*).
The same misparse appears in sizeof:
struct C;
auto s = sizeof(void (C::*)(int));
(... (sizeof_expression
(sizeof)
value: (parenthesized_expression
(()
(call_expression
function: (call_expression
function: (primitive_type)
arguments: (argument_list
(()
(qualified_identifier
scope: (namespace_identifier)
(::)
name: (pointer_type_declarator
(*)
declarator: (MISSING "type_identifier")))
())))
arguments: (argument_list
(()
(ERROR
(primitive_type))
())))
()))) ...)
;; has_error = True
And in a C-style cast:
struct C;
auto x = (void (C::*)(int))0;
(... value: (parenthesized_expression
(()
(call_expression
function: (call_expression
function: (primitive_type)
arguments: (argument_list
(()
(qualified_identifier
scope: (namespace_identifier)
(::)
name: (pointer_type_declarator
(*)
declarator: (MISSING "type_identifier")))
())))
arguments: (argument_list
(()
(ERROR
(primitive_type))
())))
())) ...)
;; has_error = True
Expected Behavior / Parse Tree
All three abstract-form snippets should parse with has_error = False. The RHS type of the alias should be a type_descriptor whose declarator is some abstract pointer-to-member declarator (mirroring the named form), roughly:
type: (type_descriptor
type: (primitive_type) ; void
declarator: (abstract_function_declarator
declarator: (abstract_parenthesized_declarator
(()
(qualified_identifier ; C::*
scope: (namespace_identifier)
(::)
name: (abstract_pointer_declarator
(*)))
()))
parameters: (parameter_list (() (parameter_declaration type: (primitive_type)) ())))) ; (int)
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by reproducing the alias, sizeof, and C-style cast examples with tree-sitter-cpp 0.23.4 and inspect the grammar production for named pointer-to-member declarators. Add coverage for the anonymous abstract form and verify that all three snippets parse with has_error = False and produce an abstract pointer-to-member declarator rather than recovery nodes.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- cpp
- Domain
- compilers
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 55/100