Skip to main content

big_code_analysis_ast/getter/
cpp.rs

1//! `Getter` implementation for C++.
2#![allow(clippy::wildcard_imports, clippy::enum_glob_use)]
3
4use super::*;
5use crate::c_declarator::declarator_name;
6
7// `GT2` is the template closer's own symbol, which the parser folds
8// onto `GT` (`cpp_template_closer_alias_never_reaches_kind_id`); it is
9// listed defensively.
10const GENERIC_ANGLES: GenericAngleKinds = GenericAngleKinds {
11    lists: &[
12        Cpp::TemplateArgumentList as u16,
13        Cpp::TemplateParameterList as u16,
14    ],
15    openers: &[Cpp::LT as u16],
16    closers: &[Cpp::GT as u16, Cpp::GT2 as u16],
17    is_misparse: never_misparsed,
18};
19
20impl Getter for CppCode {
21    fn get_func_space_name<'a, 'tree>(
22        node: &Node<'tree>,
23        code: &'a [u8],
24        _ancestors: Ancestors<'tree, '_>,
25    ) -> Option<&'a str> {
26        // Issue #285 contract: every `Cpp::FunctionDefinition*` alias
27        // must be enumerated here AND in `get_space_kind` below AND
28        // in `is_func` / `is_func_space` (see `src/checker.rs`).
29        // The aliased kind_ids 489/491/494 are not emitted by the
30        // currently pinned `tree-sitter-cpp` parse tables, so a
31        // dropped variant would silently fall through to the
32        // `_ => name-field` arm and yield the wrong name (or `None`).
33        match node.kind_id().into() {
34            Cpp::FunctionDefinition
35            | Cpp::FunctionDefinition2
36            | Cpp::FunctionDefinition3
37            | Cpp::FunctionDefinition4 => {
38                if let Some(op_cast) = node.first_child(|id| Cpp::OperatorCast == id) {
39                    return node_text(code, &op_cast);
40                }
41                // The name is not a child of the function node — see
42                // `crate::c_declarator` (#1208).
43                if let Some(name) = declarator_name::<Self>(node)
44                    && matches!(
45                        name.kind_id().into(),
46                        Cpp::TypeIdentifier
47                            | Cpp::Identifier
48                            | Cpp::FieldIdentifier
49                            | Cpp::DestructorName
50                            | Cpp::OperatorName
51                            | Cpp::QualifiedIdentifier
52                            | Cpp::QualifiedIdentifier2
53                            | Cpp::QualifiedIdentifier3
54                            | Cpp::QualifiedIdentifier4
55                            | Cpp::TemplateFunction
56                            | Cpp::TemplateMethod
57                    )
58                {
59                    return node_text(code, &name);
60                }
61            }
62            _ => {
63                if let Some(name) = node.child_by_field_name("name") {
64                    return node_text(code, &name);
65                }
66            }
67        }
68        None
69    }
70
71    fn get_space_kind(node: &Node) -> SpaceKind {
72        use Cpp::*;
73
74        // Issue #285 contract: keep every `FunctionDefinition*` alias
75        // listed here — see the comment above `get_func_space_name`.
76        match node.kind_id().into() {
77            FunctionDefinition | FunctionDefinition2 | FunctionDefinition3
78            | FunctionDefinition4 => SpaceKind::Function,
79            // Bodiless specifiers open no space (#1555); see
80            // `cpp_specifier_has_body`.
81            StructSpecifier if crate::checker::cpp_specifier_has_body(node) => SpaceKind::Struct,
82            ClassSpecifier if crate::checker::cpp_specifier_has_body(node) => SpaceKind::Class,
83            NamespaceDefinition => SpaceKind::Namespace,
84            TranslationUnit => SpaceKind::Unit,
85            _ => SpaceKind::Unknown,
86        }
87    }
88
89    fn get_op_type<'a>(node: &Node<'a>, ancestors: Ancestors<'a, '_>) -> TokenRole {
90        use Cpp::*;
91
92        if GENERIC_ANGLES.is_closer(node, ancestors) {
93            return TokenRole::Unknown;
94        }
95
96        // `LPAREN2` here (and the `LBRACK2`/`LBRACK3` aliases in the
97        // Elixir/Ruby impls) is a defensive arm, not an active one: every
98        // grammar's `public_symbol_map` collapses the second-alias opener
99        // to its base before `Node::kind_id()` (`ts_node_symbol`) returns,
100        // so `kind_id()` never yields the alias id and the arm cannot fire
101        // for real source. It guards against a future grammar bump that
102        // drops that collapse — at which point the alias would also need
103        // folding to its pair glyph in `get_operator_id_as_str`. The
104        // invariant is pinned by `second_alias_opener_collapses_to_base_kind_id`
105        // in `metrics/halstead.rs` (issue #768).
106        match node.kind_id().into() {
107            // Raw-string delimiter punctuation. A `raw_string_literal`
108            // carries its `R"(` opener as a bare `LPAREN` child — the
109            // kind id a call or a grouping uses — so `R"(raw)"`
110            // reported a `()` operator with no call in the source
111            // (#1314, the C++ sibling of Elixir #1256 and Ruby/Perl
112            // #1312). The literal is already an operand (below), so the
113            // delimiter is suppressed exactly when its parent is that
114            // node — the compound-leaf guard of grammar-dispatch
115            // section 5. Verified across `R"(x)"`, the custom-delimiter
116            // `R"tag(x)tag"` (which adds a `raw_string_delimiter` but
117            // keeps the same `(`), and the `LR` / `u8R` prefixed forms;
118            // the closing `)` needs no arm because #695 dropped every
119            // closer from the operator set.
120            //
121            // Parent, not ancestor. That distinction is unobservable
122            // here — `raw_string_content` is a leaf, so no `LPAREN` is
123            // ever a deeper descendant of a raw string — but parent
124            // scoping is correct by construction and keeps this arm the
125            // same shape as its siblings. `mozcpp` carries the twin.
126            LPAREN
127                if ancestors.parent_has_kind(node, RawStringLiteral as u16) =>
128            {
129                TokenRole::Unknown
130            }
131            DOT | DOTSTAR | LPAREN | LPAREN2 | COMMA | STAR | GTGT | COLON | SEMI | Return
132            | Break | Continue | If | Else | Switch | Case | Default | For | While | Goto | Do
133            | Delete | New | Try | Try2 | Catch | Throw | EQ | AMPAMP | PIPEPIPE | DASH
134            | DASHDASH | DASHGT | DASHGTSTAR | PLUS | PLUSPLUS | SLASH | PERCENT | PIPE | AMP
135            | LTLT | TILDE | LT | LTEQ | EQEQ | BANGEQ | GTEQ | GT | GT2 | LTEQGT | PLUSEQ
136            | DASHEQ | BANG | STAREQ | SLASHEQ | PERCENTEQ | GTGTEQ | LTLTEQ | AMPEQ | CARET
137            | CARETEQ | PIPEEQ | LBRACK | LBRACE | QMARK | COLONCOLON | PrimitiveType
138            | TypeSpecifier | Sizeof
139            // The ISO alternative tokens ([lex.digraph]): `not`, `compl`,
140            // `and`, `or`, `bitand`, `bitor`, `xor`, `not_eq`, `and_eq`,
141            // `or_eq`, `xor_eq` spell `!`, `~`, `&&`, `||`, `&`, `|`, `^`,
142            // `!=`, `&=`, `|=`, `^=`. The grammar gives each a kind of its
143            // own, so without these they fell into `Unknown` and `b and c`
144            // billed no operator at all (#1522). Each kind occurs only in
145            // productions its symbol also occurs in, so it needs no gate
146            // the symbol lacks. Keyed by `kind_id` like every operator, so
147            // `and` and `&&` are two distinct operators in n1: the `ops`
148            // vocabulary names operators by kind, and folding the two in
149            // the metric alone would make the two stores disagree.
150            | Not | Compl | And | Or | Bitand | Bitor | Xor | NotEq | AndEq | OrEq | XorEq
151            // A `sized_type_specifier` carries its `unsigned`/`signed`/`long`/
152            // `short` modifiers as bare keyword tokens, not as `primitive_type`
153            // children (`unsigned int` is `unsigned` + `primitive_type int`;
154            // `signed long` and `long long` have no `primitive_type` at all).
155            // Without these arms the modifiers fell into `Unknown` and were
156            // dropped, so `unsigned int` collapsed to just `int` and a standalone
157            // `signed long` contributed nothing to n1/N1 (issue #466). Each
158            // modifier has a distinct kind_id, so keying by kind_id (the default
159            // `operators` store) keeps them distinct in n1 while `long long`'s
160            // two `long` tokens correctly fold to one n1 entry but two N1 hits.
161            | Signed | Unsigned | Long | Short
162            // An overloaded operator's name (#1296). `operator +` already
163            // billed its `+` through the punctuation kind, but the grammar
164            // spells four overloadable names as whole tokens of their own
165            // — `[]`, `()`, the `""` of a user-defined literal and
166            // `co_await` — and none was in either arm, so
167            // `operator[]` and `operator()` billed nothing for the name
168            // that tells them apart. `Operator` is the `operator` keyword
169            // itself, which C# bills the same way. `CoAwait` is also the
170            // keyword of `co_await expr`, an operator there too. Each
171            // token occurs only under `operator_name`, `operator_cast` or
172            // `co_await_expression`, none of which is classified, so
173            // nothing bills one twice (grammar-dispatch section 5).
174            //
175            // `LPARENRPAREN` / `LBRACKRBRACK` are kinds of their own, not
176            // the `LPAREN` / `LBRACK` of a call or subscript, so declaring
177            // `operator[]` and applying `s[1]` are two `n1` entries, which
178            // `bca ops` renders `operator[]` and `[]` (see
179            // `get_operator_id_as_str`).
180            | LPARENRPAREN | LBRACKRBRACK | DQUOTEDQUOTE | CoAwait | Operator
181            // `co_return` and `co_yield` are the coroutine spellings of
182            // `return` and of handing a value back, so they bill as
183            // `return` does (#1547). Each is the keyword leaf of a
184            // `co_return_statement` / `co_yield_statement`, which no arm
185            // classifies, so nothing bills one twice.
186            | CoReturn | CoYield
187            // The type-declaration keywords, billed as Rust bills
188            // `struct` and C# its `class` / `struct` / `enum` (#1552).
189            // Each is the keyword leaf of a `class_specifier` /
190            // `struct_specifier` / `union_specifier` / `enum_specifier`,
191            // none of which is classified, so a specifier bills its
192            // keyword once — and `enum class` bills both of its two.
193            // The same leaf heads an elaborated type (`struct S s;`),
194            // where it is billed too, as `int` is in `int s;`.
195            //
196            // #1557 adds the remaining declaration keywords: `namespace`,
197            // `template`, `typename`, `typedef` and `using`, each the
198            // leaf of an unclassified wrapper (`namespace_definition`,
199            // `template_declaration`, `type_parameter_declaration`,
200            // `type_definition`, `alias_declaration` /
201            // `using_declaration`). A template-parameter `class` bills
202            // with them: it is the synonym of `typename`, so the two
203            // spellings of `template <class T>` still score alike, now
204            // both billed.
205            | Class | Struct | Union | Enum | Namespace | Template | Typename | Typedef | Using
206            // C's storage classes, function specifiers and type
207            // qualifiers (#1578; the derivation is on the C arm), plus
208            // C++'s own modifiers: `virtual`, `mutable`, `constinit`,
209            // `consteval`, `explicit`, `friend`, the access specifiers
210            // and the `override` / `final` virt-specifiers, and the
211            // `noexcept` of an exception specification. Each is the
212            // keyword leaf of an unclassified wrapper
213            // (`storage_class_specifier`, `type_qualifier`,
214            // `virtual_specifier`, `access_specifier`,
215            // `explicit_function_specifier`, the `noexcept` node that
216            // `Noexcept` names and `Noexcept2` is the leaf of) or sits
217            // bare in its declaration (`virtual`, `friend`), so each
218            // bills once. An access label bills as Java's `public` does,
219            // beside the `:` that already billed. `const` bills in a
220            // member-function trailer and a pointer declarator alike,
221            // and `constexpr` in `if constexpr`, which it qualifies.
222            //
223            // `override` and `final` are contextual: used as a name
224            // (`int override = 1;`) they parse as an `identifier` and
225            // stay operands, so they need no guard. `auto` is not here:
226            // in C++ it is a placeholder type, not a storage class, and
227            // the grammar emits it only under
228            // `placeholder_type_specifier`.
229            | Static | Extern | Register | Inline | Inline2 | Inline3 | Forceinline
230            | ThreadLocal | Thread | Const | Constexpr | Volatile | Volatile2 | Restrict | Restrict2
231            | Atomic | Noreturn | Noreturn2 | Nonnull | Alignas | Alignas2 | Extension
232            | Virtual | Mutable | Constinit | Consteval | Explicit | Friend | Public | Private
233            | Protected | Override | Final | Noexcept2
234                => TokenRole::Operator,
235            // `CharLiteral` — the full derivation lives on the same arm
236            // in `src/getter/c.rs` (#1316): the wrapper is the only
237            // classified node in a character literal, so it bills one
238            // operand per literal, keyed by text, and `Checker::is_string`
239            // deliberately stays without a `CharLiteral` arm.
240            //
241            // `This` was in *neither* arm until #1361, so a C++ `this`
242            // contributed nothing at all: not an operator and not an
243            // operand. Same shape as #1316, opposite of the #1351-#1355
244            // wrapper/leaf over-counts — the fix adds an arm rather than
245            // deleting one.
246            //
247            // Operand, not operator, on three grounds. **Structure**:
248            // `field_expression` is `<receiver> -> <field>`, and `p->x`
249            // already bills operands `p` / `x` around the `->` operator.
250            // Calling `this` an operator would make `this->x` a binary
251            // operator with one operand, and would score the identical
252            // AST differently for `this->x` than for `p->x`.
253            // **Role**: `this` is a pointer *rvalue* and stands exactly
254            // where a variable stands — `return this;`, `*this`,
255            // `f(this)`, which would otherwise pass an operator as a
256            // call argument — while `->` / `.` / `*` are the operators
257            // already counted acting on it. **Precedent**: eleven of the
258            // fourteen languages here bill their self-reference as an
259            // operand (Rust `Zelf`, Ruby `Zelf`, PHP's `$this`
260            // `variable_name`, the four JS-family `This` arms, and
261            // Python / ObjC / Groovy / Lua, whose grammars spell it a
262            // plain `identifier`), and the JS-family arm in
263            // `src/getter.rs` is the only *reasoned* one among them —
264            // its `MetaProperty` note calls `this` "one atomic
265            // operand". Java / C# / Kotlin disagree, but each
266            // swept `This` in as one entry in a run of keywords — under
267            // `// Operator: keywords`, `// Operator: other keywords` and
268            // `// Expression-keyword operators` respectively — grouping
269            // it by lexical class rather than by Halstead role; that
270            // three-way split is #1380, deliberately not settled here.
271            // Keeping C++ an operand also keeps it agreeing with ObjC,
272            // whose `self` is an operand and whose `.mm` files route to
273            // this very impl.
274            //
275            // Safe against a grammar-dispatch section 5 double count:
276            // `this` is a childless leaf in every position the grammar
277            // admits it — `this->x`, `(*this)`, `return this`, `f(this)`,
278            // the `[this]` and `[=, this]` lambda captures, a
279            // `decltype(this->x)` trailing return type, and a
280            // constructor body after a member-initialiser list — and no
281            // node that *contains* it (`field_expression`,
282            // `pointer_expression`, `lambda_capture_specifier`,
283            // `argument_list`) is classified, so it can double-count in
284            // neither direction. Neither generated enum aliases the rule
285            // (`This = 215` is the sole variant spelling `"this"` in
286            // `language_cpp.rs` and `language_mozcpp.rs`), so there is no
287            // `This2` for a single-variant arm to miss;
288            // `cpp_this_is_a_childless_unaliased_leaf` pins both facts.
289            //
290            // C++23's explicit object parameter (`int g(this S&& self)`)
291            // does *not* reach this arm: the pinned grammar cannot parse
292            // it and emits `type_identifier "this"` plus an `ERROR`. That
293            // spelling is already an operand via `TypeIdentifier`, so the
294            // construct is unaffected either way.
295            Identifier | TypeIdentifier | FieldIdentifier | RawStringLiteral | StringLiteral
296            | CharLiteral | NumberLiteral | True | False | Null | This | DOTDOTDOT => {
297                TokenRole::Operand
298            }
299            // A namespace identifier is an operand only where it
300            // *names* a namespace; the same kind also spells the
301            // qualifier in `ns::thing`, which the final arm leaves
302            // `Unknown` (#1096).
303            NamespaceIdentifier if ancestors.parent_has_kind(node, NamespaceDefinition as u16) => {
304                TokenRole::Operand
305            }
306            _ => TokenRole::Unknown,
307        }
308    }
309
310    fn get_operator_spelling<'a>(
311        node: &Node<'a>,
312        ancestors: Ancestors<'a, '_>,
313    ) -> Option<&'static str> {
314        GENERIC_ANGLES.opener_spelling(node, ancestors)
315    }
316
317    // `get_operator!`'s three bracket arms, plus the two overloaded
318    // operator names (#1296). The grammar names `LPARENRPAREN` /
319    // `LBRACKRBRACK` `()` / `[]`, the same text a call's and a
320    // subscript's opener render as, so `bca ops` would list one spelling
321    // for two `n1` entries. Rendering each as the operator it names keeps
322    // every listed operator distinct (lesson 4).
323    fn get_operator_id_as_str(id: u16) -> &'static str {
324        let typ = id.into();
325        match typ {
326            Cpp::LPAREN => "()",
327            Cpp::LBRACK => "[]",
328            Cpp::LBRACE => "{}",
329            Cpp::LPARENRPAREN => "operator()",
330            Cpp::LBRACKRBRACK => "operator[]",
331            _ => typ.into(),
332        }
333    }
334}