Skip to main content

big_code_analysis_ast/getter/
rust.rs

1//! `Getter` implementation for Rust.
2#![allow(clippy::wildcard_imports, clippy::enum_glob_use)]
3
4use super::*;
5
6// `for_lifetimes` (`for<'a> fn(&'a u8)`) and `use_bounds`
7// (`impl Tr + use<'a, T>`) are generic-parameter lists under other
8// names. `LT2` is the `token(prec(1, '<'))` spelling `type_arguments`
9// and `use_bounds` open with; the parser reports it as `LT`, so it is
10// listed defensively, here and in the operator arm, and its absence is
11// pinned by `rust_generic_opener_alias_never_reaches_kind_id`.
12const GENERIC_ANGLES: GenericAngleKinds = GenericAngleKinds {
13    lists: &[
14        Rust::TypeArguments as u16,
15        Rust::TypeParameters as u16,
16        Rust::ForLifetimes as u16,
17        Rust::UseBounds as u16,
18    ],
19    openers: &[Rust::LT as u16, Rust::LT2 as u16],
20    closers: &[Rust::GT as u16],
21    is_misparse: never_misparsed,
22};
23
24impl Getter for RustCode {
25    fn get_func_space_name<'a, 'tree>(
26        node: &Node<'tree>,
27        code: &'a [u8],
28        _ancestors: Ancestors<'tree, '_>,
29    ) -> Option<&'a str> {
30        // we're in a function or in a class or an impl
31        // for an impl: we've  'impl ... type {...'
32        if let Some(name) = node
33            .child_by_field_name("name")
34            .or_else(|| node.child_by_field_name("type"))
35        {
36            node_text(code, &name)
37        } else {
38            Some("<anonymous>")
39        }
40    }
41
42    fn get_space_kind(node: &Node) -> SpaceKind {
43        use Rust::*;
44
45        match node.kind_id().into() {
46            FunctionItem | ClosureExpression => SpaceKind::Function,
47            TraitItem => SpaceKind::Trait,
48            ImplItem => SpaceKind::Impl,
49            SourceFile => SpaceKind::Unit,
50            _ => SpaceKind::Unknown,
51        }
52    }
53
54    fn get_op_type<'a>(node: &Node<'a>, ancestors: Ancestors<'a, '_>) -> TokenRole {
55        use Rust::*;
56
57        if GENERIC_ANGLES.is_closer(node, ancestors) {
58            return TokenRole::Unknown;
59        }
60        match node.kind_id().into() {
61            // `||` is treated as an operator only if it's part of a binary expression.
62            // This prevents misclassification inside macros where closures without arguments (e.g., `let closure = || { /* ... */ };`)
63            // are not recognized as `ClosureExpression` and their `||` node is identified as `PIPEPIPE` instead of `ClosureParameters`.
64            //
65            // Similarly, exclude `/` when it corresponds to the third slash in `///` (`OuterDocCommentMarker`)
66            PIPEPIPE | SLASH => match ancestors.parent(node) {
67                Some(parent) if matches!(parent.kind_id().into(), BinaryExpression) => {
68                    TokenRole::Operator
69                }
70                _ => TokenRole::Unknown,
71            },
72            // Ensure `!` is counted as an operator unless it belongs to an `InnerDocCommentMarker` `//!`
73            BANG => match ancestors.parent(node) {
74                Some(parent) if !matches!(parent.kind_id().into(), InnerDocCommentMarker) => {
75                    TokenRole::Operator
76                }
77                _ => TokenRole::Unknown,
78            },
79            // COLONCOLON (`::`) is the path-segment separator. C++, Java,
80            // C#, and Kotlin all classify it as an operator; omitting it
81            // here (issue #394) silently dropped every path expression
82            // (`std::collections::HashMap`, `Vec::new`, `T::method`) into
83            // TokenRole::Unknown, deflating n1/N1 for path-heavy code.
84            //
85            // The 14 declaration/visibility keywords (Const, Static, Enum,
86            // Struct, Trait, Impl, Use, Mod, Pub, Type, Union, Where,
87            // Extern, Dyn) were inconsistently absent — the impl already
88            // accepted 17 other keywords (As, Async, Await, Break, …, Fn).
89            // Including them brings declaration-heavy code in line with
90            // statement-heavy code.
91            LPAREN | LBRACE | LBRACK | As | EQGT | PLUS | STAR | Async | Await | Break
92            | Continue | Else | For | If | In | Let | Loop | Match | Return | Unsafe | While
93            | EQ | COMMA | DASHGT | QMARK | LT | LT2 | GT | AMP | MutableSpecifier | DOTDOT
94            | DOTDOTEQ | DASH | AMPAMP | PIPE | CARET | EQEQ | BANGEQ | LTEQ | GTEQ | LTLT
95            | GTGT | PERCENT | PLUSEQ | DASHEQ | STAREQ | SLASHEQ | PERCENTEQ | AMPEQ | PIPEEQ
96            | CARETEQ | LTLTEQ | GTGTEQ | Move | DOT | PrimitiveType | PrimitiveType2
97            | PrimitiveType3 | PrimitiveType4 | PrimitiveType5 | PrimitiveType6
98            | PrimitiveType7 | PrimitiveType8 | PrimitiveType9 | PrimitiveType10
99            | PrimitiveType11 | PrimitiveType12 | PrimitiveType13 | PrimitiveType14
100            | PrimitiveType15 | PrimitiveType16 | PrimitiveType17 | Fn | SEMI | COLON
101            | COLONCOLON | Const | Static | Enum | Struct | Trait | Impl | Use | Mod | Pub
102            | Type | Union | Where | Extern | Dyn => TokenRole::Operator,
103            // FieldIdentifier (e.g. `p.x`) and TypeIdentifier (e.g. `Vec`,
104            // `HashMap`) are operand-class names — C++ and Go classify them
105            // the same way (see arms ~588 and ~862 below). Omitting them
106            // here silently dropped both into TokenRole::Unknown,
107            // deflating n2/N2 and the derived vocabulary/volume/effort
108            // estimates (issue #390).
109            Identifier | TypeIdentifier | FieldIdentifier | StringLiteral | RawStringLiteral
110            | IntegerLiteral | FloatLiteral | BooleanLiteral | Zelf | CharLiteral | UNDERSCORE => {
111                TokenRole::Operand
112            }
113            _ => TokenRole::Unknown,
114        }
115    }
116
117    fn get_operator_spelling<'a>(
118        node: &Node<'a>,
119        ancestors: Ancestors<'a, '_>,
120    ) -> Option<&'static str> {
121        GENERIC_ANGLES.opener_spelling(node, ancestors)
122    }
123
124    get_operator!(Rust);
125}