Skip to main content

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
7impl Getter for MozcppCode {
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 `Mozcpp::FunctionDefinition*` alias
14        // must be enumerated here AND in `get_space_kind` below AND
15        // in `is_func` / `is_func_space` (see `src/checker.rs`).
16        // The aliased kind_ids 489/491/494 are not emitted by the
17        // currently pinned `tree-sitter-mozcpp` parse tables, so a
18        // dropped variant would silently fall through to the
19        // `_ => name-field` arm and yield the wrong name (or `None`).
20        match node.kind_id().into() {
21            Mozcpp::FunctionDefinition
22            | Mozcpp::FunctionDefinition2
23            | Mozcpp::FunctionDefinition3
24            | Mozcpp::FunctionDefinition4 => {
25                if let Some(op_cast) = node.first_child(|id| Mozcpp::OperatorCast == id) {
26                    return node_text(code, &op_cast);
27                }
28                // The name is not a child of the function node — see
29                // `crate::c_declarator` (#1208).
30                if let Some(name) = declarator_name::<Self>(node)
31                    && matches!(
32                        name.kind_id().into(),
33                        Mozcpp::TypeIdentifier
34                            | Mozcpp::Identifier
35                            | Mozcpp::FieldIdentifier
36                            | Mozcpp::DestructorName
37                            | Mozcpp::OperatorName
38                            | Mozcpp::QualifiedIdentifier
39                            | Mozcpp::QualifiedIdentifier2
40                            | Mozcpp::QualifiedIdentifier3
41                            | Mozcpp::QualifiedIdentifier4
42                            | Mozcpp::TemplateFunction
43                            | Mozcpp::TemplateMethod
44                    )
45                {
46                    return node_text(code, &name);
47                }
48            }
49            _ => {
50                if let Some(name) = node.child_by_field_name("name") {
51                    return node_text(code, &name);
52                }
53            }
54        }
55        None
56    }
57
58    fn get_space_kind(node: &Node) -> SpaceKind {
59        use Mozcpp::*;
60
61        // Issue #285 contract: keep every `FunctionDefinition*` alias
62        // listed here — see the comment above `get_func_space_name`.
63        match node.kind_id().into() {
64            FunctionDefinition | FunctionDefinition2 | FunctionDefinition3
65            | FunctionDefinition4 => SpaceKind::Function,
66            StructSpecifier => SpaceKind::Struct,
67            ClassSpecifier => SpaceKind::Class,
68            NamespaceDefinition => SpaceKind::Namespace,
69            TranslationUnit => SpaceKind::Unit,
70            _ => SpaceKind::Unknown,
71        }
72    }
73
74    fn get_op_type<'a>(node: &Node<'a>, ancestors: Ancestors<'a, '_>) -> TokenRole {
75        use Mozcpp::*;
76
77        // `LPAREN2` is a defensive arm (collapsed to `LPAREN` before
78        // `kind_id()`; #768, see the Cpp note).
79        match node.kind_id().into() {
80            // Raw-string delimiter punctuation — the twin of the Cpp
81            // arm, which carries the derivation (#1314). `.mozcpp` owns
82            // no file extension, so nothing routes to it and it gets no
83            // integration-snapshot coverage; the parity assertion in
84            // `tests/parity/cpp_mozcpp_parity.rs` is what keeps this
85            // clone from drifting.
86            LPAREN
87                if ancestors.parent_has_kind(node, RawStringLiteral as u16) =>
88            {
89                TokenRole::Unknown
90            }
91            DOT | DOTSTAR | LPAREN | LPAREN2 | COMMA | STAR | GTGT | COLON | SEMI | Return
92            | Break | Continue | If | Else | Switch | Case | Default | For | While | Goto | Do
93            | Delete | New | Try | Try2 | Catch | Throw | EQ | AMPAMP | PIPEPIPE | DASH
94            | DASHDASH | DASHGT | DASHGTSTAR | PLUS | PLUSPLUS | SLASH | PERCENT | PIPE | AMP
95            | LTLT | TILDE | LT | LTEQ | EQEQ | BANGEQ | GTEQ | GT | GT2 | LTEQGT | PLUSEQ
96            | DASHEQ | BANG | STAREQ | SLASHEQ | PERCENTEQ | GTGTEQ | LTLTEQ | AMPEQ | CARET
97            | CARETEQ | PIPEEQ | LBRACK | LBRACE | QMARK | COLONCOLON | PrimitiveType
98            | TypeSpecifier | Sizeof
99            // A `sized_type_specifier` carries its `unsigned`/`signed`/`long`/
100            // `short` modifiers as bare keyword tokens, not as `primitive_type`
101            // children (`unsigned int` is `unsigned` + `primitive_type int`;
102            // `signed long` and `long long` have no `primitive_type` at all).
103            // Without these arms the modifiers fell into `Unknown` and were
104            // dropped, so `unsigned int` collapsed to just `int` and a standalone
105            // `signed long` contributed nothing to n1/N1 (issue #466). Each
106            // modifier has a distinct kind_id, so keying by kind_id (the default
107            // `operators` store) keeps them distinct in n1 while `long long`'s
108            // two `long` tokens correctly fold to one n1 entry but two N1 hits.
109            | Signed | Unsigned | Long | Short => TokenRole::Operator,
110            // `CharLiteral` — the full derivation lives on the same arm
111            // in `src/getter/c.rs` (#1316): the wrapper is the only
112            // classified node in a character literal, so it bills one
113            // operand per literal, keyed by text, and `Checker::is_string`
114            // deliberately stays without a `CharLiteral` arm.
115            //
116            // `This` — the operator-vs-operand derivation lives on the
117            // same arm in `src/getter/cpp.rs` (#1361), where the fork's
118            // upstream carries it. In short: `this` had been in neither
119            // arm and so contributed nothing, and it is an operand
120            // because it is the *receiver* half of `<recv> -> <field>`,
121            // exactly where `p` stands in `p->x`. Mozcpp owns no file
122            // extension, so nothing routes to it and no integration
123            // snapshot reaches this clone — `cpp_and_mozcpp_agree_on_this`
124            // in `tests/parity/cpp_mozcpp_parity.rs` is what keeps it
125            // from drifting, the same guard the `RawStringLiteral`
126            // `LPAREN` arm above relies on.
127            Identifier | TypeIdentifier | FieldIdentifier | RawStringLiteral | StringLiteral
128            | CharLiteral | NumberLiteral | True | False | Null | This | DOTDOTDOT => {
129                TokenRole::Operand
130            }
131            // A namespace identifier is an operand only where it
132            // *names* a namespace; the same kind also spells the
133            // qualifier in `ns::thing`, which the final arm leaves
134            // `Unknown` (#1096).
135            NamespaceIdentifier if ancestors.parent_has_kind(node, NamespaceDefinition as u16) => {
136                TokenRole::Operand
137            }
138            _ => TokenRole::Unknown,
139        }
140    }
141
142    get_operator!(Mozcpp);
143}