Skip to content

feat: add no-unused-vars rule - #14

Open
nzakas wants to merge 5 commits into
rule/no-variables-in-printf-formatfrom
rule/no-unused-vars
Open

nzakas wants to merge 5 commits into
rule/no-variables-in-printf-formatfrom
rule/no-unused-vars

Conversation

@nzakas

@nzakas nzakas commented Sep 18, 2026 •

Copy link
Copy Markdown
Member

Adds shell/no-unused-vars, which mirrors ShellCheck SC2034: variables that are assigned but never read.

Behavior

Writes tracked:

  • assignments, including declare/local/typeset and arr[i]=;
  • for loop variables;
  • variables set by read, mapfile, and readarray (skipping read flags that take an argument, like -p prompt);
  • the getopts variable;
  • arithmetic assignments ((( total = 1 + 2 )), x++).

Reads tracked:

  • any $var or ${...} expansion;
  • bare names in arithmetic ($((x + 1)));
  • the arguments of unset, export, and readonly;
  • exported declarations (export or declare -x);
  • environment prefixes (FOO=1 cmd), which are passed to the command.

Never reported:

  • names starting with _;
  • common special shell variables (IFS, PS1, PATH, OPTIND, REPLY, LC_*, and others);
  • names listed in the rule's option: { "allowed": ["NAME"] }.

Skipped files: if the file uses eval, source/., or a nameref (declare -n / nameref), the rule reports nothing, since variables can then be read in ways static analysis can't see.

Other details:

  • The rule has one object option (allowed), and this PR adds the README's "Rule options" section for it.
  • Recommended config: "error".

Known limitations

  • No function scoping. The analysis is file-wide, so an unused local x inside a function isn't reported if some other x is read elsewhere in the file.
  • Indirect references aren't resolved. ${!ref} counts as a read of ref only, not of the variable it points to.

Documentation

Adds docs/rules/no-unused-vars.md, following the format of the @eslint/json, @eslint/css, and @eslint/markdown rule docs: description, background, rule details with incorrect and correct examples, options, when not to use it, and the ShellCheck reference. The rule's meta.docs.url points at that file, and its README table entry links to it.

Testing

23 RuleTester cases and 5 documentation checks (374 total); build, lint, and format checks pass.

🤖 Generated with Claude Code


Stack created with GitHub Stacks CLI • Give Feedback 💬

@nzakas
nzakas added this pull request to stack #16 September 18, 2026 15:00
@coderabbitai

coderabbitai Bot commented Sep 18, 2026 •

Copy link
Copy Markdown

Warning

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 55 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

Learn how review limits work.

Review configuration:

⚙️ Run configuration
  • Configuration used: Repository: eslint/coderabbit/.coderabbit.yaml
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: c1113205-a002-4a19-b593-df023e43e9b5
📥 Commits

Reviewing files that changed from the base of the PR and between 74b6423 and 17c3d55.

📒 Files selected for processing (6)
  • README.md
  • docs/rules/no-unused-vars.md
  • src/index.spec.ts
  • src/index.ts
  • src/rules/no-unused-vars.spec.ts
  • src/rules/no-unused-vars.ts
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@nzakas
nzakas marked this pull request as ready for review September 18, 2026 15:02
@nzakas
nzakas force-pushed the rule/no-unused-vars branch from c7d54bf to ec12388 Compare September 18, 2026 19:01
@nzakas
nzakas force-pushed the rule/no-unused-vars branch 2 times, most recently from d5e2840 to 506c93a Compare September 22, 2026 20:23
@nzakas
nzakas force-pushed the rule/no-unused-vars branch from 506c93a to 6371159 Compare September 22, 2026 20:34
@nzakas
nzakas force-pushed the rule/no-unused-vars branch from 6371159 to 9c3d0e5 Compare September 22, 2026 21:21
@nzakas
nzakas requested a balanced review from Copilot September 28, 2026 18:25

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Several valid Bash read/write contexts are misclassified, producing both false positives and false negatives.

Review effort: Balanced
Findings: 1 Medium severity

Open (1)
What changed in this PR

Adds the recommended bash/no-unused-vars rule with documentation and tests.

Changes:

  • Implements file-wide variable read/write tracking.
  • Registers the rule in the plugin and recommended configuration.
  • Adds rule documentation, options, and test coverage.
File Description
src/​rules/​no-unused-vars.ts Implements unused-variable analysis.
src/​rules/​no-unused-vars.spec.ts Tests core rule behavior.
src/​index.ts Registers and enables the rule.
src/​index.spec.ts Verifies rule exposure.
README.md Documents the rule and option.
docs/​rules/​no-unused-vars.md Adds detailed rule documentation.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/rules/no-unused-vars.ts
@nzakas

nzakas commented Sep 29, 2026

Copy link
Copy Markdown
Member Author

@copilot address the review comment. Be sure to add tests.

Copilot AI commented Sep 29, 2026

Copy link
Copy Markdown

@copilot address the review comment. Be sure to add tests.

Addressed in d2a28ce, with tests for each case:

  • Array literal subscripts (arr=([i]=v)) and ${s:offset:length} slices are now scanned as arithmetic reads.
  • Operands of [[ a -eq b ]] (and the other arithmetic comparisons) count as reads.
  • readonly x and declare -p x count as reads.
  • export -n is no longer treated as an export or as a nameref.
  • read -a arr records arr as a write, and mapfile flags that take an argument (such as -C and -u) are skipped.
  • Every LC_* variable is now ignored, as the docs already said.

I also updated the docs.

@nzakas

nzakas commented Oct 1, 2026

Copy link
Copy Markdown
Member Author

/easycla

@nzakas
nzakas force-pushed the rule/no-unused-vars branch 2 times, most recently from 35512c5 to cdabef0 Compare October 2, 2026 16:18
Comment thread src/rules/no-unused-vars.ts
@nzakas
nzakas force-pushed the rule/no-unused-vars branch from cdabef0 to b68b241 Compare October 6, 2026 19:09
@nzakas
nzakas force-pushed the rule/no-unused-vars branch 2 times, most recently from 1fd3372 to fad280b Compare October 6, 2026 19:38
@nzakas
nzakas force-pushed the rule/no-unused-vars branch from fad280b to 92da739 Compare October 8, 2026 19:46
nzakas and others added 5 commits October 8, 2026 16:49
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: nzakas <38546+nzakas@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The rule only listed shell/bash in meta.languages, so ESLint rejected it
when used with the shell/posix or shell/mksh language.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@nzakas
nzakas force-pushed the rule/no-unused-vars branch from 92da739 to 17c3d55 Compare October 8, 2026 20:51
Comment on lines +16 to +45
const SPECIAL_VARIABLES = new Set([
"BASH_ENV",
"CDPATH",
"EDITOR",
"ENV",
"GLOBIGNORE",
"HISTCONTROL",
"HISTFILE",
"HISTFILESIZE",
"HISTSIZE",
"HOME",
"IFS",
"LANG",
"LD_LIBRARY_PATH",
"OPTARG",
"OPTERR",
"OPTIND",
"PAGER",
"PATH",
"PROMPT_COMMAND",
"PS1",
"PS2",
"PS3",
"PS4",
"REPLY",
"TERM",
"TMOUT",
"TZ",
"VISUAL",
]);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A note that ShellCheck has a more comprehensive list of special variables: https://github.com/koalaman/shellcheck/blob/61ceb720d121fb448b4e98bc10a14b756ac4da8c/src/ShellCheck/Data.hs#L27-L70

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants