Skip to content

feat(languages): parse shell to AST (functions, source edges, complexity)#768

Open
RaghavChamadiya wants to merge 1 commit into
mainfrom
feat/shell-ast
Open

feat(languages): parse shell to AST (functions, source edges, complexity)#768
RaghavChamadiya wants to merge 1 commit into
mainfrom
feat/shell-ast

Conversation

@RaghavChamadiya

Copy link
Copy Markdown
Member

Promotes shell (.sh / .bash / .zsh) from the config/data passthrough tier to a parsed language. Every repo has shell (CI glue, build scripts, dev tooling); tree-sitter-bash is a mature grammar, so this makes wiki pages and the graph honest for DevOps-heavy repos at low cost.

What ships

  • Function symbols — both foo() { ... } and function foo { ... } forms.
  • Import edgessource / . statements. The resolver handles literal relative paths, the script-directory idioms ($SCRIPT_DIR/x.sh, $(dirname "$0")/x.sh, ${BASH_SOURCE%/*}/x.sh), and project-root anchors ($BATS_ROOT/$LIBDIR/lib/x.sh) via a unique multi-segment path-suffix match. Genuinely dynamic paths (source "$1") stay external rather than guessing.
  • Call edges — command invocations resolve to functions defined in the same or a sourced file. Shell builtins are filtered; external binaries (grep, awk) mint no edge because no function symbol matches.
  • Function-level complexity — CCN / nesting / cognitive, with && / || command lists counted (cmd || exit 1 is +1, which is honest: shell branching is chained command lists). No class metrics, heritage, bindings, perf, or dataflow.
  • Dead code — shell files are never flagged (invoked by name from CI / Makefiles / humans, so static reachability is meaningless).

Parse errors degrade per-file to the previous passthrough behavior, never a crash. tree-sitter-bash parses the bash/POSIX subset, so zsh mostly works and fish is out of scope.

Scope notes

  • Extensionless executables (a shebang-only bin/foo) depend on the traverser assigning them the shell language from their shebang; that is a traverser capability, left out of scope here. .sh / .bash / .zsh files are the 90% case.

Testing

  • 57 unit tests: resolver (idioms + external fallbacks), symbol / import / call extraction, complexity walker (boolean lists, control flow, flat-vs-nested case, zero-function robustness), and fixtures including a zsh degradation case.
  • Smoke on bats-core (54 shell files): 100% parse rate, 151 function symbols, statically-resolvable source edges resolve, external binaries mint no call edges, and the gnarliest function (CCN 23) surfaces in health.
  • Full non-server unit suite green (6018 passed); docs (LANGUAGE_SUPPORT.md, README.md) updated.

…ity)

Promote shell from the config/data passthrough tier to a parsed language.
tree-sitter-bash now yields function symbols, `source` / `.` import edges,
command call edges, and function-level code-health complexity.

- specs/shell.py: grammar + scm + builtin call filter; drop passthrough
- queries/shell.scm: function_definition symbols, source/. imports, calls
- resolvers/shell.py: literal-relative paths, the $SCRIPT_DIR / dirname
  idioms, and a unique path-suffix match for project-root anchors
  (`$BATS_ROOT/$LIBDIR/lib/x.sh`); genuinely dynamic paths stay external
- complexity map: CCN / nesting with `&&` / `||` command lists counted
- dead code: never-flag *.sh / *.bash / *.zsh (invoked by name, not imported)

Smoke on bats-core (54 files): 100% parse rate, 151 function symbols,
statically-resolvable source edges resolve, external binaries mint no call
edges, and the gnarliest function (CCN 23) surfaces in health.
@repowise-bot

repowise-bot Bot commented Jul 10, 2026

Copy link
Copy Markdown

✅ Health: 7.7 (unchanged)

📋 At a glance
4 hotspots touched · 1 new finding introduced · 5 co-change pairs left out · 3 files with recent fix history.

Files & modules (2)
  • packages (3 files)
    • .../dead_code/constants.py
    • .../ingestion/language_configs.py
    • .../complexity/languages.py
  • tests (1 file)
    • .../ingestion/test_language_capabilities.py

⚠️ Change risk: moderate (riskier than 60% of this repo's commits · raw 9.2/10)
This change's risk is driven by:

  • more lines added than baseline
  • more scattered than baseline

🩹 Review priority (files here with the most recent bug-fix history — defects cluster, so review these first)

🔎 More signals (2)

🔥 Hotspots touched (4)

  • .../dead_code/constants.py — 17 commits/90d, 9 dependents · primary owner: Raghav Chamadiya (95%)
  • .../ingestion/test_language_capabilities.py — 4 commits/90d, 0 dependents · primary owner: Swati Ahuja (97%)
  • .../ingestion/language_configs.py — 6 commits/90d, 1 dependents · primary owner: Raghav Chamadiya (96%)
1 more
  • .../complexity/languages.py — 15 commits/90d, 28 dependents · primary owner: Raghav Chamadiya (99%)

🔗 Hidden coupling (2 files)

  • .../dead_code/constants.py co-changes with these files (not in this PR):
    • .../dead_code/analyzer.py (9× — 🟢 routine)
    • .../ingestion/parser.py (6× — 🟢 routine)
    • .../dead_code/dynamic_markers.py (5× — 🟢 routine)
  • .../complexity/languages.py co-changes with these files (not in this PR):
    • docs/LANGUAGE_SUPPORT.md (9× — 🟢 routine)
    • .../health/test_complexity_walker.py (6× — 🟢 routine)

📊 Full report · ⭐ Star Repowise · 📥 Install bot · Last updated 2026-07-10 18:42 UTC
Silence on a single PR with [skip repowise] in the title · Per-repo toggle on repowise.dev/settings?tab=bot

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant