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