Skip to main content

Getter

Trait Getter 

Source
pub trait Getter {
Show 19 methods // Provided methods fn get_func_name<'a, 'tree>( node: &Node<'tree>, code: &'a [u8], ancestors: Ancestors<'tree, '_>, ) -> Option<&'a str> { ... } fn get_func_space_name<'a, 'tree>( node: &Node<'tree>, code: &'a [u8], ancestors: Ancestors<'tree, '_>, ) -> Option<&'a str> { ... } fn get_space_kind(_node: &Node<'_>) -> SpaceKind { ... } fn get_space_kind_with_code<'a>( node: &Node<'a>, _code: &[u8], _ancestors: Ancestors<'a, '_>, ) -> SpaceKind { ... } fn get_op_type<'a>( _node: &Node<'a>, _ancestors: Ancestors<'a, '_>, ) -> TokenRole { ... } fn get_op_type_with_code<'a>( node: &Node<'a>, _code: &[u8], ancestors: Ancestors<'a, '_>, ) -> TokenRole { ... } fn get_operator_spelling<'a>( _node: &Node<'a>, _ancestors: Ancestors<'a, '_>, ) -> Option<&'static str> { ... } fn get_operand_id<'a>( node: &Node<'a>, code: &'a [u8], _ancestors: Ancestors<'a, '_>, ) -> &'a [u8] ⓘ { ... } fn string_operand_type(node: &Node<'_>, interp_kinds: &[u16]) -> TokenRole { ... } fn is_subsumed_braced_word<'a>( node: &Node<'a>, ancestors: Ancestors<'a, '_>, kinds: &BracedWordKinds, ) -> bool { ... } fn generic_argument_command<'tree, 'chain>( word: &Node<'tree>, ancestors: Ancestors<'tree, 'chain>, kinds: &BracedWordKinds, ) -> Option<(Node<'tree>, Ancestors<'tree, 'chain>)> { ... } fn is_value_braced_word<'a>( word: &Node<'a>, code: &[u8], ancestors: Ancestors<'a, '_>, kinds: &BracedWordKinds, ) -> bool { ... } fn is_braced_script_word<'a>( node: &Node<'a>, code: &[u8], ancestors: Ancestors<'a, '_>, kinds: &BracedWordKinds, ) -> bool { ... } fn is_braced_literal_slot<'a>( word: &Node<'a>, code: &[u8], ancestors: Ancestors<'a, '_>, kinds: &BracedWordKinds, ) -> bool { ... } fn is_switch_arm_body(word: &Node<'_>, command: &Node<'_>) -> bool { ... } fn command_leading_word<'c>( command: &Node<'_>, code: &'c [u8], kinds: &BracedWordKinds, ) -> Option<&'c str> { ... } fn is_switch_arm<'a>( command: &Node<'a>, code: &[u8], ancestors: Ancestors<'a, '_>, kinds: &BracedWordKinds, ) -> bool { ... } fn braced_word_op_type<'a>( node: &Node<'a>, code: &[u8], ancestors: Ancestors<'a, '_>, kinds: &BracedWordKinds, ) -> TokenRole { ... } fn get_operator_id_as_str(_id: u16) -> &'static str { ... }
}
Expand description

Per-language accessors that name and classify what a node is: the function or space name, the SpaceKind a node opens, and the TokenRole of a leaf.

Every method has a default that answers “nothing” (None, SpaceKind::Unknown, TokenRole::Unknown); a language overrides only the ones its grammar expresses.

Provided Methods§

Source

fn get_func_name<'a, 'tree>( node: &Node<'tree>, code: &'a [u8], ancestors: Ancestors<'tree, '_>, ) -> Option<&'a str>

Names the function node declares. Defaults to get_func_space_name.

Source

fn get_func_space_name<'a, 'tree>( node: &Node<'tree>, code: &'a [u8], ancestors: Ancestors<'tree, '_>, ) -> Option<&'a str>

Names the space node opens.

ancestors is the chain the caller descended through. Elixir needs it: its def / defmodule heads are ordinary Call nodes, and one inside a quote template names no space at all, which is a question about what encloses the call (#1088).

Source

fn get_space_kind(_node: &Node<'_>) -> SpaceKind

The kind of space node opens, or SpaceKind::Unknown when it opens none.

Source

fn get_space_kind_with_code<'a>( node: &Node<'a>, _code: &[u8], _ancestors: Ancestors<'a, '_>, ) -> SpaceKind

Source-aware variant of get_space_kind. The default forwards to the byte-less classifier; languages whose space kinds are encoded in macro identifier text (Elixir’s defmodule / def / defp / defmacro / defmacrop Calls) override this so the walker can attribute the correct SpaceKind to each promoted func space (#275).

ancestors is the chain the caller descended through; Elixir needs it to see whether the Call sits inside a quote template without paying Node::parent’s O(depth) (#1084).

Source

fn get_op_type<'a>(_node: &Node<'a>, _ancestors: Ancestors<'a, '_>) -> TokenRole

Classifies node as a Halstead operator, operand, or neither.

Punctuation and delimiters are vocabulary, so they classify as operators whatever grammatical role they serve. Halstead counts the alphabet a program is written in and has no notion of a decision, so a < is the same vocabulary entry whether it opens a JSX tag, brackets a Lua <const> attribute, names a C# operator < overload, disambiguates a Kotlin super<A>, or delimits a Perl <FH> readline. Every impl that classifies punctuation at all bills (, {, [, ,, ;, . and : on exactly that reading, for each glyph its language spells. Python and Rust were the last two exceptions, closed by #1486.

A decision metric gates the same token by role, and is supposed to disagree: ABC’s conditions counts a < only under a binary_expression (#1274, #1275, #1280, #1297), so each of the five constructs above scores zero there while bca ops still reports the bracket. #1395 settled that the two answers are both correct rather than a drift to be reconciled; tests/parity/abc_halstead_bracket_parity.rs fails if a later consistency pass flips either side.

The one suppression that reading admits is a literal’s own delimiter. There the punctuation is not vocabulary in its own right — the literal it wraps is already counted, so billing its quotes fabricates an arithmetic or comparison operator the source never spelled, and the score moves with the author’s choice of delimiter. It takes two shapes, and only the first is an arm of this method.

Nine arms suppress a delimiter whose enclosing literal node is itself the operand, each guarded on the delimiter’s parent being that literal: Elixir’s sigil (#1256), the regex / of the JS family (#1314), of Perl and of Ruby (#1312), Ruby’s subshell backtick (#1360), Groovy’s slashy string and the raw-string ( of both C++ grammars (#1314), and Objective-C’s @ before an NSString literal.

The tenth is braced_word_op_type, which Tcl and iRules route their { through. A braced value is a literal to Checker::is_string_with_code and to the dump, so its opener is withdrawn for the same reason — puts {c d} and puts "c d" have to agree on the operator column — but the operands are the words inside it rather than the braced word itself, so the parent test above does not describe it. #1318 established that arm and #1382 widened it to the value slots of a script-taking command.

A bracket delimiting syntax fits neither shape: no literal would absorb its contribution, so it stays an operator. A generic list’s < / > is billed like the other bracket pairs, once as <> on the opener (Self::get_operator_spelling, #1559), which leaves the comparison < and > their own entries.

ancestors is the chain the walker descended through. Most impls read a parent from it to disambiguate a token whose role depends on what encloses it — the nine literal-delimiter arms above, plus, among others, Python’s not / in / is inside the compound not in / is not, Rust’s || and ! inside a binary expression rather than a doc-comment marker, the namespace identifier in both C++ grammars, Bash’s $name, the name inside a Tcl or iRules variable substitution, and Groovy’s super in a wildcard bound. Reaching those parents with Node::parent instead costs O(depth) per node (#1096).

Source

fn get_op_type_with_code<'a>( node: &Node<'a>, _code: &[u8], ancestors: Ancestors<'a, '_>, ) -> TokenRole

Source-aware variant of get_op_type. The default forwards to the byte-less classifier; languages whose Halstead operand classification depends on token text override this. Kotlin uses it to recover the variable in a short-form string template ("Hi $name"), which the grammar emits as bare string_content tokens with no structured interpolation node — the distinction between an interpolated $name and a literal $5 is only visible in the source bytes (#454).

Source

fn get_operator_spelling<'a>( _node: &Node<'a>, _ancestors: Ancestors<'a, '_>, ) -> Option<&'static str>

The fixed spelling an operator node is keyed under in place of its kind id, for a token whose kind the grammar shares with a different operator; None keys it by kind. A generic list’s < is the case (#1559): it has the comparison’s kind but is keyed as the "<>" pair, as (), [] and {} are. Consulted only for a node get_op_type_with_code calls an operator.

Source

fn get_operand_id<'a>( node: &Node<'a>, code: &'a [u8], _ancestors: Ancestors<'a, '_>, ) -> &'a [u8] ⓘ

Returns the source-byte slice used to key a Halstead operand. The default keys on the operand node’s full byte range. Kotlin overrides this to narrow a short-interpolation name token ($name) to its leading identifier prefix, because the grammar glues trailing inter-segment text onto the name token ("$a $b" → "a "); keying the raw bytes would record a distinct "a " operand and break parity with the long ${a} form (#454).

Source

fn string_operand_type(node: &Node<'_>, interp_kinds: &[u16]) -> TokenRole

Classifies a string-literal node as a single Halstead operand, unless it wraps an interpolation child drawn from interp_kinds — in which case the wrapper yields TokenRole::Unknown because the inner expressions are walked and counted separately. Counting the wrapper too would double-count their contribution to N2.

This declares the per-language interpolation skip once (issue #420), replacing nine independently-added regression fixes (#183 / #184 / #191 / #192 / #199 / #277, …). Each language supplies only its own grammar’s interpolation child-kind ids; the per-call rationale lives at each call site.

Source

fn is_subsumed_braced_word<'a>( node: &Node<'a>, ancestors: Ancestors<'a, '_>, kinds: &BracedWordKinds, ) -> bool

Whether node is a fragment of a Tcl-family braced word rather than a token of its own, and so contributes no operand (#1354).

The two dialects are deliberate clones, so the rule is declared once and instantiated with each grammar’s [BracedWordKinds]; the per-call rationale lives at each call site. It answers yes in two cases:

  • node sits directly inside a braced value (braced_word_simple). Tcl evaluates nothing between braces, so the value is its whole span and the parts inside it are letters — the wrapper carries the one operand. This subsumes #1314’s narrower guard on the opening {, which is one more such child.
  • node is a braced script (braced_word) holding at least one command — a named child other than a comment. Commands are what the walk descends into and counts in their own right, so billing the block as well would count its whole text a second time. A comment is named too, but no arm classifies it, so a body holding only comments is billed like an empty one rather than like nothing at all.

A script with no command is deliberately not subsumed: the same kind serves as the value slot of every command the grammar does not special-case, where lappend l {} is an empty list and the brace pair is its only carrier (grammar-dispatch §6). An empty or comment-only proc body is spelled identically and so also scores one operand, its whole text. #1318 can now tell the two apart by the enclosing command, but it bills them alike, so this arm needs no help from it.

This is the byte-less half of the braced-word rule and it is not the whole of it. braced_word_op_type suppresses the { of a script-kind word the enclosing command shows to be a plain value, and both Tcl-family getters reach it through get_op_type_with_code, which is what the walk calls (grammar-dispatch §7). It revises no operand, so this arm’s answers stand unchanged.

Source

fn generic_argument_command<'tree, 'chain>( word: &Node<'tree>, ancestors: Ancestors<'tree, 'chain>, kinds: &BracedWordKinds, ) -> Option<(Node<'tree>, Ancestors<'tree, 'chain>)>

The generic command node word is an argument of, paired with that command’s own ancestry — or None when word instead fills a slot of a construct the grammar models, and so is a script by construction.

Both dialects hang a generic command’s arguments off a word_list and a modelled construct’s slots off that construct’s own node (procedure, if, else, elseif, while, foreach, catch, try, finally, argument, plus iRules’ when_event, for, switch_arm, on_handler, trap_handler and the three dict loops). Asking the parent kind therefore answers “is this a generic argument” without enumerating the modelled parents — a list that would be a coverage claim to re-derive on every grammar bump (grammar-dispatch §1), and that a grammar gaining one more modelled construct would silently invalidate.

A word_list whose own parent is not a command is namespace’s argument list — the one modelled construct in either grammar that reaches its body through a word_list. The grandparent test answers None there, so namespace eval ns {…} keeps its body a script.

A braced word directly under a command is the command name ({puts} hi), not an argument, and also answers None: its caller decides that case before asking, because a name is always a literal and must not pick up the switch-arm rescue below.

Source

fn is_value_braced_word<'a>( word: &Node<'a>, code: &[u8], ancestors: Ancestors<'a, '_>, kinds: &BracedWordKinds, ) -> bool

Whether a braced script-kind word is really a plain value — the literal {a b} of lappend x {a b} rather than the block of eval {…} (#1318).

The two roles share one kind, so no kind-scoped arm can separate them; recognition is out-of-band, by the enclosing command’s leading word (grammar-dispatch §9). A word that fills a modelled construct’s slot is a script; a word passed to a command in SCRIPT_TAKING_COMMANDS is a script; everything else is a value.

That default is the load-bearing choice, and it is deliberately the opposite of “keep today’s answer for anything unrecognised”:

  • The script-taking set is closed and small — Tcl defines no user-extensible control structures, so a command that evaluates a braced argument is either a core command listed above or a proc that forwards to eval / uplevel. The value-taking set is open: every user proc and every package command that takes a list or a pattern is in it. An unrecognised name is therefore far likelier to be value-taking.
  • Defaulting to script fabricates — it reports a {} operator for a block the source does not contain. Defaulting to value can only omit: one N1 occurrence per script handed to an unlisted command, and — when no modelled construct in the same space opens a block — the {} vocabulary entry with them. That last case is where the cost shows: a top-level dict for {k v} $d { puts $k } with nothing around it has no operator left, and Halstead’s difficulty is a product with n1 in it, so effort reads 0.0 rather than a little low. Inside a proc the proc keyword and body brace keep the space non-zero, which is the scope halstead.effort is gated at; and the same 0.0 was already the answer for a file of set x {a b} lines, so it is Halstead’s shape on an operator-free space, not a new failure mode. The omission stays bounded only because the answer is scoped to the brace: see braced_word_op_type for the measurement that decided it, and for why suppressing the contents of a value would have made it unbounded instead.
  • It stops the score moving with the author’s choice of delimiter, which is what #695, #1312 and #1314 each restored elsewhere: puts {c d} and puts "c d" now agree on the operator column.

The cost is a script passed to an unlisted command — a Tk -command {…} callback, trace add variable v w {…}, a subcommand dispatch (dict for, interp eval), a user-defined with_lock {…} — losing the {} its block deserves. No structural signal distinguishes those cases: the grammar parses {a b} and {puts hi} into the same shape. The code inside them is still counted, so the loss is one operator occurrence per such block, plus the vocabulary entry in the case above.

Source

fn is_braced_script_word<'a>( node: &Node<'a>, code: &[u8], ancestors: Ancestors<'a, '_>, kinds: &BracedWordKinds, ) -> bool

Whether node is a Tcl-family braced script — a proc or if body, an iRules when handler — rather than a braced literal (#1381).

The positive form of is_value_braced_word, narrowed to the script kind so it answers false for every node of every other kind and for the two literal spellings, and narrowed again by the value slots is_braced_literal_slot recognises. That makes it the question the non-Halstead classifiers ask: Checker::is_string_with_code must not call a proc body a string literal, and Alterator::keeps_children must stop the dump flattening one into a leaf, which dropped the body from it. Both once listed braced_word beside quoted_word and braced_word_simple, which is right for the literal role and wrong for the script role the same kind also serves.

Stated here rather than in each of the five call sites so the string, dump and Halstead classifiers cannot drift apart on the same bytes (grammar-dispatch §7). Halstead was the deliberate exception between #1318 and #1382: braced_word_op_type asked is_value_braced_word alone, so the braces of the value slots is_braced_literal_slot adds still billed a {} operator while the other two called the same bytes a literal. #1382 closed it — that call site now asks this predicate — so a braced value is a value to all three, and after {100} {puts hi} bills the one {} its {puts hi} block earns rather than two.

The remaining §7 note is one of scope, not of disagreement: the three differ only where Halstead’s operand half keys on whether a body holds a command, which makes an empty or comment-only proc body an operand there and not a string here.

Source

fn is_braced_literal_slot<'a>( word: &Node<'a>, code: &[u8], ancestors: Ancestors<'a, '_>, kinds: &BracedWordKinds, ) -> bool

Whether word, a braced word is_value_braced_word calls a script, fills a slot whose documented syntax takes a value.

is_value_braced_word answers by command name alone. Most of the names it recognises have a mixed signature, though, so that answer made a value — a millisecond count, a namespace name, a switch subject — a script (#1381 review). This is the per-argument half: proc has one value slot the grammar names as a field, and every other construct declares its layout in one of the two tables in lang_helpers::tcl_family, which fills_script_slot applies.

Each value here was a string and a flat dump leaf before #1381, and would otherwise have become a script under it: the dump rendered {my proc} as a command named my and {100} as one named 100, and namespace export {…} and namespace ensemble create -map {…} both occur in the Tcl 8.6 standard library. Since #1382 Halstead reads the same answer through is_braced_script_word, so the braces of these values quote rather than open (after {100} {…} scores the {} of its script and no other).

Two guards keep the construct-wide answer. A switch arm list parses as commands, so an arm whose pattern is spelled proc, namespace, after or switch builds one of these shapes around what are really arm bodies — is_switch_arm recognises it first. And an argument list whose own slot sequence is unreadable has no positions worth trusting.

That second guard is tcl_family::argument_slots_are_readable, which asks the slots rather than the owner — asking Node::has_error of the command withdrew every value slot of a command whose only error sat inside one of its script arguments. Its doc carries the measurement. A list longer than the signature admits is the second way to be unreadable, and tcl_family::ScriptSlots::LastOf is where that one lives.

A try … trap clause is out of reach only where the grammar wraps it: inside a proc or a namespace eval body the Tcl grammar recovers into an ERROR node and no role signal survives, but at the top level the same clause parses as a plain trap command whose slots this does read. An earlier revision of this paragraph said “the multi-line try … trap clause” was out of reach outright, which is where the finally defect LastOf now guards hid (#1382 review).

Source

fn is_switch_arm_body(word: &Node<'_>, command: &Node<'_>) -> bool

Whether word, an argument of a switch arm command (is_switch_arm), sits in a body position rather than a pattern position.

The arm list is a flat run of pattern body pattern body …, and the grammar breaks it into commands at newlines. One arm per line gives each command a pattern for its name and a body for its sole argument; several arms on one line give the first pattern’s command the whole run as arguments, so the arguments alternate body pattern body …. The command-name test above keeps the first braced pattern a literal, but without this gate every later one was rescued as a body, and a one-line switch -regexp $v { {^a} {…} {^b} {…} } fabricated a {} around {^b} that the same arms written one per line did not — the score moving with layout, which is what the rule exists to stop.

Position is the only signal because it is what Tcl itself uses: switch pairs the list up by index, with no marker on either half. The parity holds across the words that can interpose — a - fall-through body and a default pattern are both simple_words and take a slot each — so an even index is a body and an odd one a pattern. This is the one place the rule needs a sibling index rather than a parent kind, and it runs once per braced argument of such a command.

The index comes from Cursor::goto_first_child_for_byte, which is O(log n) in the argument count. A children().position(..) scan answers identically in O(n), and since every braced word of a one-line arm list asks, that made the Halstead walk — and, once #1381 routed them through this rule, find / count --type string and the Ast dump — quadratic in the width of one line: seconds per request at tens of KB (#1381 review). The id check keeps the scan’s answer for a word that is not one of the command’s arguments at all.

Source

fn command_leading_word<'c>( command: &Node<'_>, code: &'c [u8], kinds: &BracedWordKinds, ) -> Option<&'c str>

A command’s leading word, when it is a statically resolvable simple_word.

Located by field rather than by index (grammar-dispatch §3): the name is command’s name field in both grammars. A computed name ($cmd {…}, [pick] {…}) parses as a substitution node and is not resolvable at all, and a name spelled in quotes or braces ("eval" {…}) is legal Tcl that this deliberately leaves unresolved — the same limitation tcl_command_name records for the Cognitive and Cyclomatic walkers.

Source

fn is_switch_arm<'a>( command: &Node<'a>, code: &[u8], ancestors: Ancestors<'a, '_>, kinds: &BracedWordKinds, ) -> bool

Whether command is really one pattern body pair of a Tcl switch arm list rather than a command of its own.

Tcl models no switch, so switch $v {a {…} b {…}} parses the arm list as a braced word whose interior is a command named after the first pattern — a here — with the arm bodies as its arguments (grammar-dispatch §9; the Cognitive walker reads the same shape through tcl_switch_arm_list). Without this test the bodies would read as literals passed to a command called a, which is exactly what the value default is for everywhere else. iRules models switch with switch_arm children, so its arm bodies never reach here.

Source

fn braced_word_op_type<'a>( node: &Node<'a>, code: &[u8], ancestors: Ancestors<'a, '_>, kinds: &BracedWordKinds, ) -> TokenRole

Whether the { of node’s parent braced word opens a block, or merely quotes a literal (#1318).

This is #1314’s guard with its kind test replaced by a role test. #1314 suppressed the opener of a braced_word_simple, the literal form the grammars emit only in the value slots they special-case; everywhere else a literal is a braced_word, the same kind a block uses, and its { still reported a {} operator for a block the source does not contain — lappend x {a b}, puts {c d}, and every user proc taking a list.

The role is is_braced_script_word, the same predicate Checker::is_string_with_code and Alterator::keeps_children ask, so the three classifiers answer one question about one set of bytes (grammar-dispatch §7). That was not so between #1318 and #1382: this call site asked is_value_braced_word alone and billed a {} for the value slots of a script-taking command, which the other two had called literals since #1381. The exception, and what closing it moved, is recorded on is_braced_script_word, which owns the §7 story for all five call sites.

It revises the operator only, and deliberately leaves the words inside a value alone. Suppressing them too would make lappend x {a b} score like its set x {a b} synonym, which is the tidier answer for that line — and the wrong one in general, because the same braced argument of an unrecognised command is just as often real code: an oo::class create C {…} body, a tcltest -body {…}, an apply {{x} {…}} lambda. Suppressing contents was built and measured on a file holding one of each; it took n1 5 → 2, N1 8 → 2, n2 25 → 15 and N2 32 → 15, the class body, the test body and the lambda each collapsing into a single operand, and halstead.effort — a gated threshold metric — collapsing with them. So the rule only ever withdraws a claim the classifier cannot support; it never discards code the walk has already read.

The residual is the asymmetry #1382 opens with, and it is the contract, not an outstanding defect: a braced value scores one operand where the grammar names the command (set x {a b}) and one per word where only the command name would (lappend x {a b}). #1382 weighed the two ways of closing it and took neither. Reading the contents — no substitution, no nested block, therefore list-like — is the measurement above, already taken and already rejected. Widening the one-operand treatment to a value-taking command list needs the mirror of SCRIPT_TAKING_COMMANDS, and that set is open where this one is closed: every user proc is in it, so the list is incomplete by construction and would leave three behaviours where there are two. Both trade a bounded over-report of the operand vocabulary for an unbounded loss of real code, which is the trade the paragraph above declines.

Keeping to the operator also keeps the whole thing cheap: only a braced word’s own opener can change answer, so the test is one kind comparison and one parent lookup, the same scope #1354 and #1314 use. Two lookups are O(log n) rather than O(1), both cursor seeks by byte and both reached only from a command whose name resolves: is_switch_arm_body needs a switch arm word’s index among its command’s arguments, and — since #1382 — is_braced_literal_slot needs the same index to place a word against the layout lang_helpers::tcl_family records. An ancestor scan would have been O(depth) per node and quadratic on a deeply nested expr, the shape #1122 warns about.

Source

fn get_operator_id_as_str(_id: u16) -> &'static str

The grammar’s name for the operator kind id, used to render a Halstead operator; empty for languages that do not report them.

Dyn Compatibility§

This trait is not dyn compatible.

In older versions of Rust, dyn compatibility was called "object safety".

Implementors§