Cha

1.13.0 - 2026-04-30

v1.13.0 Apr 30 2026 at 09:05 UTC

Added

  • primitive_representation detector (roadmap S8.2). Flags function parameters whose name carries a domain concept (user_id, email, status_code, api_url, password, language, …) but whose type is a raw scalar primitive (String, i32, bool, char, …). Signals an opportunity to introduce a newtype / value object to preserve the invariant. Per-parameter detection groups all offending params of one function into a single hint. Complements the existing primitive_obsession (which looks at per-function ratio): this fires on even a single param when it's clearly a business concept.
    • Business-token and noise-token vocabularies are deliberately narrow to keep signal-to-noise high. Substring matches are ruled out (tokens must be standalone words — widget_identifier does not trigger on id).
    • Parameters already typed with project-local newtypes (e.g. id: UserId where UserId is TypeOrigin::Local) are skipped — the author already did the right thing.
    • Container types (Path, PathBuf, Vec, Arc, Box, HashMap, …) are treated as domain-carrying and excluded; wrapping path: &Path in a newtype would destroy the abstraction.
    • Only runs on is_exported functions — private helpers are noise for a design signal aimed at public API hygiene.
    • Cha self-analyze: 14 findings (all genuine — rel_path/env_hash/language/key/hash as raw types). lvgl src/ baseline: 53 findings (TTF platformID/encodingID/languageID/nameID: int, file-explorer path/dir: char pointers, …).
  • stringly_typed_dispatch detector (roadmap S8.8). Flags functions whose switch/match body dispatches on ≥ 3 string or ≥ 3 integer literal arms — classic "the arm values should have been an enum" smell. Char-literal arms (C tokenisers) skipped. Enum-variant / structural-pattern arms classify as Other and never contribute to the threshold, so match event { Event::Click => …, Event::Scroll => …, _ => … } stays quiet while match s { "click" => …, "scroll" => …, "submit" => … } fires. Severity Hint. Complements S8.2 primitive_representation (signature side) with the body-side dispatch signal.
    • New cha_core::ArmValue enum (Str / Int / Char / Other) + FunctionInfo.switch_arm_values + FunctionSymbol.switch_arm_values. Populated by every parser via a new shared cha-parser/src/switch_arms.rs helper — language-specific arm-node kinds funnel through one classifier.
    • Cha self-baseline: 20 findings (all node-kind dispatchers in the 6 language parsers — valid detections, users can add // cha:ignore stringly_typed_dispatch if the dispatch shape is forced by tree-sitter). lvgl src/ baseline: 23 findings (PNG/JPEG/QR error-code dispatchers, color-format size tables, TTF bytecode interpreter).
  • cross_boundary_chain detector (roadmap S8.U4). Flags functions where chain_depth ≥ 3 and the chain's root parameter is externally-typed (TypeOrigin::External(crate)) — the function is reaching into a third-party library's internal field layout, not just over-chaining local data. Companion to the existing message_chain (which fires on depth regardless of source): cross_boundary_chain is narrower but a stronger abstraction-leak signal. Severity Hint.
    • Workspace crates are auto-whitelisted (same mechanism leaky_public_signature uses), so sibling cha_core::Finding traversals inside this repo don't fire. Cha self-baseline: 4 findings, all genuine tree_sitter::Node traversals in cha-parser. lvgl src/ baseline: 0 (C project, few External origins by design).
    • Zero parser changes — reuses chain_depth, parameter_types (with origin), parameter_names, external_refs. Pure post-pass on ProjectIndex.
  • FunctionInfo.parameter_names + FunctionSymbol.parameter_names (cha-core). Parallel to parameter_types: identifier names in declaration order. All six parsers (Rust / TS / Python / Go / C / C++) extract these; self / C++ this positions skipped to stay length-aligned with parameter_types. Enables name-semantic analyses like primitive_representation, future LSP hover with full signatures, future cha summary.
  • New helpers cha_parser::rust_imports::rust_param_names and cha_parser::cpp::c_param_name extract identifier names from their language's declarator chains; reused across all C/C++ function-definition sites.