big_code_analysis_ast/getter/mozcpp.rs
1//! `Getter` implementation for MozC++.
2#![allow(clippy::wildcard_imports, clippy::enum_glob_use)]
3
4use super::*;
5use crate::c_declarator::declarator_name;
6
7// `GT2` is listed defensively, as in the Cpp getter.
8const GENERIC_ANGLES: GenericAngleKinds = GenericAngleKinds {
9 lists: &[
10 Mozcpp::TemplateArgumentList as u16,
11 Mozcpp::TemplateParameterList as u16,
12 ],
13 openers: &[Mozcpp::LT as u16],
14 closers: &[Mozcpp::GT as u16, Mozcpp::GT2 as u16],
15 is_misparse: never_misparsed,
16};
17
18impl Getter for MozcppCode {
19 fn get_func_space_name<'a, 'tree>(
20 node: &Node<'tree>,
21 code: &'a [u8],
22 _ancestors: Ancestors<'tree, '_>,
23 ) -> Option<&'a str> {
24 // Issue #285 contract: every `Mozcpp::FunctionDefinition*` alias
25 // must be enumerated here AND in `get_space_kind` below AND
26 // in `is_func` / `is_func_space` (see `src/checker.rs`).
27 // The aliased kind_ids 489/491/494 are not emitted by the
28 // currently pinned `tree-sitter-mozcpp` parse tables, so a
29 // dropped variant would silently fall through to the
30 // `_ => name-field` arm and yield the wrong name (or `None`).
31 match node.kind_id().into() {
32 Mozcpp::FunctionDefinition
33 | Mozcpp::FunctionDefinition2
34 | Mozcpp::FunctionDefinition3
35 | Mozcpp::FunctionDefinition4 => {
36 if let Some(op_cast) = node.first_child(|id| Mozcpp::OperatorCast == id) {
37 return node_text(code, &op_cast);
38 }
39 // The name is not a child of the function node — see
40 // `crate::c_declarator` (#1208).
41 if let Some(name) = declarator_name::<Self>(node)
42 && matches!(
43 name.kind_id().into(),
44 Mozcpp::TypeIdentifier
45 | Mozcpp::Identifier
46 | Mozcpp::FieldIdentifier
47 | Mozcpp::DestructorName
48 | Mozcpp::OperatorName
49 | Mozcpp::QualifiedIdentifier
50 | Mozcpp::QualifiedIdentifier2
51 | Mozcpp::QualifiedIdentifier3
52 | Mozcpp::QualifiedIdentifier4
53 | Mozcpp::TemplateFunction
54 | Mozcpp::TemplateMethod
55 )
56 {
57 return node_text(code, &name);
58 }
59 }
60 _ => {
61 if let Some(name) = node.child_by_field_name("name") {
62 return node_text(code, &name);
63 }
64 }
65 }
66 None
67 }
68
69 fn get_space_kind(node: &Node) -> SpaceKind {
70 use Mozcpp::*;
71
72 // Issue #285 contract: keep every `FunctionDefinition*` alias
73 // listed here — see the comment above `get_func_space_name`.
74 match node.kind_id().into() {
75 FunctionDefinition | FunctionDefinition2 | FunctionDefinition3
76 | FunctionDefinition4 => SpaceKind::Function,
77 // Bodiless specifiers open no space (#1555); see
78 // `cpp_specifier_has_body`.
79 StructSpecifier if crate::checker::cpp_specifier_has_body(node) => SpaceKind::Struct,
80 ClassSpecifier if crate::checker::cpp_specifier_has_body(node) => SpaceKind::Class,
81 NamespaceDefinition => SpaceKind::Namespace,
82 TranslationUnit => SpaceKind::Unit,
83 _ => SpaceKind::Unknown,
84 }
85 }
86
87 fn get_op_type<'a>(node: &Node<'a>, ancestors: Ancestors<'a, '_>) -> TokenRole {
88 use Mozcpp::*;
89
90 if GENERIC_ANGLES.is_closer(node, ancestors) {
91 return TokenRole::Unknown;
92 }
93
94 // `LPAREN2` is a defensive arm (collapsed to `LPAREN` before
95 // `kind_id()`; #768, see the Cpp note).
96 match node.kind_id().into() {
97 // Raw-string delimiter punctuation — the twin of the Cpp
98 // arm, which carries the derivation (#1314). `.mozcpp` owns
99 // no file extension, so nothing routes to it and it gets no
100 // integration-snapshot coverage; the parity assertion in
101 // `tests/parity/cpp_mozcpp_parity.rs` is what keeps this
102 // clone from drifting.
103 LPAREN
104 if ancestors.parent_has_kind(node, RawStringLiteral as u16) =>
105 {
106 TokenRole::Unknown
107 }
108 DOT | DOTSTAR | LPAREN | LPAREN2 | COMMA | STAR | GTGT | COLON | SEMI | Return
109 | Break | Continue | If | Else | Switch | Case | Default | For | While | Goto | Do
110 | Delete | New | Try | Try2 | Catch | Throw | EQ | AMPAMP | PIPEPIPE | DASH
111 | DASHDASH | DASHGT | DASHGTSTAR | PLUS | PLUSPLUS | SLASH | PERCENT | PIPE | AMP
112 | LTLT | TILDE | LT | LTEQ | EQEQ | BANGEQ | GTEQ | GT | GT2 | LTEQGT | PLUSEQ
113 | DASHEQ | BANG | STAREQ | SLASHEQ | PERCENTEQ | GTGTEQ | LTLTEQ | AMPEQ | CARET
114 | CARETEQ | PIPEEQ | LBRACK | LBRACE | QMARK | COLONCOLON | PrimitiveType
115 | TypeSpecifier | Sizeof
116 // The ISO alternative tokens (`not`, `and`, `not_eq`, …) —
117 // the derivation is on the Cpp twin (#1522).
118 | Not | Compl | And | Or | Bitand | Bitor | Xor | NotEq | AndEq | OrEq | XorEq
119 // A `sized_type_specifier` carries its `unsigned`/`signed`/`long`/
120 // `short` modifiers as bare keyword tokens, not as `primitive_type`
121 // children (`unsigned int` is `unsigned` + `primitive_type int`;
122 // `signed long` and `long long` have no `primitive_type` at all).
123 // Without these arms the modifiers fell into `Unknown` and were
124 // dropped, so `unsigned int` collapsed to just `int` and a standalone
125 // `signed long` contributed nothing to n1/N1 (issue #466). Each
126 // modifier has a distinct kind_id, so keying by kind_id (the default
127 // `operators` store) keeps them distinct in n1 while `long long`'s
128 // two `long` tokens correctly fold to one n1 entry but two N1 hits.
129 | Signed | Unsigned | Long | Short
130 // An overloaded operator's name, the `operator` keyword and
131 // `co_await` — the derivation is on the Cpp twin (#1296).
132 | LPARENRPAREN | LBRACKRBRACK | DQUOTEDQUOTE | CoAwait | Operator
133 // `co_return` / `co_yield`, billed as `return` is — the
134 // derivation is on the Cpp twin (#1547).
135 | CoReturn | CoYield
136 // The type-declaration keywords — the derivation is on the
137 // Cpp twin (#1552, #1557).
138 | Class | Struct | Union | Enum | Namespace | Template | Typename | Typedef | Using
139 => TokenRole::Operator,
140 // `CharLiteral` — the full derivation lives on the same arm
141 // in `src/getter/c.rs` (#1316): the wrapper is the only
142 // classified node in a character literal, so it bills one
143 // operand per literal, keyed by text, and `Checker::is_string`
144 // deliberately stays without a `CharLiteral` arm.
145 //
146 // `This` — the operator-vs-operand derivation lives on the
147 // same arm in `src/getter/cpp.rs` (#1361), where the fork's
148 // upstream carries it. In short: `this` had been in neither
149 // arm and so contributed nothing, and it is an operand
150 // because it is the *receiver* half of `<recv> -> <field>`,
151 // exactly where `p` stands in `p->x`. Mozcpp owns no file
152 // extension, so nothing routes to it and no integration
153 // snapshot reaches this clone — `cpp_and_mozcpp_agree_on_this`
154 // in `tests/parity/cpp_mozcpp_parity.rs` is what keeps it
155 // from drifting, the same guard the `RawStringLiteral`
156 // `LPAREN` arm above relies on.
157 Identifier | TypeIdentifier | FieldIdentifier | RawStringLiteral | StringLiteral
158 | CharLiteral | NumberLiteral | True | False | Null | This | DOTDOTDOT => {
159 TokenRole::Operand
160 }
161 // A namespace identifier is an operand only where it
162 // *names* a namespace; the same kind also spells the
163 // qualifier in `ns::thing`, which the final arm leaves
164 // `Unknown` (#1096).
165 NamespaceIdentifier if ancestors.parent_has_kind(node, NamespaceDefinition as u16) => {
166 TokenRole::Operand
167 }
168 _ => TokenRole::Unknown,
169 }
170 }
171
172 fn get_operator_spelling<'a>(
173 node: &Node<'a>,
174 ancestors: Ancestors<'a, '_>,
175 ) -> Option<&'static str> {
176 GENERIC_ANGLES.opener_spelling(node, ancestors)
177 }
178
179 // The overloaded operator names render as the operator they name;
180 // the derivation is on the Cpp twin (#1296).
181 fn get_operator_id_as_str(id: u16) -> &'static str {
182 let typ = id.into();
183 match typ {
184 Mozcpp::LPAREN => "()",
185 Mozcpp::LBRACK => "[]",
186 Mozcpp::LBRACE => "{}",
187 Mozcpp::LPARENRPAREN => "operator()",
188 Mozcpp::LBRACKRBRACK => "operator[]",
189 _ => typ.into(),
190 }
191 }
192}