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