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}