Skip to main content

big_code_analysis_ast/getter/
irules.rs

1//! `Getter` implementation for iRules.
2#![allow(clippy::wildcard_imports, clippy::enum_glob_use)]
3
4use super::*;
5
6use crate::lang_helpers::irules::BRACED_WORD_KINDS;
7
8impl Getter for IrulesCode {
9    fn get_space_kind(node: &Node) -> SpaceKind {
10        match node.kind_id().into() {
11            // Event handlers and procs are the function units (see the
12            // `IrulesCode` Checker impl for the handler-as-function
13            // rationale, and for why `try` handlers are excluded — this arm
14            // must stay gated by the same set as `is_func_space`,
15            // grammar-dispatch §6/§7).
16            Irules::Procedure | Irules::WhenEvent => SpaceKind::Function,
17            Irules::SourceFile => SpaceKind::Unit,
18            _ => SpaceKind::Unknown,
19        }
20    }
21
22    fn get_op_type<'a>(node: &Node<'a>, ancestors: Ancestors<'a, '_>) -> TokenRole {
23        match node.kind_id().into() {
24            // The braced-word rule (#1314, #1354 / #1317) — the twin of
25            // the Tcl arm, which carries the derivation and every
26            // caveat, over the shared
27            // `Getter::is_subsumed_braced_word`. The same split holds
28            // at this grammar's own ids: handler and `if` bodies are
29            // `BracedWord` (132), conditions are `Expr` (141), and only
30            // the value form is `BracedWordSimple` (133), which admits
31            // the same six child kinds Tcl's does.
32            _ if Self::is_subsumed_braced_word(node, ancestors, &BRACED_WORD_KINDS) => {
33                TokenRole::Unknown
34            }
35            // Anonymous keyword tokens (the `*2` aliases are the keyword
36            // literals; the unsuffixed high-id variants are the statement
37            // nodes counted by the branching metrics, not here).
38            Irules::Proc
39            | Irules::When
40            | Irules::On
41            | Irules::Off
42            | Irules::Trap
43            | Irules::If2
44            | Irules::Elseif2
45            | Irules::Else2
46            | Irules::While2
47            | Irules::For2
48            | Irules::Foreach2
49            | Irules::Switch2
50            | Irules::Set2
51            | Irules::Global2
52            | Irules::Namespace2
53            | Irules::Try2
54            | Irules::Catch2
55            | Irules::Finally2
56            | Irules::Regexp2
57            | Irules::Expr2
58            | Irules::Dict
59            | Irules::Update
60            | Irules::With
61            // String comparison operators (`eq`, `contains`, `matches`, …):
62            // operators, not branches (the branching metrics ignore them).
63            | Irules::Eq
64            | Irules::Ne
65            | Irules::StartsWith
66            | Irules::EndsWith
67            | Irules::Contains
68            | Irules::Equals
69            | Irules::Matches
70            | Irules::MatchesRegex
71            | Irules::MatchesGlob
72            | Irules::In
73            | Irules::Ni
74            // Structural punctuation. Only the *opening* delimiter is an
75            // operator (the pair folds to one glyph in
76            // `get_operator_id_as_str`); counting the matching closer too
77            // (former `RBRACE`/`RBRACK`/`RPAREN` arms) double-counted every
78            // balanced pair, inflating n1/N1 (#695).
79            // `LPAREN2` is defensive — the runtime collapses it to `LPAREN`
80            // before `kind_id()`, so it never fires (#768; see the Cpp note).
81            | Irules::LBRACE
82            | Irules::LBRACK
83            | Irules::LPAREN
84            | Irules::LPAREN2
85            | Irules::SEMI
86            | Irules::COLON
87            | Irules::COLONCOLON
88            | Irules::NsDelim
89            // Arithmetic / exponent operators.
90            | Irules::PLUS
91            | Irules::DASH
92            | Irules::STAR
93            | Irules::SLASH
94            | Irules::PERCENT
95            | Irules::STARSTAR
96            // Bitwise operators.
97            | Irules::AMP
98            | Irules::PIPE
99            | Irules::CARET
100            | Irules::TILDE
101            | Irules::LTLT
102            | Irules::GTGT
103            // Comparison operators.
104            | Irules::EQEQ
105            | Irules::BANGEQ
106            | Irules::LT
107            | Irules::GT
108            | Irules::LTEQ
109            | Irules::GTEQ
110            // Logical operators (symbolic and keyword forms).
111            | Irules::BANG
112            | Irules::Not
113            | Irules::AMPAMP
114            | Irules::And
115            | Irules::PIPEPIPE
116            | Irules::Or
117            // Ternary conditional operator.
118            | Irules::QMARK => TokenRole::Operator,
119
120            // `Id` (named, id 49) is a standalone identifier operand — e.g.
121            // a `set` target (`set s …` → `s`). But it is ALSO the inner
122            // leaf of every `variable_substitution` (`$s` → `(variable_
123            // substitution (id "s"))`). The `VariableSubstitution` node is
124            // already the operand for the reference, so counting its inner
125            // `Id` too would double-count every `$var` (inflating n2/N2 and
126            // every derived Halstead value). Exclude `Id` exactly in that
127            // position. NB: Tcl has the identical shape — its anonymous
128            // `Id2` token is emitted both as the `set` target and as the
129            // var-sub leaf, so it needs this same parent guard; the blanket
130            // `Id2` kind exclusion it used to carry dropped every `set`
131            // target from n2/N2 (#1294). (`Id2` here is a different,
132            // non-surfacing token.)
133            Irules::Id => {
134                if ancestors.parent_has_kind(node, Irules::VariableSubstitution as u16) {
135                    TokenRole::Unknown
136                } else {
137                    TokenRole::Operand
138                }
139            }
140
141            // Operands: identifiers and literals.
142            //
143            // `BracedWord` (132) reaches this arm only when the guard
144            // above let it through, which since #1354 means only when
145            // it holds no command — the reason the Tcl twin spells
146            // out. A `when` handler body is the largest instance of the
147            // over-count that removed: its operand text was the entire
148            // event handler.
149            //
150            // `ArrayIndex` is deliberately absent, as in the Tcl twin:
151            // the grammar makes `(k)` a pure wrapper around one of the
152            // six word kinds, and its parens are already the `()`
153            // operator, so listing it billed `$arr(k)` three times —
154            // the reference, the wrapper and the index (grammar-
155            // dispatch §5). The reference and the index are the two
156            // operands, the index being walked because Tcl substitutes
157            // inside the parens (`$arr($i)`), unlike inside braces.
158            Irules::SimpleWord
159            | Irules::Number
160            | Irules::Boolean
161            | Irules::EventName
162            | Irules::BracedWord
163            | Irules::BracedWordSimple
164            | Irules::VariableSubstitution => TokenRole::Operand,
165
166            // Double-quoted strings count as a single operand when inert
167            // (`"hello world"`). When they carry a `$var` or `[cmd]`
168            // interpolation child, the inner substitution nodes are walked
169            // separately and contribute their own operands; counting the
170            // wrapping `QuotedWord` too would double-count `N2` (same pattern
171            // as Tcl #277 / Bash #180 / C# #183 / PHP #184).
172            Irules::QuotedWord => Self::string_operand_type(
173                node,
174                &[
175                    Irules::VariableSubstitution as u16,
176                    Irules::CommandSubstitution as u16,
177                ],
178            ),
179
180            _ => TokenRole::Unknown,
181        }
182    }
183
184    // The byte-reading half of the braced-word rule (#1318) — the twin
185    // of the Tcl override, which carries the derivation. This grammar
186    // models more of the script positions than Tcl's does (`for`,
187    // `switch` with `switch_arm` children, `when`, and the three `dict`
188    // loops all have nodes of their own), so fewer braced words reach
189    // the command-name recognition here; the ones that do — `eval`,
190    // `uplevel`, `after`, `time` and every user proc — behave exactly
191    // as they do in Tcl. Two entries in that list exist for this
192    // dialect alone: `on_handler` / `trap_handler` are emitted only
193    // under `try` (pinned by
194    // `irules_try_handler_kinds_appear_only_under_try`), so a
195    // statement-level `on error {…}` or `trap {…}` is a generic
196    // command and needs its name recognised.
197    fn get_op_type_with_code<'a>(
198        node: &Node<'a>,
199        code: &[u8],
200        ancestors: Ancestors<'a, '_>,
201    ) -> TokenRole {
202        Self::braced_word_op_type(node, code, ancestors, &BRACED_WORD_KINDS)
203    }
204
205    get_operator!(Irules);
206}