Skip to content

fix: recognize sourced bash library usage in unused-import detector - #699

Open
daBOB wants to merge 1 commit into
peteromallet:mainfrom
daBOB:fix/bash-sourced-function-usage
Open

fix: recognize sourced bash library usage in unused-import detector#699
daBOB wants to merge 1 commit into
peteromallet:mainfrom
daBOB:fix/bash-sourced-function-usage

Conversation

@daBOB

@daBOB daBOB commented Aug 10, 2026

Copy link
Copy Markdown

Problem

The bash unused-import detector flags source ./lib.sh directives in scripts that demonstrably use the sourced library. Usage was recognized only when the sourced file's basename reappeared in the script body — but scripts that source a function library call its functions and read its variables; they never repeat the basename.

Repro (project with 30 shell scripts, 28 of which source ./lib-acceptance.sh and then call functions it defines, e.g. require_server, or read variables like $API):

$ desloppify --lang bash scan --path .

Before this fix, all 28 sourcing scripts are flagged, e.g.:

unused::scripts/acceptance-step-simulator.sh::unused_import::7   "Unused import: lib-acceptance"

...even though line 8 of that script calls require_server, defined in scripts/lib-acceptance.sh. In other words the detector produced a false positive for essentially every bash script that sources a shared function library.

Fix

In detect_unused_imports, for spec.grammar == "bash": resolve the sourced path relative to the sourcing script (reusing the spec's resolve_import / resolve_bash_source), parse the sourced file, and collect the symbols it defines:

  • function definitions in both name() {} and function name {} forms (global regardless of nesting), and
  • top-level variable assignments, including export FOO=... / declare -r FOO=... wrappers, while excluding function-local local/declare assignments.

The source directive counts as used when any defined symbol is referenced in the rest of the script. When the sourced path cannot be resolved (variable-based paths, missing files), the existing basename heuristic is kept, so current behavior — including all existing tests — is unchanged for unresolvable sources. Defined-symbol sets are cached per run since many scripts typically source the same library.

Verified on the repro project: 28 unused_import findings before, 0 after; the remaining 111 work items from other detectors are unaffected. Full test suite passes (the two pre-existing test_do_run_batches_dry_run_generates_packet_and_prompts failures reproduce identically on a clean checkout of main in this environment).

Note: while investigating I also checked a suspected companion issue — findings surviving rescans after their file is deleted. That is already handled on current main (verify_disappeared auto-resolves with "source file no longer exists"); confirmed empirically by deleting a file with 11 open findings and rescanning.

The bash unused-import check treated a source directive as used only
when the sourced file's basename reappeared in the script body. Scripts
that source a function library (source ./lib.sh) and then call its
functions or read its variables never repeat the basename, so every
such script was flagged as an unused import.

Resolve the sourced path relative to the sourcing script, parse it, and
collect the symbols it defines: function definitions in both name() and
'function name' forms plus top-level variable assignments, including
export/declare wrappers while excluding function-local declarations.
The import counts as used when any defined symbol is referenced in the
script. Unresolvable paths keep the existing basename heuristic, and
per-run caching avoids reparsing a library shared by many scripts.
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