fix(cpp): a function is named by its declarator, never by its noexcept, throw or requires clause (#2727)

Both C++ extractors name a function by the first qualified identifier
found breadth-first in its declarator, which is how an out-of-line
`Foo::bar` is named. The walk skipped only the parameter list and the
trailing return type, so a qualified name in the clauses after them
became the name of a function whose own name is plain. nlohmann/json's
constructor `basic_json(CompatibleType&& val) noexcept(noexcept(
JSONSerializer<U>::to_json(…)))` was indexed as a method
`JSONSerializer::to_json` that doesn't exist, with no node for the
constructor; `throw(std::bad_alloc)` named a method `std::bad_alloc`,
and a trailing `requires std::integral<T>` a function `std::integral<T>`.

The walk now also skips `noexcept`, `throw_specifier` and
`requires_clause`, in the TS extractor and the kernel alike; the parity
fixture covers all three.

Measured on main 226ac930 with fresh indexes, and byte-identical again
after syncing onto e97012eb: json 42, abseil 28 and
protobuf 1 phantom nodes become the real functions at the same lines;
fmt, leveldb, rocksdb, redis and three controls in other languages are
byte-identical. Kernel parity sweeps on json, fmt, abseil, leveldb and
rocksdb find no file with diffs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Colby Mchenry
2026-10-21 04:20:15 +00:00
committed by GitHub
co-authored by Claude Opus 5.5
parent e97012eb31
commit f66d064279
5 changed files with 172 additions and 6 deletions
+8 -3
View File
@@ -2455,8 +2455,10 @@ fn cpp_qualified_id_name(qid: Node) -> Option<Node> {
}
/// findDeclaratorQualifiedId (languages/c-cpp.ts:13): BFS for the declarator's
/// `qualified_identifier`, skipping parameter_list + trailing_return_type so a
/// qualified PARAMETER type can't be mistaken for the method name.
/// `qualified_identifier`, skipping the parts that hold types or expressions
/// (DECLARATOR_NON_NAME_PARTS: parameter_list, trailing_return_type, noexcept,
/// throw_specifier, requires_clause) so a qualified PARAMETER type, or a call
/// in `noexcept(…)`, can't be mistaken for the method name.
fn find_declarator_qualified_id(declarator: Node) -> Option<Node> {
let mut queue: VecDeque<Node> = VecDeque::new();
queue.push_back(declarator);
@@ -2466,7 +2468,10 @@ fn find_declarator_qualified_id(declarator: Node) -> Option<Node> {
}
for i in 0..current.named_child_count() {
if let Some(child) = current.named_child(i) {
if child.kind() != "parameter_list" && child.kind() != "trailing_return_type" {
if !matches!(
child.kind(),
"parameter_list" | "trailing_return_type" | "noexcept" | "throw_specifier" | "requires_clause"
) {
queue.push_back(child);
}
}