|
| 1 | +# CLAUDE.md |
| 2 | + |
| 3 | +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. |
| 4 | + |
| 5 | +## Build and test commands |
| 6 | + |
| 7 | +```sh |
| 8 | +cabal build # compile |
| 9 | +cabal test # run unit tests (source of truth) |
| 10 | +cabal run shellcheck -- file.sh # run on a file |
| 11 | +cabal run shellcheck - <<< 'cmd' # run on inline input |
| 12 | +./quickrun - <<< 'cmd' # run interpreted (fast, no recompile) |
| 13 | +./quicktest # run tests interpreted (fast, no recompile) |
| 14 | +./nextnumber # print next available SC1xxx/SC2xxx/SC3xxx code |
| 15 | +``` |
| 16 | + |
| 17 | +For interactive development, use `cabal repl` then `:load ShellCheck.Debug`. After editing, reload with `:r` and test with `shellcheckString "your shell code"`. |
| 18 | + |
| 19 | +To inspect the AST without an interactive session: |
| 20 | + |
| 21 | +```sh |
| 22 | +cabal run -fdev-mode shellcheck-dev -- ast 'myshellcommand' |
| 23 | +``` |
| 24 | + |
| 25 | +## Architecture |
| 26 | + |
| 27 | +ShellCheck processes shell scripts in three stages: |
| 28 | + |
| 29 | +1. **Parsing** (`Parser.hs`) — produces an AST plus warnings (SC1xxx). Parser notes (non-fatal) are buffered and discarded if parsing fails; parser problems (fatal) are always emitted. |
| 30 | +2. **AST Analysis** (`Analytics.hs`, `Checks/`) — walks the AST and emits warnings (SC2xxx/SC3xxx). |
| 31 | +3. **Output** (`Formatter/`) — formats results as TTY, JSON, GCC-style, diff, etc. |
| 32 | + |
| 33 | +### Key source files |
| 34 | + |
| 35 | +| File | Purpose | |
| 36 | +|---|---| |
| 37 | +| `src/ShellCheck/AST.hs` | Token type definitions (the AST node types) | |
| 38 | +| `src/ShellCheck/ASTLib.hs` | Helpers for working with AST nodes (e.g. `getLiteralString`) | |
| 39 | +| `src/ShellCheck/Analytics.hs` | Main analysis: `treeChecks` and `nodeChecks` lists | |
| 40 | +| `src/ShellCheck/AnalyzerLib.hs` | Shared utilities for check authors (`warn`, `err`, `style`, etc.) | |
| 41 | +| `src/ShellCheck/Checks/Commands.hs` | Per-command checks (dispatched by command name) | |
| 42 | +| `src/ShellCheck/Checks/ShellSupport.hs` | Shell-specific checks (dispatched by shell dialect) | |
| 43 | +| `src/ShellCheck/Checks/ControlFlow.hs` | Control-flow / CFG-based checks | |
| 44 | +| `src/ShellCheck/CFG.hs`, `CFGAnalysis.hs` | Control-flow graph construction and analysis | |
| 45 | +| `src/ShellCheck/Parser.hs` | The Parsec-based shell parser | |
| 46 | +| `src/ShellCheck/Interface.hs` | Public API types (`CheckResult`, `PositionedComment`, etc.) | |
| 47 | +| `src/ShellCheck/Debug.hs` | Dev helpers: `stringToAst`, `shellcheckString`, etc. | |
| 48 | + |
| 49 | +### Adding a check |
| 50 | + |
| 51 | +Most checks live in `Analytics.hs` as either: |
| 52 | + |
| 53 | +- **Node checks** — run on every AST node; append to `nodeChecks`. |
| 54 | +- **Tree checks** — run once on the root; append to `treeChecks`. |
| 55 | + |
| 56 | +Checks are pure functions `Parameters -> Token -> Writer [TokenComment] ()`. Use `warn`, `err`, `info`, or `style` from `AnalyzerLib.hs` to emit diagnostics. |
| 57 | + |
| 58 | +Each check should have `prop_` unit tests immediately above it: |
| 59 | + |
| 60 | +```haskell |
| 61 | +prop_checkFoo1 = verify checkFoo "bad shell code" |
| 62 | +prop_checkFoo2 = verifyNot checkFoo "good shell code" |
| 63 | +``` |
| 64 | + |
| 65 | +`cabal test` auto-discovers all `prop_` functions. Tests must pass before submitting. |
| 66 | + |
| 67 | +Command-specific checks go in `Checks/Commands.hs`; shell-dialect-specific checks go in `Checks/ShellSupport.hs`. |
| 68 | + |
| 69 | +### AST conventions |
| 70 | + |
| 71 | +Always use the sugared pattern aliases when matching or constructing AST nodes, e.g. `T_Literal id str` or `T_IoFile id op filename`. Never use the desugared internal classes like `OuterToken (Id id) (Inner_T_Literal str)` — those are GHC's internal representation and should not appear in check code. |
| 72 | + |
| 73 | +### Guidelines |
| 74 | + |
| 75 | +- Add unit tests for new and updated checks; cover both positive and negative cases. |
| 76 | +- Keep changes targeted — avoid sweeping refactors to propagate new data. |
| 77 | +- Account for equivalent command forms (e.g. `echo > foo bar` vs `echo bar > foo`). |
| 78 | +- Always verify `cabal test` passes cleanly. |
| 79 | +- Verify new and modified checks end-to-end via `cabal run shellcheck - <<< 'bad code'` (or `./quickrun`) to confirm the warning fires as expected. |
0 commit comments