Skip to content

Commit 68f243e

Browse files
committed
Jump on the AI bandwagon
1 parent 1b59697 commit 68f243e

4 files changed

Lines changed: 152 additions & 2 deletions

File tree

.claude/CLAUDE.md

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
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.

ShellCheck.cabal

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,10 @@ source-repository head
4141
type: git
4242
location: https://github.com/koalaman/shellcheck.git
4343

44+
flag dev-mode
45+
manual: True
46+
default: False
47+
4448
library
4549
hs-source-dirs: src
4650
if impl(ghc < 8.0)
@@ -83,7 +87,6 @@ library
8387
ShellCheck.Checks.Custom
8488
ShellCheck.Checks.ShellSupport
8589
ShellCheck.Data
86-
ShellCheck.Debug
8790
ShellCheck.Fixer
8891
ShellCheck.Formatter.Format
8992
ShellCheck.Formatter.CheckStyle
@@ -97,6 +100,11 @@ library
97100
ShellCheck.Parser
98101
ShellCheck.Prelude
99102
ShellCheck.Regex
103+
104+
if flag(dev-mode)
105+
exposed-modules:
106+
ShellCheck.Debug
107+
100108
other-modules:
101109
Paths_ShellCheck
102110
default-language: Haskell2010
@@ -125,6 +133,17 @@ executable shellcheck
125133
default-language: Haskell2010
126134
main-is: shellcheck.hs
127135

136+
executable shellcheck-dev
137+
if flag(dev-mode)
138+
buildable: True
139+
else
140+
buildable: False
141+
build-depends:
142+
base,
143+
containers,
144+
ShellCheck
145+
main-is: shellcheck-dev.hs
146+
128147
test-suite test-shellcheck
129148
type: exitcode-stdio-1.0
130149
build-depends:

shellcheck-dev.hs

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
{-
2+
Copyright 2026 Vidar Holen
3+
4+
This file is part of ShellCheck.
5+
https://www.shellcheck.net
6+
7+
ShellCheck is free software: you can redistribute it and/or modify
8+
it under the terms of the GNU General Public License as published by
9+
the Free Software Foundation, either version 3 of the License, or
10+
(at your option) any later version.
11+
12+
ShellCheck is distributed in the hope that it will be useful,
13+
but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+
GNU General Public License for more details.
16+
17+
You should have received a copy of the GNU General Public License
18+
along with this program. If not, see <https://www.gnu.org/licenses/>.
19+
-}
20+
21+
-- shellcheck-dev is primarily meant for the potential benefits of AI.
22+
-- It can be run with `cabal run -fdev-mode shellcheck-dev -- ast 'myshellcommand'`
23+
24+
import ShellCheck.Debug
25+
import System.Environment
26+
import System.Exit
27+
import System.IO
28+
import Data.List
29+
import qualified Data.Map as Map
30+
31+
commands :: Map.Map String (String -> String)
32+
commands = Map.fromList [
33+
("ast", show . stringToAst)
34+
]
35+
36+
validCommands :: String
37+
validCommands = intercalate ", " $ Map.keys commands
38+
39+
putStrLnErr = hPutStrLn stderr
40+
41+
main = do
42+
args <- getArgs
43+
case args of
44+
[cmd, arg] ->
45+
case Map.lookup cmd commands of
46+
Just f -> putStrLn $ f arg
47+
Nothing -> do
48+
putStrLnErr $ "Unknown command. Try one of: " ++ validCommands
49+
exitFailure
50+
_ -> do
51+
putStrLnErr $ "Usage: shellcheck-dev command argument, where command is: " ++ validCommands
52+
exitFailure

src/ShellCheck/Debug.hs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ Afterwards, you can run the ShellCheck tool, as if from the shell, with:
7474
7575
-}
7676

77-
module ShellCheck.Debug () where
77+
module ShellCheck.Debug where
7878

7979
import ShellCheck.Analyzer
8080
import ShellCheck.AST

0 commit comments

Comments
 (0)