big_code_analysis_ast/getter/c.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
7impl Getter for CCode {
8 fn get_func_space_name<'a, 'tree>(
9 node: &Node<'tree>,
10 code: &'a [u8],
11 _ancestors: Ancestors<'tree, '_>,
12 ) -> Option<&'a str> {
13 // Issue #285 contract: every `C::FunctionDefinition*` alias must
14 // be enumerated here AND in `get_space_kind` below AND in
15 // `is_func` / `is_func_space` (see `src/checker.rs`). C has no
16 // C++ name forms (operator-cast / destructor / operator / qualified
17 // / template names), so the declarator name is a plain identifier.
18 match node.kind_id().into() {
19 C::FunctionDefinition | C::FunctionDefinition2 => {
20 // The name is not a child of the function node — see
21 // `crate::c_declarator` (#1208).
22 if let Some(name) = declarator_name::<Self>(node)
23 && matches!(
24 name.kind_id().into(),
25 C::TypeIdentifier | C::Identifier | C::FieldIdentifier
26 )
27 {
28 return node_text(code, &name);
29 }
30 }
31 _ => {
32 if let Some(name) = node.child_by_field_name("name") {
33 return node_text(code, &name);
34 }
35 }
36 }
37 None
38 }
39
40 fn get_space_kind(node: &Node) -> SpaceKind {
41 use C::*;
42
43 // C has no classes/namespaces, and struct/union/enum hold no
44 // functions, so the only spaces are functions and the unit
45 // (matching `is_func_space` in `src/checker.rs`, #285).
46 match node.kind_id().into() {
47 FunctionDefinition | FunctionDefinition2 => SpaceKind::Function,
48 TranslationUnit => SpaceKind::Unit,
49 _ => SpaceKind::Unknown,
50 }
51 }
52
53 fn get_op_type<'a>(node: &Node<'a>, _ancestors: Ancestors<'a, '_>) -> TokenRole {
54 use C::*;
55
56 // C's operator alphabet is the C++ set minus the C++-only forms
57 // (`.*` / `->*`, `new` / `delete`, `try` / `catch` / `throw`,
58 // `<=>`, the `>>`-closing `GT2`). Raw string literals and the
59 // `namespace`-qualified identifier likewise do not exist in C.
60 // `LPAREN2` is a defensive arm (collapsed to `LPAREN` before
61 // `kind_id()`; #768, see the Cpp note).
62 match node.kind_id().into() {
63 DOT | LPAREN | LPAREN2 | COMMA | STAR | GTGT | COLON | SEMI | Return | Break
64 | Continue | If | Else | Switch | Case | Default | For | While | Goto | Do | EQ
65 | AMPAMP | PIPEPIPE | DASH | DASHDASH | DASHGT | PLUS | PLUSPLUS | SLASH | PERCENT
66 | PIPE | AMP | LTLT | TILDE | LT | LTEQ | EQEQ | BANGEQ | GTEQ | GT | PLUSEQ
67 | DASHEQ | BANG | STAREQ | SLASHEQ | PERCENTEQ | GTGTEQ | LTLTEQ | AMPEQ | CARET
68 | CARETEQ | PIPEEQ | LBRACK | LBRACE | QMARK | PrimitiveType
69 | TypeSpecifier | Sizeof
70 // A `sized_type_specifier` carries its `unsigned`/`signed`/`long`/
71 // `short` modifiers as bare keyword tokens, not as `primitive_type`
72 // children (`unsigned int` is `unsigned` + `primitive_type int`;
73 // `signed long` and `long long` have no `primitive_type` at all).
74 // Without these arms the modifiers fall into `Unknown` and are
75 // dropped, so `unsigned int` collapses to just `int` and a standalone
76 // `signed long` contributes nothing to n1/N1 (issue #466). Each
77 // modifier has a distinct kind_id, so keying by kind_id (the default
78 // `operators` store) keeps them distinct in n1 while `long long`'s
79 // two `long` tokens correctly fold to one n1 entry but two N1 hits.
80 | Signed | Unsigned | Long | Short => TokenRole::Operator,
81 // `CharLiteral` joins the operand list here for the whole
82 // C family — `cpp.rs`, `mozcpp.rs` and `objc.rs` carry the
83 // same arm and point back at this note (#1316). Before it a
84 // character literal contributed *nothing*: not an operator
85 // (correct) and not an operand (wrong), so `char b = 'x';`
86 // scored `b` alone while Rust, Java, Kotlin, C#, Go and
87 // Elixir all counted their character literal.
88 //
89 // Listing the wrapper is safe against the grammar-dispatch
90 // section 5 double count because none of `char_literal`'s
91 // children is classified: the opening delimiter (`'`, and
92 // the `L'` / `u'` / `U'` / `u8'` prefixed spellings, each a
93 // distinct kind), the closing `'`, and the `character` /
94 // `escape_sequence` payload are all absent from every arm
95 // in this match. So a literal bills exactly one operand
96 // however many `character` leaves it holds — a multi-char
97 // constant like `'ab'` has two and still counts once.
98 //
99 // Operands are keyed by source text (`get_operand_id`), so
100 // `'x'` and `L'x'` are separate vocabulary entries while a
101 // repeated `'x'` folds to one `n2` with two `N2` hits.
102 //
103 // `Checker::is_string` deliberately does **not** grow a
104 // `CharLiteral` arm to match: a char is not a string, the
105 // same split Rust and Go apply to their char / rune
106 // literals. The alterator flattens `CharLiteral` in all four
107 // languages and records that decision alongside each arm;
108 // `c_family_char_literal_is_not_a_string` in `checker.rs`
109 // pins it both ways.
110 Identifier | TypeIdentifier | FieldIdentifier | StringLiteral | CharLiteral
111 | NumberLiteral | True | False | Null | DOTDOTDOT => TokenRole::Operand,
112 _ => TokenRole::Unknown,
113 }
114 }
115
116 get_operator!(C);
117}