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