108 Commits

Author SHA1 Message Date
Vidar Holen
c5479b8ca3 Stable version 0.3.5
This release is dedicated to Maru, internet celebrity cat.
Where would the web be without you? (Runner-up: Tim Berners-Lee)
2014-11-09 16:30:00 -08:00
Vidar Holen
d9dd58bec8 Warn about 'for $var in values'. 2014-11-09 16:22:01 -08:00
Vidar Holen
af1bb93aba Better warnings for repeated ;;s 2014-11-09 14:33:36 -08:00
Vidar Holen
e909c8ac42 More lenient line feed handling in test expressions. 2014-11-08 15:35:06 -08:00
koalaman
93140e31a0 Merge pull request #253 from vlajos/typofixes-vlajos-20141104
typo fixes - https://github.com/vlajos/misspell_fixer
2014-11-04 15:27:56 -08:00
Veres Lajos
97f3834852 typo fixes - https://github.com/vlajos/misspell_fixer 2014-11-04 21:55:42 +00:00
Vidar Holen
0369f43bac Fixed 2148 to not trigger if a shell is specified with -s. 2014-11-01 13:51:19 -07:00
Vidar Holen
eb2eae2888 Don't warn about ${args[@]} when nested in other ${} 2014-11-01 12:44:27 -07:00
Vidar Holen
30c0c1f27d Allow export "foo"="bar" in 2140 2014-11-01 12:20:10 -07:00
Vidar Holen
bff5d11566 Warn about `` in '' 2014-11-01 12:17:12 -07:00
Vidar Holen
eccb9f3f71 Added -or and -print0 to SC2146 2014-11-01 12:07:09 -07:00
Vidar Holen
2814572116 cat "$@" is not UUOC 2014-10-18 19:59:13 -07:00
Vidar Holen
90bafb9aba Fixed bug where (($b)) counted as a positional reference 2014-10-18 19:51:13 -07:00
Vidar Holen
39b88bbaac Removed Arch from readme, added Debian. 2014-10-12 17:13:35 -07:00
Vidar Holen
39805ab200 Don't warn about unpassed parameters in functions using 'set ..'. 2014-10-12 17:10:46 -07:00
Vidar Holen
9dadce96c0 Improve messages for missing 'then' statements. 2014-10-12 16:17:03 -07:00
Vidar Holen
1a0e208cc3 Consider find -exec when warning about vars in single quotes. 2014-10-12 14:00:17 -07:00
Vidar Holen
a69e27b774 Warn about swapped !# in the shebang. 2014-10-11 12:35:45 -07:00
Vidar Holen
b05c12223f Don't trigger SC2004 for (( $$ )) 2014-09-23 10:27:26 -07:00
Vidar Holen
38ead0385b Fixed quoting warnings for variables in $".." 2014-09-23 10:18:28 -07:00
Vidar Holen
9e8a11e57c Merge branch 'master' of github.com:koalaman/shellcheck 2014-09-23 10:12:23 -07:00
Vidar Holen
6b84b35ec0 Don't crash on empty files with -f gcc. 2014-09-23 10:11:15 -07:00
koalaman
669fdf8e5e Merge pull request #226 from aycanirican/patch-1
Update License in ShellCheck.cabal
2014-09-18 07:09:46 -07:00
Aycan iRiCAN
dccfb3c4a1 Update ShellCheck.cabal
Fixed License.
2014-09-18 09:10:07 +03:00
Vidar Holen
40ce949a56 Only warn once per unused variable name. 2014-09-07 12:55:08 -07:00
Vidar Holen
9f3802138f Prevent overlap of 2116 and 2005 in foo $(echo $(bar)) 2014-09-04 08:41:09 -07:00
Vidar Holen
2f3533fff6 Improve warnings for $ in (()). Also improves array subscripts. 2014-08-16 17:08:57 -07:00
Vidar Holen
f9c346cfd7 Ignore SC2033 when passing quoted function names. 2014-08-16 10:45:46 -07:00
Vidar Holen
5f7419ca37 Require a QuickCheck that doesn't break on UTF-8. 2014-08-10 17:16:27 -07:00
Vidar Holen
8494509150 Warn about missing shebangs. 2014-08-09 17:32:42 -07:00
Vidar Holen
8ba1f2fdf2 Better handling of directories and inaccessible files. 2014-08-08 09:36:17 -07:00
Vidar Holen
dbadca9f61 Check PS1/PROMPT_COMMAND/trap for simple variable references 2014-07-27 09:51:48 -07:00
Vidar Holen
0347ce1b7a Warn about quoted ~ in PATH 2014-07-26 13:14:28 -07:00
Vidar Holen
7fbe66e1c6 Warn about ineffectual quotes in a="/foo/'bar baz'"; $a 2014-07-26 12:15:54 -07:00
Vidar Holen
b000b05507 Parse empty and comment-only backtick expansions. 2014-07-26 12:07:59 -07:00
Vidar Holen
39423ddf81 Stable version 0.3.4
This release is dedicated to Kerbal Space Program,
which has delayed the project by at least a month.
2014-07-08 18:43:33 -07:00
Vidar Holen
875c2d2aad Removed Makefile from cabal file 2014-07-08 18:18:32 -07:00
Vidar Holen
64cc7c691a Warn about precedence in find -name -o -name -exec. 2014-06-22 14:16:24 -07:00
Vidar Holen
b9784cbcc0 Parse let arguments as arithmetic expressions. 2014-06-22 13:23:44 -07:00
Vidar Holen
1a3f6aadaf Support indices in array declarations 2014-06-22 10:35:45 -07:00
Vidar Holen
35756c2cd6 Delete outdated Makefile. 2014-06-22 09:16:54 -07:00
Vidar Holen
0fd351404f Allow escape sequences in here documents. 2014-06-16 14:18:29 -07:00
Vidar Holen
4caa7e7900 Removed accidentally submotted debug code 2014-06-10 00:49:43 -07:00
Vidar Holen
c11c0196d5 Fixed broken parsing of <( in arithmetics 2014-06-10 00:42:07 -07:00
Vidar Holen
b035331d4a Fixed failing test 2014-06-10 00:33:54 -07:00
Vidar Holen
d13253973b Updated readme 2014-06-07 23:25:01 -07:00
koalaman
d9c622ae33 Merge pull request #172 from jbnicolai/master
Adds homebrew installation steps to README.
2014-06-07 23:07:38 -07:00
Vidar Holen
aac7d76047 Don't warn when using find -print0 | xargs --null 2014-06-07 22:41:37 -07:00
Vidar Holen
fc421adb45 Reworked arithmetics to allow composite terms 2014-06-07 22:09:34 -07:00
Joshua Appelman
e0d3c6923a Removes trailing whitespace. 2014-06-08 01:42:32 +02:00
Joshua Appelman
9772ba9de4 Adds homebrew installation steps to README. 2014-06-08 01:42:20 +02:00
Vidar Holen
3a944de606 Warn when concatening strings and arrays. 2014-06-07 13:47:40 -07:00
Vidar Holen
3dd592a02a Support ;& and ;;& in case statements 2014-06-07 12:23:42 -07:00
koalaman
61531cbb10 Merge pull request #169 from Dridi/hackage
Add extra source files in the source tarball
2014-06-01 12:46:08 -07:00
Dridi Boukelmoune
d53087f056 Updated Extra-Source-Files accordingly with #165 2014-06-01 21:35:45 +02:00
Dridi Boukelmoune
39756b420e Add extra source files in the source tarball
This way ShellCheck can be entirely built (including the test suite)
from the Hackage tarball.

The source tarball can be generated using:

    cabal sdist
2014-06-01 17:59:24 +02:00
koalaman
52d4efc951 Merge pull request #168 from rodrigosetti/hlint
Collection of HLint fixes
2014-05-31 16:07:51 -07:00
Rodrigo Setti
5dac723593 Collection of HLint fixes
http://community.haskell.org/~ndm/hlint/
2014-05-31 22:20:49 +00:00
koalaman
2364fd58b6 Merge pull request #166 from rodrigosetti/better-gitignore
Using a more complete Haskell .gitignore
2014-05-31 10:42:17 -07:00
Vidar Holen
cde364c97b Updated README with new cabal instructions 2014-05-31 10:40:45 -07:00
Vidar Holen
98b790f87a Removed outdated version comment 2014-05-31 10:16:11 -07:00
Vidar Holen
726a4e5848 Merge branch 'cabal-version' of https://github.com/rodrigosetti/shellcheck into rodrigosetti-cabal-version
Conflicts:
	ShellCheck/Analytics.hs
	ShellCheck/Data.hs
2014-05-31 09:55:07 -07:00
Rodrigo Setti
0a9ed917e7 Test Suite in Cabal (cabal test)
Please run using "cabal test --show-details=streaming", there's a known
issue about this that was fixed in the latest version of cabal:
https://github.com/haskell/cabal/issues/1810
2014-05-31 01:30:23 +00:00
Vidar Holen
b10d31c8b7 Stable version 0.3.3
This release is dedicated to Jarkko Oikarinen, creator of IRC,
the fabric of the author's existence for so many years.
2014-05-29 21:01:34 -07:00
Vidar Holen
133c779701 Also check nested ifs for ssh/ffmpeg in read loops 2014-05-29 20:55:38 -07:00
Rodrigo Setti
b18ee3fdef Using a more complete Haskell .gitignore
Specially for using cabal sandbox.
2014-05-30 00:10:21 +00:00
Rodrigo Setti
3fcc6c44d8 Use version from generated cabal Paths module 2014-05-30 00:08:09 +00:00
Vidar Holen
d830a36bc8 Check for globs in test, e.g. [[ -e file* ]] 2014-05-25 12:04:18 -07:00
Vidar Holen
1af23fd131 Fix SC2051 to only warn about 1..$n and not 1,$n 2014-05-25 11:41:24 -07:00
Vidar Holen
d21b3362b2 Don't warn about splitting in select statements 2014-05-17 12:06:04 -07:00
Vidar Holen
6cd454e88b Suggest grep -q instead of [ "$(.. | grep)" ] 2014-05-17 10:56:36 -07:00
Vidar Holen
0b5f6b9762 Warn about aliases referencing $1/$*/$@ 2014-05-17 09:26:53 -07:00
Vidar Holen
3824e9cfc2 Fixed not recognizing --f=* as option in checkGrepRe 2014-05-15 09:14:57 -07:00
Vidar Holen
fdce0116da Fix parsing {} in regex 2014-05-13 19:20:34 -07:00
Vidar Holen
b069f7ed27 Added a shellcheck-static Makefile target 2014-05-11 13:58:56 -07:00
Vidar Holen
c4181d45d2 Warn about suspicious IFS, such as IFS="\n" 2014-05-10 15:37:02 -07:00
Vidar Holen
680f838c63 Warn about literal, unquoted {/} 2014-05-10 14:07:53 -07:00
Vidar Holen
e6d81ca7b7 Warn about using numerical test operators with strings 2014-05-10 12:37:02 -07:00
Vidar Holen
fd909eeca0 Fix parsing of &;; in case statements 2014-05-10 11:29:30 -07:00
Vidar Holen
deab146fab Don't warn about &&+|| when used with return 2014-05-09 18:08:55 -07:00
Vidar Holen
f9aeabc245 Improved error messages for SC2044/SC2045 2014-05-08 19:38:40 -07:00
Vidar Holen
558d8ffc6c Warn about suspiciously unquoted literal parts like "var="value"" 2014-05-07 21:47:27 -07:00
Vidar Holen
e96c4c3ffa Warn about aliases that expand at define time 2014-05-07 20:14:21 -07:00
Vidar Holen
c566efd442 Warn about UTF-8 BOMs in scripts. 2014-05-03 10:37:12 -07:00
Vidar Holen
47c220d59c Removed noisy SC1000 about unescaped $s 2014-05-03 10:19:01 -07:00
Vidar Holen
4bd902c5c4 Suggested updating cabal-install in the readme 2014-05-03 10:13:36 -07:00
Vidar Holen
033ce6d941 Allow zsh =(..) process substitution 2014-05-02 20:36:38 -07:00
Vidar Holen
6ad3f557fe Don't warn about sed '$s/foo/bar/' 2014-04-19 12:29:49 -07:00
Vidar Holen
d0bad6c057 Suggest grouping redirections when appending on 3+ lines 2014-04-19 11:53:54 -07:00
Vidar Holen
58c362f97c Warn about foo=(bar baz); echo $foo 2014-04-19 10:20:24 -07:00
Vidar Holen
5f568dd207 Merge branch 'master' of github.com:koalaman/shellcheck 2014-04-19 08:52:57 -07:00
Vidar Holen
2c1e414ac5 Only get vars after the last option in read 2014-04-19 08:50:47 -07:00
koalaman
6699109ab8 Merge pull request #139 from MichaelPereira/patch-1
Add pandoc to needed dependencies
2014-04-17 12:14:38 -07:00
Michael Pereira
423ca82296 Add pandoc to needed dependencies
The pandoc command is needed when compiling with make to run the tests
2014-04-17 15:08:37 -04:00
Vidar Holen
0a263579e0 Support for zsh short form for loops and anonymous functions 2014-04-13 13:37:37 -07:00
Vidar Holen
d63406abe4 Prevent SC2101 to collide with 2060 for tr -d [:space:] 2014-04-12 16:43:57 -07:00
Vidar Holen
81956d324d Don't warn when single quoting PROMPT_COMMAND and PS1 2014-04-05 17:08:03 -07:00
Vidar Holen
f549aad809 Suggest grep -c for grep|wc 2014-04-05 16:53:09 -07:00
Vidar Holen
f9f965693d Don't warn about single quoting $0 for xprop 2014-04-05 16:32:59 -07:00
Vidar Holen
727d940e10 Don't warn about expr when using <, > and friends 2014-04-05 16:29:35 -07:00
Vidar Holen
c26c2b8536 Stop using generic char 'c' in 2022 2014-04-05 16:17:52 -07:00
Vidar Holen
d8878ed852 Fixed warnings when assigning arrays to scalars 2014-04-05 14:56:12 -07:00
Vidar Holen
c3cc5f649f Fixed warning about \n in echo -n -e '\n' 2014-04-05 12:49:24 -07:00
koalaman
8bd4365cdb Merge pull request #122 from guywithnose/master
Add cabal update to install instructions
2014-04-05 11:40:46 -07:00
koalaman
a00a6fb53b Merge pull request #125 from inthecloud247/master
Instructions for adding ShellCheck to PATH
2014-04-01 17:59:48 -07:00
John Albietz
3332eba9a0 Instructions for adding ShellCheck to PATH 2014-04-01 14:26:03 -07:00
guywithnose
ad08bb64aa Add cabal update to install instructions
Before I ran `cabal update` I got this on `cabal install`

```
Config file path source is default config file.
Config file ~/.cabal/config not found.
Writing default configuration to ~/.cabal/config
Warning: The package list for 'hackage.haskell.org' does not exist. Run 'cabal
update' to download it.
Resolving dependencies...
cabal: Could not resolve dependencies:
trying: ShellCheck-0.3.2
```
2014-04-01 11:48:09 -04:00
Vidar Holen
f01e6e1a99 Check for accidentally overriding $PATH 2014-03-29 10:07:23 -07:00
15 changed files with 1623 additions and 891 deletions

20
.gitignore vendored
View File

@@ -1,7 +1,15 @@
*.hi
*.o
.tests
jsoncheck
shellcheck
shellcheck.1
# Created by http://www.gitignore.io
### Haskell ###
dist
cabal-dev
*.o
*.hi
*.chi
*.chs.h
.virtualenv
.hsenv
.cabal-sandbox/
cabal.sandbox.config
cabal.config

View File

@@ -1,24 +0,0 @@
# TODO: Phase out Makefile in favor of Cabal
GHCFLAGS=-O9
all: shellcheck .tests shellcheck.1
: Done
shellcheck: regardless
: Conditionally compiling shellcheck
ghc $(GHCFLAGS) --make shellcheck
.tests: *.hs */*.hs
: Running unit tests
./test/runQuack && touch .tests
shellcheck.1: shellcheck.1.md
pandoc -s -t man $< -o $@
clean:
rm -f .tests shellcheck shellcheck.1
rm -f *.hi *.o ShellCheck/*.hi ShellCheck/*.o
rm -rf dist
regardless:

View File

@@ -16,12 +16,32 @@ The goals of ShellCheck are:
- To point out subtle caveats, corner cases and pitfalls, that may cause an
advanced user's otherwise working script to fail under future circumstances.
ShellCheck requires at least 1 GB of RAM to compile. Executables can be built with cabal. Tests currently still rely on a Makefile.
ShellCheck is written in Haskell, and requires 2 GB of memory to compile.
## Installing
On systems with Cabal:
cabal update
cabal install shellcheck
On Debian based distros:
apt-get install shellcheck
On OS X with homebrew:
brew install shellcheck
ShellCheck is also available as an online service:
http://www.shellcheck.net
## Building with Cabal
Make sure cabal is installed. On Debian based distros:
This sections describes how to build ShellCheck from a source directory.
First, make sure cabal is installed. On Debian based distros:
apt-get install cabal-install
@@ -37,32 +57,32 @@ On Mac OS X with MacPorts (http://www.macports.org/):
port install hs-cabal-install
With cabal installed, cd to the shellcheck source directory and:
Let cabal update itself, in case your distro version is outdated:
$ cabal update
$ cabal install cabal-install
With cabal installed, cd to the ShellCheck source directory and:
$ cabal install
...
This will install ShellCheck to your ~/.cabal/bin directory.
Add the directory to your PATH (for bash, add this to your ~/.bashrc file):
export PATH=$HOME/.cabal/bin:$PATH
Verify that your PATH is set up correctly:
$ which shellcheck
~/.cabal/bin/shellcheck
## Running tests
## Building with Make
ShellCheck is written in Haskell, and requires GHC, Parsec3, JSON and
Text.Regex. To run the unit tests, it also requires QuickCheck2.
On Fedora, these can be installed with:
yum install ghc ghc-parsec-devel ghc-QuickCheck-devel \
ghc-json-devel ghc-regex-compat-devel
On Ubuntu and similar, use:
apt-get install ghc libghc-parsec3-dev libghc-json-dev \
libghc-regex-compat-dev libghc-quickcheck2-dev
To build and run the tests, cd to the shellcheck source directory and:
$ make
To run the unit test suite:
cabal configure --enable-tests
cabal build
cabal test
Happy ShellChecking!

View File

@@ -1,8 +1,7 @@
Name: ShellCheck
-- Must also be updated in ShellCheck/Data.hs :
Version: 0.3.2
Version: 0.3.5
Synopsis: Shell script analysis tool
License: OtherLicense
License: AGPL-3
License-file: LICENSE
Category: Static Analysis
Author: Vidar Holen
@@ -23,6 +22,13 @@ Description:
* To point out subtle caveats, corner cases and pitfalls, that may cause an
advanced user's otherwise working script to fail under future circumstances.
Extra-Source-Files:
-- documentation
README.md
shellcheck.1.md
-- tests
test/shellcheck.hs
source-repository head
type: git
location: git://github.com/koalaman/shellcheck.git
@@ -35,13 +41,17 @@ library
json,
mtl,
parsec,
regex-compat
regex-compat,
QuickCheck >= 2.7.4
exposed-modules:
ShellCheck.Analytics
ShellCheck.AST
ShellCheck.Data
ShellCheck.Options
ShellCheck.Parser
ShellCheck.Simple
other-modules:
Paths_ShellCheck
executable shellcheck
build-depends:
@@ -52,5 +62,23 @@ executable shellcheck
json,
mtl,
parsec,
regex-compat
regex-compat,
transformers,
QuickCheck >= 2.7.4
main-is: shellcheck.hs
test-suite test-shellcheck
type: exitcode-stdio-1.0
build-depends:
ShellCheck,
base >= 4 && < 5,
containers,
directory,
json,
mtl,
parsec,
regex-compat,
transformers,
QuickCheck >= 2.7.4
main-is: test/shellcheck.hs

View File

@@ -28,16 +28,16 @@ data Dashed = Dashed | Undashed deriving (Show, Eq)
data AssignmentMode = Assign | Append deriving (Show, Eq)
data FunctionKeyword = FunctionKeyword Bool deriving (Show, Eq)
data FunctionParentheses = FunctionParentheses Bool deriving (Show, Eq)
data ForInType = NormalForIn | ShortForIn deriving (Show, Eq)
data CaseType = CaseBreak | CaseFallThrough | CaseContinue deriving (Show, Eq)
data Token =
TA_Base Id String Token
| TA_Binary Id String Token Token
| TA_Expansion Id Token
| TA_Literal Id String
TA_Binary Id String Token Token
| TA_Expansion Id [Token]
| TA_Index Id Token
| TA_Sequence Id [Token]
| TA_Trinary Id Token Token Token
| TA_Unary Id String Token
| TA_Variable Id String
| TC_And Id ConditionType String Token Token
| TC_Binary Id ConditionType String Token Token
| TC_Group Id ConditionType Token
@@ -48,6 +48,8 @@ data Token =
| T_AndIf Id (Token) (Token)
| T_Arithmetic Id Token
| T_Array Id [Token]
| T_IndexedElement Id Token Token
| T_ Id [Token]
| T_Assignment Id AssignmentMode String (Maybe Token) Token
| T_Backgrounded Id Token
| T_Backticked Id [Token]
@@ -57,7 +59,7 @@ data Token =
| T_BraceGroup Id [Token]
| T_CLOBBER Id
| T_Case Id
| T_CaseExpression Id Token [([Token],[Token])]
| T_CaseExpression Id Token [(CaseType, [Token], [Token])]
| T_Condition Id ConditionType Token
| T_DGREAT Id
| T_DLESS Id
@@ -81,7 +83,7 @@ data Token =
| T_Fi Id
| T_For Id
| T_ForArithmetic Id Token Token Token [Token]
| T_ForIn Id String [Token] [Token]
| T_ForIn Id ForInType [String] [Token] [Token]
| T_Function Id FunctionKeyword FunctionParentheses String Token
| T_GREATAND Id
| T_Glob Id String
@@ -178,6 +180,7 @@ analyze f g i =
b <- round value
return $ T_Assignment id mode var a b
delve (T_Array id t) = dl t $ T_Array id
delve (T_IndexedElement id t1 t2) = d2 t1 t2 $ T_IndexedElement id
delve (T_Redirecting id redirs cmd) = do
a <- roundAll redirs
b <- round cmd
@@ -202,14 +205,14 @@ analyze f g i =
delve (T_BraceGroup id l) = dl l $ T_BraceGroup id
delve (T_WhileExpression id c l) = dll c l $ T_WhileExpression id
delve (T_UntilExpression id c l) = dll c l $ T_UntilExpression id
delve (T_ForIn id v w l) = dll w l $ T_ForIn id v
delve (T_ForIn id t v w l) = dll w l $ T_ForIn id t v
delve (T_SelectIn id v w l) = dll w l $ T_SelectIn id v
delve (T_CaseExpression id word cases) = do
newWord <- round word
newCases <- mapM (\(c, t) -> do
newCases <- mapM (\(o, c, t) -> do
x <- mapM round c
y <- mapM round t
return (x,y)
return (o, x,y)
) cases
return $ T_CaseExpression id newWord newCases
@@ -242,8 +245,8 @@ analyze f g i =
b <- round t2
c <- round t3
return $ TA_Trinary id a b c
delve (TA_Expansion id t) = d1 t $ TA_Expansion id
delve (TA_Base id b t) = d1 t $ TA_Base id b
delve (TA_Expansion id t) = dl t $ TA_Expansion id
delve (TA_Index id t) = d1 t $ TA_Index id
delve (T_Annotation id anns t) = d1 t $ T_Annotation id anns
delve t = return t
@@ -296,6 +299,7 @@ getId t = case t of
T_FdRedirect id _ _ -> id
T_Assignment id _ _ _ _ -> id
T_Array id _ -> id
T_IndexedElement id _ _ -> id
T_Redirecting id _ _ -> id
T_SimpleCommand id _ _ -> id
T_Pipeline id _ _ -> id
@@ -308,7 +312,7 @@ getId t = case t of
T_BraceGroup id _ -> id
T_WhileExpression id _ _ -> id
T_UntilExpression id _ _ -> id
T_ForIn id _ _ _ -> id
T_ForIn id _ _ _ _ -> id
T_SelectIn id _ _ _ -> id
T_CaseExpression id _ _ -> id
T_Function id _ _ _ _ -> id
@@ -326,11 +330,9 @@ getId t = case t of
TA_Binary id _ _ _ -> id
TA_Unary id _ _ -> id
TA_Sequence id _ -> id
TA_Variable id _ -> id
TA_Trinary id _ _ _ -> id
TA_Expansion id _ -> id
TA_Literal id _ -> id
TA_Base id _ _ -> id
TA_Index id _ -> id
T_ProcSub id _ _ -> id
T_Glob id _ -> id
T_ForArithmetic id _ _ _ _ -> id

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,9 @@
module ShellCheck.Data where
shellcheckVersion = "0.3.2" -- Must also be updated in ShellCheck.cabal
import Data.Version (showVersion)
import Paths_ShellCheck (version)
shellcheckVersion = showVersion version
internalVariables = [
-- Generic
@@ -74,3 +77,11 @@ commonCommands = [
"val", "vi", "wait", "wc", "what", "who", "write", "xargs", "yacc",
"zcat"
]
sampleWords = [
"alpha", "bravo", "charlie", "delta", "echo", "foxtrot",
"golf", "hotel", "india", "juliett", "kilo", "lima", "mike",
"november", "oscar", "papa", "quebec", "romeo", "sierra",
"tango", "uniform", "victor", "whiskey", "xray", "yankee",
"zulu"
]

14
ShellCheck/Options.hs Normal file
View File

@@ -0,0 +1,14 @@
module ShellCheck.Options where
data Shell = Ksh | Zsh | Sh | Bash
deriving (Show, Eq)
data AnalysisOptions = AnalysisOptions {
optionShellType :: Maybe Shell,
optionExcludes :: [Integer]
}
defaultAnalysisOptions = AnalysisOptions {
optionShellType = Nothing,
optionExcludes = []
}

File diff suppressed because it is too large Load Diff

View File

@@ -15,35 +15,21 @@
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
-}
module ShellCheck.Simple (shellCheck, ShellCheckComment, scLine, scColumn, scSeverity, scCode, scMessage) where
{-# LANGUAGE TemplateHaskell #-}
module ShellCheck.Simple (shellCheck, ShellCheckComment, scLine, scColumn, scSeverity, scCode, scMessage, runTests) where
import ShellCheck.Parser
import ShellCheck.Analytics
import Data.Maybe
import Text.Parsec.Pos
import Data.List
import Data.Maybe
import ShellCheck.Analytics hiding (runTests)
import ShellCheck.Options
import ShellCheck.Parser hiding (runTests)
import Test.QuickCheck.All (quickCheckAll)
import Text.Parsec.Pos
prop_findsParseIssue =
let comments = shellCheck "echo \"$12\"" [] in
(length comments) == 1 && (scCode $ head comments) == 1037
prop_commentDisablesParseIssue1 =
null $ shellCheck "#shellcheck disable=SC1037\necho \"$12\"" []
prop_commentDisablesParseIssue2 =
null $ shellCheck "#shellcheck disable=SC1037\n#lol\necho \"$12\"" []
prop_findsAnalysisIssue =
let comments = shellCheck "echo $1" [] in
(length comments) == 1 && (scCode $ head comments) == 2086
prop_commentDisablesAnalysisIssue1 =
null $ shellCheck "#shellcheck disable=SC2086\necho $1" []
prop_commentDisablesAnalysisIssue2 =
null $ shellCheck "#shellcheck disable=SC2086\n#lol\necho $1" []
shellCheck :: String -> [AnalysisOption] -> [ShellCheckComment]
shellCheck script options =
shellCheck :: AnalysisOptions -> String -> [ShellCheckComment]
shellCheck options script =
let (ParseResult result notes) = parseShell "-" script in
let allNotes = notes ++ (concat $ maybeToList $ do
let allNotes = notes ++ concat (maybeToList $ do
(tree, posMap) <- result
let list = runAnalytics options tree
return $ map (noteToParseNote posMap) $ filterByAnnotation tree list
@@ -65,3 +51,27 @@ severityToString s =
formatNote (ParseNote pos severity code text) =
ShellCheckComment (sourceLine pos) (sourceColumn pos) (severityToString severity) (fromIntegral code) text
testCheck = shellCheck defaultAnalysisOptions { optionExcludes = [2148] } -- Ignore #! warnings
prop_findsParseIssue =
let comments = testCheck "echo \"$12\"" in
length comments == 1 && scCode (head comments) == 1037
prop_commentDisablesParseIssue1 =
null $ testCheck "#shellcheck disable=SC1037\necho \"$12\""
prop_commentDisablesParseIssue2 =
null $ testCheck "#shellcheck disable=SC1037\n#lol\necho \"$12\""
prop_findsAnalysisIssue =
let comments = testCheck "echo $1" in
length comments == 1 && scCode (head comments) == 2086
prop_commentDisablesAnalysisIssue1 =
null $ testCheck "#shellcheck disable=SC2086\necho $1"
prop_commentDisablesAnalysisIssue2 =
null $ testCheck "#shellcheck disable=SC2086\n#lol\necho $1"
prop_optionDisablesIssue1 =
null $ shellCheck (defaultAnalysisOptions { optionExcludes = [2086, 2148] }) "echo $1"
return []
runTests = $quickCheckAll

View File

@@ -18,24 +18,28 @@ corner cases can cause delayed failures.
# OPTIONS
**-f** *FORMAT*, **--format=***FORMAT*
: Specify the output format of shellcheck, which prints its results in the
standard output. Subsequent **-f** options are ignored, see **FORMATS**
below for more information.
**-e**\ *CODE1*[,*CODE2*...],\ **--exclude=***CODE1*[,*CODE2*...]
: Explicitly exclude the specified codes from the report. Subsequent **-e**
options are cumulative, but all the codes can be specified at once,
comma-separated as a single argument.
**-f** *FORMAT*, **--format=***FORMAT*
: Specify the output format of shellcheck, which prints its results in the
standard output. Subsequent **-f** options are ignored, see **FORMATS**
below for more information.
**-s**\ *shell*,\ **--shell=***shell*
: Specify Bourne shell dialect. Valid values are *sh*, *bash*, *ksh* and
*zsh*. The default is to use the file's shebang, or *bash* if the target
shell can't be determined.
**-V**\ *version*,\ **--version**
: Print version and exit.
# FORMATS
**tty**

View File

@@ -17,12 +17,16 @@
-}
import Control.Exception
import Control.Monad
import Control.Monad.Trans
import Control.Monad.Trans.Error
import Data.Char
import Data.Maybe
import Data.Monoid
import GHC.Exts
import GHC.IO.Device
import Prelude hiding (catch)
import ShellCheck.Data
import ShellCheck.Options
import ShellCheck.Simple
import ShellCheck.Analytics
import System.Console.GetOpt
@@ -34,23 +38,29 @@ import Text.JSON
import qualified Data.Map as Map
data Flag = Flag String String
data Status = NoProblems | SomeProblems | BadInput | SupportFailure | SyntaxFailure | RuntimeException deriving (Ord, Eq)
instance Error Status where
noMsg = RuntimeException
instance Monoid Status where
mempty = NoProblems
mappend = max
header = "Usage: shellcheck [OPTIONS...] FILES..."
options = [
Option ['f'] ["format"]
(ReqArg (Flag "format") "FORMAT") "output format",
Option ['e'] ["exclude"]
Option "e" ["exclude"]
(ReqArg (Flag "exclude") "CODE1,CODE2..") "exclude types of warnings",
Option ['s'] ["shell"]
Option "f" ["format"]
(ReqArg (Flag "format") "FORMAT") "output format",
Option "s" ["shell"]
(ReqArg (Flag "shell") "SHELLNAME") "Specify dialect (bash,sh,ksh,zsh)",
Option ['V'] ["version"]
Option "V" ["version"]
(NoArg $ Flag "version" "true") "Print version information"
]
printErr = hPutStrLn stderr
syntaxFailure = ExitFailure 3
supportFailure = ExitFailure 4
instance JSON ShellCheckComment where
showJSON c = makeObj [
@@ -62,16 +72,15 @@ instance JSON ShellCheckComment where
]
readJSON = undefined
parseArguments :: [String] -> ErrorT Status IO ([Flag], [FilePath])
parseArguments argv =
case getOpt Permute options argv of
(opts, files, []) -> do
verifyOptions opts files
return $ Just (opts, files)
(opts, files, []) -> return (opts, files)
(_, _, errors) -> do
printErr $ concat errors ++ "\n" ++ usageInfo header options
exitWith syntaxFailure
liftIO . printErr $ concat errors ++ "\n" ++ usageInfo header options
throwError SyntaxFailure
formats :: Map.Map String (AnalysisOptions -> [FilePath] -> IO Status)
formats = Map.fromList [
("json", forJson),
("gcc", forGcc),
@@ -79,9 +88,21 @@ formats = Map.fromList [
("tty", forTty)
]
toStatus = liftM (either id (const NoProblems)) . runErrorT
catchExceptions :: IO Status -> IO Status
catchExceptions action = action -- action `catch` handler
where
handler err = do
printErr $ show (err :: SomeException)
return RuntimeException
checkComments comments = if null comments then NoProblems else SomeProblems
forTty :: AnalysisOptions -> [FilePath] -> IO Status
forTty options files = do
output <- mapM doFile files
return $ and output
return $ mconcat output
where
clear = ansi 0
ansi n = "\x1B[" ++ show n ++ "m"
@@ -97,7 +118,7 @@ forTty options files = do
colorComment level comment =
ansi (colorForLevel level) ++ comment ++ clear
doFile path = do
doFile path = catchExceptions $ do
contents <- readContents path
doInput path contents
@@ -119,34 +140,36 @@ forTty options files = do
mapM_ (\c -> putStrLn (colorFunc (scSeverity c) $ cuteIndent c)) x
putStrLn ""
) groups
return $ null comments
return . checkComments $ comments
cuteIndent comment =
replicate (scColumn comment - 1) ' ' ++
"^-- " ++ code (scCode comment) ++ ": " ++ scMessage comment
code code = "SC" ++ (show code)
code code = "SC" ++ show code
getColorFunc = do
term <- hIsTerminalDevice stdout
return $ if term then colorComment else const id
-- This totally ignores the filenames. Fixme?
forJson options files = do
forJson :: AnalysisOptions -> [FilePath] -> IO Status
forJson options files = catchExceptions $ do
comments <- liftM concat $ mapM (commentsFor options) files
putStrLn $ encodeStrict comments
return . null $ comments
return $ checkComments comments
-- Mimic GCC "file:line:col: (error|warning|note): message" format
forGcc :: AnalysisOptions -> [FilePath] -> IO Status
forGcc options files = do
files <- mapM process files
return $ and files
return $ mconcat files
where
process file = do
process file = catchExceptions $ do
contents <- readContents file
let comments = makeNonVirtual (getComments options contents) contents
mapM_ (putStrLn . format file) comments
return $ null comments
return $ checkComments comments
format filename c = concat [
filename, ":",
@@ -162,20 +185,18 @@ forGcc options files = do
]
-- Checkstyle compatible output. A bit of a hack to avoid XML dependencies
forCheckstyle :: AnalysisOptions -> [FilePath] -> IO Status
forCheckstyle options files = do
putStrLn "<?xml version='1.0' encoding='UTF-8'?>"
putStrLn "<checkstyle version='4.3'>"
statuses <- mapM (\x -> process x `catch` report) files
statuses <- mapM process files
putStrLn "</checkstyle>"
return $ and statuses
return $ mconcat statuses
where
process file = do
process file = catchExceptions $ do
comments <- commentsFor options file
putStrLn (formatFile file comments)
return $ null comments
report error = do
printErr $ show (error :: SomeException)
return False
return $ checkComments comments
severity "error" = "error"
severity "warning" = "warning"
@@ -197,31 +218,31 @@ forCheckstyle options files = do
attr "column" $ show . scColumn $ c,
attr "severity" $ severity . scSeverity $ c,
attr "message" $ scMessage c,
attr "source" $ "ShellCheck.SC" ++ (show $ scCode c),
attr "source" $ "ShellCheck.SC" ++ show (scCode c),
"/>\n"
]
commentsFor options file =
liftM (getComments options) $ readContents file
commentsFor options file = liftM (getComments options) $ readContents file
getComments options contents =
excludeCodes (getExclusions options) $ shellCheck contents analysisOptions
where
analysisOptions = catMaybes [ shellOption ]
shellOption = do
option <- getOption options "shell"
sh <- shellForExecutable option
return $ ForceShell sh
getComments = shellCheck
readContents file = if file == "-" then getContents else readFile file
readContents :: FilePath -> IO String
readContents file =
if file == "-"
then getContents
else readFile file
-- Realign comments from a tabstop of 8 to 1
makeNonVirtual comments contents =
map fix comments
where
ls = lines contents
fix c = c { scColumn = real (ls !! (scLine c - 1)) 0 0 (scColumn c) }
fix c = c {
scColumn =
if scLine c > 0 && scLine c <= length ls
then real (ls !! (scLine c - 1)) 0 0 (scColumn c)
else scColumn c
}
real _ r v target | target <= v = r
real [] r v _ = r -- should never happen
real ('\t':rest) r v target =
@@ -240,7 +261,7 @@ split char str =
where
split' (a:rest) element =
if a == char
then (reverse element) : split' rest []
then reverse element : split' rest []
else split' rest (a:element)
split' [] element = [reverse element]
@@ -257,45 +278,71 @@ excludeCodes codes =
main = do
args <- getArgs
parsedArgs <- parseArguments args
code <- do
status <- process parsedArgs
return $ if status then ExitSuccess else ExitFailure 1
`catch` return
`catch` \err -> do
printErr $ show (err :: SomeException)
return $ ExitFailure 2
exitWith code
status <- toStatus $ do
(flags, files) <- parseArguments args
process flags files
exitWith $ statusToCode status
process Nothing = return False
process (Just (options, files)) =
let format = fromMaybe "tty" $ getOption options "format" in
statusToCode status =
case status of
NoProblems -> ExitSuccess
SomeProblems -> ExitFailure 1
BadInput -> ExitFailure 5
SyntaxFailure -> ExitFailure 3
SupportFailure -> ExitFailure 4
RuntimeException -> ExitFailure 2
process :: [Flag] -> [FilePath] -> ErrorT Status IO ()
process flags files = do
options <- foldM (flip parseOption) defaultAnalysisOptions flags
verifyFiles files
let format = fromMaybe "tty" $ getOption flags "format"
case Map.lookup format formats of
Nothing -> do
printErr $ "Unknown format " ++ format
printErr $ "Supported formats:"
mapM_ (printErr . write) $ Map.keys formats
exitWith supportFailure
liftIO $ do
printErr $ "Unknown format " ++ format
printErr "Supported formats:"
mapM_ (printErr . write) $ Map.keys formats
throwError SupportFailure
where write s = " " ++ s
Just f -> do
f options files
Just f -> ErrorT $ liftM Left $ f options files
verifyOptions opts files = do
when (isJust $ getOption opts "version") printVersionAndExit
parseOption flag options =
case flag of
Flag "shell" str ->
fromMaybe (die $ "Unknown shell: " ++ str) $ do
shell <- shellForExecutable str
return $ return options { optionShellType = Just shell }
let shell = getOption opts "shell" in
when (isJust shell && isNothing (shell >>= shellForExecutable)) $ do
printErr $ "Unknown shell: " ++ (fromJust shell)
exitWith supportFailure
Flag "exclude" str -> do
new <- mapM parseNum $ split ',' str
let old = optionExcludes options
return options { optionExcludes = new ++ old }
Flag "version" _ -> do
liftIO printVersion
throwError NoProblems
_ -> return options
where
die s = do
liftIO $ printErr s
throwError SupportFailure
parseNum ('S':'C':str) = parseNum str
parseNum num = do
unless (all isDigit num) $ do
liftIO . printErr $ "Bad exclusion: " ++ num
throwError SyntaxFailure
return (Prelude.read num :: Integer)
verifyFiles files =
when (null files) $ do
printErr "No files specified.\n"
printErr $ usageInfo header options
exitWith syntaxFailure
liftIO $ printErr "No files specified.\n"
liftIO $ printErr $ usageInfo header options
throwError SyntaxFailure
printVersionAndExit = do
putStrLn $ "ShellCheck - shell script analysis tool"
printVersion = do
putStrLn "ShellCheck - shell script analysis tool"
putStrLn $ "version: " ++ shellcheckVersion
putStrLn $ "license: GNU Affero General Public License, version 3"
putStrLn $ "website: http://www.shellcheck.net"
exitWith ExitSuccess
putStrLn "license: GNU Affero General Public License, version 3"
putStrLn "website: http://www.shellcheck.net"

View File

@@ -1,65 +0,0 @@
#!/usr/bin/env runhaskell
-- #!/usr/bin/env runhugs
-- $Id: quickcheck,v 1.4 2003/01/08 15:09:22 shae Exp $
-- This file defines a command
-- quickCheck <options> <files>
-- which invokes quickCheck on all properties defined in the files given as
-- arguments, by generating an input script for hugs and then invoking it.
-- quickCheck recognises the options
-- +names print the name of each property before checking it
-- -names do not print property names (the default)
-- +verbose displays each test case before running
-- -verbose do not displays each test case before running (the default)
-- Other options (beginning with + or -) are passed unchanged to hugs.
--
-- Change the first line of this file to the location of runhaskell or runhugs
-- on your system.
-- Make the file executable.
--
-- TODO:
-- someone on #haskell asked about supporting QC tests inside LaTeX, ex. \{begin} \{end}, how?
import System.Cmd
import System.Directory (findExecutable)
import System.Environment
import Data.List
import Data.Maybe (fromJust)
main :: IO ()
main = do as<-getArgs
sequence_ (map (process (filter isOption as))
(filter (not.isOption) as))
-- ugly hack for .lhs files, is there a better way?
unlit [] = []
unlit x = if (head x) == '>' then (tail x) else x
process opts file =
let (namesOpt,opts') = getOption "names" "-names" opts
(verboseOpt,opts'') = getOption "verbose" "-verbose" opts' in
do xs<-readFile file
let names = nub$ filter (\x -> (("> prop_" `isPrefixOf` x) || ("prop_" `isPrefixOf` x)))
(map (fst.head.lex.unlit) (lines xs))
if null names then
putStr (file++": no properties to check\n")
else do writeFile "hugsin"$
unlines ((":load "++file):
":m +Test.QuickCheck":
"let quackCheck p = quickCheckWith (stdArgs { maxSuccess = 1 }) p ":
[(if namesOpt=="+names" then
"putStr \""++p++": \" >> "
else "") ++
("quackCheck ")
++ p | p<-names])
-- To use ghci
ghci <- findExecutable "ghci"
system (fromJust ghci ++options opts''++" <hugsin")
return ()
isOption xs = head xs `elem` "-+"
options opts = unwords ["\""++opt++"\"" | opt<-opts]
getOption name def opts =
let opt = head [opt | opt<-opts++[def], isPrefixOf name (drop 1 opt)] in
(opt, filter (/=opt) opts)

View File

@@ -1,22 +0,0 @@
#!/bin/bash
# Todo: Find a way to make this not suck.
ulimit -t 60 # Sometimes GHC ends in a spin loop, and this is easier than debugging
[[ -e test/quackCheck.hs ]] || { echo "Are you running me from the wrong directory?"; exit 1; }
[[ $1 == -v ]] && pattern="" || pattern="FAIL"
find . -name '*.hs' -exec bash -c '
grep -v "^module " "$1" > quack.tmp.hs
./test/quackCheck.hs +names quack.tmp.hs
' -- {} \; 2>&1 | grep -i "$pattern"
result=$?
rm -f quack.tmp.hs hugsin
if [[ $result == 0 ]]
then
exit 1
else
exit 0
fi

16
test/shellcheck.hs Normal file
View File

@@ -0,0 +1,16 @@
module Main where
import Control.Monad
import System.Exit
import qualified ShellCheck.Simple
import qualified ShellCheck.Analytics
import qualified ShellCheck.Parser
main = do
putStrLn "Running ShellCheck tests..."
results <- sequence [ShellCheck.Simple.runTests,
ShellCheck.Analytics.runTests,
ShellCheck.Parser.runTests]
if and results then exitSuccess
else exitFailure