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
//! `Getter` implementation for C.
#![allow(clippy::wildcard_imports, clippy::enum_glob_use)]
use super::*;
use crate::c_declarator::declarator_name;
impl Getter for CCode {
fn get_func_space_name<'a, 'tree>(
node: &Node<'tree>,
code: &'a [u8],
_ancestors: Ancestors<'tree, '_>,
) -> Option<&'a str> {
// Issue #285 contract: every `C::FunctionDefinition*` alias must
// be enumerated here AND in `get_space_kind` below AND in
// `is_func` / `is_func_space` (see `src/checker.rs`). C has no
// C++ name forms (operator-cast / destructor / operator / qualified
// / template names), so the declarator name is a plain identifier.
match node.kind_id().into() {
C::FunctionDefinition | C::FunctionDefinition2 => {
// 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(),
C::TypeIdentifier | C::Identifier | C::FieldIdentifier
)
{
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 C::*;
// C has no classes/namespaces, and struct/union/enum hold no
// functions, so the only spaces are functions and the unit
// (matching `is_func_space` in `src/checker.rs`, #285).
match node.kind_id().into() {
FunctionDefinition | FunctionDefinition2 => SpaceKind::Function,
TranslationUnit => SpaceKind::Unit,
_ => SpaceKind::Unknown,
}
}
fn get_op_type<'a>(node: &Node<'a>, _ancestors: Ancestors<'a, '_>) -> TokenRole {
use C::*;
// C's operator alphabet is the C++ set minus the C++-only forms
// (`.*` / `->*`, `new` / `delete`, `try` / `catch` / `throw`,
// `<=>`). Raw string literals, the `namespace`-qualified
// identifier and template lists likewise do not exist in C, so
// nor does the C++ getters' `GT2` arm — a defensive one even
// there, since the runtime folds the template-list closer onto
// `GT` (`cpp_template_closer_alias_never_reaches_kind_id`).
// `LPAREN2` is a defensive arm (collapsed to `LPAREN` before
// `kind_id()`; #768, see the Cpp note).
match node.kind_id().into() {
DOT | LPAREN | LPAREN2 | COMMA | STAR | GTGT | COLON | SEMI | Return | Break
| Continue | If | Else | Switch | Case | Default | For | While | Goto | Do | EQ
| AMPAMP | PIPEPIPE | DASH | DASHDASH | DASHGT | PLUS | PLUSPLUS | SLASH | PERCENT
| PIPE | AMP | LTLT | TILDE | LT | LTEQ | EQEQ | BANGEQ | GTEQ | GT | PLUSEQ
| DASHEQ | BANG | STAREQ | SLASHEQ | PERCENTEQ | GTGTEQ | LTLTEQ | AMPEQ | CARET
| CARETEQ | PIPEEQ | LBRACK | LBRACE | QMARK | PrimitiveType
| TypeSpecifier | Sizeof
// 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 fall into `Unknown` and are
// dropped, so `unsigned int` collapses to just `int` and a standalone
// `signed long` contributes 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
// `struct` / `union` / `enum`, which C++ bills since #1552,
// and `typedef` since #1557. They are C keywords, not
// C++-only forms, so leaving them out here would score a
// header differently by the grammar that parsed it. The
// derivation is on the Cpp arm.
| Struct | Union | Enum | Typedef
// Storage classes, function specifiers and type qualifiers,
// billed as every class language bills its modifiers
// (#1578). Each is the keyword leaf of an unclassified
// `storage_class_specifier` / `type_qualifier` /
// `alignas_qualifier`, so it bills once, as `const` does in
// a pointer declarator (`int * const p`). The vendor
// spellings (`__inline`, `__restrict__`, `__thread`,
// `__volatile__`, …) are kinds of their own and bill under
// their own name; `__volatile__` the grammar emits only as a
// GNU `asm` qualifier, where `volatile` bills too. `auto`
// is a storage class in C, the only place this grammar
// emits it. `__extension__` bills in an expression too,
// where it is the prefix of an `extension_expression`.
// Attributes (`__attribute__`, `__declspec`, `[[…]]`) and
// the MS calling conventions stay unbilled: they annotate a
// declaration rather than spell part of it. `cpp.rs` copies
// this list, though `restrict` and `noreturn` are no C++
// keywords: `int restrict = 0;` is a valid C++ variable, as
// `int noreturn = 1;` is in C without `<stdnoreturn.h>`. Both
// grammars still parse the name as the qualifier before a
// zero-width `identifier`, so the keyword bills and no
// operand does — the grammar decides that, not this arm, as
// `objc.rs` notes for `nullable`.
| Static | Extern | Register | Auto | Inline | Inline2 | Inline3 | Forceinline
| ThreadLocal | Thread | Const | Constexpr | Volatile | Volatile2 | Restrict | Restrict2
| Atomic | Noreturn | Noreturn2 | Nonnull | Alignas | Alignas2 | Extension
=> TokenRole::Operator,
// `CharLiteral` joins the operand list here for the whole
// C family — `cpp.rs`, `mozcpp.rs` and `objc.rs` carry the
// same arm and point back at this note (#1316). Before it a
// character literal contributed *nothing*: not an operator
// (correct) and not an operand (wrong), so `char b = 'x';`
// scored `b` alone while Rust, Java, Kotlin, C#, Go and
// Elixir all counted their character literal.
//
// Listing the wrapper is safe against the grammar-dispatch
// section 5 double count because none of `char_literal`'s
// children is classified: the opening delimiter (`'`, and
// the `L'` / `u'` / `U'` / `u8'` prefixed spellings, each a
// distinct kind), the closing `'`, and the `character` /
// `escape_sequence` payload are all absent from every arm
// in this match. So a literal bills exactly one operand
// however many `character` leaves it holds — a multi-char
// constant like `'ab'` has two and still counts once.
//
// Operands are keyed by source text (`get_operand_id`), so
// `'x'` and `L'x'` are separate vocabulary entries while a
// repeated `'x'` folds to one `n2` with two `N2` hits.
//
// `Checker::is_string` deliberately does **not** grow a
// `CharLiteral` arm to match: a char is not a string, the
// same split Rust and Go apply to their char / rune
// literals. The alterator flattens `CharLiteral` in all four
// languages and records that decision alongside each arm;
// `c_family_char_literal_is_not_a_string` in `checker.rs`
// pins it both ways.
Identifier | TypeIdentifier | FieldIdentifier | StringLiteral | CharLiteral
| NumberLiteral | True | False | Null | DOTDOTDOT => TokenRole::Operand,
_ => TokenRole::Unknown,
}
}
get_operator!(C);
}