diff --git a/.gitignore b/.gitignore
index 6d5f1ae..ae6bd07 100644
--- a/.gitignore
+++ b/.gitignore
@@ -13,6 +13,9 @@ cabal-dev
cabal.sandbox.config
cabal.config
.stack-work
+dist-newstyle/
+.ghc.environment.*
+cabal.project.local
### Snap ###
/snap/.snapcraft/
diff --git a/Dockerfile b/Dockerfile
index 2b65291..6db2840 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -3,20 +3,53 @@ FROM ubuntu:18.04 AS build
USER root
WORKDIR /opt/shellCheck
-# Install OS deps
-RUN apt-get update && apt-get install -y ghc cabal-install
+# Install OS deps, including GHC from HVR-PPA
+# https://launchpad.net/~hvr/+archive/ubuntu/ghc
+RUN apt-get -yq update \
+ && apt-get -yq install software-properties-common \
+ && apt-add-repository -y "ppa:hvr/ghc" \
+ && apt-get -yq update \
+ && apt-get -yq install cabal-install-2.4 ghc-8.4.3 pandoc \
+ && rm -rf /var/lib/apt/lists/*
+
+ENV PATH="/opt/ghc/bin:${PATH}"
+
+# Use gold linker and check tools versions
+RUN ln -s $(which ld.gold) /usr/local/bin/ld && \
+ cabal --version \
+ && ghc --version \
+ && ld --version
# Install Haskell deps
# (This is a separate copy/run so that source changes don't require rebuilding)
+#
+# We also patch regex-tdfa and aeson removing hard-coded -O2 flag.
+# This makes compilation faster and binary smaller.
+# Performance loss is unnoticeable for ShellCheck
+#
+# Remember to update versions, once in a while.
COPY ShellCheck.cabal ./
-RUN cabal update && cabal install --dependencies-only --ghc-options="-optlo-Os -split-sections"
+RUN cabal update && \
+ cabal get regex-tdfa-1.2.3.1 && sed -i 's/-O2//' regex-tdfa-1.2.3.1/regex-tdfa.cabal && \
+ cabal get aeson-1.4.0.0 && sed -i 's/-O2//' aeson-1.4.0.0/aeson.cabal && \
+ echo 'packages: . regex-tdfa-1.2.3.1 aeson-1.4.0.0 > cabal.project' && \
+ cabal new-build --dependencies-only \
+ --disable-executable-dynamic --enable-split-sections --disable-tests
# Copy source and build it
-COPY LICENSE Setup.hs shellcheck.hs ./
+COPY LICENSE Setup.hs shellcheck.hs shellcheck.1.md ./
COPY src src
-RUN cabal build Paths_ShellCheck && \
- ghc -optl-static -optl-pthread -isrc -idist/build/autogen --make shellcheck -split-sections -optc-Wl,--gc-sections -optlo-Os && \
- strip --strip-all shellcheck
+COPY test test
+# This SED is the only "nastyness" we have to do
+# Hopefully soon we could add per-component ld-options to cabal.project
+RUN sed -i 's/-- STATIC/ld-options: -static -pthread -Wl,--gc-sections/' ShellCheck.cabal && \
+ cat ShellCheck.cabal && \
+ cabal new-build \
+ --disable-executable-dynamic --enable-split-sections --disable-tests && \
+ cp $(find dist-newstyle -type f -name shellcheck) . && \
+ strip --strip-all shellcheck && \
+ file shellcheck && \
+ ls -l shellcheck
RUN mkdir -p /out/bin && \
cp shellcheck /out/bin/
diff --git a/Setup.hs b/Setup.hs
index a909cf6..229e8a6 100644
--- a/Setup.hs
+++ b/Setup.hs
@@ -1,3 +1,8 @@
+{-# LANGUAGE CPP #-}
+{-# OPTIONS_GHC -Wall #-}
+
+module Main (main) where
+
import Distribution.PackageDescription (
HookedBuildInfo,
emptyHookedBuildInfo )
@@ -9,12 +14,42 @@ import Distribution.Simple (
import Distribution.Simple.Setup ( SDistFlags )
import System.Process ( system )
+import System.Directory ( doesFileExist, getModificationTime )
+#ifndef MIN_VERSION_cabal_doctest
+#define MIN_VERSION_cabal_doctest(x,y,z) 0
+#endif
+#if MIN_VERSION_cabal_doctest(1,0,0)
+
+import Distribution.Extra.Doctest ( addDoctestsUserHook )
+main :: IO ()
+main = defaultMainWithHooks $ addDoctestsUserHook "doctests" myHooks
+ where
+ myHooks = simpleUserHooks { preSDist = myPreSDist }
+
+#else
+
+#ifdef MIN_VERSION_Cabal
+-- If the macro is defined, we have new cabal-install,
+-- but for some reason we don't have cabal-doctest in package-db
+--
+-- Probably we are running cabal sdist, when otherwise using new-build
+-- workflow
+#warning You are configuring this package without cabal-doctest installed. \
+ The doctests test-suite will not work as a result. \
+ To fix this, install cabal-doctest before configuring.
+#endif
+
+main :: IO ()
main = defaultMainWithHooks myHooks
where
myHooks = simpleUserHooks { preSDist = myPreSDist }
+#endif
+
+
+
-- | This hook will be executed before e.g. @cabal sdist@. It runs
-- pandoc to create the man page from shellcheck.1.md. If the pandoc
-- command is not found, this will fail with an error message:
@@ -27,10 +62,20 @@ main = defaultMainWithHooks myHooks
--
myPreSDist :: Args -> SDistFlags -> IO HookedBuildInfo
myPreSDist _ _ = do
- putStrLn "Building the man page (shellcheck.1) with pandoc..."
- putStrLn pandoc_cmd
- result <- system pandoc_cmd
- putStrLn $ "pandoc exited with " ++ show result
+ exists <- doesFileExist "shellcheck.1"
+ if exists
+ then do
+ source <- getModificationTime "shellcheck.1.md"
+ target <- getModificationTime "shellcheck.1"
+ if target < source
+ then makeManPage
+ else putStrLn "shellcheck.1 is more recent than shellcheck.1.md"
+ else makeManPage
return emptyHookedBuildInfo
where
- pandoc_cmd = "pandoc -s -f markdown-smart -t man shellcheck.1.md -o shellcheck.1"
+ makeManPage = do
+ putStrLn "Building the man page (shellcheck.1) with pandoc..."
+ putStrLn pandoc_cmd
+ result <- system pandoc_cmd
+ putStrLn $ "pandoc exited with " ++ show result
+ pandoc_cmd = "pandoc -s -t man shellcheck.1.md -o shellcheck.1"
diff --git a/ShellCheck.cabal b/ShellCheck.cabal
index 721da3f..00dea36 100644
--- a/ShellCheck.cabal
+++ b/ShellCheck.cabal
@@ -28,16 +28,14 @@ Extra-Source-Files:
shellcheck.1.md
-- built with a cabal sdist hook
shellcheck.1
- -- convenience script for stripping tests
- striptests
- -- tests
- test/shellcheck.hs
custom-setup
setup-depends:
- base >= 4 && <5,
- process >= 1.0 && <1.7,
- Cabal >= 1.10 && <2.5
+ base >= 4 && <5,
+ directory >= 1.2 && <1.4,
+ process >= 1.0 && <1.7,
+ cabal-doctest >= 1.0.6 && <1.1,
+ Cabal >= 1.10 && <2.5
source-repository head
type: git
@@ -60,7 +58,6 @@ library
mtl >= 2.2.1,
parsec,
regex-tdfa,
- QuickCheck >= 2.7.4,
-- When cabal supports it, move this to setup-depends:
process
exposed-modules:
@@ -98,23 +95,23 @@ executable shellcheck
directory,
mtl >= 2.2.1,
parsec >= 3.0,
- QuickCheck >= 2.7.4,
regex-tdfa
main-is: shellcheck.hs
-test-suite test-shellcheck
- type: exitcode-stdio-1.0
- build-depends:
- aeson,
- base >= 4 && < 5,
- bytestring,
- deepseq >= 1.4.0.0,
- ShellCheck,
- containers,
- directory,
- mtl >= 2.2.1,
- parsec,
- QuickCheck >= 2.7.4,
- regex-tdfa
- main-is: test/shellcheck.hs
+ -- Marker to add flags for static linking
+ -- STATIC
+test-suite doctests
+ type: exitcode-stdio-1.0
+ main-is: doctests.hs
+ build-depends:
+ base,
+ doctest >= 0.16.0 && <0.17,
+ QuickCheck >=2.11 && <2.13,
+ ShellCheck,
+ template-haskell
+
+ x-doctest-options: --fast
+
+ ghc-options: -Wall -threaded
+ hs-source-dirs: test
diff --git a/quickrun b/quickrun
index 172ae88..f53f1b5 100755
--- a/quickrun
+++ b/quickrun
@@ -3,3 +3,10 @@
# This allows testing changes without recompiling.
runghc -isrc -idist/build/autogen shellcheck.hs "$@"
+
+# Note: with new-build you can
+#
+# % cabal new-run --disable-optimization -- shellcheck "$@"
+#
+# This does build the executable, but as the optimisation is disabled,
+# the build is quite fast.
diff --git a/quicktest b/quicktest
index 4f0702d..7d7cb05 100755
--- a/quicktest
+++ b/quicktest
@@ -1,22 +1,21 @@
-#!/usr/bin/env bash
-# quicktest runs the ShellCheck unit tests in an interpreted mode.
-# This allows running tests without compiling, which can be faster.
+#!/bin/bash
+# shellcheck disable=SC2091
+
+# quicktest runs the ShellCheck unit tests.
+# Once `doctests` test executable is build, we can just run it
+# This allows running tests without compiling library, which is faster.
# 'cabal test' remains the source of truth.
-(
- var=$(echo 'liftM and $ sequence [
- ShellCheck.Analytics.runTests
- ,ShellCheck.Parser.runTests
- ,ShellCheck.Checker.runTests
- ,ShellCheck.Checks.Commands.runTests
- ,ShellCheck.Checks.ShellSupport.runTests
- ,ShellCheck.AnalyzerLib.runTests
- ]' | tr -d '\n' | cabal repl ShellCheck 2>&1 | tee /dev/stderr)
-if [[ $var == *$'\nTrue'* ]]
-then
- exit 0
-else
- grep -C 3 -e "Fail" -e "Tracing" <<< "$var"
- exit 1
-fi
-) 2>&1
+$(find dist -type f -name doctests)
+
+# Note: if you have build the project with new-build
+#
+# % cabal new-build -w ghc-8.4.3 --enable-tests
+#
+# and have cabal-plan installed (e.g. with cabal new-install cabal-plan),
+# then you can quicktest with
+#
+# % $(cabal-plan list-bin doctests)
+#
+# Once the test executable exists, we can simply run it to perform doctests
+# which use GHCi under the hood.
diff --git a/src/ShellCheck/Analytics.hs b/src/ShellCheck/Analytics.hs
index a164de1..a2d6152 100644
--- a/src/ShellCheck/Analytics.hs
+++ b/src/ShellCheck/Analytics.hs
@@ -17,9 +17,8 @@
You should have received a copy of the GNU General Public License
along with this program. If not, see .
-}
-{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE FlexibleContexts #-}
-module ShellCheck.Analytics (runAnalytics, ShellCheck.Analytics.runTests) where
+module ShellCheck.Analytics (runAnalytics) where
import ShellCheck.AST
import ShellCheck.ASTLib
@@ -43,8 +42,6 @@ import Data.Maybe
import Data.Ord
import Debug.Trace
import qualified Data.Map.Strict as Map
-import Test.QuickCheck.All (forAllProperties)
-import Test.QuickCheck.Test (quickCheckWithResult, stdArgs, maxSuccess)
-- Checks that are run on the AST root
treeChecks :: [Parameters -> Token -> [TokenComment]]
@@ -275,7 +272,7 @@ replaceEnd id params n r =
surroundWidth id params s = fixWith [replaceStart id params 0 s, replaceEnd id params 0 s]
fixWith fixes = newFix { fixReplacements = fixes }
-prop_checkEchoWc3 = verify checkEchoWc "n=$(echo $foo | wc -c)"
+-- >>> prop $ verify checkEchoWc "n=$(echo $foo | wc -c)"
checkEchoWc _ (T_Pipeline id _ [a, b]) =
when (acmd == ["echo", "${VAR}"]) $
case bcmd of
@@ -288,20 +285,22 @@ checkEchoWc _ (T_Pipeline id _ [a, b]) =
countMsg = style id 2000 "See if you can use ${#variable} instead."
checkEchoWc _ _ = return ()
-prop_checkPipedAssignment1 = verify checkPipedAssignment "A=ls | grep foo"
-prop_checkPipedAssignment2 = verifyNot checkPipedAssignment "A=foo cmd | grep foo"
-prop_checkPipedAssignment3 = verifyNot checkPipedAssignment "A=foo"
+-- |
+-- >>> prop $ verify checkPipedAssignment "A=ls | grep foo"
+-- >>> prop $ verifyNot checkPipedAssignment "A=foo cmd | grep foo"
+-- >>> prop $ verifyNot checkPipedAssignment "A=foo"
checkPipedAssignment _ (T_Pipeline _ _ (T_Redirecting _ _ (T_SimpleCommand id (_:_) []):_:_)) =
warn id 2036 "If you wanted to assign the output of the pipeline, use a=$(b | c) ."
checkPipedAssignment _ _ = return ()
-prop_checkAssignAteCommand1 = verify checkAssignAteCommand "A=ls -l"
-prop_checkAssignAteCommand2 = verify checkAssignAteCommand "A=ls --sort=$foo"
-prop_checkAssignAteCommand3 = verify checkAssignAteCommand "A=cat foo | grep bar"
-prop_checkAssignAteCommand4 = verifyNot checkAssignAteCommand "A=foo ls -l"
-prop_checkAssignAteCommand5 = verify checkAssignAteCommand "PAGER=cat grep bar"
-prop_checkAssignAteCommand6 = verifyNot checkAssignAteCommand "PAGER=\"cat\" grep bar"
-prop_checkAssignAteCommand7 = verify checkAssignAteCommand "here=pwd"
+-- |
+-- >>> prop $ verify checkAssignAteCommand "A=ls -l"
+-- >>> prop $ verify checkAssignAteCommand "A=ls --sort=$foo"
+-- >>> prop $ verify checkAssignAteCommand "A=cat foo | grep bar"
+-- >>> prop $ verifyNot checkAssignAteCommand "A=foo ls -l"
+-- >>> prop $ verify checkAssignAteCommand "PAGER=cat grep bar"
+-- >>> prop $ verifyNot checkAssignAteCommand "PAGER=\"cat\" grep bar"
+-- >>> prop $ verify checkAssignAteCommand "here=pwd"
checkAssignAteCommand _ (T_SimpleCommand id (T_Assignment _ _ _ _ assignmentTerm:[]) list) =
-- Check if first word is intended as an argument (flag or glob).
if firstWordIsArg list
@@ -320,9 +319,10 @@ checkAssignAteCommand _ (T_SimpleCommand id (T_Assignment _ _ _ _ assignmentTerm
checkAssignAteCommand _ _ = return ()
-prop_checkArithmeticOpCommand1 = verify checkArithmeticOpCommand "i=i + 1"
-prop_checkArithmeticOpCommand2 = verify checkArithmeticOpCommand "foo=bar * 2"
-prop_checkArithmeticOpCommand3 = verifyNot checkArithmeticOpCommand "foo + opts"
+-- |
+-- >>> prop $ verify checkArithmeticOpCommand "i=i + 1"
+-- >>> prop $ verify checkArithmeticOpCommand "foo=bar * 2"
+-- >>> prop $ verifyNot checkArithmeticOpCommand "foo + opts"
checkArithmeticOpCommand _ (T_SimpleCommand id [T_Assignment {}] (firstWord:_)) =
fromMaybe (return ()) $ check <$> getGlobOrLiteralString firstWord
where
@@ -332,8 +332,9 @@ checkArithmeticOpCommand _ (T_SimpleCommand id [T_Assignment {}] (firstWord:_))
"Use $((..)) for arithmetics, e.g. i=$((i " ++ op ++ " 2))"
checkArithmeticOpCommand _ _ = return ()
-prop_checkWrongArit = verify checkWrongArithmeticAssignment "i=i+1"
-prop_checkWrongArit2 = verify checkWrongArithmeticAssignment "n=2; i=n*2"
+-- |
+-- >>> prop $ verify checkWrongArithmeticAssignment "i=i+1"
+-- >>> prop $ verify checkWrongArithmeticAssignment "n=2; i=n*2"
checkWrongArithmeticAssignment params (T_SimpleCommand id (T_Assignment _ _ _ _ val:[]) []) =
fromMaybe (return ()) $ do
str <- getNormalString val
@@ -361,12 +362,13 @@ checkWrongArithmeticAssignment params (T_SimpleCommand id (T_Assignment _ _ _ _
checkWrongArithmeticAssignment _ _ = return ()
-prop_checkUuoc1 = verify checkUuoc "cat foo | grep bar"
-prop_checkUuoc2 = verifyNot checkUuoc "cat * | grep bar"
-prop_checkUuoc3 = verify checkUuoc "cat $var | grep bar"
-prop_checkUuoc4 = verifyNot checkUuoc "cat $var"
-prop_checkUuoc5 = verifyNot checkUuoc "cat \"$@\""
-prop_checkUuoc6 = verifyNot checkUuoc "cat -n | grep bar"
+-- |
+-- >>> prop $ verify checkUuoc "cat foo | grep bar"
+-- >>> prop $ verifyNot checkUuoc "cat * | grep bar"
+-- >>> prop $ verify checkUuoc "cat $var | grep bar"
+-- >>> prop $ verifyNot checkUuoc "cat $var"
+-- >>> prop $ verifyNot checkUuoc "cat \"$@\""
+-- >>> prop $ verifyNot checkUuoc "cat -n | grep bar"
checkUuoc _ (T_Pipeline _ _ (T_Redirecting _ _ cmd:_:_)) =
checkCommand "cat" (const f) cmd
where
@@ -376,20 +378,21 @@ checkUuoc _ (T_Pipeline _ _ (T_Redirecting _ _ cmd:_:_)) =
isOption word = "-" `isPrefixOf` onlyLiteralString word
checkUuoc _ _ = return ()
-prop_checkPipePitfalls3 = verify checkPipePitfalls "ls | grep -v mp3"
-prop_checkPipePitfalls4 = verifyNot checkPipePitfalls "find . -print0 | xargs -0 foo"
-prop_checkPipePitfalls5 = verifyNot checkPipePitfalls "ls -N | foo"
-prop_checkPipePitfalls6 = verify checkPipePitfalls "find . | xargs foo"
-prop_checkPipePitfalls7 = verifyNot checkPipePitfalls "find . -printf '%s\\n' | xargs foo"
-prop_checkPipePitfalls8 = verify checkPipePitfalls "foo | grep bar | wc -l"
-prop_checkPipePitfalls9 = verifyNot checkPipePitfalls "foo | grep -o bar | wc -l"
-prop_checkPipePitfalls10 = verifyNot checkPipePitfalls "foo | grep -o bar | wc"
-prop_checkPipePitfalls11 = verifyNot checkPipePitfalls "foo | grep bar | wc"
-prop_checkPipePitfalls12 = verifyNot checkPipePitfalls "foo | grep -o bar | wc -c"
-prop_checkPipePitfalls13 = verifyNot checkPipePitfalls "foo | grep bar | wc -c"
-prop_checkPipePitfalls14 = verifyNot checkPipePitfalls "foo | grep -o bar | wc -cmwL"
-prop_checkPipePitfalls15 = verifyNot checkPipePitfalls "foo | grep bar | wc -cmwL"
-prop_checkPipePitfalls16 = verifyNot checkPipePitfalls "foo | grep -r bar | wc -l"
+-- |
+-- >>> prop $ verify checkPipePitfalls "ls | grep -v mp3"
+-- >>> prop $ verifyNot checkPipePitfalls "find . -print0 | xargs -0 foo"
+-- >>> prop $ verifyNot checkPipePitfalls "ls -N | foo"
+-- >>> prop $ verify checkPipePitfalls "find . | xargs foo"
+-- >>> prop $ verifyNot checkPipePitfalls "find . -printf '%s\\n' | xargs foo"
+-- >>> prop $ verify checkPipePitfalls "foo | grep bar | wc -l"
+-- >>> prop $ verifyNot checkPipePitfalls "foo | grep -o bar | wc -l"
+-- >>> prop $ verifyNot checkPipePitfalls "foo | grep -o bar | wc"
+-- >>> prop $ verifyNot checkPipePitfalls "foo | grep bar | wc"
+-- >>> prop $ verifyNot checkPipePitfalls "foo | grep -o bar | wc -c"
+-- >>> prop $ verifyNot checkPipePitfalls "foo | grep bar | wc -c"
+-- >>> prop $ verifyNot checkPipePitfalls "foo | grep -o bar | wc -cmwL"
+-- >>> prop $ verifyNot checkPipePitfalls "foo | grep bar | wc -cmwL"
+-- >>> prop $ verifyNot checkPipePitfalls "foo | grep -r bar | wc -l"
checkPipePitfalls _ (T_Pipeline id _ commands) = do
for ["find", "xargs"] $
\(find:xargs:_) ->
@@ -453,22 +456,24 @@ indexOfSublists sub = f 0
match _ _ = False
-prop_checkShebangParameters1 = verifyTree checkShebangParameters "#!/usr/bin/env bash -x\necho cow"
-prop_checkShebangParameters2 = verifyNotTree checkShebangParameters "#! /bin/sh -l "
+-- |
+-- >>> prop $ verifyTree checkShebangParameters "#!/usr/bin/env bash -x\necho cow"
+-- >>> prop $ verifyNotTree checkShebangParameters "#! /bin/sh -l "
checkShebangParameters p (T_Annotation _ _ t) = checkShebangParameters p t
checkShebangParameters _ (T_Script id sb _) =
[makeComment ErrorC id 2096 "On most OS, shebangs can only specify a single parameter." | length (words sb) > 2]
-prop_checkShebang1 = verifyNotTree checkShebang "#!/usr/bin/env bash -x\necho cow"
-prop_checkShebang2 = verifyNotTree checkShebang "#! /bin/sh -l "
-prop_checkShebang3 = verifyTree checkShebang "ls -l"
-prop_checkShebang4 = verifyNotTree checkShebang "#shellcheck shell=sh\nfoo"
-prop_checkShebang5 = verifyTree checkShebang "#!/usr/bin/env ash"
-prop_checkShebang6 = verifyNotTree checkShebang "#!/usr/bin/env ash\n# shellcheck shell=dash\n"
-prop_checkShebang7 = verifyNotTree checkShebang "#!/usr/bin/env ash\n# shellcheck shell=sh\n"
-prop_checkShebang8 = verifyTree checkShebang "#!bin/sh\ntrue"
-prop_checkShebang9 = verifyNotTree checkShebang "# shellcheck shell=sh\ntrue"
-prop_checkShebang10= verifyNotTree checkShebang "#!foo\n# shellcheck shell=sh ignore=SC2239\ntrue"
+-- |
+-- >>> prop $ verifyNotTree checkShebang "#!/usr/bin/env bash -x\necho cow"
+-- >>> prop $ verifyNotTree checkShebang "#! /bin/sh -l "
+-- >>> prop $ verifyTree checkShebang "ls -l"
+-- >>> prop $ verifyNotTree checkShebang "#shellcheck shell=sh\nfoo"
+-- >>> prop $ verifyTree checkShebang "#!/usr/bin/env ash"
+-- >>> prop $ verifyNotTree checkShebang "#!/usr/bin/env ash\n# shellcheck shell=dash\n"
+-- >>> prop $ verifyNotTree checkShebang "#!/usr/bin/env ash\n# shellcheck shell=sh\n"
+-- >>> prop $ verifyTree checkShebang "#!bin/sh\ntrue"
+-- >>> prop $ verifyNotTree checkShebang "# shellcheck shell=sh\ntrue"
+-- >>> prop $ verifyNotTree checkShebang "#!foo\n# shellcheck shell=sh ignore=SC2239\ntrue"
checkShebang params (T_Annotation _ list t) =
if any isOverride list then [] else checkShebang params t
where
@@ -484,15 +489,16 @@ checkShebang params (T_Script id sb _) = execWriter $ do
err id 2239 "Ensure the shebang uses an absolute path to the interpreter."
-prop_checkForInQuoted = verify checkForInQuoted "for f in \"$(ls)\"; do echo foo; done"
-prop_checkForInQuoted2 = verifyNot checkForInQuoted "for f in \"$@\"; do echo foo; done"
-prop_checkForInQuoted2a = verifyNot checkForInQuoted "for f in *.mp3; do echo foo; done"
-prop_checkForInQuoted2b = verify checkForInQuoted "for f in \"*.mp3\"; do echo foo; done"
-prop_checkForInQuoted3 = verify checkForInQuoted "for f in 'find /'; do true; done"
-prop_checkForInQuoted4 = verify checkForInQuoted "for f in 1,2,3; do true; done"
-prop_checkForInQuoted4a = verifyNot checkForInQuoted "for f in foo{1,2,3}; do true; done"
-prop_checkForInQuoted5 = verify checkForInQuoted "for f in ls; do true; done"
-prop_checkForInQuoted6 = verifyNot checkForInQuoted "for f in \"${!arr}\"; do true; done"
+-- |
+-- >>> prop $ verify checkForInQuoted "for f in \"$(ls)\"; do echo foo; done"
+-- >>> prop $ verifyNot checkForInQuoted "for f in \"$@\"; do echo foo; done"
+-- >>> prop $ verifyNot checkForInQuoted "for f in *.mp3; do echo foo; done"
+-- >>> prop $ verify checkForInQuoted "for f in \"*.mp3\"; do echo foo; done"
+-- >>> prop $ verify checkForInQuoted "for f in 'find /'; do true; done"
+-- >>> prop $ verify checkForInQuoted "for f in 1,2,3; do true; done"
+-- >>> prop $ verifyNot checkForInQuoted "for f in foo{1,2,3}; do true; done"
+-- >>> prop $ verify checkForInQuoted "for f in ls; do true; done"
+-- >>> prop $ verifyNot checkForInQuoted "for f in \"${!arr}\"; do true; done"
checkForInQuoted _ (T_ForIn _ f [T_NormalWord _ [word@(T_DoubleQuoted id list)]] _) =
when (any (\x -> willSplit x && not (mayBecomeMultipleArgs x)) list
|| (fmap wouldHaveBeenGlob (getLiteralString word) == Just True)) $
@@ -506,11 +512,12 @@ checkForInQuoted _ (T_ForIn _ f [T_NormalWord _ [T_Literal id s]] _) =
else warn id 2043 "This loop will only ever run once for a constant value. Did you perhaps mean to loop over dir/*, $var or $(cmd)?"
checkForInQuoted _ _ = return ()
-prop_checkForInCat1 = verify checkForInCat "for f in $(cat foo); do stuff; done"
-prop_checkForInCat1a= verify checkForInCat "for f in `cat foo`; do stuff; done"
-prop_checkForInCat2 = verify checkForInCat "for f in $(cat foo | grep lol); do stuff; done"
-prop_checkForInCat2a= verify checkForInCat "for f in `cat foo | grep lol`; do stuff; done"
-prop_checkForInCat3 = verifyNot checkForInCat "for f in $(cat foo | grep bar | wc -l); do stuff; done"
+-- |
+-- >>> prop $ verify checkForInCat "for f in $(cat foo); do stuff; done"
+-- >>> prop $ verify checkForInCat "for f in `cat foo`; do stuff; done"
+-- >>> prop $ verify checkForInCat "for f in $(cat foo | grep lol); do stuff; done"
+-- >>> prop $ verify checkForInCat "for f in `cat foo | grep lol`; do stuff; done"
+-- >>> prop $ verifyNot checkForInCat "for f in $(cat foo | grep bar | wc -l); do stuff; done"
checkForInCat _ (T_ForIn _ f [T_NormalWord _ w] _) = mapM_ checkF w
where
checkF (T_DollarExpansion id [T_Pipeline _ _ r])
@@ -522,9 +529,10 @@ checkForInCat _ (T_ForIn _ f [T_NormalWord _ w] _) = mapM_ checkF w
["grep", "fgrep", "egrep", "sed", "cat", "awk", "cut", "sort"]
checkForInCat _ _ = return ()
-prop_checkForInLs = verify checkForInLs "for f in $(ls *.mp3); do mplayer \"$f\"; done"
-prop_checkForInLs2 = verify checkForInLs "for f in `ls *.mp3`; do mplayer \"$f\"; done"
-prop_checkForInLs3 = verify checkForInLs "for f in `find / -name '*.mp3'`; do mplayer \"$f\"; done"
+-- |
+-- >>> prop $ verify checkForInLs "for f in $(ls *.mp3); do mplayer \"$f\"; done"
+-- >>> prop $ verify checkForInLs "for f in `ls *.mp3`; do mplayer \"$f\"; done"
+-- >>> prop $ verify checkForInLs "for f in `find / -name '*.mp3'`; do mplayer \"$f\"; done"
checkForInLs _ = try
where
try (T_ForIn _ f [T_NormalWord _ [T_DollarExpansion id [x]]] _) =
@@ -541,12 +549,13 @@ checkForInLs _ = try
_ -> return ()
-prop_checkFindExec1 = verify checkFindExec "find / -name '*.php' -exec rm {};"
-prop_checkFindExec2 = verify checkFindExec "find / -exec touch {} && ls {} \\;"
-prop_checkFindExec3 = verify checkFindExec "find / -execdir cat {} | grep lol +"
-prop_checkFindExec4 = verifyNot checkFindExec "find / -name '*.php' -exec foo {} +"
-prop_checkFindExec5 = verifyNot checkFindExec "find / -execdir bash -c 'a && b' \\;"
-prop_checkFindExec6 = verify checkFindExec "find / -type d -execdir rm *.jpg \\;"
+-- |
+-- >>> prop $ verify checkFindExec "find / -name '*.php' -exec rm {};"
+-- >>> prop $ verify checkFindExec "find / -exec touch {} && ls {} \\;"
+-- >>> prop $ verify checkFindExec "find / -execdir cat {} | grep lol +"
+-- >>> prop $ verifyNot checkFindExec "find / -name '*.php' -exec foo {} +"
+-- >>> prop $ verifyNot checkFindExec "find / -execdir bash -c 'a && b' \\;"
+-- >>> prop $ verify checkFindExec "find / -type d -execdir rm *.jpg \\;"
checkFindExec _ cmd@(T_SimpleCommand _ _ t@(h:r)) | cmd `isCommand` "find" = do
c <- broken r False
when c $
@@ -581,17 +590,18 @@ checkFindExec _ cmd@(T_SimpleCommand _ _ t@(h:r)) | cmd `isCommand` "find" = do
checkFindExec _ _ = return ()
-prop_checkUnquotedExpansions1 = verify checkUnquotedExpansions "rm $(ls)"
-prop_checkUnquotedExpansions1a= verify checkUnquotedExpansions "rm `ls`"
-prop_checkUnquotedExpansions2 = verify checkUnquotedExpansions "rm foo$(date)"
-prop_checkUnquotedExpansions3 = verify checkUnquotedExpansions "[ $(foo) == cow ]"
-prop_checkUnquotedExpansions3a= verify checkUnquotedExpansions "[ ! $(foo) ]"
-prop_checkUnquotedExpansions4 = verifyNot checkUnquotedExpansions "[[ $(foo) == cow ]]"
-prop_checkUnquotedExpansions5 = verifyNot checkUnquotedExpansions "for f in $(cmd); do echo $f; done"
-prop_checkUnquotedExpansions6 = verifyNot checkUnquotedExpansions "$(cmd)"
-prop_checkUnquotedExpansions7 = verifyNot checkUnquotedExpansions "cat << foo\n$(ls)\nfoo"
-prop_checkUnquotedExpansions8 = verifyNot checkUnquotedExpansions "set -- $(seq 1 4)"
-prop_checkUnquotedExpansions9 = verifyNot checkUnquotedExpansions "echo foo `# inline comment`"
+-- |
+-- >>> prop $ verify checkUnquotedExpansions "rm $(ls)"
+-- >>> prop $ verify checkUnquotedExpansions "rm `ls`"
+-- >>> prop $ verify checkUnquotedExpansions "rm foo$(date)"
+-- >>> prop $ verify checkUnquotedExpansions "[ $(foo) == cow ]"
+-- >>> prop $ verify checkUnquotedExpansions "[ ! $(foo) ]"
+-- >>> prop $ verifyNot checkUnquotedExpansions "[[ $(foo) == cow ]]"
+-- >>> prop $ verifyNot checkUnquotedExpansions "for f in $(cmd); do echo $f; done"
+-- >>> prop $ verifyNot checkUnquotedExpansions "$(cmd)"
+-- >>> prop $ verifyNot checkUnquotedExpansions "cat << foo\n$(ls)\nfoo"
+-- >>> prop $ verifyNot checkUnquotedExpansions "set -- $(seq 1 4)"
+-- >>> prop $ verifyNot checkUnquotedExpansions "echo foo `# inline comment`"
checkUnquotedExpansions params =
check
where
@@ -608,14 +618,15 @@ checkUnquotedExpansions params =
getCommandNameFromExpansion t == Just "seq"
-prop_checkRedirectToSame = verify checkRedirectToSame "cat foo > foo"
-prop_checkRedirectToSame2 = verify checkRedirectToSame "cat lol | sed -e 's/a/b/g' > lol"
-prop_checkRedirectToSame3 = verifyNot checkRedirectToSame "cat lol | sed -e 's/a/b/g' > foo.bar && mv foo.bar lol"
-prop_checkRedirectToSame4 = verifyNot checkRedirectToSame "foo /dev/null > /dev/null"
-prop_checkRedirectToSame5 = verifyNot checkRedirectToSame "foo > bar 2> bar"
-prop_checkRedirectToSame6 = verifyNot checkRedirectToSame "echo foo > foo"
-prop_checkRedirectToSame7 = verifyNot checkRedirectToSame "sed 's/foo/bar/g' file | sponge file"
-prop_checkRedirectToSame8 = verifyNot checkRedirectToSame "while read -r line; do _=\"$fname\"; done <\"$fname\""
+-- |
+-- >>> prop $ verify checkRedirectToSame "cat foo > foo"
+-- >>> prop $ verify checkRedirectToSame "cat lol | sed -e 's/a/b/g' > lol"
+-- >>> prop $ verifyNot checkRedirectToSame "cat lol | sed -e 's/a/b/g' > foo.bar && mv foo.bar lol"
+-- >>> prop $ verifyNot checkRedirectToSame "foo /dev/null > /dev/null"
+-- >>> prop $ verifyNot checkRedirectToSame "foo > bar 2> bar"
+-- >>> prop $ verifyNot checkRedirectToSame "echo foo > foo"
+-- >>> prop $ verifyNot checkRedirectToSame "sed 's/foo/bar/g' file | sponge file"
+-- >>> prop $ verifyNot checkRedirectToSame "while read -r line; do _=\"$fname\"; done <\"$fname\""
checkRedirectToSame params s@(T_Pipeline _ _ list) =
mapM_ (\l -> (mapM_ (\x -> doAnalysis (checkOccurrences x) l) (getAllRedirs list))) list
where
@@ -661,14 +672,15 @@ checkRedirectToSame params s@(T_Pipeline _ _ list) =
checkRedirectToSame _ _ = return ()
-prop_checkShorthandIf = verify checkShorthandIf "[[ ! -z file ]] && scp file host || rm file"
-prop_checkShorthandIf2 = verifyNot checkShorthandIf "[[ ! -z file ]] && { scp file host || echo 'Eek'; }"
-prop_checkShorthandIf3 = verifyNot checkShorthandIf "foo && bar || echo baz"
-prop_checkShorthandIf4 = verifyNot checkShorthandIf "foo && a=b || a=c"
-prop_checkShorthandIf5 = verifyNot checkShorthandIf "foo && rm || printf b"
-prop_checkShorthandIf6 = verifyNot checkShorthandIf "if foo && bar || baz; then true; fi"
-prop_checkShorthandIf7 = verifyNot checkShorthandIf "while foo && bar || baz; do true; done"
-prop_checkShorthandIf8 = verify checkShorthandIf "if true; then foo && bar || baz; fi"
+-- |
+-- >>> prop $ verify checkShorthandIf "[[ ! -z file ]] && scp file host || rm file"
+-- >>> prop $ verifyNot checkShorthandIf "[[ ! -z file ]] && { scp file host || echo 'Eek'; }"
+-- >>> prop $ verifyNot checkShorthandIf "foo && bar || echo baz"
+-- >>> prop $ verifyNot checkShorthandIf "foo && a=b || a=c"
+-- >>> prop $ verifyNot checkShorthandIf "foo && rm || printf b"
+-- >>> prop $ verifyNot checkShorthandIf "if foo && bar || baz; then true; fi"
+-- >>> prop $ verifyNot checkShorthandIf "while foo && bar || baz; do true; done"
+-- >>> prop $ verify checkShorthandIf "if true; then foo && bar || baz; fi"
checkShorthandIf params x@(T_AndIf id _ (T_OrIf _ _ (T_Pipeline _ _ t)))
| not (isOk t || inCondition) =
info id 2015 "Note that A && B || C is not if-then-else. C may run when A is true."
@@ -681,9 +693,10 @@ checkShorthandIf params x@(T_AndIf id _ (T_OrIf _ _ (T_Pipeline _ _ t)))
checkShorthandIf _ _ = return ()
-prop_checkDollarStar = verify checkDollarStar "for f in $*; do ..; done"
-prop_checkDollarStar2 = verifyNot checkDollarStar "a=$*"
-prop_checkDollarStar3 = verifyNot checkDollarStar "[[ $* = 'a b' ]]"
+-- |
+-- >>> prop $ verify checkDollarStar "for f in $*; do ..; done"
+-- >>> prop $ verifyNot checkDollarStar "a=$*"
+-- >>> prop $ verifyNot checkDollarStar "[[ $* = 'a b' ]]"
checkDollarStar p t@(T_NormalWord _ [b@(T_DollarBraced id _)])
| bracedString b == "*" =
unless (isStrictlyQuoteFree (parentMap p) t) $
@@ -691,17 +704,18 @@ checkDollarStar p t@(T_NormalWord _ [b@(T_DollarBraced id _)])
checkDollarStar _ _ = return ()
-prop_checkUnquotedDollarAt = verify checkUnquotedDollarAt "ls $@"
-prop_checkUnquotedDollarAt1= verifyNot checkUnquotedDollarAt "ls ${#@}"
-prop_checkUnquotedDollarAt2 = verify checkUnquotedDollarAt "ls ${foo[@]}"
-prop_checkUnquotedDollarAt3 = verifyNot checkUnquotedDollarAt "ls ${#foo[@]}"
-prop_checkUnquotedDollarAt4 = verifyNot checkUnquotedDollarAt "ls \"$@\""
-prop_checkUnquotedDollarAt5 = verifyNot checkUnquotedDollarAt "ls ${foo/@/ at }"
-prop_checkUnquotedDollarAt6 = verifyNot checkUnquotedDollarAt "a=$@"
-prop_checkUnquotedDollarAt7 = verify checkUnquotedDollarAt "for f in ${var[@]}; do true; done"
-prop_checkUnquotedDollarAt8 = verifyNot checkUnquotedDollarAt "echo \"${args[@]:+${args[@]}}\""
-prop_checkUnquotedDollarAt9 = verifyNot checkUnquotedDollarAt "echo ${args[@]:+\"${args[@]}\"}"
-prop_checkUnquotedDollarAt10 = verifyNot checkUnquotedDollarAt "echo ${@+\"$@\"}"
+-- |
+-- >>> prop $ verify checkUnquotedDollarAt "ls $@"
+-- >>> prop $ verifyNot checkUnquotedDollarAt "ls ${#@}"
+-- >>> prop $ verify checkUnquotedDollarAt "ls ${foo[@]}"
+-- >>> prop $ verifyNot checkUnquotedDollarAt "ls ${#foo[@]}"
+-- >>> prop $ verifyNot checkUnquotedDollarAt "ls \"$@\""
+-- >>> prop $ verifyNot checkUnquotedDollarAt "ls ${foo/@/ at }"
+-- >>> prop $ verifyNot checkUnquotedDollarAt "a=$@"
+-- >>> prop $ verify checkUnquotedDollarAt "for f in ${var[@]}; do true; done"
+-- >>> prop $ verifyNot checkUnquotedDollarAt "echo \"${args[@]:+${args[@]}}\""
+-- >>> prop $ verifyNot checkUnquotedDollarAt "echo ${args[@]:+\"${args[@]}\"}"
+-- >>> prop $ verifyNot checkUnquotedDollarAt "echo ${@+\"$@\"}"
checkUnquotedDollarAt p word@(T_NormalWord _ parts) | not $ isStrictlyQuoteFree (parentMap p) word =
forM_ (take 1 $ filter isArrayExpansion parts) $ \x ->
unless (isQuotedAlternativeReference x) $
@@ -709,11 +723,12 @@ checkUnquotedDollarAt p word@(T_NormalWord _ parts) | not $ isStrictlyQuoteFree
"Double quote array expansions to avoid re-splitting elements."
checkUnquotedDollarAt _ _ = return ()
-prop_checkConcatenatedDollarAt1 = verify checkConcatenatedDollarAt "echo \"foo$@\""
-prop_checkConcatenatedDollarAt2 = verify checkConcatenatedDollarAt "echo ${arr[@]}lol"
-prop_checkConcatenatedDollarAt3 = verify checkConcatenatedDollarAt "echo $a$@"
-prop_checkConcatenatedDollarAt4 = verifyNot checkConcatenatedDollarAt "echo $@"
-prop_checkConcatenatedDollarAt5 = verifyNot checkConcatenatedDollarAt "echo \"${arr[@]}\""
+-- |
+-- >>> prop $ verify checkConcatenatedDollarAt "echo \"foo$@\""
+-- >>> prop $ verify checkConcatenatedDollarAt "echo ${arr[@]}lol"
+-- >>> prop $ verify checkConcatenatedDollarAt "echo $a$@"
+-- >>> prop $ verifyNot checkConcatenatedDollarAt "echo $@"
+-- >>> prop $ verifyNot checkConcatenatedDollarAt "echo \"${arr[@]}\""
checkConcatenatedDollarAt p word@T_NormalWord {}
| not $ isQuoteFree (parentMap p) word =
unless (null $ drop 1 parts) $
@@ -724,13 +739,14 @@ checkConcatenatedDollarAt p word@T_NormalWord {}
for t = err (getId t) 2145 "Argument mixes string and array. Use * or separate argument."
checkConcatenatedDollarAt _ _ = return ()
-prop_checkArrayAsString1 = verify checkArrayAsString "a=$@"
-prop_checkArrayAsString2 = verify checkArrayAsString "a=\"${arr[@]}\""
-prop_checkArrayAsString3 = verify checkArrayAsString "a=*.png"
-prop_checkArrayAsString4 = verify checkArrayAsString "a={1..10}"
-prop_checkArrayAsString5 = verifyNot checkArrayAsString "a='*.gif'"
-prop_checkArrayAsString6 = verifyNot checkArrayAsString "a=$*"
-prop_checkArrayAsString7 = verifyNot checkArrayAsString "a=( $@ )"
+-- |
+-- >>> prop $ verify checkArrayAsString "a=$@"
+-- >>> prop $ verify checkArrayAsString "a=\"${arr[@]}\""
+-- >>> prop $ verify checkArrayAsString "a=*.png"
+-- >>> prop $ verify checkArrayAsString "a={1..10}"
+-- >>> prop $ verifyNot checkArrayAsString "a='*.gif'"
+-- >>> prop $ verifyNot checkArrayAsString "a=$*"
+-- >>> prop $ verifyNot checkArrayAsString "a=( $@ )"
checkArrayAsString _ (T_Assignment id _ _ _ word) =
if willConcatInAssignment word
then
@@ -742,15 +758,16 @@ checkArrayAsString _ (T_Assignment id _ _ _ word) =
"Brace expansions and globs are literal in assignments. Quote it or use an array."
checkArrayAsString _ _ = return ()
-prop_checkArrayWithoutIndex1 = verifyTree checkArrayWithoutIndex "foo=(a b); echo $foo"
-prop_checkArrayWithoutIndex2 = verifyNotTree checkArrayWithoutIndex "foo='bar baz'; foo=($foo); echo ${foo[0]}"
-prop_checkArrayWithoutIndex3 = verifyTree checkArrayWithoutIndex "coproc foo while true; do echo cow; done; echo $foo"
-prop_checkArrayWithoutIndex4 = verifyTree checkArrayWithoutIndex "coproc tail -f log; echo $COPROC"
-prop_checkArrayWithoutIndex5 = verifyTree checkArrayWithoutIndex "a[0]=foo; echo $a"
-prop_checkArrayWithoutIndex6 = verifyTree checkArrayWithoutIndex "echo $PIPESTATUS"
-prop_checkArrayWithoutIndex7 = verifyTree checkArrayWithoutIndex "a=(a b); a+=c"
-prop_checkArrayWithoutIndex8 = verifyTree checkArrayWithoutIndex "declare -a foo; foo=bar;"
-prop_checkArrayWithoutIndex9 = verifyTree checkArrayWithoutIndex "read -r -a arr <<< 'foo bar'; echo \"$arr\""
+-- |
+-- >>> prop $ verifyTree checkArrayWithoutIndex "foo=(a b); echo $foo"
+-- >>> prop $ verifyNotTree checkArrayWithoutIndex "foo='bar baz'; foo=($foo); echo ${foo[0]}"
+-- >>> prop $ verifyTree checkArrayWithoutIndex "coproc foo while true; do echo cow; done; echo $foo"
+-- >>> prop $ verifyTree checkArrayWithoutIndex "coproc tail -f log; echo $COPROC"
+-- >>> prop $ verifyTree checkArrayWithoutIndex "a[0]=foo; echo $a"
+-- >>> prop $ verifyTree checkArrayWithoutIndex "echo $PIPESTATUS"
+-- >>> prop $ verifyTree checkArrayWithoutIndex "a=(a b); a+=c"
+-- >>> prop $ verifyTree checkArrayWithoutIndex "declare -a foo; foo=bar;"
+-- >>> prop $ verifyTree checkArrayWithoutIndex "read -r -a arr <<< 'foo bar'; echo \"$arr\""
checkArrayWithoutIndex params _ =
doVariableFlowAnalysis readF writeF defaultMap (variableFlow params)
where
@@ -785,13 +802,14 @@ checkArrayWithoutIndex params _ =
T_Assignment _ _ _ (_:_) _ -> True
_ -> False
-prop_checkStderrRedirect = verify checkStderrRedirect "test 2>&1 > cow"
-prop_checkStderrRedirect2 = verifyNot checkStderrRedirect "test > cow 2>&1"
-prop_checkStderrRedirect3 = verifyNot checkStderrRedirect "test 2>&1 > file | grep stderr"
-prop_checkStderrRedirect4 = verifyNot checkStderrRedirect "errors=$(test 2>&1 > file)"
-prop_checkStderrRedirect5 = verifyNot checkStderrRedirect "read < <(test 2>&1 > file)"
-prop_checkStderrRedirect6 = verify checkStderrRedirect "foo | bar 2>&1 > /dev/null"
-prop_checkStderrRedirect7 = verifyNot checkStderrRedirect "{ cmd > file; } 2>&1"
+-- |
+-- >>> prop $ verify checkStderrRedirect "test 2>&1 > cow"
+-- >>> prop $ verifyNot checkStderrRedirect "test > cow 2>&1"
+-- >>> prop $ verifyNot checkStderrRedirect "test 2>&1 > file | grep stderr"
+-- >>> prop $ verifyNot checkStderrRedirect "errors=$(test 2>&1 > file)"
+-- >>> prop $ verifyNot checkStderrRedirect "read < <(test 2>&1 > file)"
+-- >>> prop $ verify checkStderrRedirect "foo | bar 2>&1 > /dev/null"
+-- >>> prop $ verifyNot checkStderrRedirect "{ cmd > file; } 2>&1"
checkStderrRedirect params redir@(T_Redirecting _ [
T_FdRedirect id "2" (T_IoDuplicate _ (T_GREATAND _) "1"),
T_FdRedirect _ _ (T_IoFile _ op _)
@@ -818,27 +836,28 @@ lt x = trace ("Tracing " ++ show x) x
ltt t = trace ("Tracing " ++ show t)
-prop_checkSingleQuotedVariables = verify checkSingleQuotedVariables "echo '$foo'"
-prop_checkSingleQuotedVariables2 = verify checkSingleQuotedVariables "echo 'lol$1.jpg'"
-prop_checkSingleQuotedVariables3 = verifyNot checkSingleQuotedVariables "sed 's/foo$/bar/'"
-prop_checkSingleQuotedVariables3a= verify checkSingleQuotedVariables "sed 's/${foo}/bar/'"
-prop_checkSingleQuotedVariables3b= verify checkSingleQuotedVariables "sed 's/$(echo cow)/bar/'"
-prop_checkSingleQuotedVariables3c= verify checkSingleQuotedVariables "sed 's/$((1+foo))/bar/'"
-prop_checkSingleQuotedVariables4 = verifyNot checkSingleQuotedVariables "awk '{print $1}'"
-prop_checkSingleQuotedVariables5 = verifyNot checkSingleQuotedVariables "trap 'echo $SECONDS' EXIT"
-prop_checkSingleQuotedVariables6 = verifyNot checkSingleQuotedVariables "sed -n '$p'"
-prop_checkSingleQuotedVariables6a= verify checkSingleQuotedVariables "sed -n '$pattern'"
-prop_checkSingleQuotedVariables7 = verifyNot checkSingleQuotedVariables "PS1='$PWD \\$ '"
-prop_checkSingleQuotedVariables8 = verify checkSingleQuotedVariables "find . -exec echo '$1' {} +"
-prop_checkSingleQuotedVariables9 = verifyNot checkSingleQuotedVariables "find . -exec awk '{print $1}' {} \\;"
-prop_checkSingleQuotedVariables10= verify checkSingleQuotedVariables "echo '`pwd`'"
-prop_checkSingleQuotedVariables11= verifyNot checkSingleQuotedVariables "sed '${/lol/d}'"
-prop_checkSingleQuotedVariables12= verifyNot checkSingleQuotedVariables "eval 'echo $1'"
-prop_checkSingleQuotedVariables13= verifyNot checkSingleQuotedVariables "busybox awk '{print $1}'"
-prop_checkSingleQuotedVariables14= verifyNot checkSingleQuotedVariables "[ -v 'bar[$foo]' ]"
-prop_checkSingleQuotedVariables15= verifyNot checkSingleQuotedVariables "git filter-branch 'test $GIT_COMMIT'"
-prop_checkSingleQuotedVariables16= verify checkSingleQuotedVariables "git '$a'"
-prop_checkSingleQuotedVariables17= verifyNot checkSingleQuotedVariables "rename 's/(.)a/$1/g' *"
+-- |
+-- >>> prop $ verify checkSingleQuotedVariables "echo '$foo'"
+-- >>> prop $ verify checkSingleQuotedVariables "echo 'lol$1.jpg'"
+-- >>> prop $ verifyNot checkSingleQuotedVariables "sed 's/foo$/bar/'"
+-- >>> prop $ verify checkSingleQuotedVariables "sed 's/${foo}/bar/'"
+-- >>> prop $ verify checkSingleQuotedVariables "sed 's/$(echo cow)/bar/'"
+-- >>> prop $ verify checkSingleQuotedVariables "sed 's/$((1+foo))/bar/'"
+-- >>> prop $ verifyNot checkSingleQuotedVariables "awk '{print $1}'"
+-- >>> prop $ verifyNot checkSingleQuotedVariables "trap 'echo $SECONDS' EXIT"
+-- >>> prop $ verifyNot checkSingleQuotedVariables "sed -n '$p'"
+-- >>> prop $ verify checkSingleQuotedVariables "sed -n '$pattern'"
+-- >>> prop $ verifyNot checkSingleQuotedVariables "PS1='$PWD \\$ '"
+-- >>> prop $ verify checkSingleQuotedVariables "find . -exec echo '$1' {} +"
+-- >>> prop $ verifyNot checkSingleQuotedVariables "find . -exec awk '{print $1}' {} \\;"
+-- >>> prop $ verify checkSingleQuotedVariables "echo '`pwd`'"
+-- >>> prop $ verifyNot checkSingleQuotedVariables "sed '${/lol/d}'"
+-- >>> prop $ verifyNot checkSingleQuotedVariables "eval 'echo $1'"
+-- >>> prop $ verifyNot checkSingleQuotedVariables "busybox awk '{print $1}'"
+-- >>> prop $ verifyNot checkSingleQuotedVariables "[ -v 'bar[$foo]' ]"
+-- >>> prop $ verifyNot checkSingleQuotedVariables "git filter-branch 'test $GIT_COMMIT'"
+-- >>> prop $ verify checkSingleQuotedVariables "git '$a'"
+-- >>> prop $ verifyNot checkSingleQuotedVariables "rename 's/(.)a/$1/g' *"
checkSingleQuotedVariables params t@(T_SingleQuoted id s) =
when (s `matches` re) $
@@ -905,29 +924,31 @@ checkSingleQuotedVariables params t@(T_SingleQuoted id s) =
checkSingleQuotedVariables _ _ = return ()
-prop_checkUnquotedN = verify checkUnquotedN "if [ -n $foo ]; then echo cow; fi"
-prop_checkUnquotedN2 = verify checkUnquotedN "[ -n $cow ]"
-prop_checkUnquotedN3 = verifyNot checkUnquotedN "[[ -n $foo ]] && echo cow"
-prop_checkUnquotedN4 = verify checkUnquotedN "[ -n $cow -o -t 1 ]"
-prop_checkUnquotedN5 = verifyNot checkUnquotedN "[ -n \"$@\" ]"
+-- |
+-- >>> prop $ verify checkUnquotedN "if [ -n $foo ]; then echo cow; fi"
+-- >>> prop $ verify checkUnquotedN "[ -n $cow ]"
+-- >>> prop $ verifyNot checkUnquotedN "[[ -n $foo ]] && echo cow"
+-- >>> prop $ verify checkUnquotedN "[ -n $cow -o -t 1 ]"
+-- >>> prop $ verifyNot checkUnquotedN "[ -n \"$@\" ]"
checkUnquotedN _ (TC_Unary _ SingleBracket "-n" (T_NormalWord id [t])) | willSplit t =
err id 2070 "-n doesn't work with unquoted arguments. Quote or use [[ ]]."
checkUnquotedN _ _ = return ()
-prop_checkNumberComparisons1 = verify checkNumberComparisons "[[ $foo < 3 ]]"
-prop_checkNumberComparisons2 = verify checkNumberComparisons "[[ 0 >= $(cmd) ]]"
-prop_checkNumberComparisons3 = verifyNot checkNumberComparisons "[[ $foo ]] > 3"
-prop_checkNumberComparisons4 = verify checkNumberComparisons "[[ $foo > 2.72 ]]"
-prop_checkNumberComparisons5 = verify checkNumberComparisons "[[ $foo -le 2.72 ]]"
-prop_checkNumberComparisons6 = verify checkNumberComparisons "[[ 3.14 -eq $foo ]]"
-prop_checkNumberComparisons7 = verifyNot checkNumberComparisons "[[ 3.14 == $foo ]]"
-prop_checkNumberComparisons8 = verify checkNumberComparisons "[ foo <= bar ]"
-prop_checkNumberComparisons9 = verify checkNumberComparisons "[ foo \\>= bar ]"
-prop_checkNumberComparisons11 = verify checkNumberComparisons "[ $foo -eq 'N' ]"
-prop_checkNumberComparisons12 = verify checkNumberComparisons "[ x$foo -gt x${N} ]"
-prop_checkNumberComparisons13 = verify checkNumberComparisons "[ $foo > $bar ]"
-prop_checkNumberComparisons14 = verifyNot checkNumberComparisons "[[ foo < bar ]]"
-prop_checkNumberComparisons15 = verifyNot checkNumberComparisons "[ $foo '>' $bar ]"
+-- |
+-- >>> prop $ verify checkNumberComparisons "[[ $foo < 3 ]]"
+-- >>> prop $ verify checkNumberComparisons "[[ 0 >= $(cmd) ]]"
+-- >>> prop $ verifyNot checkNumberComparisons "[[ $foo ]] > 3"
+-- >>> prop $ verify checkNumberComparisons "[[ $foo > 2.72 ]]"
+-- >>> prop $ verify checkNumberComparisons "[[ $foo -le 2.72 ]]"
+-- >>> prop $ verify checkNumberComparisons "[[ 3.14 -eq $foo ]]"
+-- >>> prop $ verifyNot checkNumberComparisons "[[ 3.14 == $foo ]]"
+-- >>> prop $ verify checkNumberComparisons "[ foo <= bar ]"
+-- >>> prop $ verify checkNumberComparisons "[ foo \\>= bar ]"
+-- >>> prop $ verify checkNumberComparisons "[ $foo -eq 'N' ]"
+-- >>> prop $ verify checkNumberComparisons "[ x$foo -gt x${N} ]"
+-- >>> prop $ verify checkNumberComparisons "[ $foo > $bar ]"
+-- >>> prop $ verifyNot checkNumberComparisons "[[ foo < bar ]]"
+-- >>> prop $ verifyNot checkNumberComparisons "[ $foo '>' $bar ]"
checkNumberComparisons params (TC_Binary id typ op lhs rhs) = do
if isNum lhs || isNum rhs
then do
@@ -1010,24 +1031,27 @@ checkNumberComparisons params (TC_Binary id typ op lhs rhs) = do
floatRegex = mkRegex "^[-+]?[0-9]+\\.[0-9]+$"
checkNumberComparisons _ _ = return ()
-prop_checkSingleBracketOperators1 = verify checkSingleBracketOperators "[ test =~ foo ]"
+-- |
+-- >>> prop $ verify checkSingleBracketOperators "[ test =~ foo ]"
checkSingleBracketOperators params (TC_Binary id SingleBracket "=~" lhs rhs) =
when (shellType params `elem` [Bash, Ksh]) $
err id 2074 $ "Can't use =~ in [ ]. Use [[..]] instead."
checkSingleBracketOperators _ _ = return ()
-prop_checkDoubleBracketOperators1 = verify checkDoubleBracketOperators "[[ 3 \\< 4 ]]"
-prop_checkDoubleBracketOperators3 = verifyNot checkDoubleBracketOperators "[[ foo < bar ]]"
+-- |
+-- >>> prop $ verify checkDoubleBracketOperators "[[ 3 \\< 4 ]]"
+-- >>> prop $ verifyNot checkDoubleBracketOperators "[[ foo < bar ]]"
checkDoubleBracketOperators _ x@(TC_Binary id typ op lhs rhs)
| typ == DoubleBracket && op `elem` ["\\<", "\\>"] =
err id 2075 $ "Escaping " ++ op ++" is required in [..], but invalid in [[..]]"
checkDoubleBracketOperators _ _ = return ()
-prop_checkConditionalAndOrs1 = verify checkConditionalAndOrs "[ foo && bar ]"
-prop_checkConditionalAndOrs2 = verify checkConditionalAndOrs "[[ foo -o bar ]]"
-prop_checkConditionalAndOrs3 = verifyNot checkConditionalAndOrs "[[ foo || bar ]]"
-prop_checkConditionalAndOrs4 = verify checkConditionalAndOrs "[ foo -a bar ]"
-prop_checkConditionalAndOrs5 = verify checkConditionalAndOrs "[ -z 3 -o a = b ]"
+-- |
+-- >>> prop $ verify checkConditionalAndOrs "[ foo && bar ]"
+-- >>> prop $ verify checkConditionalAndOrs "[[ foo -o bar ]]"
+-- >>> prop $ verifyNot checkConditionalAndOrs "[[ foo || bar ]]"
+-- >>> prop $ verify checkConditionalAndOrs "[ foo -a bar ]"
+-- >>> prop $ verify checkConditionalAndOrs "[ -z 3 -o a = b ]"
checkConditionalAndOrs _ t =
case t of
(TC_And id SingleBracket "&&" _ _) ->
@@ -1046,12 +1070,13 @@ checkConditionalAndOrs _ t =
_ -> return ()
-prop_checkQuotedCondRegex1 = verify checkQuotedCondRegex "[[ $foo =~ \"bar.*\" ]]"
-prop_checkQuotedCondRegex2 = verify checkQuotedCondRegex "[[ $foo =~ '(cow|bar)' ]]"
-prop_checkQuotedCondRegex3 = verifyNot checkQuotedCondRegex "[[ $foo =~ $foo ]]"
-prop_checkQuotedCondRegex4 = verifyNot checkQuotedCondRegex "[[ $foo =~ \"bar\" ]]"
-prop_checkQuotedCondRegex5 = verifyNot checkQuotedCondRegex "[[ $foo =~ 'cow bar' ]]"
-prop_checkQuotedCondRegex6 = verify checkQuotedCondRegex "[[ $foo =~ 'cow|bar' ]]"
+-- |
+-- >>> prop $ verify checkQuotedCondRegex "[[ $foo =~ \"bar.*\" ]]"
+-- >>> prop $ verify checkQuotedCondRegex "[[ $foo =~ '(cow|bar)' ]]"
+-- >>> prop $ verifyNot checkQuotedCondRegex "[[ $foo =~ $foo ]]"
+-- >>> prop $ verifyNot checkQuotedCondRegex "[[ $foo =~ \"bar\" ]]"
+-- >>> prop $ verifyNot checkQuotedCondRegex "[[ $foo =~ 'cow bar' ]]"
+-- >>> prop $ verify checkQuotedCondRegex "[[ $foo =~ 'cow|bar' ]]"
checkQuotedCondRegex _ (TC_Binary _ _ "=~" _ rhs) =
case rhs of
T_NormalWord id [T_DoubleQuoted _ _] -> error rhs
@@ -1069,14 +1094,15 @@ checkQuotedCondRegex _ (TC_Binary _ _ "=~" _ rhs) =
return . not $ hasMetachars s
checkQuotedCondRegex _ _ = return ()
-prop_checkGlobbedRegex1 = verify checkGlobbedRegex "[[ $foo =~ *foo* ]]"
-prop_checkGlobbedRegex2 = verify checkGlobbedRegex "[[ $foo =~ f* ]]"
-prop_checkGlobbedRegex3 = verifyNot checkGlobbedRegex "[[ $foo =~ $foo ]]"
-prop_checkGlobbedRegex4 = verifyNot checkGlobbedRegex "[[ $foo =~ ^c.* ]]"
-prop_checkGlobbedRegex5 = verifyNot checkGlobbedRegex "[[ $foo =~ \\* ]]"
-prop_checkGlobbedRegex6 = verifyNot checkGlobbedRegex "[[ $foo =~ (o*) ]]"
-prop_checkGlobbedRegex7 = verifyNot checkGlobbedRegex "[[ $foo =~ \\*foo ]]"
-prop_checkGlobbedRegex8 = verifyNot checkGlobbedRegex "[[ $foo =~ x\\* ]]"
+-- |
+-- >>> prop $ verify checkGlobbedRegex "[[ $foo =~ *foo* ]]"
+-- >>> prop $ verify checkGlobbedRegex "[[ $foo =~ f* ]]"
+-- >>> prop $ verifyNot checkGlobbedRegex "[[ $foo =~ $foo ]]"
+-- >>> prop $ verifyNot checkGlobbedRegex "[[ $foo =~ ^c.* ]]"
+-- >>> prop $ verifyNot checkGlobbedRegex "[[ $foo =~ \\* ]]"
+-- >>> prop $ verifyNot checkGlobbedRegex "[[ $foo =~ (o*) ]]"
+-- >>> prop $ verifyNot checkGlobbedRegex "[[ $foo =~ \\*foo ]]"
+-- >>> prop $ verifyNot checkGlobbedRegex "[[ $foo =~ x\\* ]]"
checkGlobbedRegex _ (TC_Binary _ DoubleBracket "=~" _ rhs) =
let s = concat $ oversimplify rhs in
when (isConfusedGlobRegex s) $
@@ -1084,16 +1110,17 @@ checkGlobbedRegex _ (TC_Binary _ DoubleBracket "=~" _ rhs) =
checkGlobbedRegex _ _ = return ()
-prop_checkConstantIfs1 = verify checkConstantIfs "[[ foo != bar ]]"
-prop_checkConstantIfs2a= verify checkConstantIfs "[ n -le 4 ]"
-prop_checkConstantIfs2b= verifyNot checkConstantIfs "[[ n -le 4 ]]"
-prop_checkConstantIfs3 = verify checkConstantIfs "[[ $n -le 4 && n != 2 ]]"
-prop_checkConstantIfs4 = verifyNot checkConstantIfs "[[ $n -le 3 ]]"
-prop_checkConstantIfs5 = verifyNot checkConstantIfs "[[ $n -le $n ]]"
-prop_checkConstantIfs6 = verifyNot checkConstantIfs "[[ a -ot b ]]"
-prop_checkConstantIfs7 = verifyNot checkConstantIfs "[ a -nt b ]"
-prop_checkConstantIfs8 = verifyNot checkConstantIfs "[[ ~foo == '~foo' ]]"
-prop_checkConstantIfs9 = verify checkConstantIfs "[[ *.png == [a-z] ]]"
+-- |
+-- >>> prop $ verify checkConstantIfs "[[ foo != bar ]]"
+-- >>> prop $ verify checkConstantIfs "[ n -le 4 ]"
+-- >>> prop $ verifyNot checkConstantIfs "[[ n -le 4 ]]"
+-- >>> prop $ verify checkConstantIfs "[[ $n -le 4 && n != 2 ]]"
+-- >>> prop $ verifyNot checkConstantIfs "[[ $n -le 3 ]]"
+-- >>> prop $ verifyNot checkConstantIfs "[[ $n -le $n ]]"
+-- >>> prop $ verifyNot checkConstantIfs "[[ a -ot b ]]"
+-- >>> prop $ verifyNot checkConstantIfs "[ a -nt b ]"
+-- >>> prop $ verifyNot checkConstantIfs "[[ ~foo == '~foo' ]]"
+-- >>> prop $ verify checkConstantIfs "[[ *.png == [a-z] ]]"
checkConstantIfs _ (TC_Binary id typ op lhs rhs) | not isDynamic =
if isConstant lhs && isConstant rhs
then warn id 2050 "This expression is constant. Did you forget the $ on a variable?"
@@ -1109,15 +1136,16 @@ checkConstantIfs _ (TC_Binary id typ op lhs rhs) | not isDynamic =
warn id 2193 "The arguments to this comparison can never be equal. Make sure your syntax is correct."
checkConstantIfs _ _ = return ()
-prop_checkLiteralBreakingTest = verify checkLiteralBreakingTest "[[ a==$foo ]]"
-prop_checkLiteralBreakingTest2 = verify checkLiteralBreakingTest "[ $foo=3 ]"
-prop_checkLiteralBreakingTest3 = verify checkLiteralBreakingTest "[ $foo!=3 ]"
-prop_checkLiteralBreakingTest4 = verify checkLiteralBreakingTest "[ \"$(ls) \" ]"
-prop_checkLiteralBreakingTest5 = verify checkLiteralBreakingTest "[ -n \"$(true) \" ]"
-prop_checkLiteralBreakingTest6 = verify checkLiteralBreakingTest "[ -z $(true)z ]"
-prop_checkLiteralBreakingTest7 = verifyNot checkLiteralBreakingTest "[ -z $(true) ]"
-prop_checkLiteralBreakingTest8 = verifyNot checkLiteralBreakingTest "[ $(true)$(true) ]"
-prop_checkLiteralBreakingTest10 = verify checkLiteralBreakingTest "[ -z foo ]"
+-- |
+-- >>> prop $ verify checkLiteralBreakingTest "[[ a==$foo ]]"
+-- >>> prop $ verify checkLiteralBreakingTest "[ $foo=3 ]"
+-- >>> prop $ verify checkLiteralBreakingTest "[ $foo!=3 ]"
+-- >>> prop $ verify checkLiteralBreakingTest "[ \"$(ls) \" ]"
+-- >>> prop $ verify checkLiteralBreakingTest "[ -n \"$(true) \" ]"
+-- >>> prop $ verify checkLiteralBreakingTest "[ -z $(true)z ]"
+-- >>> prop $ verifyNot checkLiteralBreakingTest "[ -z $(true) ]"
+-- >>> prop $ verifyNot checkLiteralBreakingTest "[ $(true)$(true) ]"
+-- >>> prop $ verify checkLiteralBreakingTest "[ -z foo ]"
checkLiteralBreakingTest _ t = potentially $
case t of
(TC_Nullary _ _ w@(T_NormalWord _ l)) -> do
@@ -1144,13 +1172,14 @@ checkLiteralBreakingTest _ t = potentially $
token <- listToMaybe $ filter isNonEmpty $ getWordParts t
return $ err (getId token) 2157 s
-prop_checkConstantNullary = verify checkConstantNullary "[[ '$(foo)' ]]"
-prop_checkConstantNullary2 = verify checkConstantNullary "[ \"-f lol\" ]"
-prop_checkConstantNullary3 = verify checkConstantNullary "[[ cmd ]]"
-prop_checkConstantNullary4 = verify checkConstantNullary "[[ ! cmd ]]"
-prop_checkConstantNullary5 = verify checkConstantNullary "[[ true ]]"
-prop_checkConstantNullary6 = verify checkConstantNullary "[ 1 ]"
-prop_checkConstantNullary7 = verify checkConstantNullary "[ false ]"
+-- |
+-- >>> prop $ verify checkConstantNullary "[[ '$(foo)' ]]"
+-- >>> prop $ verify checkConstantNullary "[ \"-f lol\" ]"
+-- >>> prop $ verify checkConstantNullary "[[ cmd ]]"
+-- >>> prop $ verify checkConstantNullary "[[ ! cmd ]]"
+-- >>> prop $ verify checkConstantNullary "[[ true ]]"
+-- >>> prop $ verify checkConstantNullary "[ 1 ]"
+-- >>> prop $ verify checkConstantNullary "[ false ]"
checkConstantNullary _ (TC_Nullary _ _ t) | isConstant t =
case fromMaybe "" $ getLiteralString t of
"false" -> err (getId t) 2158 "[ false ] is true. Remove the brackets."
@@ -1163,9 +1192,10 @@ checkConstantNullary _ (TC_Nullary _ _ t) | isConstant t =
checkConstantNullary _ _ = return ()
-prop_checkForDecimals1 = verify checkForDecimals "((3.14*c))"
-prop_checkForDecimals2 = verify checkForDecimals "foo[1.2]=bar"
-prop_checkForDecimals3 = verifyNot checkForDecimals "declare -A foo; foo[1.2]=bar"
+-- |
+-- >>> prop $ verify checkForDecimals "((3.14*c))"
+-- >>> prop $ verify checkForDecimals "foo[1.2]=bar"
+-- >>> prop $ verifyNot checkForDecimals "declare -A foo; foo[1.2]=bar"
checkForDecimals params t@(TA_Expansion id _) = potentially $ do
guard $ not (hasFloatingPoint params)
str <- getLiteralString t
@@ -1174,29 +1204,31 @@ checkForDecimals params t@(TA_Expansion id _) = potentially $ do
return $ err id 2079 "(( )) doesn't support decimals. Use bc or awk."
checkForDecimals _ _ = return ()
-prop_checkDivBeforeMult = verify checkDivBeforeMult "echo $((c/n*100))"
-prop_checkDivBeforeMult2 = verifyNot checkDivBeforeMult "echo $((c*100/n))"
-prop_checkDivBeforeMult3 = verifyNot checkDivBeforeMult "echo $((c/10*10))"
+-- |
+-- >>> prop $ verify checkDivBeforeMult "echo $((c/n*100))"
+-- >>> prop $ verifyNot checkDivBeforeMult "echo $((c*100/n))"
+-- >>> prop $ verifyNot checkDivBeforeMult "echo $((c/10*10))"
checkDivBeforeMult params (TA_Binary _ "*" (TA_Binary id "/" _ x) y)
| not (hasFloatingPoint params) && x /= y =
info id 2017 "Increase precision by replacing a/b*c with a*c/b."
checkDivBeforeMult _ _ = return ()
-prop_checkArithmeticDeref = verify checkArithmeticDeref "echo $((3+$foo))"
-prop_checkArithmeticDeref2 = verify checkArithmeticDeref "cow=14; (( s+= $cow ))"
-prop_checkArithmeticDeref3 = verifyNot checkArithmeticDeref "cow=1/40; (( s+= ${cow%%/*} ))"
-prop_checkArithmeticDeref4 = verifyNot checkArithmeticDeref "(( ! $? ))"
-prop_checkArithmeticDeref5 = verifyNot checkArithmeticDeref "(($1))"
-prop_checkArithmeticDeref6 = verify checkArithmeticDeref "(( a[$i] ))"
-prop_checkArithmeticDeref7 = verifyNot checkArithmeticDeref "(( 10#$n ))"
-prop_checkArithmeticDeref8 = verifyNot checkArithmeticDeref "let i=$i+1"
-prop_checkArithmeticDeref9 = verifyNot checkArithmeticDeref "(( a[foo] ))"
-prop_checkArithmeticDeref10= verifyNot checkArithmeticDeref "(( a[\\$foo] ))"
-prop_checkArithmeticDeref11= verifyNot checkArithmeticDeref "a[$foo]=wee"
-prop_checkArithmeticDeref12= verify checkArithmeticDeref "for ((i=0; $i < 3; i)); do true; done"
-prop_checkArithmeticDeref13= verifyNot checkArithmeticDeref "(( $$ ))"
-prop_checkArithmeticDeref14= verifyNot checkArithmeticDeref "(( $! ))"
-prop_checkArithmeticDeref15= verifyNot checkArithmeticDeref "(( ${!var} ))"
+-- |
+-- >>> prop $ verify checkArithmeticDeref "echo $((3+$foo))"
+-- >>> prop $ verify checkArithmeticDeref "cow=14; (( s+= $cow ))"
+-- >>> prop $ verifyNot checkArithmeticDeref "cow=1/40; (( s+= ${cow%%/*} ))"
+-- >>> prop $ verifyNot checkArithmeticDeref "(( ! $? ))"
+-- >>> prop $ verifyNot checkArithmeticDeref "(($1))"
+-- >>> prop $ verify checkArithmeticDeref "(( a[$i] ))"
+-- >>> prop $ verifyNot checkArithmeticDeref "(( 10#$n ))"
+-- >>> prop $ verifyNot checkArithmeticDeref "let i=$i+1"
+-- >>> prop $ verifyNot checkArithmeticDeref "(( a[foo] ))"
+-- >>> prop $ verifyNot checkArithmeticDeref "(( a[\\$foo] ))"
+-- >>> prop $ verifyNot checkArithmeticDeref "a[$foo]=wee"
+-- >>> prop $ verify checkArithmeticDeref "for ((i=0; $i < 3; i)); do true; done"
+-- >>> prop $ verifyNot checkArithmeticDeref "(( $$ ))"
+-- >>> prop $ verifyNot checkArithmeticDeref "(( $! ))"
+-- >>> prop $ verifyNot checkArithmeticDeref "(( ${!var} ))"
checkArithmeticDeref params t@(TA_Expansion _ [b@(T_DollarBraced id _)]) =
unless (isException $ bracedString b) getWarning
where
@@ -1215,9 +1247,10 @@ checkArithmeticDeref params t@(TA_Expansion _ [b@(T_DollarBraced id _)]) =
noWarning = return ()
checkArithmeticDeref _ _ = return ()
-prop_checkArithmeticBadOctal1 = verify checkArithmeticBadOctal "(( 0192 ))"
-prop_checkArithmeticBadOctal2 = verifyNot checkArithmeticBadOctal "(( 0x192 ))"
-prop_checkArithmeticBadOctal3 = verifyNot checkArithmeticBadOctal "(( 1 ^ 0777 ))"
+-- |
+-- >>> prop $ verify checkArithmeticBadOctal "(( 0192 ))"
+-- >>> prop $ verifyNot checkArithmeticBadOctal "(( 0x192 ))"
+-- >>> prop $ verifyNot checkArithmeticBadOctal "(( 1 ^ 0777 ))"
checkArithmeticBadOctal _ t@(TA_Expansion id _) = potentially $ do
str <- getLiteralString t
guard $ str `matches` octalRE
@@ -1226,12 +1259,13 @@ checkArithmeticBadOctal _ t@(TA_Expansion id _) = potentially $ do
octalRE = mkRegex "^0[0-7]*[8-9]"
checkArithmeticBadOctal _ _ = return ()
-prop_checkComparisonAgainstGlob = verify checkComparisonAgainstGlob "[[ $cow == $bar ]]"
-prop_checkComparisonAgainstGlob2 = verifyNot checkComparisonAgainstGlob "[[ $cow == \"$bar\" ]]"
-prop_checkComparisonAgainstGlob3 = verify checkComparisonAgainstGlob "[ $cow = *foo* ]"
-prop_checkComparisonAgainstGlob4 = verifyNot checkComparisonAgainstGlob "[ $cow = foo ]"
-prop_checkComparisonAgainstGlob5 = verify checkComparisonAgainstGlob "[[ $cow != $bar ]]"
-prop_checkComparisonAgainstGlob6 = verify checkComparisonAgainstGlob "[ $f != /* ]"
+-- |
+-- >>> prop $ verify checkComparisonAgainstGlob "[[ $cow == $bar ]]"
+-- >>> prop $ verifyNot checkComparisonAgainstGlob "[[ $cow == \"$bar\" ]]"
+-- >>> prop $ verify checkComparisonAgainstGlob "[ $cow = *foo* ]"
+-- >>> prop $ verifyNot checkComparisonAgainstGlob "[ $cow = foo ]"
+-- >>> prop $ verify checkComparisonAgainstGlob "[[ $cow != $bar ]]"
+-- >>> prop $ verify checkComparisonAgainstGlob "[ $f != /* ]"
checkComparisonAgainstGlob _ (TC_Binary _ DoubleBracket op _ (T_NormalWord id [T_DollarBraced _ _]))
| op `elem` ["=", "==", "!="] =
warn id 2053 $ "Quote the right-hand side of " ++ op ++ " in [[ ]] to prevent glob matching."
@@ -1240,12 +1274,13 @@ checkComparisonAgainstGlob _ (TC_Binary _ SingleBracket op _ word)
err (getId word) 2081 "[ .. ] can't match globs. Use [[ .. ]] or case statement."
checkComparisonAgainstGlob _ _ = return ()
-prop_checkCommarrays1 = verify checkCommarrays "a=(1, 2)"
-prop_checkCommarrays2 = verify checkCommarrays "a+=(1,2,3)"
-prop_checkCommarrays3 = verifyNot checkCommarrays "cow=(1 \"foo,bar\" 3)"
-prop_checkCommarrays4 = verifyNot checkCommarrays "cow=('one,' 'two')"
-prop_checkCommarrays5 = verify checkCommarrays "a=([a]=b, [c]=d)"
-prop_checkCommarrays6 = verify checkCommarrays "a=([a]=b,[c]=d,[e]=f)"
+-- |
+-- >>> prop $ verify checkCommarrays "a=(1, 2)"
+-- >>> prop $ verify checkCommarrays "a+=(1,2,3)"
+-- >>> prop $ verifyNot checkCommarrays "cow=(1 \"foo,bar\" 3)"
+-- >>> prop $ verifyNot checkCommarrays "cow=('one,' 'two')"
+-- >>> prop $ verify checkCommarrays "a=([a]=b, [c]=d)"
+-- >>> prop $ verify checkCommarrays "a=([a]=b,[c]=d,[e]=f)"
checkCommarrays _ (T_Array id l) =
when (any (isCommaSeparated . literal) l) $
warn id 2054 "Use spaces, not commas, to separate array elements."
@@ -1258,11 +1293,13 @@ checkCommarrays _ (T_Array id l) =
isCommaSeparated str = "," `isSuffixOf` str || length (filter (== ',') str) > 1
checkCommarrays _ _ = return ()
-prop_checkOrNeq1 = verify checkOrNeq "if [[ $lol -ne cow || $lol -ne foo ]]; then echo foo; fi"
-prop_checkOrNeq2 = verify checkOrNeq "(( a!=lol || a!=foo ))"
-prop_checkOrNeq3 = verify checkOrNeq "[ \"$a\" != lol || \"$a\" != foo ]"
-prop_checkOrNeq4 = verifyNot checkOrNeq "[ a != $cow || b != $foo ]"
-prop_checkOrNeq5 = verifyNot checkOrNeq "[[ $a != /home || $a != */public_html/* ]]"
+-- |
+-- >>> prop $ verify checkOrNeq "if [[ $lol -ne cow || $lol -ne foo ]]; then echo foo; fi"
+-- >>> prop $ verify checkOrNeq "(( a!=lol || a!=foo ))"
+-- >>> prop $ verify checkOrNeq "[ \"$a\" != lol || \"$a\" != foo ]"
+-- >>> prop $ verifyNot checkOrNeq "[ a != $cow || b != $foo ]"
+-- >>> prop $ verifyNot checkOrNeq "[[ $a != /home || $a != */public_html/* ]]"
+--
-- This only catches the most idiomatic cases. Fixme?
checkOrNeq _ (TC_Or id typ op (TC_Binary _ _ op1 lhs1 rhs1 ) (TC_Binary _ _ op2 lhs2 rhs2))
| lhs1 == lhs2 && (op1 == op2 && (op1 == "-ne" || op1 == "!=")) && not (any isGlob [rhs1,rhs2]) =
@@ -1274,11 +1311,12 @@ checkOrNeq _ (TA_Binary id "||" (TA_Binary _ "!=" word1 _) (TA_Binary _ "!=" wor
checkOrNeq _ _ = return ()
-prop_checkValidCondOps1 = verify checkValidCondOps "[[ a -xz b ]]"
-prop_checkValidCondOps2 = verify checkValidCondOps "[ -M a ]"
-prop_checkValidCondOps2a= verifyNot checkValidCondOps "[ 3 \\> 2 ]"
-prop_checkValidCondOps3 = verifyNot checkValidCondOps "[ 1 = 2 -a 3 -ge 4 ]"
-prop_checkValidCondOps4 = verifyNot checkValidCondOps "[[ ! -v foo ]]"
+-- |
+-- >>> prop $ verify checkValidCondOps "[[ a -xz b ]]"
+-- >>> prop $ verify checkValidCondOps "[ -M a ]"
+-- >>> prop $ verifyNot checkValidCondOps "[ 3 \\> 2 ]"
+-- >>> prop $ verifyNot checkValidCondOps "[ 1 = 2 -a 3 -ge 4 ]"
+-- >>> prop $ verifyNot checkValidCondOps "[[ ! -v foo ]]"
checkValidCondOps _ (TC_Binary id _ s _ _)
| s `notElem` binaryTestOps =
warn id 2057 "Unknown binary operator."
@@ -1287,15 +1325,16 @@ checkValidCondOps _ (TC_Unary id _ s _)
warn id 2058 "Unknown unary operator."
checkValidCondOps _ _ = return ()
-prop_checkUuoeVar1 = verify checkUuoeVar "for f in $(echo $tmp); do echo lol; done"
-prop_checkUuoeVar2 = verify checkUuoeVar "date +`echo \"$format\"`"
-prop_checkUuoeVar3 = verifyNot checkUuoeVar "foo \"$(echo -e '\r')\""
-prop_checkUuoeVar4 = verifyNot checkUuoeVar "echo $tmp"
-prop_checkUuoeVar5 = verify checkUuoeVar "foo \"$(echo \"$(date) value:\" $value)\""
-prop_checkUuoeVar6 = verifyNot checkUuoeVar "foo \"$(echo files: *.png)\""
-prop_checkUuoeVar7 = verifyNot checkUuoeVar "foo $(echo $(bar))" -- covered by 2005
-prop_checkUuoeVar8 = verifyNot checkUuoeVar "#!/bin/sh\nz=$(echo)"
-prop_checkUuoeVar9 = verify checkUuoeVar "foo $(echo $(>> prop $ verify checkUuoeVar "for f in $(echo $tmp); do echo lol; done"
+-- >>> prop $ verify checkUuoeVar "date +`echo \"$format\"`"
+-- >>> prop $ verifyNot checkUuoeVar "foo \"$(echo -e '\r')\""
+-- >>> prop $ verifyNot checkUuoeVar "echo $tmp"
+-- >>> prop $ verify checkUuoeVar "foo \"$(echo \"$(date) value:\" $value)\""
+-- >>> prop $ verifyNot checkUuoeVar "foo \"$(echo files: *.png)\""
+-- >>> prop $ verifyNot checkUuoeVar "foo $(echo $(bar))" -- covered by 2005
+-- >>> prop $ verifyNot checkUuoeVar "#!/bin/sh\nz=$(echo)"
+-- >>> prop $ verify checkUuoeVar "foo $(echo $( check id cmd
@@ -1322,10 +1361,11 @@ checkUuoeVar _ p =
_ -> return ()
-prop_checkTestRedirects1 = verify checkTestRedirects "test 3 > 1"
-prop_checkTestRedirects2 = verifyNot checkTestRedirects "test 3 \\> 1"
-prop_checkTestRedirects3 = verify checkTestRedirects "/usr/bin/test $var > $foo"
-prop_checkTestRedirects4 = verifyNot checkTestRedirects "test 1 -eq 2 2> file"
+-- |
+-- >>> prop $ verify checkTestRedirects "test 3 > 1"
+-- >>> prop $ verifyNot checkTestRedirects "test 3 \\> 1"
+-- >>> prop $ verify checkTestRedirects "/usr/bin/test $var > $foo"
+-- >>> prop $ verifyNot checkTestRedirects "test 1 -eq 2 2> file"
checkTestRedirects _ (T_Redirecting id redirs cmd) | cmd `isCommand` "test" =
mapM_ check redirs
where
@@ -1343,16 +1383,17 @@ checkTestRedirects _ (T_Redirecting id redirs cmd) | cmd `isCommand` "test" =
_ -> False
checkTestRedirects _ _ = return ()
-prop_checkPS11 = verify checkPS1Assignments "PS1='\\033[1;35m\\$ '"
-prop_checkPS11a= verify checkPS1Assignments "export PS1='\\033[1;35m\\$ '"
-prop_checkPSf2 = verify checkPS1Assignments "PS1='\\h \\e[0m\\$ '"
-prop_checkPS13 = verify checkPS1Assignments "PS1=$'\\x1b[c '"
-prop_checkPS14 = verify checkPS1Assignments "PS1=$'\\e[3m; '"
-prop_checkPS14a= verify checkPS1Assignments "export PS1=$'\\e[3m; '"
-prop_checkPS15 = verifyNot checkPS1Assignments "PS1='\\[\\033[1;35m\\]\\$ '"
-prop_checkPS16 = verifyNot checkPS1Assignments "PS1='\\[\\e1m\\e[1m\\]\\$ '"
-prop_checkPS17 = verifyNot checkPS1Assignments "PS1='e033x1B'"
-prop_checkPS18 = verifyNot checkPS1Assignments "PS1='\\[\\e\\]'"
+-- |
+-- >>> prop $ verify checkPS1Assignments "PS1='\\033[1;35m\\$ '"
+-- >>> prop $ verify checkPS1Assignments "export PS1='\\033[1;35m\\$ '"
+-- >>> prop $ verify checkPS1Assignments "PS1='\\h \\e[0m\\$ '"
+-- >>> prop $ verify checkPS1Assignments "PS1=$'\\x1b[c '"
+-- >>> prop $ verify checkPS1Assignments "PS1=$'\\e[3m; '"
+-- >>> prop $ verify checkPS1Assignments "export PS1=$'\\e[3m; '"
+-- >>> prop $ verifyNot checkPS1Assignments "PS1='\\[\\033[1;35m\\]\\$ '"
+-- >>> prop $ verifyNot checkPS1Assignments "PS1='\\[\\e1m\\e[1m\\]\\$ '"
+-- >>> prop $ verifyNot checkPS1Assignments "PS1='e033x1B'"
+-- >>> prop $ verifyNot checkPS1Assignments "PS1='\\[\\e\\]'"
checkPS1Assignments _ (T_Assignment _ _ "PS1" _ word) = warnFor word
where
warnFor word =
@@ -1366,20 +1407,21 @@ checkPS1Assignments _ (T_Assignment _ _ "PS1" _ word) = warnFor word
escapeRegex = mkRegex "\\\\x1[Bb]|\\\\e|\x1B|\\\\033"
checkPS1Assignments _ _ = return ()
-prop_checkBackticks1 = verify checkBackticks "echo `foo`"
-prop_checkBackticks2 = verifyNot checkBackticks "echo $(foo)"
-prop_checkBackticks3 = verifyNot checkBackticks "echo `#inlined comment` foo"
+-- >>> prop $ verify checkBackticks "echo `foo`"
+-- >>> prop $ verifyNot checkBackticks "echo $(foo)"
+-- >>> prop $ verifyNot checkBackticks "echo `#inlined comment` foo"
checkBackticks params (T_Backticked id list) | not (null list) =
addComment $
makeCommentWithFix StyleC id 2006 "Use $(...) notation instead of legacy backticked `...`."
(fixWith [replaceStart id params 1 "$(", replaceEnd id params 1 ")"])
checkBackticks _ _ = return ()
-prop_checkIndirectExpansion1 = verify checkIndirectExpansion "${foo$n}"
-prop_checkIndirectExpansion2 = verifyNot checkIndirectExpansion "${foo//$n/lol}"
-prop_checkIndirectExpansion3 = verify checkIndirectExpansion "${$#}"
-prop_checkIndirectExpansion4 = verify checkIndirectExpansion "${var${n}_$((i%2))}"
-prop_checkIndirectExpansion5 = verifyNot checkIndirectExpansion "${bar}"
+-- |
+-- >>> prop $ verify checkIndirectExpansion "${foo$n}"
+-- >>> prop $ verifyNot checkIndirectExpansion "${foo//$n/lol}"
+-- >>> prop $ verify checkIndirectExpansion "${$#}"
+-- >>> prop $ verify checkIndirectExpansion "${var${n}_$((i%2))}"
+-- >>> prop $ verifyNot checkIndirectExpansion "${bar}"
checkIndirectExpansion _ (T_DollarBraced i (T_NormalWord _ contents)) =
when (isIndirection contents) $
err i 2082 "To expand via indirection, use arrays, ${!name} or (for sh only) eval."
@@ -1399,14 +1441,15 @@ checkIndirectExpansion _ (T_DollarBraced i (T_NormalWord _ contents)) =
checkIndirectExpansion _ _ = return ()
-prop_checkInexplicablyUnquoted1 = verify checkInexplicablyUnquoted "echo 'var='value';'"
-prop_checkInexplicablyUnquoted2 = verifyNot checkInexplicablyUnquoted "'foo'*"
-prop_checkInexplicablyUnquoted3 = verifyNot checkInexplicablyUnquoted "wget --user-agent='something'"
-prop_checkInexplicablyUnquoted4 = verify checkInexplicablyUnquoted "echo \"VALUES (\"id\")\""
-prop_checkInexplicablyUnquoted5 = verifyNot checkInexplicablyUnquoted "\"$dir\"/\"$file\""
-prop_checkInexplicablyUnquoted6 = verifyNot checkInexplicablyUnquoted "\"$dir\"some_stuff\"$file\""
-prop_checkInexplicablyUnquoted7 = verifyNot checkInexplicablyUnquoted "${dir/\"foo\"/\"bar\"}"
-prop_checkInexplicablyUnquoted8 = verifyNot checkInexplicablyUnquoted " 'foo'\\\n 'bar'"
+-- |
+-- >>> prop $ verify checkInexplicablyUnquoted "echo 'var='value';'"
+-- >>> prop $ verifyNot checkInexplicablyUnquoted "'foo'*"
+-- >>> prop $ verifyNot checkInexplicablyUnquoted "wget --user-agent='something'"
+-- >>> prop $ verify checkInexplicablyUnquoted "echo \"VALUES (\"id\")\""
+-- >>> prop $ verifyNot checkInexplicablyUnquoted "\"$dir\"/\"$file\""
+-- >>> prop $ verifyNot checkInexplicablyUnquoted "\"$dir\"some_stuff\"$file\""
+-- >>> prop $ verifyNot checkInexplicablyUnquoted "${dir/\"foo\"/\"bar\"}"
+-- >>> prop $ verifyNot checkInexplicablyUnquoted " 'foo'\\\n 'bar'"
checkInexplicablyUnquoted _ (T_NormalWord id tokens) = mapM_ check (tails tokens)
where
check (T_SingleQuoted _ _:T_Literal id str:_)
@@ -1438,11 +1481,12 @@ checkInexplicablyUnquoted _ (T_NormalWord id tokens) = mapM_ check (tails tokens
warn id 2140 "Word is of the form \"A\"B\"C\" (B indicated). Did you mean \"ABC\" or \"A\\\"B\\\"C\"?"
checkInexplicablyUnquoted _ _ = return ()
-prop_checkTildeInQuotes1 = verify checkTildeInQuotes "var=\"~/out.txt\""
-prop_checkTildeInQuotes2 = verify checkTildeInQuotes "foo > '~/dir'"
-prop_checkTildeInQuotes4 = verifyNot checkTildeInQuotes "~/file"
-prop_checkTildeInQuotes5 = verifyNot checkTildeInQuotes "echo '/~foo/cow'"
-prop_checkTildeInQuotes6 = verifyNot checkTildeInQuotes "awk '$0 ~ /foo/'"
+-- |
+-- >>> prop $ verify checkTildeInQuotes "var=\"~/out.txt\""
+-- >>> prop $ verify checkTildeInQuotes "foo > '~/dir'"
+-- >>> prop $ verifyNot checkTildeInQuotes "~/file"
+-- >>> prop $ verifyNot checkTildeInQuotes "echo '/~foo/cow'"
+-- >>> prop $ verifyNot checkTildeInQuotes "awk '$0 ~ /foo/'"
checkTildeInQuotes _ = check
where
verify id ('~':'/':_) = warn id 2088 "Tilde does not expand in quotes. Use $HOME."
@@ -1453,22 +1497,23 @@ checkTildeInQuotes _ = check
verify id str
check _ = return ()
-prop_checkLonelyDotDash1 = verify checkLonelyDotDash "./ file"
-prop_checkLonelyDotDash2 = verifyNot checkLonelyDotDash "./file"
+-- >>> prop $ verify checkLonelyDotDash "./ file"
+-- >>> prop $ verifyNot checkLonelyDotDash "./file"
checkLonelyDotDash _ t@(T_Redirecting id _ _)
| isUnqualifiedCommand t "./" =
err id 2083 "Don't add spaces after the slash in './file'."
checkLonelyDotDash _ _ = return ()
-prop_checkSpuriousExec1 = verify checkSpuriousExec "exec foo; true"
-prop_checkSpuriousExec2 = verify checkSpuriousExec "if a; then exec b; exec c; fi"
-prop_checkSpuriousExec3 = verifyNot checkSpuriousExec "echo cow; exec foo"
-prop_checkSpuriousExec4 = verifyNot checkSpuriousExec "if a; then exec b; fi"
-prop_checkSpuriousExec5 = verifyNot checkSpuriousExec "exec > file; cmd"
-prop_checkSpuriousExec6 = verify checkSpuriousExec "exec foo > file; cmd"
-prop_checkSpuriousExec7 = verifyNot checkSpuriousExec "exec file; echo failed; exit 3"
-prop_checkSpuriousExec8 = verifyNot checkSpuriousExec "exec {origout}>&1- >tmp.log 2>&1; bar"
+-- |
+-- >>> prop $ verify checkSpuriousExec "exec foo; true"
+-- >>> prop $ verify checkSpuriousExec "if a; then exec b; exec c; fi"
+-- >>> prop $ verifyNot checkSpuriousExec "echo cow; exec foo"
+-- >>> prop $ verifyNot checkSpuriousExec "if a; then exec b; fi"
+-- >>> prop $ verifyNot checkSpuriousExec "exec > file; cmd"
+-- >>> prop $ verify checkSpuriousExec "exec foo > file; cmd"
+-- >>> prop $ verifyNot checkSpuriousExec "exec file; echo failed; exit 3"
+-- >>> prop $ verifyNot checkSpuriousExec "exec {origout}>&1- >tmp.log 2>&1; bar"
checkSpuriousExec _ = doLists
where
doLists (T_Script _ _ cmds) = doList cmds
@@ -1503,10 +1548,11 @@ checkSpuriousExec _ = doLists
commentIfExec _ = return ()
-prop_checkSpuriousExpansion1 = verify checkSpuriousExpansion "if $(true); then true; fi"
-prop_checkSpuriousExpansion2 = verify checkSpuriousExpansion "while \"$(cmd)\"; do :; done"
-prop_checkSpuriousExpansion3 = verifyNot checkSpuriousExpansion "$(cmd) --flag1 --flag2"
-prop_checkSpuriousExpansion4 = verify checkSpuriousExpansion "$((i++))"
+-- |
+-- >>> prop $ verify checkSpuriousExpansion "if $(true); then true; fi"
+-- >>> prop $ verify checkSpuriousExpansion "while \"$(cmd)\"; do :; done"
+-- >>> prop $ verifyNot checkSpuriousExpansion "$(cmd) --flag1 --flag2"
+-- >>> prop $ verify checkSpuriousExpansion "$((i++))"
checkSpuriousExpansion _ (T_SimpleCommand _ _ [T_NormalWord _ [word]]) = check word
where
check word = case word of
@@ -1521,14 +1567,16 @@ checkSpuriousExpansion _ (T_SimpleCommand _ _ [T_NormalWord _ [word]]) = check w
checkSpuriousExpansion _ _ = return ()
-prop_checkDollarBrackets1 = verify checkDollarBrackets "echo $[1+2]"
-prop_checkDollarBrackets2 = verifyNot checkDollarBrackets "echo $((1+2))"
+-- |
+-- >>> prop $ verify checkDollarBrackets "echo $[1+2]"
+-- >>> prop $ verifyNot checkDollarBrackets "echo $((1+2))"
checkDollarBrackets _ (T_DollarBracket id _) =
style id 2007 "Use $((..)) instead of deprecated $[..]"
checkDollarBrackets _ _ = return ()
-prop_checkSshHereDoc1 = verify checkSshHereDoc "ssh host << foo\necho $PATH\nfoo"
-prop_checkSshHereDoc2 = verifyNot checkSshHereDoc "ssh host << 'foo'\necho $PATH\nfoo"
+-- |
+-- >>> prop $ verify checkSshHereDoc "ssh host << foo\necho $PATH\nfoo"
+-- >>> prop $ verifyNot checkSshHereDoc "ssh host << 'foo'\necho $PATH\nfoo"
checkSshHereDoc _ (T_Redirecting _ redirs cmd)
| cmd `isCommand` "ssh" =
mapM_ checkHereDoc redirs
@@ -1540,27 +1588,28 @@ checkSshHereDoc _ (T_Redirecting _ redirs cmd)
checkHereDoc _ = return ()
checkSshHereDoc _ _ = return ()
---- Subshell detection
-prop_subshellAssignmentCheck = verifyTree subshellAssignmentCheck "cat foo | while read bar; do a=$bar; done; echo \"$a\""
-prop_subshellAssignmentCheck2 = verifyNotTree subshellAssignmentCheck "while read bar; do a=$bar; done < file; echo \"$a\""
-prop_subshellAssignmentCheck3 = verifyTree subshellAssignmentCheck "( A=foo; ); rm $A"
-prop_subshellAssignmentCheck4 = verifyNotTree subshellAssignmentCheck "( A=foo; rm $A; )"
-prop_subshellAssignmentCheck5 = verifyTree subshellAssignmentCheck "cat foo | while read cow; do true; done; echo $cow;"
-prop_subshellAssignmentCheck6 = verifyTree subshellAssignmentCheck "( export lol=$(ls); ); echo $lol;"
-prop_subshellAssignmentCheck6a= verifyTree subshellAssignmentCheck "( typeset -a lol=a; ); echo $lol;"
-prop_subshellAssignmentCheck7 = verifyTree subshellAssignmentCheck "cmd | while read foo; do (( n++ )); done; echo \"$n lines\""
-prop_subshellAssignmentCheck8 = verifyTree subshellAssignmentCheck "n=3 & echo $((n++))"
-prop_subshellAssignmentCheck9 = verifyTree subshellAssignmentCheck "read n & n=foo$n"
-prop_subshellAssignmentCheck10 = verifyTree subshellAssignmentCheck "(( n <<= 3 )) & (( n |= 4 )) &"
-prop_subshellAssignmentCheck11 = verifyTree subshellAssignmentCheck "cat /etc/passwd | while read line; do let n=n+1; done\necho $n"
-prop_subshellAssignmentCheck12 = verifyTree subshellAssignmentCheck "cat /etc/passwd | while read line; do let ++n; done\necho $n"
-prop_subshellAssignmentCheck13 = verifyTree subshellAssignmentCheck "#!/bin/bash\necho foo | read bar; echo $bar"
-prop_subshellAssignmentCheck14 = verifyNotTree subshellAssignmentCheck "#!/bin/ksh93\necho foo | read bar; echo $bar"
-prop_subshellAssignmentCheck15 = verifyNotTree subshellAssignmentCheck "#!/bin/ksh\ncat foo | while read bar; do a=$bar; done\necho \"$a\""
-prop_subshellAssignmentCheck16 = verifyNotTree subshellAssignmentCheck "(set -e); echo $@"
-prop_subshellAssignmentCheck17 = verifyNotTree subshellAssignmentCheck "foo=${ { bar=$(baz); } 2>&1; }; echo $foo $bar"
-prop_subshellAssignmentCheck18 = verifyTree subshellAssignmentCheck "( exec {n}>&2; ); echo $n"
-prop_subshellAssignmentCheck19 = verifyNotTree subshellAssignmentCheck "#!/bin/bash\nshopt -s lastpipe; echo a | read -r b; echo \"$b\""
+-- | Subshell detection.
+--
+-- >>> prop $ verifyTree subshellAssignmentCheck "cat foo | while read bar; do a=$bar; done; echo \"$a\""
+-- >>> prop $ verifyNotTree subshellAssignmentCheck "while read bar; do a=$bar; done < file; echo \"$a\""
+-- >>> prop $ verifyTree subshellAssignmentCheck "( A=foo; ); rm $A"
+-- >>> prop $ verifyNotTree subshellAssignmentCheck "( A=foo; rm $A; )"
+-- >>> prop $ verifyTree subshellAssignmentCheck "cat foo | while read cow; do true; done; echo $cow;"
+-- >>> prop $ verifyTree subshellAssignmentCheck "( export lol=$(ls); ); echo $lol;"
+-- >>> prop $ verifyTree subshellAssignmentCheck "( typeset -a lol=a; ); echo $lol;"
+-- >>> prop $ verifyTree subshellAssignmentCheck "cmd | while read foo; do (( n++ )); done; echo \"$n lines\""
+-- >>> prop $ verifyTree subshellAssignmentCheck "n=3 & echo $((n++))"
+-- >>> prop $ verifyTree subshellAssignmentCheck "read n & n=foo$n"
+-- >>> prop $ verifyTree subshellAssignmentCheck "(( n <<= 3 )) & (( n |= 4 )) &"
+-- >>> prop $ verifyTree subshellAssignmentCheck "cat /etc/passwd | while read line; do let n=n+1; done\necho $n"
+-- >>> prop $ verifyTree subshellAssignmentCheck "cat /etc/passwd | while read line; do let ++n; done\necho $n"
+-- >>> prop $ verifyTree subshellAssignmentCheck "#!/bin/bash\necho foo | read bar; echo $bar"
+-- >>> prop $ verifyNotTree subshellAssignmentCheck "#!/bin/ksh93\necho foo | read bar; echo $bar"
+-- >>> prop $ verifyNotTree subshellAssignmentCheck "#!/bin/ksh\ncat foo | while read bar; do a=$bar; done\necho \"$a\""
+-- >>> prop $ verifyNotTree subshellAssignmentCheck "(set -e); echo $@"
+-- >>> prop $ verifyNotTree subshellAssignmentCheck "foo=${ { bar=$(baz); } 2>&1; }; echo $foo $bar"
+-- >>> prop $ verifyTree subshellAssignmentCheck "( exec {n}>&2; ); echo $n"
+-- >>> prop $ verifyNotTree subshellAssignmentCheck "#!/bin/bash\nshopt -s lastpipe; echo a | read -r b; echo \"$b\""
subshellAssignmentCheck params t =
let flow = variableFlow params
check = findSubshelled flow [("oops",[])] Map.empty
@@ -1610,43 +1659,44 @@ doVariableFlowAnalysis readFunc writeFunc empty flow = evalState (
writeFunc base token name values
doFlow _ = return []
----- Check whether variables could have spaces/globs
-prop_checkSpacefulness1 = verifyTree checkSpacefulness "a='cow moo'; echo $a"
-prop_checkSpacefulness2 = verifyNotTree checkSpacefulness "a='cow moo'; [[ $a ]]"
-prop_checkSpacefulness3 = verifyNotTree checkSpacefulness "a='cow*.mp3'; echo \"$a\""
-prop_checkSpacefulness4 = verifyTree checkSpacefulness "for f in *.mp3; do echo $f; done"
-prop_checkSpacefulness4a= verifyNotTree checkSpacefulness "foo=3; foo=$(echo $foo)"
-prop_checkSpacefulness5 = verifyTree checkSpacefulness "a='*'; b=$a; c=lol${b//foo/bar}; echo $c"
-prop_checkSpacefulness6 = verifyTree checkSpacefulness "a=foo$(lol); echo $a"
-prop_checkSpacefulness7 = verifyTree checkSpacefulness "a=foo\\ bar; rm $a"
-prop_checkSpacefulness8 = verifyNotTree checkSpacefulness "a=foo\\ bar; a=foo; rm $a"
-prop_checkSpacefulness10= verifyTree checkSpacefulness "rm $1"
-prop_checkSpacefulness11= verifyTree checkSpacefulness "rm ${10//foo/bar}"
-prop_checkSpacefulness12= verifyNotTree checkSpacefulness "(( $1 + 3 ))"
-prop_checkSpacefulness13= verifyNotTree checkSpacefulness "if [[ $2 -gt 14 ]]; then true; fi"
-prop_checkSpacefulness14= verifyNotTree checkSpacefulness "foo=$3 env"
-prop_checkSpacefulness15= verifyNotTree checkSpacefulness "local foo=$1"
-prop_checkSpacefulness16= verifyNotTree checkSpacefulness "declare foo=$1"
-prop_checkSpacefulness17= verifyTree checkSpacefulness "echo foo=$1"
-prop_checkSpacefulness18= verifyNotTree checkSpacefulness "$1 --flags"
-prop_checkSpacefulness19= verifyTree checkSpacefulness "echo $PWD"
-prop_checkSpacefulness20= verifyNotTree checkSpacefulness "n+='foo bar'"
-prop_checkSpacefulness21= verifyNotTree checkSpacefulness "select foo in $bar; do true; done"
-prop_checkSpacefulness22= verifyNotTree checkSpacefulness "echo $\"$1\""
-prop_checkSpacefulness23= verifyNotTree checkSpacefulness "a=(1); echo ${a[@]}"
-prop_checkSpacefulness24= verifyTree checkSpacefulness "a='a b'; cat <<< $a"
-prop_checkSpacefulness25= verifyTree checkSpacefulness "a='s/[0-9]//g'; sed $a"
-prop_checkSpacefulness26= verifyTree checkSpacefulness "a='foo bar'; echo {1,2,$a}"
-prop_checkSpacefulness27= verifyNotTree checkSpacefulness "echo ${a:+'foo'}"
-prop_checkSpacefulness28= verifyNotTree checkSpacefulness "exec {n}>&1; echo $n"
-prop_checkSpacefulness29= verifyNotTree checkSpacefulness "n=$(stuff); exec {n}>&-;"
-prop_checkSpacefulness30= verifyTree checkSpacefulness "file='foo bar'; echo foo > $file;"
-prop_checkSpacefulness31= verifyNotTree checkSpacefulness "echo \"`echo \\\"$1\\\"`\""
-prop_checkSpacefulness32= verifyNotTree checkSpacefulness "var=$1; [ -v var ]"
-prop_checkSpacefulness33= verifyTree checkSpacefulness "for file; do echo $file; done"
-prop_checkSpacefulness34= verifyTree checkSpacefulness "declare foo$n=$1"
-prop_checkSpacefulness35= verifyNotTree checkSpacefulness "echo ${1+\"$1\"}"
-prop_checkSpacefulness36= verifyNotTree checkSpacefulness "arg=$#; echo $arg"
+-- | Check whether variables could have spaces/globs.
+--
+-- >>> prop $ verifyTree checkSpacefulness "a='cow moo'; echo $a"
+-- >>> prop $ verifyNotTree checkSpacefulness "a='cow moo'; [[ $a ]]"
+-- >>> prop $ verifyNotTree checkSpacefulness "a='cow*.mp3'; echo \"$a\""
+-- >>> prop $ verifyTree checkSpacefulness "for f in *.mp3; do echo $f; done"
+-- >>> prop $ verifyNotTree checkSpacefulness "foo=3; foo=$(echo $foo)"
+-- >>> prop $ verifyTree checkSpacefulness "a='*'; b=$a; c=lol${b//foo/bar}; echo $c"
+-- >>> prop $ verifyTree checkSpacefulness "a=foo$(lol); echo $a"
+-- >>> prop $ verifyTree checkSpacefulness "a=foo\\ bar; rm $a"
+-- >>> prop $ verifyNotTree checkSpacefulness "a=foo\\ bar; a=foo; rm $a"
+-- >>> prop $ verifyTree checkSpacefulness "rm $1"
+-- >>> prop $ verifyTree checkSpacefulness "rm ${10//foo/bar}"
+-- >>> prop $ verifyNotTree checkSpacefulness "(( $1 + 3 ))"
+-- >>> prop $ verifyNotTree checkSpacefulness "if [[ $2 -gt 14 ]]; then true; fi"
+-- >>> prop $ verifyNotTree checkSpacefulness "foo=$3 env"
+-- >>> prop $ verifyNotTree checkSpacefulness "local foo=$1"
+-- >>> prop $ verifyNotTree checkSpacefulness "declare foo=$1"
+-- >>> prop $ verifyTree checkSpacefulness "echo foo=$1"
+-- >>> prop $ verifyNotTree checkSpacefulness "$1 --flags"
+-- >>> prop $ verifyTree checkSpacefulness "echo $PWD"
+-- >>> prop $ verifyNotTree checkSpacefulness "n+='foo bar'"
+-- >>> prop $ verifyNotTree checkSpacefulness "select foo in $bar; do true; done"
+-- >>> prop $ verifyNotTree checkSpacefulness "echo $\"$1\""
+-- >>> prop $ verifyNotTree checkSpacefulness "a=(1); echo ${a[@]}"
+-- >>> prop $ verifyTree checkSpacefulness "a='a b'; cat <<< $a"
+-- >>> prop $ verifyTree checkSpacefulness "a='s/[0-9]//g'; sed $a"
+-- >>> prop $ verifyTree checkSpacefulness "a='foo bar'; echo {1,2,$a}"
+-- >>> prop $ verifyNotTree checkSpacefulness "echo ${a:+'foo'}"
+-- >>> prop $ verifyNotTree checkSpacefulness "exec {n}>&1; echo $n"
+-- >>> prop $ verifyNotTree checkSpacefulness "n=$(stuff); exec {n}>&-;"
+-- >>> prop $ verifyTree checkSpacefulness "file='foo bar'; echo foo > $file;"
+-- >>> prop $ verifyNotTree checkSpacefulness "echo \"`echo \\\"$1\\\"`\""
+-- >>> prop $ verifyNotTree checkSpacefulness "var=$1; [ -v var ]"
+-- >>> prop $ verifyTree checkSpacefulness "for file; do echo $file; done"
+-- >>> prop $ verifyTree checkSpacefulness "declare foo$n=$1"
+-- >>> prop $ verifyNotTree checkSpacefulness "echo ${1+\"$1\"}"
+-- >>> prop $ verifyNotTree checkSpacefulness "arg=$#; echo $arg"
checkSpacefulness params t =
doVariableFlowAnalysis readF writeF (Map.fromList defaults) (variableFlow params)
@@ -1724,17 +1774,18 @@ checkSpacefulness params t =
&& any (`isPrefixOf` modifier) ["=", ":="]
&& isParamTo parents ":" token
-prop_checkQuotesInLiterals1 = verifyTree checkQuotesInLiterals "param='--foo=\"bar\"'; app $param"
-prop_checkQuotesInLiterals1a= verifyTree checkQuotesInLiterals "param=\"--foo='lolbar'\"; app $param"
-prop_checkQuotesInLiterals2 = verifyNotTree checkQuotesInLiterals "param='--foo=\"bar\"'; app \"$param\""
-prop_checkQuotesInLiterals3 =verifyNotTree checkQuotesInLiterals "param=('--foo='); app \"${param[@]}\""
-prop_checkQuotesInLiterals4 = verifyNotTree checkQuotesInLiterals "param=\"don't bother with this one\"; app $param"
-prop_checkQuotesInLiterals5 = verifyNotTree checkQuotesInLiterals "param=\"--foo='lolbar'\"; eval app $param"
-prop_checkQuotesInLiterals6 = verifyTree checkQuotesInLiterals "param='my\\ file'; cmd=\"rm $param\"; $cmd"
-prop_checkQuotesInLiterals6a= verifyNotTree checkQuotesInLiterals "param='my\\ file'; cmd=\"rm ${#param}\"; $cmd"
-prop_checkQuotesInLiterals7 = verifyTree checkQuotesInLiterals "param='my\\ file'; rm $param"
-prop_checkQuotesInLiterals8 = verifyTree checkQuotesInLiterals "param=\"/foo/'bar baz'/etc\"; rm $param"
-prop_checkQuotesInLiterals9 = verifyNotTree checkQuotesInLiterals "param=\"/foo/'bar baz'/etc\"; rm ${#param}"
+-- |
+-- >>> prop $ verifyTree checkQuotesInLiterals "param='--foo=\"bar\"'; app $param"
+-- >>> prop $ verifyTree checkQuotesInLiterals "param=\"--foo='lolbar'\"; app $param"
+-- >>> prop $ verifyNotTree checkQuotesInLiterals "param='--foo=\"bar\"'; app \"$param\""
+-- >>> prop $ verifyNotTree checkQuotesInLiterals "param=('--foo='); app \"${param[@]}\""
+-- >>> prop $ verifyNotTree checkQuotesInLiterals "param=\"don't bother with this one\"; app $param"
+-- >>> prop $ verifyNotTree checkQuotesInLiterals "param=\"--foo='lolbar'\"; eval app $param"
+-- >>> prop $ verifyTree checkQuotesInLiterals "param='my\\ file'; cmd=\"rm $param\"; $cmd"
+-- >>> prop $ verifyNotTree checkQuotesInLiterals "param='my\\ file'; cmd=\"rm ${#param}\"; $cmd"
+-- >>> prop $ verifyTree checkQuotesInLiterals "param='my\\ file'; rm $param"
+-- >>> prop $ verifyTree checkQuotesInLiterals "param=\"/foo/'bar baz'/etc\"; rm $param"
+-- >>> prop $ verifyNotTree checkQuotesInLiterals "param=\"/foo/'bar baz'/etc\"; rm ${#param}"
checkQuotesInLiterals params t =
doVariableFlowAnalysis readF writeF Map.empty (variableFlow params)
where
@@ -1787,14 +1838,11 @@ checkQuotesInLiterals params t =
else [])
-prop_checkFunctionsUsedExternally1 =
- verifyTree checkFunctionsUsedExternally "foo() { :; }; sudo foo"
-prop_checkFunctionsUsedExternally2 =
- verifyTree checkFunctionsUsedExternally "alias f='a'; xargs -n 1 f"
-prop_checkFunctionsUsedExternally3 =
- verifyNotTree checkFunctionsUsedExternally "f() { :; }; echo f"
-prop_checkFunctionsUsedExternally4 =
- verifyNotTree checkFunctionsUsedExternally "foo() { :; }; sudo \"foo\""
+-- |
+-- >>> prop $ verifyTree checkFunctionsUsedExternally "foo() { :; }; sudo foo"
+-- >>> prop $ verifyTree checkFunctionsUsedExternally "alias f='a'; xargs -n 1 f"
+-- >>> prop $ verifyNotTree checkFunctionsUsedExternally "f() { :; }; echo f"
+-- >>> prop $ verifyNotTree checkFunctionsUsedExternally "foo() { :; }; sudo \"foo\""
checkFunctionsUsedExternally params t =
runNodeAnalysis checkCommand params t
where
@@ -1832,47 +1880,48 @@ checkFunctionsUsedExternally params t =
info definitionId 2032 $
"Use own script or sh -c '..' to run this from " ++ cmd ++ "."
-prop_checkUnused0 = verifyNotTree checkUnusedAssignments "var=foo; echo $var"
-prop_checkUnused1 = verifyTree checkUnusedAssignments "var=foo; echo $bar"
-prop_checkUnused2 = verifyNotTree checkUnusedAssignments "var=foo; export var;"
-prop_checkUnused3 = verifyTree checkUnusedAssignments "for f in *; do echo '$f'; done"
-prop_checkUnused4 = verifyTree checkUnusedAssignments "local i=0"
-prop_checkUnused5 = verifyNotTree checkUnusedAssignments "read lol; echo $lol"
-prop_checkUnused6 = verifyNotTree checkUnusedAssignments "var=4; (( var++ ))"
-prop_checkUnused7 = verifyNotTree checkUnusedAssignments "var=2; $((var))"
-prop_checkUnused8 = verifyTree checkUnusedAssignments "var=2; var=3;"
-prop_checkUnused9 = verifyNotTree checkUnusedAssignments "read ''"
-prop_checkUnused10= verifyNotTree checkUnusedAssignments "read -p 'test: '"
-prop_checkUnused11= verifyNotTree checkUnusedAssignments "bar=5; export foo[$bar]=3"
-prop_checkUnused12= verifyNotTree checkUnusedAssignments "read foo; echo ${!foo}"
-prop_checkUnused13= verifyNotTree checkUnusedAssignments "x=(1); (( x[0] ))"
-prop_checkUnused14= verifyNotTree checkUnusedAssignments "x=(1); n=0; echo ${x[n]}"
-prop_checkUnused15= verifyNotTree checkUnusedAssignments "x=(1); n=0; (( x[n] ))"
-prop_checkUnused16= verifyNotTree checkUnusedAssignments "foo=5; declare -x foo"
-prop_checkUnused17= verifyNotTree checkUnusedAssignments "read -i 'foo' -e -p 'Input: ' bar; $bar;"
-prop_checkUnused18= verifyNotTree checkUnusedAssignments "a=1; arr=( [$a]=42 ); echo \"${arr[@]}\""
-prop_checkUnused19= verifyNotTree checkUnusedAssignments "a=1; let b=a+1; echo $b"
-prop_checkUnused20= verifyNotTree checkUnusedAssignments "a=1; PS1='$a'"
-prop_checkUnused21= verifyNotTree checkUnusedAssignments "a=1; trap 'echo $a' INT"
-prop_checkUnused22= verifyNotTree checkUnusedAssignments "a=1; [ -v a ]"
-prop_checkUnused23= verifyNotTree checkUnusedAssignments "a=1; [ -R a ]"
-prop_checkUnused24= verifyNotTree checkUnusedAssignments "mapfile -C a b; echo ${b[@]}"
-prop_checkUnused25= verifyNotTree checkUnusedAssignments "readarray foo; echo ${foo[@]}"
-prop_checkUnused26= verifyNotTree checkUnusedAssignments "declare -F foo"
-prop_checkUnused27= verifyTree checkUnusedAssignments "var=3; [ var -eq 3 ]"
-prop_checkUnused28= verifyNotTree checkUnusedAssignments "var=3; [[ var -eq 3 ]]"
-prop_checkUnused29= verifyNotTree checkUnusedAssignments "var=(a b); declare -p var"
-prop_checkUnused30= verifyTree checkUnusedAssignments "let a=1"
-prop_checkUnused31= verifyTree checkUnusedAssignments "let 'a=1'"
-prop_checkUnused32= verifyTree checkUnusedAssignments "let a=b=c; echo $a"
-prop_checkUnused33= verifyNotTree checkUnusedAssignments "a=foo; [[ foo =~ ^{$a}$ ]]"
-prop_checkUnused34= verifyNotTree checkUnusedAssignments "foo=1; (( t = foo )); echo $t"
-prop_checkUnused35= verifyNotTree checkUnusedAssignments "a=foo; b=2; echo ${a:b}"
-prop_checkUnused36= verifyNotTree checkUnusedAssignments "if [[ -v foo ]]; then true; fi"
-prop_checkUnused37= verifyNotTree checkUnusedAssignments "fd=2; exec {fd}>&-"
-prop_checkUnused38= verifyTree checkUnusedAssignments "(( a=42 ))"
-prop_checkUnused39= verifyNotTree checkUnusedAssignments "declare -x -f foo"
-prop_checkUnused40= verifyNotTree checkUnusedAssignments "arr=(1 2); num=2; echo \"${arr[@]:num}\""
+-- |
+-- >>> prop $ verifyNotTree checkUnusedAssignments "var=foo; echo $var"
+-- >>> prop $ verifyTree checkUnusedAssignments "var=foo; echo $bar"
+-- >>> prop $ verifyNotTree checkUnusedAssignments "var=foo; export var;"
+-- >>> prop $ verifyTree checkUnusedAssignments "for f in *; do echo '$f'; done"
+-- >>> prop $ verifyTree checkUnusedAssignments "local i=0"
+-- >>> prop $ verifyNotTree checkUnusedAssignments "read lol; echo $lol"
+-- >>> prop $ verifyNotTree checkUnusedAssignments "var=4; (( var++ ))"
+-- >>> prop $ verifyNotTree checkUnusedAssignments "var=2; $((var))"
+-- >>> prop $ verifyTree checkUnusedAssignments "var=2; var=3;"
+-- >>> prop $ verifyNotTree checkUnusedAssignments "read ''"
+-- >>> prop $ verifyNotTree checkUnusedAssignments "read -p 'test: '"
+-- >>> prop $ verifyNotTree checkUnusedAssignments "bar=5; export foo[$bar]=3"
+-- >>> prop $ verifyNotTree checkUnusedAssignments "read foo; echo ${!foo}"
+-- >>> prop $ verifyNotTree checkUnusedAssignments "x=(1); (( x[0] ))"
+-- >>> prop $ verifyNotTree checkUnusedAssignments "x=(1); n=0; echo ${x[n]}"
+-- >>> prop $ verifyNotTree checkUnusedAssignments "x=(1); n=0; (( x[n] ))"
+-- >>> prop $ verifyNotTree checkUnusedAssignments "foo=5; declare -x foo"
+-- >>> prop $ verifyNotTree checkUnusedAssignments "read -i 'foo' -e -p 'Input: ' bar; $bar;"
+-- >>> prop $ verifyNotTree checkUnusedAssignments "a=1; arr=( [$a]=42 ); echo \"${arr[@]}\""
+-- >>> prop $ verifyNotTree checkUnusedAssignments "a=1; let b=a+1; echo $b"
+-- >>> prop $ verifyNotTree checkUnusedAssignments "a=1; PS1='$a'"
+-- >>> prop $ verifyNotTree checkUnusedAssignments "a=1; trap 'echo $a' INT"
+-- >>> prop $ verifyNotTree checkUnusedAssignments "a=1; [ -v a ]"
+-- >>> prop $ verifyNotTree checkUnusedAssignments "a=1; [ -R a ]"
+-- >>> prop $ verifyNotTree checkUnusedAssignments "mapfile -C a b; echo ${b[@]}"
+-- >>> prop $ verifyNotTree checkUnusedAssignments "readarray foo; echo ${foo[@]}"
+-- >>> prop $ verifyNotTree checkUnusedAssignments "declare -F foo"
+-- >>> prop $ verifyTree checkUnusedAssignments "var=3; [ var -eq 3 ]"
+-- >>> prop $ verifyNotTree checkUnusedAssignments "var=3; [[ var -eq 3 ]]"
+-- >>> prop $ verifyNotTree checkUnusedAssignments "var=(a b); declare -p var"
+-- >>> prop $ verifyTree checkUnusedAssignments "let a=1"
+-- >>> prop $ verifyTree checkUnusedAssignments "let 'a=1'"
+-- >>> prop $ verifyTree checkUnusedAssignments "let a=b=c; echo $a"
+-- >>> prop $ verifyNotTree checkUnusedAssignments "a=foo; [[ foo =~ ^{$a}$ ]]"
+-- >>> prop $ verifyNotTree checkUnusedAssignments "foo=1; (( t = foo )); echo $t"
+-- >>> prop $ verifyNotTree checkUnusedAssignments "a=foo; b=2; echo ${a:b}"
+-- >>> prop $ verifyNotTree checkUnusedAssignments "if [[ -v foo ]]; then true; fi"
+-- >>> prop $ verifyNotTree checkUnusedAssignments "fd=2; exec {fd}>&-"
+-- >>> prop $ verifyTree checkUnusedAssignments "(( a=42 ))"
+-- >>> prop $ verifyNotTree checkUnusedAssignments "declare -x -f foo"
+-- >>> prop $ verifyNotTree checkUnusedAssignments "arr=(1 2); num=2; echo \"${arr[@]:num}\""
checkUnusedAssignments params t = execWriter (mapM_ warnFor unused)
where
flow = variableFlow params
@@ -1895,42 +1944,43 @@ checkUnusedAssignments params t = execWriter (mapM_ warnFor unused)
stripSuffix = takeWhile isVariableChar
defaultMap = Map.fromList $ zip internalVariables $ repeat ()
-prop_checkUnassignedReferences1 = verifyTree checkUnassignedReferences "echo $foo"
-prop_checkUnassignedReferences2 = verifyNotTree checkUnassignedReferences "foo=hello; echo $foo"
-prop_checkUnassignedReferences3 = verifyTree checkUnassignedReferences "MY_VALUE=3; echo $MYVALUE"
-prop_checkUnassignedReferences4 = verifyNotTree checkUnassignedReferences "RANDOM2=foo; echo $RANDOM"
-prop_checkUnassignedReferences5 = verifyNotTree checkUnassignedReferences "declare -A foo=([bar]=baz); echo ${foo[bar]}"
-prop_checkUnassignedReferences6 = verifyNotTree checkUnassignedReferences "foo=..; echo ${foo-bar}"
-prop_checkUnassignedReferences7 = verifyNotTree checkUnassignedReferences "getopts ':h' foo; echo $foo"
-prop_checkUnassignedReferences8 = verifyNotTree checkUnassignedReferences "let 'foo = 1'; echo $foo"
-prop_checkUnassignedReferences9 = verifyNotTree checkUnassignedReferences "echo ${foo-bar}"
-prop_checkUnassignedReferences10= verifyNotTree checkUnassignedReferences "echo ${foo:?}"
-prop_checkUnassignedReferences11= verifyNotTree checkUnassignedReferences "declare -A foo; echo \"${foo[@]}\""
-prop_checkUnassignedReferences12= verifyNotTree checkUnassignedReferences "typeset -a foo; echo \"${foo[@]}\""
-prop_checkUnassignedReferences13= verifyNotTree checkUnassignedReferences "f() { local foo; echo $foo; }"
-prop_checkUnassignedReferences14= verifyNotTree checkUnassignedReferences "foo=; echo $foo"
-prop_checkUnassignedReferences15= verifyNotTree checkUnassignedReferences "f() { true; }; export -f f"
-prop_checkUnassignedReferences16= verifyNotTree checkUnassignedReferences "declare -A foo=( [a b]=bar ); echo ${foo[a b]}"
-prop_checkUnassignedReferences17= verifyNotTree checkUnassignedReferences "USERS=foo; echo $USER"
-prop_checkUnassignedReferences18= verifyNotTree checkUnassignedReferences "FOOBAR=42; export FOOBAR="
-prop_checkUnassignedReferences19= verifyNotTree checkUnassignedReferences "readonly foo=bar; echo $foo"
-prop_checkUnassignedReferences20= verifyNotTree checkUnassignedReferences "printf -v foo bar; echo $foo"
-prop_checkUnassignedReferences21= verifyTree checkUnassignedReferences "echo ${#foo}"
-prop_checkUnassignedReferences22= verifyNotTree checkUnassignedReferences "echo ${!os*}"
-prop_checkUnassignedReferences23= verifyTree checkUnassignedReferences "declare -a foo; foo[bar]=42;"
-prop_checkUnassignedReferences24= verifyNotTree checkUnassignedReferences "declare -A foo; foo[bar]=42;"
-prop_checkUnassignedReferences25= verifyNotTree checkUnassignedReferences "declare -A foo=(); foo[bar]=42;"
-prop_checkUnassignedReferences26= verifyNotTree checkUnassignedReferences "a::b() { foo; }; readonly -f a::b"
-prop_checkUnassignedReferences27= verifyNotTree checkUnassignedReferences ": ${foo:=bar}"
-prop_checkUnassignedReferences28= verifyNotTree checkUnassignedReferences "#!/bin/ksh\necho \"${.sh.version}\"\n"
-prop_checkUnassignedReferences29= verifyNotTree checkUnassignedReferences "if [[ -v foo ]]; then echo $foo; fi"
-prop_checkUnassignedReferences30= verifyNotTree checkUnassignedReferences "if [[ -v foo[3] ]]; then echo ${foo[3]}; fi"
-prop_checkUnassignedReferences31= verifyNotTree checkUnassignedReferences "X=1; if [[ -v foo[$X+42] ]]; then echo ${foo[$X+42]}; fi"
-prop_checkUnassignedReferences32= verifyNotTree checkUnassignedReferences "if [[ -v \"foo[1]\" ]]; then echo ${foo[@]}; fi"
-prop_checkUnassignedReferences33= verifyNotTree checkUnassignedReferences "f() { local -A foo; echo \"${foo[@]}\"; }"
-prop_checkUnassignedReferences34= verifyNotTree checkUnassignedReferences "declare -A foo; (( foo[bar] ))"
-prop_checkUnassignedReferences35= verifyNotTree checkUnassignedReferences "echo ${arr[foo-bar]:?fail}"
-prop_checkUnassignedReferences36= verifyNotTree checkUnassignedReferences "read -a foo -r <<<\"foo bar\"; echo \"$foo\""
+-- |
+-- >>> prop $ verifyTree checkUnassignedReferences "echo $foo"
+-- >>> prop $ verifyNotTree checkUnassignedReferences "foo=hello; echo $foo"
+-- >>> prop $ verifyTree checkUnassignedReferences "MY_VALUE=3; echo $MYVALUE"
+-- >>> prop $ verifyNotTree checkUnassignedReferences "RANDOM2=foo; echo $RANDOM"
+-- >>> prop $ verifyNotTree checkUnassignedReferences "declare -A foo=([bar]=baz); echo ${foo[bar]}"
+-- >>> prop $ verifyNotTree checkUnassignedReferences "foo=..; echo ${foo-bar}"
+-- >>> prop $ verifyNotTree checkUnassignedReferences "getopts ':h' foo; echo $foo"
+-- >>> prop $ verifyNotTree checkUnassignedReferences "let 'foo = 1'; echo $foo"
+-- >>> prop $ verifyNotTree checkUnassignedReferences "echo ${foo-bar}"
+-- >>> prop $ verifyNotTree checkUnassignedReferences "echo ${foo:?}"
+-- >>> prop $ verifyNotTree checkUnassignedReferences "declare -A foo; echo \"${foo[@]}\""
+-- >>> prop $ verifyNotTree checkUnassignedReferences "typeset -a foo; echo \"${foo[@]}\""
+-- >>> prop $ verifyNotTree checkUnassignedReferences "f() { local foo; echo $foo; }"
+-- >>> prop $ verifyNotTree checkUnassignedReferences "foo=; echo $foo"
+-- >>> prop $ verifyNotTree checkUnassignedReferences "f() { true; }; export -f f"
+-- >>> prop $ verifyNotTree checkUnassignedReferences "declare -A foo=( [a b]=bar ); echo ${foo[a b]}"
+-- >>> prop $ verifyNotTree checkUnassignedReferences "USERS=foo; echo $USER"
+-- >>> prop $ verifyNotTree checkUnassignedReferences "FOOBAR=42; export FOOBAR="
+-- >>> prop $ verifyNotTree checkUnassignedReferences "readonly foo=bar; echo $foo"
+-- >>> prop $ verifyNotTree checkUnassignedReferences "printf -v foo bar; echo $foo"
+-- >>> prop $ verifyTree checkUnassignedReferences "echo ${#foo}"
+-- >>> prop $ verifyNotTree checkUnassignedReferences "echo ${!os*}"
+-- >>> prop $ verifyTree checkUnassignedReferences "declare -a foo; foo[bar]=42;"
+-- >>> prop $ verifyNotTree checkUnassignedReferences "declare -A foo; foo[bar]=42;"
+-- >>> prop $ verifyNotTree checkUnassignedReferences "declare -A foo=(); foo[bar]=42;"
+-- >>> prop $ verifyNotTree checkUnassignedReferences "a::b() { foo; }; readonly -f a::b"
+-- >>> prop $ verifyNotTree checkUnassignedReferences ": ${foo:=bar}"
+-- >>> prop $ verifyNotTree checkUnassignedReferences "#!/bin/ksh\necho \"${.sh.version}\"\n"
+-- >>> prop $ verifyNotTree checkUnassignedReferences "if [[ -v foo ]]; then echo $foo; fi"
+-- >>> prop $ verifyNotTree checkUnassignedReferences "if [[ -v foo[3] ]]; then echo ${foo[3]}; fi"
+-- >>> prop $ verifyNotTree checkUnassignedReferences "X=1; if [[ -v foo[$X+42] ]]; then echo ${foo[$X+42]}; fi"
+-- >>> prop $ verifyNotTree checkUnassignedReferences "if [[ -v \"foo[1]\" ]]; then echo ${foo[@]}; fi"
+-- >>> prop $ verifyNotTree checkUnassignedReferences "f() { local -A foo; echo \"${foo[@]}\"; }"
+-- >>> prop $ verifyNotTree checkUnassignedReferences "declare -A foo; (( foo[bar] ))"
+-- >>> prop $ verifyNotTree checkUnassignedReferences "echo ${arr[foo-bar]:?fail}"
+-- >>> prop $ verifyNotTree checkUnassignedReferences "read -a foo -r <<<\"foo bar\"; echo \"$foo\""
checkUnassignedReferences params t = warnings
where
(readMap, writeMap) = execState (mapM tally $ variableFlow params) (Map.empty, Map.empty)
@@ -2004,10 +2054,11 @@ checkUnassignedReferences params t = warnings
else dist var candidate
-prop_checkGlobsAsOptions1 = verify checkGlobsAsOptions "rm *.txt"
-prop_checkGlobsAsOptions2 = verify checkGlobsAsOptions "ls ??.*"
-prop_checkGlobsAsOptions3 = verifyNot checkGlobsAsOptions "rm -- *.txt"
-prop_checkGlobsAsOptions4 = verifyNot checkGlobsAsOptions "*.txt"
+-- |
+-- >>> prop $ verify checkGlobsAsOptions "rm *.txt"
+-- >>> prop $ verify checkGlobsAsOptions "ls ??.*"
+-- >>> prop $ verifyNot checkGlobsAsOptions "rm -- *.txt"
+-- >>> prop $ verifyNot checkGlobsAsOptions "*.txt"
checkGlobsAsOptions _ (T_SimpleCommand _ _ args) =
mapM_ check $ takeWhile (not . isEndOfArgs) (drop 1 args)
where
@@ -2025,14 +2076,15 @@ checkGlobsAsOptions _ (T_SimpleCommand _ _ args) =
checkGlobsAsOptions _ _ = return ()
-prop_checkWhileReadPitfalls1 = verify checkWhileReadPitfalls "while read foo; do ssh $foo uptime; done < file"
-prop_checkWhileReadPitfalls2 = verifyNot checkWhileReadPitfalls "while read -u 3 foo; do ssh $foo uptime; done 3< file"
-prop_checkWhileReadPitfalls3 = verifyNot checkWhileReadPitfalls "while true; do ssh host uptime; done"
-prop_checkWhileReadPitfalls4 = verifyNot checkWhileReadPitfalls "while read foo; do ssh $foo hostname < /dev/null; done"
-prop_checkWhileReadPitfalls5 = verifyNot checkWhileReadPitfalls "while read foo; do echo ls | ssh $foo; done"
-prop_checkWhileReadPitfalls6 = verifyNot checkWhileReadPitfalls "while read foo <&3; do ssh $foo; done 3< foo"
-prop_checkWhileReadPitfalls7 = verify checkWhileReadPitfalls "while read foo; do if true; then ssh $foo uptime; fi; done < file"
-prop_checkWhileReadPitfalls8 = verifyNot checkWhileReadPitfalls "while read foo; do ssh -n $foo uptime; done < file"
+-- |
+-- >>> prop $ verify checkWhileReadPitfalls "while read foo; do ssh $foo uptime; done < file"
+-- >>> prop $ verifyNot checkWhileReadPitfalls "while read -u 3 foo; do ssh $foo uptime; done 3< file"
+-- >>> prop $ verifyNot checkWhileReadPitfalls "while true; do ssh host uptime; done"
+-- >>> prop $ verifyNot checkWhileReadPitfalls "while read foo; do ssh $foo hostname < /dev/null; done"
+-- >>> prop $ verifyNot checkWhileReadPitfalls "while read foo; do echo ls | ssh $foo; done"
+-- >>> prop $ verifyNot checkWhileReadPitfalls "while read foo <&3; do ssh $foo; done 3< foo"
+-- >>> prop $ verify checkWhileReadPitfalls "while read foo; do if true; then ssh $foo uptime; fi; done < file"
+-- >>> prop $ verifyNot checkWhileReadPitfalls "while read foo; do ssh -n $foo uptime; done < file"
checkWhileReadPitfalls _ (T_WhileExpression id [command] contents)
| isStdinReadCommand command =
@@ -2073,8 +2125,9 @@ checkWhileReadPitfalls _ (T_WhileExpression id [command] contents)
checkWhileReadPitfalls _ _ = return ()
-prop_checkPrefixAssign1 = verify checkPrefixAssignmentReference "var=foo echo $var"
-prop_checkPrefixAssign2 = verifyNot checkPrefixAssignmentReference "var=$(echo $var) cmd"
+-- |
+-- >>> prop $ verify checkPrefixAssignmentReference "var=foo echo $var"
+-- >>> prop $ verifyNot checkPrefixAssignmentReference "var=$(echo $var) cmd"
checkPrefixAssignmentReference params t@(T_DollarBraced id value) =
check path
where
@@ -2095,11 +2148,12 @@ checkPrefixAssignmentReference params t@(T_DollarBraced id value) =
checkPrefixAssignmentReference _ _ = return ()
-prop_checkCharRangeGlob1 = verify checkCharRangeGlob "ls *[:digit:].jpg"
-prop_checkCharRangeGlob2 = verifyNot checkCharRangeGlob "ls *[[:digit:]].jpg"
-prop_checkCharRangeGlob3 = verify checkCharRangeGlob "ls [10-15]"
-prop_checkCharRangeGlob4 = verifyNot checkCharRangeGlob "ls [a-zA-Z]"
-prop_checkCharRangeGlob5 = verifyNot checkCharRangeGlob "tr -d [a-zA-Z]" -- tr has 2060
+-- |
+-- >>> prop $ verify checkCharRangeGlob "ls *[:digit:].jpg"
+-- >>> prop $ verifyNot checkCharRangeGlob "ls *[[:digit:]].jpg"
+-- >>> prop $ verify checkCharRangeGlob "ls [10-15]"
+-- >>> prop $ verifyNot checkCharRangeGlob "ls [a-zA-Z]"
+-- >>> prop $ verifyNot checkCharRangeGlob "tr -d [a-zA-Z]" -- tr has 2060
checkCharRangeGlob p t@(T_Glob id str) |
isCharClass str && not (isParamTo (parentMap p) "tr" t) =
if ":" `isPrefixOf` contents
@@ -2116,12 +2170,12 @@ checkCharRangeGlob p t@(T_Glob id str) |
checkCharRangeGlob _ _ = return ()
-
-prop_checkCdAndBack1 = verify checkCdAndBack "for f in *; do cd $f; git pull; cd ..; done"
-prop_checkCdAndBack2 = verifyNot checkCdAndBack "for f in *; do cd $f || continue; git pull; cd ..; done"
-prop_checkCdAndBack3 = verifyNot checkCdAndBack "while [[ $PWD != / ]]; do cd ..; done"
-prop_checkCdAndBack4 = verify checkCdAndBack "cd $tmp; foo; cd -"
-prop_checkCdAndBack5 = verifyNot checkCdAndBack "cd ..; foo; cd .."
+-- |
+-- >>> prop $ verify checkCdAndBack "for f in *; do cd $f; git pull; cd ..; done"
+-- >>> prop $ verifyNot checkCdAndBack "for f in *; do cd $f || continue; git pull; cd ..; done"
+-- >>> prop $ verifyNot checkCdAndBack "while [[ $PWD != / ]]; do cd ..; done"
+-- >>> prop $ verify checkCdAndBack "cd $tmp; foo; cd -"
+-- >>> prop $ verifyNot checkCdAndBack "cd ..; foo; cd .."
checkCdAndBack params = doLists
where
shell = shellType params
@@ -2161,13 +2215,14 @@ checkCdAndBack params = doLists
message = "Use a ( subshell ) to avoid having to cd back."
-prop_checkLoopKeywordScope1 = verify checkLoopKeywordScope "continue 2"
-prop_checkLoopKeywordScope2 = verify checkLoopKeywordScope "for f; do ( break; ); done"
-prop_checkLoopKeywordScope3 = verify checkLoopKeywordScope "if true; then continue; fi"
-prop_checkLoopKeywordScope4 = verifyNot checkLoopKeywordScope "while true; do break; done"
-prop_checkLoopKeywordScope5 = verify checkLoopKeywordScope "if true; then break; fi"
-prop_checkLoopKeywordScope6 = verify checkLoopKeywordScope "while true; do true | { break; }; done"
-prop_checkLoopKeywordScope7 = verifyNot checkLoopKeywordScope "#!/bin/ksh\nwhile true; do true | { break; }; done"
+-- |
+-- >>> prop $ verify checkLoopKeywordScope "continue 2"
+-- >>> prop $ verify checkLoopKeywordScope "for f; do ( break; ); done"
+-- >>> prop $ verify checkLoopKeywordScope "if true; then continue; fi"
+-- >>> prop $ verifyNot checkLoopKeywordScope "while true; do break; done"
+-- >>> prop $ verify checkLoopKeywordScope "if true; then break; fi"
+-- >>> prop $ verify checkLoopKeywordScope "while true; do true | { break; }; done"
+-- >>> prop $ verifyNot checkLoopKeywordScope "#!/bin/ksh\nwhile true; do true | { break; }; done"
checkLoopKeywordScope params t |
name `elem` map Just ["continue", "break"] =
if not $ any isLoop path
@@ -2189,9 +2244,10 @@ checkLoopKeywordScope params t |
checkLoopKeywordScope _ _ = return ()
-prop_checkFunctionDeclarations1 = verify checkFunctionDeclarations "#!/bin/ksh\nfunction foo() { command foo --lol \"$@\"; }"
-prop_checkFunctionDeclarations2 = verify checkFunctionDeclarations "#!/bin/dash\nfunction foo { lol; }"
-prop_checkFunctionDeclarations3 = verifyNot checkFunctionDeclarations "foo() { echo bar; }"
+-- |
+-- >>> prop $ verify checkFunctionDeclarations "#!/bin/ksh\nfunction foo() { command foo --lol \"$@\"; }"
+-- >>> prop $ verify checkFunctionDeclarations "#!/bin/dash\nfunction foo { lol; }"
+-- >>> prop $ verifyNot checkFunctionDeclarations "foo() { echo bar; }"
checkFunctionDeclarations params
(T_Function id (FunctionKeyword hasKeyword) (FunctionParentheses hasParens) _ _) =
case shellType params of
@@ -2212,8 +2268,9 @@ checkFunctionDeclarations _ _ = return ()
-prop_checkStderrPipe1 = verify checkStderrPipe "#!/bin/ksh\nfoo |& bar"
-prop_checkStderrPipe2 = verifyNot checkStderrPipe "#!/bin/bash\nfoo |& bar"
+-- |
+-- >>> prop $ verify checkStderrPipe "#!/bin/ksh\nfoo |& bar"
+-- >>> prop $ verifyNot checkStderrPipe "#!/bin/bash\nfoo |& bar"
checkStderrPipe params =
case shellType params of
Ksh -> match
@@ -2223,18 +2280,19 @@ checkStderrPipe params =
err id 2118 "Ksh does not support |&. Use 2>&1 |."
match _ = return ()
-prop_checkUnpassedInFunctions1 = verifyTree checkUnpassedInFunctions "foo() { echo $1; }; foo"
-prop_checkUnpassedInFunctions2 = verifyNotTree checkUnpassedInFunctions "foo() { echo $1; };"
-prop_checkUnpassedInFunctions3 = verifyNotTree checkUnpassedInFunctions "foo() { echo $lol; }; foo"
-prop_checkUnpassedInFunctions4 = verifyNotTree checkUnpassedInFunctions "foo() { echo $0; }; foo"
-prop_checkUnpassedInFunctions5 = verifyNotTree checkUnpassedInFunctions "foo() { echo $1; }; foo 'lol'; foo"
-prop_checkUnpassedInFunctions6 = verifyNotTree checkUnpassedInFunctions "foo() { set -- *; echo $1; }; foo"
-prop_checkUnpassedInFunctions7 = verifyTree checkUnpassedInFunctions "foo() { echo $1; }; foo; foo;"
-prop_checkUnpassedInFunctions8 = verifyNotTree checkUnpassedInFunctions "foo() { echo $((1)); }; foo;"
-prop_checkUnpassedInFunctions9 = verifyNotTree checkUnpassedInFunctions "foo() { echo $(($b)); }; foo;"
-prop_checkUnpassedInFunctions10= verifyNotTree checkUnpassedInFunctions "foo() { echo $!; }; foo;"
-prop_checkUnpassedInFunctions11= verifyNotTree checkUnpassedInFunctions "foo() { bar() { echo $1; }; bar baz; }; foo;"
-prop_checkUnpassedInFunctions12= verifyNotTree checkUnpassedInFunctions "foo() { echo ${!var*}; }; foo;"
+-- |
+-- >>> prop $ verifyTree checkUnpassedInFunctions "foo() { echo $1; }; foo"
+-- >>> prop $ verifyNotTree checkUnpassedInFunctions "foo() { echo $1; };"
+-- >>> prop $ verifyNotTree checkUnpassedInFunctions "foo() { echo $lol; }; foo"
+-- >>> prop $ verifyNotTree checkUnpassedInFunctions "foo() { echo $0; }; foo"
+-- >>> prop $ verifyNotTree checkUnpassedInFunctions "foo() { echo $1; }; foo 'lol'; foo"
+-- >>> prop $ verifyNotTree checkUnpassedInFunctions "foo() { set -- *; echo $1; }; foo"
+-- >>> prop $ verifyTree checkUnpassedInFunctions "foo() { echo $1; }; foo; foo;"
+-- >>> prop $ verifyNotTree checkUnpassedInFunctions "foo() { echo $((1)); }; foo;"
+-- >>> prop $ verifyNotTree checkUnpassedInFunctions "foo() { echo $(($b)); }; foo;"
+-- >>> prop $ verifyNotTree checkUnpassedInFunctions "foo() { echo $!; }; foo;"
+-- >>> prop $ verifyNotTree checkUnpassedInFunctions "foo() { bar() { echo $1; }; bar baz; }; foo;"
+-- >>> prop $ verifyNotTree checkUnpassedInFunctions "foo() { echo ${!var*}; }; foo;"
checkUnpassedInFunctions params root =
execWriter $ mapM_ warnForGroup referenceGroups
where
@@ -2298,14 +2356,15 @@ checkUnpassedInFunctions params root =
name ++ " references arguments, but none are ever passed."
-prop_checkOverridingPath1 = verify checkOverridingPath "PATH=\"$var/$foo\""
-prop_checkOverridingPath2 = verify checkOverridingPath "PATH=\"mydir\""
-prop_checkOverridingPath3 = verify checkOverridingPath "PATH=/cow/foo"
-prop_checkOverridingPath4 = verifyNot checkOverridingPath "PATH=/cow/foo/bin"
-prop_checkOverridingPath5 = verifyNot checkOverridingPath "PATH='/bin:/sbin'"
-prop_checkOverridingPath6 = verifyNot checkOverridingPath "PATH=\"$var/$foo\" cmd"
-prop_checkOverridingPath7 = verifyNot checkOverridingPath "PATH=$OLDPATH"
-prop_checkOverridingPath8 = verifyNot checkOverridingPath "PATH=$PATH:/stuff"
+-- |
+-- >>> prop $ verify checkOverridingPath "PATH=\"$var/$foo\""
+-- >>> prop $ verify checkOverridingPath "PATH=\"mydir\""
+-- >>> prop $ verify checkOverridingPath "PATH=/cow/foo"
+-- >>> prop $ verifyNot checkOverridingPath "PATH=/cow/foo/bin"
+-- >>> prop $ verifyNot checkOverridingPath "PATH='/bin:/sbin'"
+-- >>> prop $ verifyNot checkOverridingPath "PATH=\"$var/$foo\" cmd"
+-- >>> prop $ verifyNot checkOverridingPath "PATH=$OLDPATH"
+-- >>> prop $ verifyNot checkOverridingPath "PATH=$PATH:/stuff"
checkOverridingPath _ (T_SimpleCommand _ vars []) =
mapM_ checkVar vars
where
@@ -2318,9 +2377,10 @@ checkOverridingPath _ (T_SimpleCommand _ vars []) =
notify id = warn id 2123 "PATH is the shell search path. Use another name."
checkOverridingPath _ _ = return ()
-prop_checkTildeInPath1 = verify checkTildeInPath "PATH=\"$PATH:~/bin\""
-prop_checkTildeInPath2 = verify checkTildeInPath "PATH='~foo/bin'"
-prop_checkTildeInPath3 = verifyNot checkTildeInPath "PATH=~/bin"
+-- |
+-- >>> prop $ verify checkTildeInPath "PATH=\"$PATH:~/bin\""
+-- >>> prop $ verify checkTildeInPath "PATH='~foo/bin'"
+-- >>> prop $ verifyNot checkTildeInPath "PATH=~/bin"
checkTildeInPath _ (T_SimpleCommand _ vars _) =
mapM_ checkVar vars
where
@@ -2335,9 +2395,10 @@ checkTildeInPath _ (T_SimpleCommand _ vars _) =
isQuoted _ = False
checkTildeInPath _ _ = return ()
-prop_checkUnsupported3 = verify checkUnsupported "#!/bin/sh\ncase foo in bar) baz ;& esac"
-prop_checkUnsupported4 = verify checkUnsupported "#!/bin/ksh\ncase foo in bar) baz ;;& esac"
-prop_checkUnsupported5 = verify checkUnsupported "#!/bin/bash\necho \"${ ls; }\""
+-- |
+-- >>> prop $ verify checkUnsupported "#!/bin/sh\ncase foo in bar) baz ;& esac"
+-- >>> prop $ verify checkUnsupported "#!/bin/ksh\ncase foo in bar) baz ;;& esac"
+-- >>> prop $ verify checkUnsupported "#!/bin/bash\necho \"${ ls; }\""
checkUnsupported params t =
when (not (null support) && (shellType params `notElem` support)) $
report name
@@ -2361,9 +2422,10 @@ shellSupport t =
groupWith f = groupBy ((==) `on` f)
-prop_checkMultipleAppends1 = verify checkMultipleAppends "foo >> file; bar >> file; baz >> file;"
-prop_checkMultipleAppends2 = verify checkMultipleAppends "foo >> file; bar | grep f >> file; baz >> file;"
-prop_checkMultipleAppends3 = verifyNot checkMultipleAppends "foo < file; bar < file; baz < file;"
+-- |
+-- >>> prop $ verify checkMultipleAppends "foo >> file; bar >> file; baz >> file;"
+-- >>> prop $ verify checkMultipleAppends "foo >> file; bar | grep f >> file; baz >> file;"
+-- >>> prop $ verifyNot checkMultipleAppends "foo < file; bar < file; baz < file;"
checkMultipleAppends params t =
mapM_ checkList $ getCommandSequences t
where
@@ -2383,8 +2445,9 @@ checkMultipleAppends params t =
getAppend _ = Nothing
-prop_checkSuspiciousIFS1 = verify checkSuspiciousIFS "IFS=\"\\n\""
-prop_checkSuspiciousIFS2 = verifyNot checkSuspiciousIFS "IFS=$'\\t'"
+-- |
+-- >>> prop $ verify checkSuspiciousIFS "IFS=\"\\n\""
+-- >>> prop $ verifyNot checkSuspiciousIFS "IFS=$'\\t'"
checkSuspiciousIFS params (T_Assignment id Assign "IFS" [] value) =
potentially $ do
str <- getLiteralString value
@@ -2403,12 +2466,13 @@ checkSuspiciousIFS params (T_Assignment id Assign "IFS" [] value) =
checkSuspiciousIFS _ _ = return ()
-prop_checkGrepQ1= verify checkShouldUseGrepQ "[[ $(foo | grep bar) ]]"
-prop_checkGrepQ2= verify checkShouldUseGrepQ "[ -z $(fgrep lol) ]"
-prop_checkGrepQ3= verify checkShouldUseGrepQ "[ -n \"$(foo | zgrep lol)\" ]"
-prop_checkGrepQ4= verifyNot checkShouldUseGrepQ "[ -z $(grep bar | cmd) ]"
-prop_checkGrepQ5= verifyNot checkShouldUseGrepQ "rm $(ls | grep file)"
-prop_checkGrepQ6= verifyNot checkShouldUseGrepQ "[[ -n $(pgrep foo) ]]"
+-- |
+-- >>> prop $ verify checkShouldUseGrepQ "[[ $(foo | grep bar) ]]"
+-- >>> prop $ verify checkShouldUseGrepQ "[ -z $(fgrep lol) ]"
+-- >>> prop $ verify checkShouldUseGrepQ "[ -n \"$(foo | zgrep lol)\" ]"
+-- >>> prop $ verifyNot checkShouldUseGrepQ "[ -z $(grep bar | cmd) ]"
+-- >>> prop $ verifyNot checkShouldUseGrepQ "rm $(ls | grep file)"
+-- >>> prop $ verifyNot checkShouldUseGrepQ "[[ -n $(pgrep foo) ]]"
checkShouldUseGrepQ params t =
potentially $ case t of
TC_Nullary id _ token -> check id True token
@@ -2439,22 +2503,23 @@ checkShouldUseGrepQ params t =
_ -> fail "unknown"
isGrep = (`elem` ["grep", "egrep", "fgrep", "zgrep"])
-prop_checkTestArgumentSplitting1 = verify checkTestArgumentSplitting "[ -e *.mp3 ]"
-prop_checkTestArgumentSplitting2 = verifyNot checkTestArgumentSplitting "[[ $a == *b* ]]"
-prop_checkTestArgumentSplitting3 = verify checkTestArgumentSplitting "[[ *.png == '' ]]"
-prop_checkTestArgumentSplitting4 = verify checkTestArgumentSplitting "[[ foo == f{o,oo,ooo} ]]"
-prop_checkTestArgumentSplitting5 = verify checkTestArgumentSplitting "[[ $@ ]]"
-prop_checkTestArgumentSplitting6 = verify checkTestArgumentSplitting "[ -e $@ ]"
-prop_checkTestArgumentSplitting7 = verify checkTestArgumentSplitting "[ $@ == $@ ]"
-prop_checkTestArgumentSplitting8 = verify checkTestArgumentSplitting "[[ $@ = $@ ]]"
-prop_checkTestArgumentSplitting9 = verifyNot checkTestArgumentSplitting "[[ foo =~ bar{1,2} ]]"
-prop_checkTestArgumentSplitting10 = verifyNot checkTestArgumentSplitting "[ \"$@\" ]"
-prop_checkTestArgumentSplitting11 = verify checkTestArgumentSplitting "[[ \"$@\" ]]"
-prop_checkTestArgumentSplitting12 = verify checkTestArgumentSplitting "[ *.png ]"
-prop_checkTestArgumentSplitting13 = verify checkTestArgumentSplitting "[ \"$@\" == \"\" ]"
-prop_checkTestArgumentSplitting14 = verify checkTestArgumentSplitting "[[ \"$@\" == \"\" ]]"
-prop_checkTestArgumentSplitting15 = verifyNot checkTestArgumentSplitting "[[ \"$*\" == \"\" ]]"
-prop_checkTestArgumentSplitting16 = verifyNot checkTestArgumentSplitting "[[ -v foo[123] ]]"
+-- |
+-- >>> prop $ verify checkTestArgumentSplitting "[ -e *.mp3 ]"
+-- >>> prop $ verifyNot checkTestArgumentSplitting "[[ $a == *b* ]]"
+-- >>> prop $ verify checkTestArgumentSplitting "[[ *.png == '' ]]"
+-- >>> prop $ verify checkTestArgumentSplitting "[[ foo == f{o,oo,ooo} ]]"
+-- >>> prop $ verify checkTestArgumentSplitting "[[ $@ ]]"
+-- >>> prop $ verify checkTestArgumentSplitting "[ -e $@ ]"
+-- >>> prop $ verify checkTestArgumentSplitting "[ $@ == $@ ]"
+-- >>> prop $ verify checkTestArgumentSplitting "[[ $@ = $@ ]]"
+-- >>> prop $ verifyNot checkTestArgumentSplitting "[[ foo =~ bar{1,2} ]]"
+-- >>> prop $ verifyNot checkTestArgumentSplitting "[ \"$@\" ]"
+-- >>> prop $ verify checkTestArgumentSplitting "[[ \"$@\" ]]"
+-- >>> prop $ verify checkTestArgumentSplitting "[ *.png ]"
+-- >>> prop $ verify checkTestArgumentSplitting "[ \"$@\" == \"\" ]"
+-- >>> prop $ verify checkTestArgumentSplitting "[[ \"$@\" == \"\" ]]"
+-- >>> prop $ verifyNot checkTestArgumentSplitting "[[ \"$*\" == \"\" ]]"
+-- >>> prop $ verifyNot checkTestArgumentSplitting "[[ -v foo[123] ]]"
checkTestArgumentSplitting :: Parameters -> Token -> Writer [TokenComment] ()
checkTestArgumentSplitting _ t =
case t of
@@ -2509,11 +2574,12 @@ checkTestArgumentSplitting _ t =
else err (getId token) 2203 "Globs are ignored in [[ ]] except right of =/!=. Use a loop."
-prop_checkMaskedReturns1 = verify checkMaskedReturns "f() { local a=$(false); }"
-prop_checkMaskedReturns2 = verify checkMaskedReturns "declare a=$(false)"
-prop_checkMaskedReturns3 = verify checkMaskedReturns "declare a=\"`false`\""
-prop_checkMaskedReturns4 = verifyNot checkMaskedReturns "declare a; a=$(false)"
-prop_checkMaskedReturns5 = verifyNot checkMaskedReturns "f() { local -r a=$(false); }"
+-- |
+-- >>> prop $ verify checkMaskedReturns "f() { local a=$(false); }"
+-- >>> prop $ verify checkMaskedReturns "declare a=$(false)"
+-- >>> prop $ verify checkMaskedReturns "declare a=\"`false`\""
+-- >>> prop $ verifyNot checkMaskedReturns "declare a; a=$(false)"
+-- >>> prop $ verifyNot checkMaskedReturns "f() { local -r a=$(false); }"
checkMaskedReturns _ t@(T_SimpleCommand id _ (cmd:rest)) = potentially $ do
name <- getCommandName t
guard $ name `elem` ["declare", "export"]
@@ -2531,40 +2597,42 @@ checkMaskedReturns _ t@(T_SimpleCommand id _ (cmd:rest)) = potentially $ do
checkMaskedReturns _ _ = return ()
-prop_checkReadWithoutR1 = verify checkReadWithoutR "read -a foo"
-prop_checkReadWithoutR2 = verifyNot checkReadWithoutR "read -ar foo"
+-- |
+-- >>> prop $ verify checkReadWithoutR "read -a foo"
+-- >>> prop $ verifyNot checkReadWithoutR "read -ar foo"
checkReadWithoutR _ t@T_SimpleCommand {} | t `isUnqualifiedCommand` "read" =
unless ("r" `elem` map snd (getAllFlags t)) $
info (getId $ getCommandTokenOrThis t) 2162 "read without -r will mangle backslashes."
checkReadWithoutR _ _ = return ()
-prop_checkUncheckedCd1 = verifyTree checkUncheckedCdPushdPopd "cd ~/src; rm -r foo"
-prop_checkUncheckedCd2 = verifyNotTree checkUncheckedCdPushdPopd "cd ~/src || exit; rm -r foo"
-prop_checkUncheckedCd3 = verifyNotTree checkUncheckedCdPushdPopd "set -e; cd ~/src; rm -r foo"
-prop_checkUncheckedCd4 = verifyNotTree checkUncheckedCdPushdPopd "if cd foo; then rm foo; fi"
-prop_checkUncheckedCd5 = verifyTree checkUncheckedCdPushdPopd "if true; then cd foo; fi"
-prop_checkUncheckedCd6 = verifyNotTree checkUncheckedCdPushdPopd "cd .."
-prop_checkUncheckedCd7 = verifyNotTree checkUncheckedCdPushdPopd "#!/bin/bash -e\ncd foo\nrm bar"
-prop_checkUncheckedCd8 = verifyNotTree checkUncheckedCdPushdPopd "set -o errexit; cd foo; rm bar"
-prop_checkUncheckedCd9 = verifyTree checkUncheckedCdPushdPopd "builtin cd ~/src; rm -r foo"
-prop_checkUncheckedPushd1 = verifyTree checkUncheckedCdPushdPopd "pushd ~/src; rm -r foo"
-prop_checkUncheckedPushd2 = verifyNotTree checkUncheckedCdPushdPopd "pushd ~/src || exit; rm -r foo"
-prop_checkUncheckedPushd3 = verifyNotTree checkUncheckedCdPushdPopd "set -e; pushd ~/src; rm -r foo"
-prop_checkUncheckedPushd4 = verifyNotTree checkUncheckedCdPushdPopd "if pushd foo; then rm foo; fi"
-prop_checkUncheckedPushd5 = verifyTree checkUncheckedCdPushdPopd "if true; then pushd foo; fi"
-prop_checkUncheckedPushd6 = verifyNotTree checkUncheckedCdPushdPopd "pushd .."
-prop_checkUncheckedPushd7 = verifyNotTree checkUncheckedCdPushdPopd "#!/bin/bash -e\npushd foo\nrm bar"
-prop_checkUncheckedPushd8 = verifyNotTree checkUncheckedCdPushdPopd "set -o errexit; pushd foo; rm bar"
-prop_checkUncheckedPushd9 = verifyNotTree checkUncheckedCdPushdPopd "pushd -n foo"
-prop_checkUncheckedPopd1 = verifyTree checkUncheckedCdPushdPopd "popd; rm -r foo"
-prop_checkUncheckedPopd2 = verifyNotTree checkUncheckedCdPushdPopd "popd || exit; rm -r foo"
-prop_checkUncheckedPopd3 = verifyNotTree checkUncheckedCdPushdPopd "set -e; popd; rm -r foo"
-prop_checkUncheckedPopd4 = verifyNotTree checkUncheckedCdPushdPopd "if popd; then rm foo; fi"
-prop_checkUncheckedPopd5 = verifyTree checkUncheckedCdPushdPopd "if true; then popd; fi"
-prop_checkUncheckedPopd6 = verifyTree checkUncheckedCdPushdPopd "popd"
-prop_checkUncheckedPopd7 = verifyNotTree checkUncheckedCdPushdPopd "#!/bin/bash -e\npopd\nrm bar"
-prop_checkUncheckedPopd8 = verifyNotTree checkUncheckedCdPushdPopd "set -o errexit; popd; rm bar"
-prop_checkUncheckedPopd9 = verifyNotTree checkUncheckedCdPushdPopd "popd -n foo"
+-- |
+-- >>> prop $ verifyTree checkUncheckedCdPushdPopd "cd ~/src; rm -r foo"
+-- >>> prop $ verifyNotTree checkUncheckedCdPushdPopd "cd ~/src || exit; rm -r foo"
+-- >>> prop $ verifyNotTree checkUncheckedCdPushdPopd "set -e; cd ~/src; rm -r foo"
+-- >>> prop $ verifyNotTree checkUncheckedCdPushdPopd "if cd foo; then rm foo; fi"
+-- >>> prop $ verifyTree checkUncheckedCdPushdPopd "if true; then cd foo; fi"
+-- >>> prop $ verifyNotTree checkUncheckedCdPushdPopd "cd .."
+-- >>> prop $ verifyNotTree checkUncheckedCdPushdPopd "#!/bin/bash -e\ncd foo\nrm bar"
+-- >>> prop $ verifyNotTree checkUncheckedCdPushdPopd "set -o errexit; cd foo; rm bar"
+-- >>> prop $ verifyTree checkUncheckedCdPushdPopd "builtin cd ~/src; rm -r foo"
+-- >>> prop $ verifyTree checkUncheckedCdPushdPopd "pushd ~/src; rm -r foo"
+-- >>> prop $ verifyNotTree checkUncheckedCdPushdPopd "pushd ~/src || exit; rm -r foo"
+-- >>> prop $ verifyNotTree checkUncheckedCdPushdPopd "set -e; pushd ~/src; rm -r foo"
+-- >>> prop $ verifyNotTree checkUncheckedCdPushdPopd "if pushd foo; then rm foo; fi"
+-- >>> prop $ verifyTree checkUncheckedCdPushdPopd "if true; then pushd foo; fi"
+-- >>> prop $ verifyNotTree checkUncheckedCdPushdPopd "pushd .."
+-- >>> prop $ verifyNotTree checkUncheckedCdPushdPopd "#!/bin/bash -e\npushd foo\nrm bar"
+-- >>> prop $ verifyNotTree checkUncheckedCdPushdPopd "set -o errexit; pushd foo; rm bar"
+-- >>> prop $ verifyNotTree checkUncheckedCdPushdPopd "pushd -n foo"
+-- >>> prop $ verifyTree checkUncheckedCdPushdPopd "popd; rm -r foo"
+-- >>> prop $ verifyNotTree checkUncheckedCdPushdPopd "popd || exit; rm -r foo"
+-- >>> prop $ verifyNotTree checkUncheckedCdPushdPopd "set -e; popd; rm -r foo"
+-- >>> prop $ verifyNotTree checkUncheckedCdPushdPopd "if popd; then rm foo; fi"
+-- >>> prop $ verifyTree checkUncheckedCdPushdPopd "if true; then popd; fi"
+-- >>> prop $ verifyTree checkUncheckedCdPushdPopd "popd"
+-- >>> prop $ verifyNotTree checkUncheckedCdPushdPopd "#!/bin/bash -e\npopd\nrm bar"
+-- >>> prop $ verifyNotTree checkUncheckedCdPushdPopd "set -o errexit; popd; rm bar"
+-- >>> prop $ verifyNotTree checkUncheckedCdPushdPopd "popd -n foo"
checkUncheckedCdPushdPopd params root =
if hasSetE params then
@@ -2584,9 +2652,10 @@ checkUncheckedCdPushdPopd params root =
[_, ".."] -> True;
_ -> False
-prop_checkLoopVariableReassignment1 = verify checkLoopVariableReassignment "for i in *; do for i in *.bar; do true; done; done"
-prop_checkLoopVariableReassignment2 = verify checkLoopVariableReassignment "for i in *; do for((i=0; i<3; i++)); do true; done; done"
-prop_checkLoopVariableReassignment3 = verifyNot checkLoopVariableReassignment "for i in *; do for j in *.bar; do true; done; done"
+-- |
+-- >>> prop $ verify checkLoopVariableReassignment "for i in *; do for i in *.bar; do true; done; done"
+-- >>> prop $ verify checkLoopVariableReassignment "for i in *; do for((i=0; i<3; i++)); do true; done; done"
+-- >>> prop $ verifyNot checkLoopVariableReassignment "for i in *; do for j in *.bar; do true; done; done"
checkLoopVariableReassignment params token =
potentially $ case token of
T_ForIn {} -> check
@@ -2611,11 +2680,12 @@ checkLoopVariableReassignment params token =
_ _ _ -> return var
_ -> fail "not loop"
-prop_checkTrailingBracket1 = verify checkTrailingBracket "if -z n ]]; then true; fi "
-prop_checkTrailingBracket2 = verifyNot checkTrailingBracket "if [[ -z n ]]; then true; fi "
-prop_checkTrailingBracket3 = verify checkTrailingBracket "a || b ] && thing"
-prop_checkTrailingBracket4 = verifyNot checkTrailingBracket "run [ foo ]"
-prop_checkTrailingBracket5 = verifyNot checkTrailingBracket "run bar ']'"
+-- |
+-- >>> prop $ verify checkTrailingBracket "if -z n ]]; then true; fi "
+-- >>> prop $ verifyNot checkTrailingBracket "if [[ -z n ]]; then true; fi "
+-- >>> prop $ verify checkTrailingBracket "a || b ] && thing"
+-- >>> prop $ verifyNot checkTrailingBracket "run [ foo ]"
+-- >>> prop $ verifyNot checkTrailingBracket "run bar ']'"
checkTrailingBracket _ token =
case token of
T_SimpleCommand _ _ tokens@(_:_) -> check (last tokens) token
@@ -2637,15 +2707,16 @@ checkTrailingBracket _ token =
"]" -> "["
x -> x
-prop_checkReturnAgainstZero1 = verify checkReturnAgainstZero "[ $? -eq 0 ]"
-prop_checkReturnAgainstZero2 = verify checkReturnAgainstZero "[[ \"$?\" -gt 0 ]]"
-prop_checkReturnAgainstZero3 = verify checkReturnAgainstZero "[[ 0 -ne $? ]]"
-prop_checkReturnAgainstZero4 = verifyNot checkReturnAgainstZero "[[ $? -eq 4 ]]"
-prop_checkReturnAgainstZero5 = verify checkReturnAgainstZero "[[ 0 -eq $? ]]"
-prop_checkReturnAgainstZero6 = verifyNot checkReturnAgainstZero "[[ $R -eq 0 ]]"
-prop_checkReturnAgainstZero7 = verify checkReturnAgainstZero "(( $? == 0 ))"
-prop_checkReturnAgainstZero8 = verify checkReturnAgainstZero "(( $? ))"
-prop_checkReturnAgainstZero9 = verify checkReturnAgainstZero "(( ! $? ))"
+-- |
+-- >>> prop $ verify checkReturnAgainstZero "[ $? -eq 0 ]"
+-- >>> prop $ verify checkReturnAgainstZero "[[ \"$?\" -gt 0 ]]"
+-- >>> prop $ verify checkReturnAgainstZero "[[ 0 -ne $? ]]"
+-- >>> prop $ verifyNot checkReturnAgainstZero "[[ $? -eq 4 ]]"
+-- >>> prop $ verify checkReturnAgainstZero "[[ 0 -eq $? ]]"
+-- >>> prop $ verifyNot checkReturnAgainstZero "[[ $R -eq 0 ]]"
+-- >>> prop $ verify checkReturnAgainstZero "(( $? == 0 ))"
+-- >>> prop $ verify checkReturnAgainstZero "(( $? ))"
+-- >>> prop $ verify checkReturnAgainstZero "(( ! $? ))"
checkReturnAgainstZero _ token =
case token of
TC_Binary id _ _ lhs rhs -> check lhs rhs
@@ -2667,14 +2738,15 @@ checkReturnAgainstZero _ token =
_ -> False
message id = style id 2181 "Check exit code directly with e.g. 'if mycmd;', not indirectly with $?."
-prop_checkRedirectedNowhere1 = verify checkRedirectedNowhere "> file"
-prop_checkRedirectedNowhere2 = verify checkRedirectedNowhere "> file | grep foo"
-prop_checkRedirectedNowhere3 = verify checkRedirectedNowhere "grep foo | > bar"
-prop_checkRedirectedNowhere4 = verifyNot checkRedirectedNowhere "grep foo > bar"
-prop_checkRedirectedNowhere5 = verifyNot checkRedirectedNowhere "foo | grep bar > baz"
-prop_checkRedirectedNowhere6 = verifyNot checkRedirectedNowhere "var=$(value) 2> /dev/null"
-prop_checkRedirectedNowhere7 = verifyNot checkRedirectedNowhere "var=$(< file)"
-prop_checkRedirectedNowhere8 = verifyNot checkRedirectedNowhere "var=`< file`"
+-- |
+-- >>> prop $ verify checkRedirectedNowhere "> file"
+-- >>> prop $ verify checkRedirectedNowhere "> file | grep foo"
+-- >>> prop $ verify checkRedirectedNowhere "grep foo | > bar"
+-- >>> prop $ verifyNot checkRedirectedNowhere "grep foo > bar"
+-- >>> prop $ verifyNot checkRedirectedNowhere "foo | grep bar > baz"
+-- >>> prop $ verifyNot checkRedirectedNowhere "var=$(value) 2> /dev/null"
+-- >>> prop $ verifyNot checkRedirectedNowhere "var=$(< file)"
+-- >>> prop $ verifyNot checkRedirectedNowhere "var=`< file`"
checkRedirectedNowhere params token =
case token of
T_Pipeline _ _ [single] -> potentially $ do
@@ -2700,15 +2772,16 @@ checkRedirectedNowhere params token =
_ -> Nothing
-prop_checkArrayAssignmentIndices1 = verifyTree checkArrayAssignmentIndices "declare -A foo; foo=(bar)"
-prop_checkArrayAssignmentIndices2 = verifyNotTree checkArrayAssignmentIndices "declare -a foo; foo=(bar)"
-prop_checkArrayAssignmentIndices3 = verifyNotTree checkArrayAssignmentIndices "declare -A foo; foo=([i]=bar)"
-prop_checkArrayAssignmentIndices4 = verifyTree checkArrayAssignmentIndices "typeset -A foo; foo+=(bar)"
-prop_checkArrayAssignmentIndices5 = verifyTree checkArrayAssignmentIndices "arr=( [foo]= bar )"
-prop_checkArrayAssignmentIndices6 = verifyTree checkArrayAssignmentIndices "arr=( [foo] = bar )"
-prop_checkArrayAssignmentIndices7 = verifyTree checkArrayAssignmentIndices "arr=( var=value )"
-prop_checkArrayAssignmentIndices8 = verifyNotTree checkArrayAssignmentIndices "arr=( [foo]=bar )"
-prop_checkArrayAssignmentIndices9 = verifyNotTree checkArrayAssignmentIndices "arr=( [foo]=\"\" )"
+-- |
+-- >>> prop $ verifyTree checkArrayAssignmentIndices "declare -A foo; foo=(bar)"
+-- >>> prop $ verifyNotTree checkArrayAssignmentIndices "declare -a foo; foo=(bar)"
+-- >>> prop $ verifyNotTree checkArrayAssignmentIndices "declare -A foo; foo=([i]=bar)"
+-- >>> prop $ verifyTree checkArrayAssignmentIndices "typeset -A foo; foo+=(bar)"
+-- >>> prop $ verifyTree checkArrayAssignmentIndices "arr=( [foo]= bar )"
+-- >>> prop $ verifyTree checkArrayAssignmentIndices "arr=( [foo] = bar )"
+-- >>> prop $ verifyTree checkArrayAssignmentIndices "arr=( var=value )"
+-- >>> prop $ verifyNotTree checkArrayAssignmentIndices "arr=( [foo]=bar )"
+-- >>> prop $ verifyNotTree checkArrayAssignmentIndices "arr=( [foo]=\"\" )"
checkArrayAssignmentIndices params root =
runNodeAnalysis check params root
where
@@ -2742,15 +2815,16 @@ checkArrayAssignmentIndices params root =
_ -> return ()
-prop_checkUnmatchableCases1 = verify checkUnmatchableCases "case foo in bar) true; esac"
-prop_checkUnmatchableCases2 = verify checkUnmatchableCases "case foo-$bar in ??|*) true; esac"
-prop_checkUnmatchableCases3 = verify checkUnmatchableCases "case foo in foo) true; esac"
-prop_checkUnmatchableCases4 = verifyNot checkUnmatchableCases "case foo-$bar in foo*|*bar|*baz*) true; esac"
-prop_checkUnmatchableCases5 = verify checkUnmatchableCases "case $f in *.txt) true;; f??.txt) false;; esac"
-prop_checkUnmatchableCases6 = verifyNot checkUnmatchableCases "case $f in ?*) true;; *) false;; esac"
-prop_checkUnmatchableCases7 = verifyNot checkUnmatchableCases "case $f in $(x)) true;; asdf) false;; esac"
-prop_checkUnmatchableCases8 = verify checkUnmatchableCases "case $f in cow) true;; bar|cow) false;; esac"
-prop_checkUnmatchableCases9 = verifyNot checkUnmatchableCases "case $f in x) true;;& x) false;; esac"
+-- |
+-- >>> prop $ verify checkUnmatchableCases "case foo in bar) true; esac"
+-- >>> prop $ verify checkUnmatchableCases "case foo-$bar in ??|*) true; esac"
+-- >>> prop $ verify checkUnmatchableCases "case foo in foo) true; esac"
+-- >>> prop $ verifyNot checkUnmatchableCases "case foo-$bar in foo*|*bar|*baz*) true; esac"
+-- >>> prop $ verify checkUnmatchableCases "case $f in *.txt) true;; f??.txt) false;; esac"
+-- >>> prop $ verifyNot checkUnmatchableCases "case $f in ?*) true;; *) false;; esac"
+-- >>> prop $ verifyNot checkUnmatchableCases "case $f in $(x)) true;; asdf) false;; esac"
+-- >>> prop $ verify checkUnmatchableCases "case $f in cow) true;; bar|cow) false;; esac"
+-- >>> prop $ verifyNot checkUnmatchableCases "case $f in x) true;;& x) false;; esac"
checkUnmatchableCases _ t =
case t of
T_CaseExpression _ word list -> do
@@ -2796,13 +2870,14 @@ checkUnmatchableCases _ t =
checkDoms _ = return ()
-prop_checkSubshellAsTest1 = verify checkSubshellAsTest "( -e file )"
-prop_checkSubshellAsTest2 = verify checkSubshellAsTest "( 1 -gt 2 )"
-prop_checkSubshellAsTest3 = verifyNot checkSubshellAsTest "( grep -c foo bar )"
-prop_checkSubshellAsTest4 = verifyNot checkSubshellAsTest "[ 1 -gt 2 ]"
-prop_checkSubshellAsTest5 = verify checkSubshellAsTest "( -e file && -x file )"
-prop_checkSubshellAsTest6 = verify checkSubshellAsTest "( -e file || -x file && -t 1 )"
-prop_checkSubshellAsTest7 = verify checkSubshellAsTest "( ! -d file )"
+-- |
+-- >>> prop $ verify checkSubshellAsTest "( -e file )"
+-- >>> prop $ verify checkSubshellAsTest "( 1 -gt 2 )"
+-- >>> prop $ verifyNot checkSubshellAsTest "( grep -c foo bar )"
+-- >>> prop $ verifyNot checkSubshellAsTest "[ 1 -gt 2 ]"
+-- >>> prop $ verify checkSubshellAsTest "( -e file && -x file )"
+-- >>> prop $ verify checkSubshellAsTest "( -e file || -x file && -t 1 )"
+-- >>> prop $ verify checkSubshellAsTest "( ! -d file )"
checkSubshellAsTest _ t =
case t of
T_Subshell id [w] -> check id w
@@ -2824,14 +2899,15 @@ checkSubshellAsTest _ t =
warn id 2205 "(..) is a subshell. Did you mean [ .. ], a test expression?"
-prop_checkSplittingInArrays1 = verify checkSplittingInArrays "a=( $var )"
-prop_checkSplittingInArrays2 = verify checkSplittingInArrays "a=( $(cmd) )"
-prop_checkSplittingInArrays3 = verifyNot checkSplittingInArrays "a=( \"$var\" )"
-prop_checkSplittingInArrays4 = verifyNot checkSplittingInArrays "a=( \"$(cmd)\" )"
-prop_checkSplittingInArrays5 = verifyNot checkSplittingInArrays "a=( $! $$ $# )"
-prop_checkSplittingInArrays6 = verifyNot checkSplittingInArrays "a=( ${#arr[@]} )"
-prop_checkSplittingInArrays7 = verifyNot checkSplittingInArrays "a=( foo{1,2} )"
-prop_checkSplittingInArrays8 = verifyNot checkSplittingInArrays "a=( * )"
+-- |
+-- >>> prop $ verify checkSplittingInArrays "a=( $var )"
+-- >>> prop $ verify checkSplittingInArrays "a=( $(cmd) )"
+-- >>> prop $ verifyNot checkSplittingInArrays "a=( \"$var\" )"
+-- >>> prop $ verifyNot checkSplittingInArrays "a=( \"$(cmd)\" )"
+-- >>> prop $ verifyNot checkSplittingInArrays "a=( $! $$ $# )"
+-- >>> prop $ verifyNot checkSplittingInArrays "a=( ${#arr[@]} )"
+-- >>> prop $ verifyNot checkSplittingInArrays "a=( foo{1,2} )"
+-- >>> prop $ verifyNot checkSplittingInArrays "a=( * )"
checkSplittingInArrays params t =
case t of
T_Array _ elements -> mapM_ check elements
@@ -2861,10 +2937,11 @@ checkSplittingInArrays params t =
else "Prefer mapfile or read -a to split command output (or quote to avoid splitting)."
-prop_checkRedirectionToNumber1 = verify checkRedirectionToNumber "( 1 > 2 )"
-prop_checkRedirectionToNumber2 = verify checkRedirectionToNumber "foo 1>2"
-prop_checkRedirectionToNumber3 = verifyNot checkRedirectionToNumber "echo foo > '2'"
-prop_checkRedirectionToNumber4 = verifyNot checkRedirectionToNumber "foo 1>&2"
+-- |
+-- >>> prop $ verify checkRedirectionToNumber "( 1 > 2 )"
+-- >>> prop $ verify checkRedirectionToNumber "foo 1>2"
+-- >>> prop $ verifyNot checkRedirectionToNumber "echo foo > '2'"
+-- >>> prop $ verifyNot checkRedirectionToNumber "foo 1>&2"
checkRedirectionToNumber _ t = case t of
T_IoFile id _ word -> potentially $ do
file <- getUnquotedLiteral word
@@ -2872,9 +2949,10 @@ checkRedirectionToNumber _ t = case t of
return $ warn id 2210 "This is a file redirection. Was it supposed to be a comparison or fd operation?"
_ -> return ()
-prop_checkGlobAsCommand1 = verify checkGlobAsCommand "foo*"
-prop_checkGlobAsCommand2 = verify checkGlobAsCommand "$(var[i])"
-prop_checkGlobAsCommand3 = verifyNot checkGlobAsCommand "echo foo*"
+-- |
+-- >>> prop $ verify checkGlobAsCommand "foo*"
+-- >>> prop $ verify checkGlobAsCommand "$(var[i])"
+-- >>> prop $ verifyNot checkGlobAsCommand "echo foo*"
checkGlobAsCommand _ t = case t of
T_SimpleCommand _ _ (first:_) ->
when (isGlob first) $
@@ -2882,10 +2960,11 @@ checkGlobAsCommand _ t = case t of
_ -> return ()
-prop_checkFlagAsCommand1 = verify checkFlagAsCommand "-e file"
-prop_checkFlagAsCommand2 = verify checkFlagAsCommand "foo\n --bar=baz"
-prop_checkFlagAsCommand3 = verifyNot checkFlagAsCommand "'--myexec--' args"
-prop_checkFlagAsCommand4 = verifyNot checkFlagAsCommand "var=cmd --arg" -- Handled by SC2037
+-- |
+-- >>> prop $ verify checkFlagAsCommand "-e file"
+-- >>> prop $ verify checkFlagAsCommand "foo\n --bar=baz"
+-- >>> prop $ verifyNot checkFlagAsCommand "'--myexec--' args"
+-- >>> prop $ verifyNot checkFlagAsCommand "var=cmd --arg" -- Handled by SC2037
checkFlagAsCommand _ t = case t of
T_SimpleCommand _ [] (first:_) ->
when (isUnquotedFlag first) $
@@ -2893,21 +2972,23 @@ checkFlagAsCommand _ t = case t of
_ -> return ()
-prop_checkEmptyCondition1 = verify checkEmptyCondition "if [ ]; then ..; fi"
-prop_checkEmptyCondition2 = verifyNot checkEmptyCondition "[ foo -o bar ]"
+-- |
+-- >>> prop $ verify checkEmptyCondition "if [ ]; then ..; fi"
+-- >>> prop $ verifyNot checkEmptyCondition "[ foo -o bar ]"
checkEmptyCondition _ t = case t of
TC_Empty id _ -> style id 2212 "Use 'false' instead of empty [/[[ conditionals."
_ -> return ()
-prop_checkPipeToNowhere1 = verify checkPipeToNowhere "foo | echo bar"
-prop_checkPipeToNowhere2 = verify checkPipeToNowhere "basename < file.txt"
-prop_checkPipeToNowhere3 = verify checkPipeToNowhere "printf 'Lol' <<< str"
-prop_checkPipeToNowhere4 = verify checkPipeToNowhere "printf 'Lol' << eof\nlol\neof\n"
-prop_checkPipeToNowhere5 = verifyNot checkPipeToNowhere "echo foo | xargs du"
-prop_checkPipeToNowhere6 = verifyNot checkPipeToNowhere "ls | echo $(cat)"
-prop_checkPipeToNowhere7 = verifyNot checkPipeToNowhere "echo foo | var=$(cat) ls"
-prop_checkPipeToNowhere8 = verify checkPipeToNowhere "foo | true"
-prop_checkPipeToNowhere9 = verifyNot checkPipeToNowhere "mv -i f . < /dev/stdin"
+-- |
+-- >>> prop $ verify checkPipeToNowhere "foo | echo bar"
+-- >>> prop $ verify checkPipeToNowhere "basename < file.txt"
+-- >>> prop $ verify checkPipeToNowhere "printf 'Lol' <<< str"
+-- >>> prop $ verify checkPipeToNowhere "printf 'Lol' << eof\nlol\neof\n"
+-- >>> prop $ verifyNot checkPipeToNowhere "echo foo | xargs du"
+-- >>> prop $ verifyNot checkPipeToNowhere "ls | echo $(cat)"
+-- >>> prop $ verifyNot checkPipeToNowhere "echo foo | var=$(cat) ls"
+-- >>> prop $ verify checkPipeToNowhere "foo | true"
+-- >>> prop $ verifyNot checkPipeToNowhere "mv -i f . < /dev/stdin"
checkPipeToNowhere :: Parameters -> Token -> WriterT [TokenComment] Identity ()
checkPipeToNowhere _ t =
case t of
@@ -2959,10 +3040,11 @@ checkPipeToNowhere _ t =
T_FdRedirect _ _ T_HereString {} -> True
_ -> False
-prop_checkUseBeforeDefinition1 = verifyTree checkUseBeforeDefinition "f; f() { true; }"
-prop_checkUseBeforeDefinition2 = verifyNotTree checkUseBeforeDefinition "f() { true; }; f"
-prop_checkUseBeforeDefinition3 = verifyNotTree checkUseBeforeDefinition "if ! mycmd --version; then mycmd() { true; }; fi"
-prop_checkUseBeforeDefinition4 = verifyNotTree checkUseBeforeDefinition "mycmd || mycmd() { f; }"
+-- |
+-- >>> prop $ verifyTree checkUseBeforeDefinition "f; f() { true; }"
+-- >>> prop $ verifyNotTree checkUseBeforeDefinition "f() { true; }; f"
+-- >>> prop $ verifyNotTree checkUseBeforeDefinition "if ! mycmd --version; then mycmd() { true; }; fi"
+-- >>> prop $ verifyNotTree checkUseBeforeDefinition "mycmd || mycmd() { f; }"
checkUseBeforeDefinition _ t =
execWriter $ evalStateT (mapM_ examine $ revCommands) Map.empty
where
@@ -2990,9 +3072,10 @@ checkUseBeforeDefinition _ t =
then [x]
else concatMap recursiveSequences list
-prop_checkForLoopGlobVariables1 = verify checkForLoopGlobVariables "for i in $var/*.txt; do true; done"
-prop_checkForLoopGlobVariables2 = verifyNot checkForLoopGlobVariables "for i in \"$var\"/*.txt; do true; done"
-prop_checkForLoopGlobVariables3 = verifyNot checkForLoopGlobVariables "for i in $var; do true; done"
+-- |
+-- >>> prop $ verify checkForLoopGlobVariables "for i in $var/*.txt; do true; done"
+-- >>> prop $ verifyNot checkForLoopGlobVariables "for i in \"$var\"/*.txt; do true; done"
+-- >>> prop $ verifyNot checkForLoopGlobVariables "for i in $var; do true; done"
checkForLoopGlobVariables _ t =
case t of
T_ForIn _ _ words _ -> mapM_ check words
@@ -3004,10 +3087,11 @@ checkForLoopGlobVariables _ t =
suggest t = info (getId t) 2231
"Quote expansions in this for loop glob to prevent wordsplitting, e.g. \"$dir\"/*.txt ."
-prop_checkSubshelledTests1 = verify checkSubshelledTests "a && ( [ b ] || ! [ c ] )"
-prop_checkSubshelledTests2 = verify checkSubshelledTests "( [ a ] )"
-prop_checkSubshelledTests3 = verify checkSubshelledTests "( [ a ] && [ b ] || test c )"
-prop_checkSubshelledTests4 = verify checkSubshelledTests "( [ a ] && { [ b ] && [ c ]; } )"
+-- |
+-- >>> prop $ verify checkSubshelledTests "a && ( [ b ] || ! [ c ] )"
+-- >>> prop $ verify checkSubshelledTests "( [ a ] )"
+-- >>> prop $ verify checkSubshelledTests "( [ a ] && [ b ] || test c )"
+-- >>> prop $ verify checkSubshelledTests "( [ a ] && { [ b ] && [ c ]; } )"
checkSubshelledTests params t =
case t of
T_Subshell id list | all isTestStructure list ->
@@ -3068,11 +3152,12 @@ checkSubshelledTests params t =
T_Annotation {} -> True
_ -> False
-prop_checkInvertedStringTest1 = verify checkInvertedStringTest "[ ! -z $var ]"
-prop_checkInvertedStringTest2 = verify checkInvertedStringTest "! [[ -n $var ]]"
-prop_checkInvertedStringTest3 = verifyNot checkInvertedStringTest "! [ -x $var ]"
-prop_checkInvertedStringTest4 = verifyNot checkInvertedStringTest "[[ ! -w $var ]]"
-prop_checkInvertedStringTest5 = verifyNot checkInvertedStringTest "[ -z $var ]"
+-- |
+-- >>> prop $ verify checkInvertedStringTest "[ ! -z $var ]"
+-- >>> prop $ verify checkInvertedStringTest "! [[ -n $var ]]"
+-- >>> prop $ verifyNot checkInvertedStringTest "! [ -x $var ]"
+-- >>> prop $ verifyNot checkInvertedStringTest "[[ ! -w $var ]]"
+-- >>> prop $ verifyNot checkInvertedStringTest "[ -z $var ]"
checkInvertedStringTest _ t =
case t of
TC_Unary _ _ "!" (TC_Unary _ _ op _) ->
@@ -3088,9 +3173,9 @@ checkInvertedStringTest _ t =
_ -> return ()
_ -> return ()
-prop_checkRedirectionToCommand1 = verify checkRedirectionToCommand "ls > rm"
-prop_checkRedirectionToCommand2 = verifyNot checkRedirectionToCommand "ls > 'rm'"
-prop_checkRedirectionToCommand3 = verifyNot checkRedirectionToCommand "ls > myfile"
+-- >>> prop $ verify checkRedirectionToCommand "ls > rm"
+-- >>> prop $ verifyNot checkRedirectionToCommand "ls > 'rm'"
+-- >>> prop $ verifyNot checkRedirectionToCommand "ls > myfile"
checkRedirectionToCommand _ t =
case t of
T_IoFile _ _ (T_NormalWord id [T_Literal _ str]) | str `elem` commonCommands ->
@@ -3098,5 +3183,3 @@ checkRedirectionToCommand _ t =
warn id 2238 "Redirecting to/from command name instead of file. Did you want pipes/xargs (or quote to ignore)?"
_ -> return ()
-return []
-runTests = $( [| $(forAllProperties) (quickCheckWithResult (stdArgs { maxSuccess = 1 }) ) |])
diff --git a/src/ShellCheck/AnalyzerLib.hs b/src/ShellCheck/AnalyzerLib.hs
index 306c139..df5b181 100644
--- a/src/ShellCheck/AnalyzerLib.hs
+++ b/src/ShellCheck/AnalyzerLib.hs
@@ -18,30 +18,29 @@
along with this program. If not, see .
-}
{-# LANGUAGE FlexibleContexts #-}
-{-# LANGUAGE TemplateHaskell #-}
module ShellCheck.AnalyzerLib where
+import ShellCheck.AST
+import ShellCheck.ASTLib
+import ShellCheck.Data
+import ShellCheck.Interface
+import ShellCheck.Parser
+import ShellCheck.Regex
-import ShellCheck.AST
-import ShellCheck.ASTLib
-import ShellCheck.Data
-import ShellCheck.Interface
-import ShellCheck.Parser
-import ShellCheck.Regex
+import Control.Arrow (first)
+import Control.DeepSeq
+import Control.Monad.Identity
+import Control.Monad.RWS
+import Control.Monad.State
+import Control.Monad.Writer
+import Data.Char
+import Data.List
+import qualified Data.Map as Map
+import Data.Maybe
+import Data.Semigroup
-import Control.Arrow (first)
-import Control.DeepSeq
-import Control.Monad.Identity
-import Control.Monad.RWS
-import Control.Monad.State
-import Control.Monad.Writer
-import Data.Char
-import Data.List
-import Data.Maybe
-import Data.Semigroup
-import qualified Data.Map as Map
-
-import Test.QuickCheck.All (forAllProperties)
-import Test.QuickCheck.Test (maxSuccess, quickCheckWithResult, stdArgs)
+prop :: Bool -> IO ()
+prop False = putStrLn "FAIL"
+prop True = return ()
type Analysis = AnalyzerM ()
type AnalyzerM a = RWS Parameters [TokenComment] Cache a
@@ -216,15 +215,15 @@ containsLastpipe root =
_ -> False
-prop_determineShell0 = determineShellTest "#!/bin/sh" == Sh
-prop_determineShell1 = determineShellTest "#!/usr/bin/env ksh" == Ksh
-prop_determineShell2 = determineShellTest "" == Bash
-prop_determineShell3 = determineShellTest "#!/bin/sh -e" == Sh
-prop_determineShell4 = determineShellTest "#!/bin/ksh\n#shellcheck shell=sh\nfoo" == Sh
-prop_determineShell5 = determineShellTest "#shellcheck shell=sh\nfoo" == Sh
-prop_determineShell6 = determineShellTest "#! /bin/sh" == Sh
-prop_determineShell7 = determineShellTest "#! /bin/ash" == Dash
-
+-- |
+-- >>> prop $ determineShellTest "#!/bin/sh" == Sh
+-- >>> prop $ determineShellTest "#!/usr/bin/env ksh" == Ksh
+-- >>> prop $ determineShellTest "" == Bash
+-- >>> prop $ determineShellTest "#!/bin/sh -e" == Sh
+-- >>> prop $ determineShellTest "#!/bin/ksh\n#shellcheck shell=sh\nfoo" == Sh
+-- >>> prop $ determineShellTest "#shellcheck shell=sh\nfoo" == Sh
+-- >>> prop $ determineShellTest "#! /bin/sh" == Sh
+-- >>> prop $ determineShellTest "#! /bin/ash" == Dash
determineShellTest = determineShell . fromJust . prRoot . pScript
determineShell t = fromMaybe Bash $ do
shellString <- foldl mplus Nothing $ getCandidates t
@@ -667,10 +666,11 @@ getIndexReferences s = fromMaybe [] $ do
where
re = mkRegex "(\\[.*\\])"
-prop_getOffsetReferences1 = getOffsetReferences ":bar" == ["bar"]
-prop_getOffsetReferences2 = getOffsetReferences ":bar:baz" == ["bar", "baz"]
-prop_getOffsetReferences3 = getOffsetReferences "[foo]:bar" == ["bar"]
-prop_getOffsetReferences4 = getOffsetReferences "[foo]:bar:baz" == ["bar", "baz"]
+-- |
+-- >>> prop $ getOffsetReferences ":bar" == ["bar"]
+-- >>> prop $ getOffsetReferences ":bar:baz" == ["bar", "baz"]
+-- >>> prop $ getOffsetReferences "[foo]:bar" == ["bar"]
+-- >>> prop $ getOffsetReferences "[foo]:bar:baz" == ["bar", "baz"]
getOffsetReferences mods = fromMaybe [] $ do
-- if mods start with [, then drop until ]
match <- matchRegex re mods
@@ -745,9 +745,15 @@ isUnqualifiedCommand token str = isCommandMatch token (== str)
isCommandMatch token matcher = fromMaybe False $
fmap matcher (getCommandName token)
+-- |
-- Does this regex look like it was intended as a glob?
--- True: *foo*
--- False: .*foo.*
+--
+-- >>> isConfusedGlobRegex "*foo*"
+-- True
+--
+-- >>> isConfusedGlobRegex ".*foo.*"
+-- False
+--
isConfusedGlobRegex :: String -> Bool
isConfusedGlobRegex ('*':_) = True
isConfusedGlobRegex [x,'*'] | x /= '\\' = True
@@ -757,9 +763,10 @@ isVariableStartChar x = x == '_' || isAsciiLower x || isAsciiUpper x
isVariableChar x = isVariableStartChar x || isDigit x
variableNameRegex = mkRegex "[_a-zA-Z][_a-zA-Z0-9]*"
-prop_isVariableName1 = isVariableName "_fo123"
-prop_isVariableName2 = not $ isVariableName "4"
-prop_isVariableName3 = not $ isVariableName "test: "
+-- |
+-- >>> prop $ isVariableName "_fo123"
+-- >>> prop $ not $ isVariableName "4"
+-- >>> prop $ not $ isVariableName "test: "
isVariableName (x:r) = isVariableStartChar x && all isVariableChar r
isVariableName _ = False
@@ -768,27 +775,28 @@ getVariablesFromLiteralToken token =
-- Try to get referenced variables from a literal string like "$foo"
-- Ignores tons of cases like arithmetic evaluation and array indices.
-prop_getVariablesFromLiteral1 =
- getVariablesFromLiteral "$foo${bar//a/b}$BAZ" == ["foo", "bar", "BAZ"]
+-- >>> prop $ getVariablesFromLiteral "$foo${bar//a/b}$BAZ" == ["foo", "bar", "BAZ"]
getVariablesFromLiteral string =
map (!! 0) $ matchAllSubgroups variableRegex string
where
variableRegex = mkRegex "\\$\\{?([A-Za-z0-9_]+)"
+-- |
-- Get the variable name from an expansion like ${var:-foo}
-prop_getBracedReference1 = getBracedReference "foo" == "foo"
-prop_getBracedReference2 = getBracedReference "#foo" == "foo"
-prop_getBracedReference3 = getBracedReference "#" == "#"
-prop_getBracedReference4 = getBracedReference "##" == "#"
-prop_getBracedReference5 = getBracedReference "#!" == "!"
-prop_getBracedReference6 = getBracedReference "!#" == "#"
-prop_getBracedReference7 = getBracedReference "!foo#?" == "foo"
-prop_getBracedReference8 = getBracedReference "foo-bar" == "foo"
-prop_getBracedReference9 = getBracedReference "foo:-bar" == "foo"
-prop_getBracedReference10= getBracedReference "foo: -1" == "foo"
-prop_getBracedReference11= getBracedReference "!os*" == ""
-prop_getBracedReference12= getBracedReference "!os?bar**" == ""
-prop_getBracedReference13= getBracedReference "foo[bar]" == "foo"
+--
+-- >>> prop $ getBracedReference "foo" == "foo"
+-- >>> prop $ getBracedReference "#foo" == "foo"
+-- >>> prop $ getBracedReference "#" == "#"
+-- >>> prop $ getBracedReference "##" == "#"
+-- >>> prop $ getBracedReference "#!" == "!"
+-- >>> prop $ getBracedReference "!#" == "#"
+-- >>> prop $ getBracedReference "!foo#?" == "foo"
+-- >>> prop $ getBracedReference "foo-bar" == "foo"
+-- >>> prop $ getBracedReference "foo:-bar" == "foo"
+-- >>> prop $ getBracedReference "foo: -1" == "foo"
+-- >>> prop $ getBracedReference "!os*" == ""
+-- >>> prop $ getBracedReference "!os?bar**" == ""
+-- >>> prop $ getBracedReference "foo[bar]" == "foo"
getBracedReference s = fromMaybe s $
nameExpansion s `mplus` takeName noPrefix `mplus` getSpecial noPrefix `mplus` getSpecial s
where
@@ -811,9 +819,10 @@ getBracedReference s = fromMaybe s $
return ""
nameExpansion _ = Nothing
-prop_getBracedModifier1 = getBracedModifier "foo:bar:baz" == ":bar:baz"
-prop_getBracedModifier2 = getBracedModifier "!var:-foo" == ":-foo"
-prop_getBracedModifier3 = getBracedModifier "foo[bar]" == "[bar]"
+-- |
+-- >>> prop $ getBracedModifier "foo:bar:baz" == ":bar:baz"
+-- >>> prop $ getBracedModifier "!var:-foo" == ":-foo"
+-- >>> prop $ getBracedModifier "foo[bar]" == "[bar]"
getBracedModifier s = fromMaybe "" . listToMaybe $ do
let var = getBracedReference s
a <- dropModifier s
@@ -830,10 +839,13 @@ getBracedModifier s = fromMaybe "" . listToMaybe $ do
-- Run an action in a Maybe (or do nothing).
-- Example:
+--
+-- @
-- potentially $ do
-- s <- getLiteralString cmd
-- guard $ s `elem` ["--recursive", "-r"]
-- return $ warn .. "Something something recursive"
+-- @
potentially :: Monad m => Maybe (m ()) -> m ()
potentially = fromMaybe (return ())
@@ -918,6 +930,3 @@ getOpts flagTokenizer string cmd = process flags
else do
more <- process rest2
return $ (flag1, token1) : more
-
-return []
-runTests = $( [| $(forAllProperties) (quickCheckWithResult (stdArgs { maxSuccess = 1 }) ) |])
diff --git a/src/ShellCheck/Checker.hs b/src/ShellCheck/Checker.hs
index 10074e3..9a040ee 100644
--- a/src/ShellCheck/Checker.hs
+++ b/src/ShellCheck/Checker.hs
@@ -17,8 +17,7 @@
You should have received a copy of the GNU General Public License
along with this program. If not, see .
-}
-{-# LANGUAGE TemplateHaskell #-}
-module ShellCheck.Checker (checkScript, ShellCheck.Checker.runTests) where
+module ShellCheck.Checker (checkScript) where
import ShellCheck.Interface
import ShellCheck.Parser
@@ -35,8 +34,6 @@ import qualified System.IO
import Prelude hiding (readFile)
import Control.Monad
-import Test.QuickCheck.All
-
tokenToPosition startMap t = fromMaybe fail $ do
span <- Map.lookup (tcId t) startMap
return $ newPositionedComment {
@@ -125,113 +122,132 @@ checkRecursive includes src =
csCheckSourced = True
}
-prop_findsParseIssue = check "echo \"$12\"" == [1037]
-
-prop_commentDisablesParseIssue1 =
- null $ check "#shellcheck disable=SC1037\necho \"$12\""
-prop_commentDisablesParseIssue2 =
- null $ check "#shellcheck disable=SC1037\n#lol\necho \"$12\""
-
-prop_findsAnalysisIssue =
- check "echo $1" == [2086]
-prop_commentDisablesAnalysisIssue1 =
- null $ check "#shellcheck disable=SC2086\necho $1"
-prop_commentDisablesAnalysisIssue2 =
- null $ check "#shellcheck disable=SC2086\n#lol\necho $1"
-
-prop_optionDisablesIssue1 =
- null $ getErrors
- (mockedSystemInterface [])
- emptyCheckSpec {
- csScript = "echo $1",
- csExcludedWarnings = [2148, 2086]
- }
-
-prop_optionDisablesIssue2 =
- null $ getErrors
- (mockedSystemInterface [])
- emptyCheckSpec {
- csScript = "echo \"$10\"",
- csExcludedWarnings = [2148, 1037]
- }
-
-prop_wontParseBadShell =
- [1071] == check "#!/usr/bin/python\ntrue $1\n"
-
-prop_optionDisablesBadShebang =
- null $ getErrors
- (mockedSystemInterface [])
- emptyCheckSpec {
- csScript = "#!/usr/bin/python\ntrue\n",
- csShellTypeOverride = Just Sh
- }
-
-prop_annotationDisablesBadShebang =
- [] == check "#!/usr/bin/python\n# shellcheck shell=sh\ntrue\n"
-
-
-prop_canParseDevNull =
- [] == check "source /dev/null"
-
-prop_failsWhenNotSourcing =
- [1091, 2154] == check "source lol; echo \"$bar\""
-
-prop_worksWhenSourcing =
- null $ checkWithIncludes [("lib", "bar=1")] "source lib; echo \"$bar\""
-
-prop_worksWhenDotting =
- null $ checkWithIncludes [("lib", "bar=1")] ". lib; echo \"$bar\""
-
-prop_noInfiniteSourcing =
- [] == checkWithIncludes [("lib", "source lib")] "source lib"
-
-prop_canSourceBadSyntax =
- [1094, 2086] == checkWithIncludes [("lib", "for f; do")] "source lib; echo $1"
-
-prop_cantSourceDynamic =
- [1090] == checkWithIncludes [("lib", "")] ". \"$1\""
-
-prop_cantSourceDynamic2 =
- [1090] == checkWithIncludes [("lib", "")] "source ~/foo"
-
-prop_canSourceDynamicWhenRedirected =
- null $ checkWithIncludes [("lib", "")] "#shellcheck source=lib\n. \"$1\""
-
-prop_recursiveAnalysis =
- [2086] == checkRecursive [("lib", "echo $1")] "source lib"
-
-prop_recursiveParsing =
- [1037] == checkRecursive [("lib", "echo \"$10\"")] "source lib"
-
-prop_sourceDirectiveDoesntFollowFile =
- null $ checkWithIncludes
- [("foo", "source bar"), ("bar", "baz=3")]
- "#shellcheck source=foo\n. \"$1\"; echo \"$baz\""
-
-prop_filewideAnnotationBase = [2086] == check "#!/bin/sh\necho $1"
-prop_filewideAnnotation1 = null $
- check "#!/bin/sh\n# shellcheck disable=2086\necho $1"
-prop_filewideAnnotation2 = null $
- check "#!/bin/sh\n# shellcheck disable=2086\ntrue\necho $1"
-prop_filewideAnnotation3 = null $
- check "#!/bin/sh\n#unrelated\n# shellcheck disable=2086\ntrue\necho $1"
-prop_filewideAnnotation4 = null $
- check "#!/bin/sh\n# shellcheck disable=2086\n#unrelated\ntrue\necho $1"
-prop_filewideAnnotation5 = null $
- check "#!/bin/sh\n\n\n\n#shellcheck disable=2086\ntrue\necho $1"
-prop_filewideAnnotation6 = null $
- check "#shellcheck shell=sh\n#unrelated\n#shellcheck disable=2086\ntrue\necho $1"
-prop_filewideAnnotation7 = null $
- check "#!/bin/sh\n# shellcheck disable=2086\n#unrelated\ntrue\necho $1"
-
-prop_filewideAnnotationBase2 = [2086, 2181] == check "true\n[ $? == 0 ] && echo $1"
-prop_filewideAnnotation8 = null $
- check "# Disable $? warning\n#shellcheck disable=SC2181\n# Disable quoting warning\n#shellcheck disable=2086\ntrue\n[ $? == 0 ] && echo $1"
-
-prop_sourcePartOfOriginalScript = -- #1181: -x disabled posix warning for 'source'
- 2039 `elem` checkWithIncludes [("./saywhat.sh", "echo foo")] "#!/bin/sh\nsource ./saywhat.sh"
-
-prop_spinBug1413 = null $ check "fun() {\n# shellcheck disable=SC2188\n> /dev/null\n}\n"
-
-return []
-runTests = $quickCheckAll
+-- | Dummy binding for doctest to run
+--
+-- >>> check "echo \"$12\""
+-- [1037]
+--
+-- >>> check "#shellcheck disable=SC1037\necho \"$12\""
+-- []
+--
+-- >>> check "#shellcheck disable=SC1037\n#lol\necho \"$12\""
+-- []
+--
+-- >>> check "echo $1"
+-- [2086]
+--
+-- >>> check "#shellcheck disable=SC2086\necho $1"
+-- []
+--
+-- >>> check "#shellcheck disable=SC2086\n#lol\necho $1"
+-- []
+--
+-- >>> :{
+-- getErrors
+-- (mockedSystemInterface [])
+-- emptyCheckSpec {
+-- csScript = "echo $1",
+-- csExcludedWarnings = [2148, 2086]
+-- }
+-- :}
+-- []
+--
+-- >>> :{
+-- getErrors
+-- (mockedSystemInterface [])
+-- emptyCheckSpec {
+-- csScript = "echo \"$10\"",
+-- csExcludedWarnings = [2148, 1037]
+-- }
+-- :}
+-- []
+--
+-- >>> check "#!/usr/bin/python\ntrue $1\n"
+-- [1071]
+--
+-- >>> :{
+-- getErrors
+-- (mockedSystemInterface [])
+-- emptyCheckSpec {
+-- csScript = "#!/usr/bin/python\ntrue\n",
+-- csShellTypeOverride = Just Sh
+-- }
+-- :}
+-- []
+--
+-- >>> check "#!/usr/bin/python\n# shellcheck shell=sh\ntrue\n"
+-- []
+--
+-- >>> check "source /dev/null"
+-- []
+--
+-- >>> check "source lol; echo \"$bar\""
+-- [1091,2154]
+--
+-- >>> checkWithIncludes [("lib", "bar=1")] "source lib; echo \"$bar\""
+-- []
+--
+-- >>> checkWithIncludes [("lib", "bar=1")] ". lib; echo \"$bar\""
+-- []
+--
+-- >>> checkWithIncludes [("lib", "source lib")] "source lib"
+-- []
+--
+-- >>> checkWithIncludes [("lib", "for f; do")] "source lib; echo $1"
+-- [1094,2086]
+--
+-- >>> checkWithIncludes [("lib", "")] ". \"$1\""
+-- [1090]
+--
+-- >>> checkWithIncludes [("lib", "")] "source ~/foo"
+-- [1090]
+--
+-- >>> checkWithIncludes [("lib", "")] "#shellcheck source=lib\n. \"$1\""
+-- []
+--
+-- >>> checkRecursive [("lib", "echo $1")] "source lib"
+-- [2086]
+--
+-- >>> checkRecursive [("lib", "echo \"$10\"")] "source lib"
+-- [1037]
+--
+-- >>> checkWithIncludes [("foo", "source bar"), ("bar", "baz=3")] "#shellcheck source=foo\n. \"$1\"; echo \"$baz\""
+-- []
+--
+-- >>> check "#!/bin/sh\necho $1"
+-- [2086]
+--
+-- >>> check "#!/bin/sh\n# shellcheck disable=2086\necho $1"
+-- []
+--
+-- >>> check "#!/bin/sh\n# shellcheck disable=2086\ntrue\necho $1"
+-- []
+--
+-- >>> check "#!/bin/sh\n#unrelated\n# shellcheck disable=2086\ntrue\necho $1"
+-- []
+--
+-- >>> check "#!/bin/sh\n# shellcheck disable=2086\n#unrelated\ntrue\necho $1"
+-- []
+--
+-- >>> check "#!/bin/sh\n\n\n\n#shellcheck disable=2086\ntrue\necho $1"
+-- []
+--
+-- >>> check "#shellcheck shell=sh\n#unrelated\n#shellcheck disable=2086\ntrue\necho $1"
+-- []
+--
+-- >>> check "#!/bin/sh\n# shellcheck disable=2086\n#unrelated\ntrue\necho $1"
+-- []
+--
+-- check "true\n[ $? == 0 ] && echo $1"
+-- [2086, 2181]
+--
+-- check "# Disable $? warning\n#shellcheck disable=SC2181\n# Disable quoting warning\n#shellcheck disable=2086\ntrue\n[ $? == 0 ] && echo $1"
+-- []
+--
+-- >>> 2039 `elem` checkWithIncludes [("./saywhat.sh", "echo foo")] "#!/bin/sh\nsource ./saywhat.sh"
+-- True
+--
+-- >>> check "fun() {\n# shellcheck disable=SC2188\n> /dev/null\n}\n"
+-- []
+doctests :: ()
+doctests = ()
diff --git a/src/ShellCheck/Checks/Commands.hs b/src/ShellCheck/Checks/Commands.hs
index f4ead5b..8faae36 100644
--- a/src/ShellCheck/Checks/Commands.hs
+++ b/src/ShellCheck/Checks/Commands.hs
@@ -17,11 +17,9 @@
You should have received a copy of the GNU General Public License
along with this program. If not, see .
-}
-{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE FlexibleContexts #-}
-
-- This module contains checks that examine specific commands by name.
-module ShellCheck.Checks.Commands (checker , ShellCheck.Checks.Commands.runTests) where
+module ShellCheck.Checks.Commands (checker) where
import ShellCheck.AST
import ShellCheck.ASTLib
@@ -37,8 +35,6 @@ import Data.Char
import Data.List
import Data.Maybe
import qualified Data.Map.Strict as Map
-import Test.QuickCheck.All (forAllProperties)
-import Test.QuickCheck.Test (quickCheckWithResult, stdArgs, maxSuccess)
data CommandName = Exactly String | Basename String
deriving (Eq, Ord)
@@ -46,7 +42,6 @@ data CommandName = Exactly String | Basename String
data CommandCheck =
CommandCheck CommandName (Token -> Analysis)
-
verify :: CommandCheck -> String -> Bool
verify f s = producesComments (getChecker [f]) s == Just True
verifyNot f s = producesComments (getChecker [f]) s == Just False
@@ -130,20 +125,21 @@ getChecker list = Checker {
checker :: Parameters -> Checker
checker params = getChecker commandChecks
-prop_checkTr1 = verify checkTr "tr [a-f] [A-F]"
-prop_checkTr2 = verify checkTr "tr 'a-z' 'A-Z'"
-prop_checkTr2a= verify checkTr "tr '[a-z]' '[A-Z]'"
-prop_checkTr3 = verifyNot checkTr "tr -d '[:lower:]'"
-prop_checkTr3a= verifyNot checkTr "tr -d '[:upper:]'"
-prop_checkTr3b= verifyNot checkTr "tr -d '|/_[:upper:]'"
-prop_checkTr4 = verifyNot checkTr "ls [a-z]"
-prop_checkTr5 = verify checkTr "tr foo bar"
-prop_checkTr6 = verify checkTr "tr 'hello' 'world'"
-prop_checkTr8 = verifyNot checkTr "tr aeiou _____"
-prop_checkTr9 = verifyNot checkTr "a-z n-za-m"
-prop_checkTr10= verifyNot checkTr "tr --squeeze-repeats rl lr"
-prop_checkTr11= verifyNot checkTr "tr abc '[d*]'"
-prop_checkTr12= verifyNot checkTr "tr '[=e=]' 'e'"
+-- |
+-- >>> prop $ verify checkTr "tr [a-f] [A-F]"
+-- >>> prop $ verify checkTr "tr 'a-z' 'A-Z'"
+-- >>> prop $ verify checkTr "tr '[a-z]' '[A-Z]'"
+-- >>> prop $ verifyNot checkTr "tr -d '[:lower:]'"
+-- >>> prop $ verifyNot checkTr "tr -d '[:upper:]'"
+-- >>> prop $ verifyNot checkTr "tr -d '|/_[:upper:]'"
+-- >>> prop $ verifyNot checkTr "ls [a-z]"
+-- >>> prop $ verify checkTr "tr foo bar"
+-- >>> prop $ verify checkTr "tr 'hello' 'world'"
+-- >>> prop $ verifyNot checkTr "tr aeiou _____"
+-- >>> prop $ verifyNot checkTr "a-z n-za-m"
+-- >>> prop $ verifyNot checkTr "tr --squeeze-repeats rl lr"
+-- >>> prop $ verifyNot checkTr "tr abc '[d*]'"
+-- >>> prop $ verifyNot checkTr "tr '[=e=]' 'e'"
checkTr = CommandCheck (Basename "tr") (mapM_ f . arguments)
where
f w | isGlob w = -- The user will go [ab] -> '[ab]' -> 'ab'. Fixme?
@@ -164,9 +160,10 @@ checkTr = CommandCheck (Basename "tr") (mapM_ f . arguments)
let relevant = filter isAlpha s
in relevant /= nub relevant
-prop_checkFindNameGlob1 = verify checkFindNameGlob "find / -name *.php"
-prop_checkFindNameGlob2 = verify checkFindNameGlob "find / -type f -ipath *(foo)"
-prop_checkFindNameGlob3 = verifyNot checkFindNameGlob "find * -name '*.php'"
+-- |
+-- >>> prop $ verify checkFindNameGlob "find / -name *.php"
+-- >>> prop $ verify checkFindNameGlob "find / -type f -ipath *(foo)"
+-- >>> prop $ verifyNot checkFindNameGlob "find * -name '*.php'"
checkFindNameGlob = CommandCheck (Basename "find") (f . arguments) where
acceptsGlob (Just s) = s `elem` [ "-ilname", "-iname", "-ipath", "-iregex", "-iwholename", "-lname", "-name", "-path", "-regex", "-wholename" ]
acceptsGlob _ = False
@@ -179,10 +176,11 @@ checkFindNameGlob = CommandCheck (Basename "find") (f . arguments) where
f (b:r)
-prop_checkNeedlessExpr = verify checkNeedlessExpr "foo=$(expr 3 + 2)"
-prop_checkNeedlessExpr2 = verify checkNeedlessExpr "foo=`echo \\`expr 3 + 2\\``"
-prop_checkNeedlessExpr3 = verifyNot checkNeedlessExpr "foo=$(expr foo : regex)"
-prop_checkNeedlessExpr4 = verifyNot checkNeedlessExpr "foo=$(expr foo \\< regex)"
+-- |
+-- >>> prop $ verify checkNeedlessExpr "foo=$(expr 3 + 2)"
+-- >>> prop $ verify checkNeedlessExpr "foo=`echo \\`expr 3 + 2\\``"
+-- >>> prop $ verifyNot checkNeedlessExpr "foo=$(expr foo : regex)"
+-- >>> prop $ verifyNot checkNeedlessExpr "foo=$(expr foo \\< regex)"
checkNeedlessExpr = CommandCheck (Basename "expr") f where
f t =
when (all (`notElem` exceptions) (words $ arguments t)) $
@@ -193,21 +191,22 @@ checkNeedlessExpr = CommandCheck (Basename "expr") f where
words = mapMaybe getLiteralString
-prop_checkGrepRe1 = verify checkGrepRe "cat foo | grep *.mp3"
-prop_checkGrepRe2 = verify checkGrepRe "grep -Ev cow*test *.mp3"
-prop_checkGrepRe3 = verify checkGrepRe "grep --regex=*.mp3 file"
-prop_checkGrepRe4 = verifyNot checkGrepRe "grep foo *.mp3"
-prop_checkGrepRe5 = verifyNot checkGrepRe "grep-v --regex=moo *"
-prop_checkGrepRe6 = verifyNot checkGrepRe "grep foo \\*.mp3"
-prop_checkGrepRe7 = verify checkGrepRe "grep *foo* file"
-prop_checkGrepRe8 = verify checkGrepRe "ls | grep foo*.jpg"
-prop_checkGrepRe9 = verifyNot checkGrepRe "grep '[0-9]*' file"
-prop_checkGrepRe10= verifyNot checkGrepRe "grep '^aa*' file"
-prop_checkGrepRe11= verifyNot checkGrepRe "grep --include=*.png foo"
-prop_checkGrepRe12= verifyNot checkGrepRe "grep -F 'Foo*' file"
-prop_checkGrepRe13= verifyNot checkGrepRe "grep -- -foo bar*"
-prop_checkGrepRe14= verifyNot checkGrepRe "grep -e -foo bar*"
-prop_checkGrepRe15= verifyNot checkGrepRe "grep --regex -foo bar*"
+-- |
+-- >>> prop $ verify checkGrepRe "cat foo | grep *.mp3"
+-- >>> prop $ verify checkGrepRe "grep -Ev cow*test *.mp3"
+-- >>> prop $ verify checkGrepRe "grep --regex=*.mp3 file"
+-- >>> prop $ verifyNot checkGrepRe "grep foo *.mp3"
+-- >>> prop $ verifyNot checkGrepRe "grep-v --regex=moo *"
+-- >>> prop $ verifyNot checkGrepRe "grep foo \\*.mp3"
+-- >>> prop $ verify checkGrepRe "grep *foo* file"
+-- >>> prop $ verify checkGrepRe "ls | grep foo*.jpg"
+-- >>> prop $ verifyNot checkGrepRe "grep '[0-9]*' file"
+-- >>> prop $ verifyNot checkGrepRe "grep '^aa*' file"
+-- >>> prop $ verifyNot checkGrepRe "grep --include=*.png foo"
+-- >>> prop $ verifyNot checkGrepRe "grep -F 'Foo*' file"
+-- >>> prop $ verifyNot checkGrepRe "grep -- -foo bar*"
+-- >>> prop $ verifyNot checkGrepRe "grep -e -foo bar*"
+-- >>> prop $ verifyNot checkGrepRe "grep --regex -foo bar*"
checkGrepRe = CommandCheck (Basename "grep") check where
check cmd = f cmd (arguments cmd)
@@ -258,10 +257,11 @@ checkGrepRe = CommandCheck (Basename "grep") check where
contra = mkRegex "[^a-zA-Z1-9]\\*|[][^$+\\\\]"
-prop_checkTrapQuotes1 = verify checkTrapQuotes "trap \"echo $num\" INT"
-prop_checkTrapQuotes1a= verify checkTrapQuotes "trap \"echo `ls`\" INT"
-prop_checkTrapQuotes2 = verifyNot checkTrapQuotes "trap 'echo $num' INT"
-prop_checkTrapQuotes3 = verify checkTrapQuotes "trap \"echo $((1+num))\" EXIT DEBUG"
+-- |
+-- >>> prop $ verify checkTrapQuotes "trap \"echo $num\" INT"
+-- >>> prop $ verify checkTrapQuotes "trap \"echo `ls`\" INT"
+-- >>> prop $ verifyNot checkTrapQuotes "trap 'echo $num' INT"
+-- >>> prop $ verify checkTrapQuotes "trap \"echo $((1+num))\" EXIT DEBUG"
checkTrapQuotes = CommandCheck (Exactly "trap") (f . arguments) where
f (x:_) = checkTrap x
f _ = return ()
@@ -275,24 +275,26 @@ checkTrapQuotes = CommandCheck (Exactly "trap") (f . arguments) where
checkExpansions _ = return ()
-prop_checkReturn1 = verifyNot checkReturn "return"
-prop_checkReturn2 = verifyNot checkReturn "return 1"
-prop_checkReturn3 = verifyNot checkReturn "return $var"
-prop_checkReturn4 = verifyNot checkReturn "return $((a|b))"
-prop_checkReturn5 = verify checkReturn "return -1"
-prop_checkReturn6 = verify checkReturn "return 1000"
-prop_checkReturn7 = verify checkReturn "return 'hello world'"
+-- |
+-- >>> prop $ verifyNot checkReturn "return"
+-- >>> prop $ verifyNot checkReturn "return 1"
+-- >>> prop $ verifyNot checkReturn "return $var"
+-- >>> prop $ verifyNot checkReturn "return $((a|b))"
+-- >>> prop $ verify checkReturn "return -1"
+-- >>> prop $ verify checkReturn "return 1000"
+-- >>> prop $ verify checkReturn "return 'hello world'"
checkReturn = CommandCheck (Exactly "return") (returnOrExit
(\c -> err c 2151 "Only one integer 0-255 can be returned. Use stdout for other data.")
(\c -> err c 2152 "Can only return 0-255. Other data should be written to stdout."))
-prop_checkExit1 = verifyNot checkExit "exit"
-prop_checkExit2 = verifyNot checkExit "exit 1"
-prop_checkExit3 = verifyNot checkExit "exit $var"
-prop_checkExit4 = verifyNot checkExit "exit $((a|b))"
-prop_checkExit5 = verify checkExit "exit -1"
-prop_checkExit6 = verify checkExit "exit 1000"
-prop_checkExit7 = verify checkExit "exit 'hello world'"
+-- |
+-- >>> prop $ verifyNot checkExit "exit"
+-- >>> prop $ verifyNot checkExit "exit 1"
+-- >>> prop $ verifyNot checkExit "exit $var"
+-- >>> prop $ verifyNot checkExit "exit $((a|b))"
+-- >>> prop $ verify checkExit "exit -1"
+-- >>> prop $ verify checkExit "exit 1000"
+-- >>> prop $ verify checkExit "exit 'hello world'"
checkExit = CommandCheck (Exactly "exit") (returnOrExit
(\c -> err c 2241 "The exit status can only be one integer 0-255. Use stdout for other data.")
(\c -> err c 2242 "Can only exit with status 0-255. Other data should be written to stdout/stderr."))
@@ -317,9 +319,10 @@ returnOrExit multi invalid = (f . arguments)
lit _ = return "WTF"
-prop_checkFindExecWithSingleArgument1 = verify checkFindExecWithSingleArgument "find . -exec 'cat {} | wc -l' \\;"
-prop_checkFindExecWithSingleArgument2 = verify checkFindExecWithSingleArgument "find . -execdir 'cat {} | wc -l' +"
-prop_checkFindExecWithSingleArgument3 = verifyNot checkFindExecWithSingleArgument "find . -exec wc -l {} \\;"
+-- |
+-- >>> prop $ verify checkFindExecWithSingleArgument "find . -exec 'cat {} | wc -l' \\;"
+-- >>> prop $ verify checkFindExecWithSingleArgument "find . -execdir 'cat {} | wc -l' +"
+-- >>> prop $ verifyNot checkFindExecWithSingleArgument "find . -exec wc -l {} \\;"
checkFindExecWithSingleArgument = CommandCheck (Basename "find") (f . arguments)
where
f = void . sequence . mapMaybe check . tails
@@ -335,11 +338,12 @@ checkFindExecWithSingleArgument = CommandCheck (Basename "find") (f . arguments)
commandRegex = mkRegex "[ |;]"
-prop_checkUnusedEchoEscapes1 = verify checkUnusedEchoEscapes "echo 'foo\\nbar\\n'"
-prop_checkUnusedEchoEscapes2 = verifyNot checkUnusedEchoEscapes "echo -e 'foi\\nbar'"
-prop_checkUnusedEchoEscapes3 = verify checkUnusedEchoEscapes "echo \"n:\\t42\""
-prop_checkUnusedEchoEscapes4 = verifyNot checkUnusedEchoEscapes "echo lol"
-prop_checkUnusedEchoEscapes5 = verifyNot checkUnusedEchoEscapes "echo -n -e '\n'"
+-- |
+-- >>> prop $ verify checkUnusedEchoEscapes "echo 'foo\\nbar\\n'"
+-- >>> prop $ verifyNot checkUnusedEchoEscapes "echo -e 'foi\\nbar'"
+-- >>> prop $ verify checkUnusedEchoEscapes "echo \"n:\\t42\""
+-- >>> prop $ verifyNot checkUnusedEchoEscapes "echo lol"
+-- >>> prop $ verifyNot checkUnusedEchoEscapes "echo -n -e '\n'"
checkUnusedEchoEscapes = CommandCheck (Basename "echo") f
where
hasEscapes = mkRegex "\\\\[rnt]"
@@ -354,9 +358,10 @@ checkUnusedEchoEscapes = CommandCheck (Basename "echo") f
info (getId token) 2028 "echo may not expand escape sequences. Use printf."
-prop_checkInjectableFindSh1 = verify checkInjectableFindSh "find . -exec sh -c 'echo {}' \\;"
-prop_checkInjectableFindSh2 = verify checkInjectableFindSh "find . -execdir bash -c 'rm \"{}\"' ';'"
-prop_checkInjectableFindSh3 = verifyNot checkInjectableFindSh "find . -exec sh -c 'rm \"$@\"' _ {} \\;"
+-- |
+-- >>> prop $ verify checkInjectableFindSh "find . -exec sh -c 'echo {}' \\;"
+-- >>> prop $ verify checkInjectableFindSh "find . -execdir bash -c 'rm \"{}\"' ';'"
+-- >>> prop $ verifyNot checkInjectableFindSh "find . -exec sh -c 'rm \"$@\"' _ {} \\;"
checkInjectableFindSh = CommandCheck (Basename "find") (check . arguments)
where
check args = do
@@ -379,9 +384,10 @@ checkInjectableFindSh = CommandCheck (Basename "find") (check . arguments)
warn id 2156 "Injecting filenames is fragile and insecure. Use parameters."
-prop_checkFindActionPrecedence1 = verify checkFindActionPrecedence "find . -name '*.wav' -o -name '*.au' -exec rm {} +"
-prop_checkFindActionPrecedence2 = verifyNot checkFindActionPrecedence "find . -name '*.wav' -o \\( -name '*.au' -exec rm {} + \\)"
-prop_checkFindActionPrecedence3 = verifyNot checkFindActionPrecedence "find . -name '*.wav' -o -name '*.au'"
+-- |
+-- >>> prop $ verify checkFindActionPrecedence "find . -name '*.wav' -o -name '*.au' -exec rm {} +"
+-- >>> prop $ verifyNot checkFindActionPrecedence "find . -name '*.wav' -o \\( -name '*.au' -exec rm {} + \\)"
+-- >>> prop $ verifyNot checkFindActionPrecedence "find . -name '*.wav' -o -name '*.au'"
checkFindActionPrecedence = CommandCheck (Basename "find") (f . arguments)
where
pattern = [isMatch, const True, isParam ["-o", "-or"], isMatch, const True, isAction]
@@ -398,28 +404,29 @@ checkFindActionPrecedence = CommandCheck (Basename "find") (f . arguments)
warnFor t = warn (getId t) 2146 "This action ignores everything before the -o. Use \\( \\) to group."
-prop_checkMkdirDashPM0 = verify checkMkdirDashPM "mkdir -p -m 0755 a/b"
-prop_checkMkdirDashPM1 = verify checkMkdirDashPM "mkdir -pm 0755 $dir"
-prop_checkMkdirDashPM2 = verify checkMkdirDashPM "mkdir -vpm 0755 a/b"
-prop_checkMkdirDashPM3 = verify checkMkdirDashPM "mkdir -pm 0755 -v a/b"
-prop_checkMkdirDashPM4 = verify checkMkdirDashPM "mkdir --parents --mode=0755 a/b"
-prop_checkMkdirDashPM5 = verify checkMkdirDashPM "mkdir --parents --mode 0755 a/b"
-prop_checkMkdirDashPM6 = verify checkMkdirDashPM "mkdir -p --mode=0755 a/b"
-prop_checkMkdirDashPM7 = verify checkMkdirDashPM "mkdir --parents -m 0755 a/b"
-prop_checkMkdirDashPM8 = verifyNot checkMkdirDashPM "mkdir -p a/b"
-prop_checkMkdirDashPM9 = verifyNot checkMkdirDashPM "mkdir -m 0755 a/b"
-prop_checkMkdirDashPM10 = verifyNot checkMkdirDashPM "mkdir a/b"
-prop_checkMkdirDashPM11 = verifyNot checkMkdirDashPM "mkdir --parents a/b"
-prop_checkMkdirDashPM12 = verifyNot checkMkdirDashPM "mkdir --mode=0755 a/b"
-prop_checkMkdirDashPM13 = verifyNot checkMkdirDashPM "mkdir_func -pm 0755 a/b"
-prop_checkMkdirDashPM14 = verifyNot checkMkdirDashPM "mkdir -p -m 0755 singlelevel"
-prop_checkMkdirDashPM15 = verifyNot checkMkdirDashPM "mkdir -p -m 0755 ../bin"
-prop_checkMkdirDashPM16 = verify checkMkdirDashPM "mkdir -p -m 0755 ../bin/laden"
-prop_checkMkdirDashPM17 = verifyNot checkMkdirDashPM "mkdir -p -m 0755 ./bin"
-prop_checkMkdirDashPM18 = verify checkMkdirDashPM "mkdir -p -m 0755 ./bin/laden"
-prop_checkMkdirDashPM19 = verifyNot checkMkdirDashPM "mkdir -p -m 0755 ./../bin"
-prop_checkMkdirDashPM20 = verifyNot checkMkdirDashPM "mkdir -p -m 0755 .././bin"
-prop_checkMkdirDashPM21 = verifyNot checkMkdirDashPM "mkdir -p -m 0755 ../../bin"
+-- |
+-- >>> prop $ verify checkMkdirDashPM "mkdir -p -m 0755 a/b"
+-- >>> prop $ verify checkMkdirDashPM "mkdir -pm 0755 $dir"
+-- >>> prop $ verify checkMkdirDashPM "mkdir -vpm 0755 a/b"
+-- >>> prop $ verify checkMkdirDashPM "mkdir -pm 0755 -v a/b"
+-- >>> prop $ verify checkMkdirDashPM "mkdir --parents --mode=0755 a/b"
+-- >>> prop $ verify checkMkdirDashPM "mkdir --parents --mode 0755 a/b"
+-- >>> prop $ verify checkMkdirDashPM "mkdir -p --mode=0755 a/b"
+-- >>> prop $ verify checkMkdirDashPM "mkdir --parents -m 0755 a/b"
+-- >>> prop $ verifyNot checkMkdirDashPM "mkdir -p a/b"
+-- >>> prop $ verifyNot checkMkdirDashPM "mkdir -m 0755 a/b"
+-- >>> prop $ verifyNot checkMkdirDashPM "mkdir a/b"
+-- >>> prop $ verifyNot checkMkdirDashPM "mkdir --parents a/b"
+-- >>> prop $ verifyNot checkMkdirDashPM "mkdir --mode=0755 a/b"
+-- >>> prop $ verifyNot checkMkdirDashPM "mkdir_func -pm 0755 a/b"
+-- >>> prop $ verifyNot checkMkdirDashPM "mkdir -p -m 0755 singlelevel"
+-- >>> prop $ verifyNot checkMkdirDashPM "mkdir -p -m 0755 ../bin"
+-- >>> prop $ verify checkMkdirDashPM "mkdir -p -m 0755 ../bin/laden"
+-- >>> prop $ verifyNot checkMkdirDashPM "mkdir -p -m 0755 ./bin"
+-- >>> prop $ verify checkMkdirDashPM "mkdir -p -m 0755 ./bin/laden"
+-- >>> prop $ verifyNot checkMkdirDashPM "mkdir -p -m 0755 ./../bin"
+-- >>> prop $ verifyNot checkMkdirDashPM "mkdir -p -m 0755 .././bin"
+-- >>> prop $ verifyNot checkMkdirDashPM "mkdir -p -m 0755 ../../bin"
checkMkdirDashPM = CommandCheck (Basename "mkdir") check
where
check t = potentially $ do
@@ -435,13 +442,14 @@ checkMkdirDashPM = CommandCheck (Basename "mkdir") check
re = mkRegex "^(\\.\\.?\\/)+[^/]+$"
-prop_checkNonportableSignals1 = verify checkNonportableSignals "trap f 8"
-prop_checkNonportableSignals2 = verifyNot checkNonportableSignals "trap f 0"
-prop_checkNonportableSignals3 = verifyNot checkNonportableSignals "trap f 14"
-prop_checkNonportableSignals4 = verify checkNonportableSignals "trap f SIGKILL"
-prop_checkNonportableSignals5 = verify checkNonportableSignals "trap f 9"
-prop_checkNonportableSignals6 = verify checkNonportableSignals "trap f stop"
-prop_checkNonportableSignals7 = verifyNot checkNonportableSignals "trap 'stop' int"
+-- |
+-- >>> prop $ verify checkNonportableSignals "trap f 8"
+-- >>> prop $ verifyNot checkNonportableSignals "trap f 0"
+-- >>> prop $ verifyNot checkNonportableSignals "trap f 14"
+-- >>> prop $ verify checkNonportableSignals "trap f SIGKILL"
+-- >>> prop $ verify checkNonportableSignals "trap f 9"
+-- >>> prop $ verify checkNonportableSignals "trap f stop"
+-- >>> prop $ verifyNot checkNonportableSignals "trap 'stop' int"
checkNonportableSignals = CommandCheck (Exactly "trap") (f . arguments)
where
f args = case args of
@@ -470,10 +478,11 @@ checkNonportableSignals = CommandCheck (Exactly "trap") (f . arguments)
"SIGKILL/SIGSTOP can not be trapped."
-prop_checkInteractiveSu1 = verify checkInteractiveSu "su; rm file; su $USER"
-prop_checkInteractiveSu2 = verify checkInteractiveSu "su foo; something; exit"
-prop_checkInteractiveSu3 = verifyNot checkInteractiveSu "echo rm | su foo"
-prop_checkInteractiveSu4 = verifyNot checkInteractiveSu "su root < script"
+-- |
+-- >>> prop $ verify checkInteractiveSu "su; rm file; su $USER"
+-- >>> prop $ verify checkInteractiveSu "su foo; something; exit"
+-- >>> prop $ verifyNot checkInteractiveSu "echo rm | su foo"
+-- >>> prop $ verifyNot checkInteractiveSu "su root < script"
checkInteractiveSu = CommandCheck (Basename "su") f
where
f cmd = when (length (arguments cmd) <= 1) $ do
@@ -488,11 +497,13 @@ checkInteractiveSu = CommandCheck (Basename "su") f
undirected _ = True
+-- |
-- This is hard to get right without properly parsing ssh args
-prop_checkSshCmdStr1 = verify checkSshCommandString "ssh host \"echo $PS1\""
-prop_checkSshCmdStr2 = verifyNot checkSshCommandString "ssh host \"ls foo\""
-prop_checkSshCmdStr3 = verifyNot checkSshCommandString "ssh \"$host\""
-prop_checkSshCmdStr4 = verifyNot checkSshCommandString "ssh -i key \"$host\""
+--
+-- >>> prop $ verify checkSshCommandString "ssh host \"echo $PS1\""
+-- >>> prop $ verifyNot checkSshCommandString "ssh host \"ls foo\""
+-- >>> prop $ verifyNot checkSshCommandString "ssh \"$host\""
+-- >>> prop $ verifyNot checkSshCommandString "ssh -i key \"$host\""
checkSshCommandString = CommandCheck (Basename "ssh") (f . arguments)
where
isOption x = "-" `isPrefixOf` (concat $ oversimplify x)
@@ -508,24 +519,25 @@ checkSshCommandString = CommandCheck (Basename "ssh") (f . arguments)
checkArg _ = return ()
-prop_checkPrintfVar1 = verify checkPrintfVar "printf \"Lol: $s\""
-prop_checkPrintfVar2 = verifyNot checkPrintfVar "printf 'Lol: $s'"
-prop_checkPrintfVar3 = verify checkPrintfVar "printf -v cow $(cmd)"
-prop_checkPrintfVar4 = verifyNot checkPrintfVar "printf \"%${count}s\" var"
-prop_checkPrintfVar5 = verify checkPrintfVar "printf '%s %s %s' foo bar"
-prop_checkPrintfVar6 = verify checkPrintfVar "printf foo bar baz"
-prop_checkPrintfVar7 = verify checkPrintfVar "printf -- foo bar baz"
-prop_checkPrintfVar8 = verifyNot checkPrintfVar "printf '%s %s %s' \"${var[@]}\""
-prop_checkPrintfVar9 = verifyNot checkPrintfVar "printf '%s %s %s\\n' *.png"
-prop_checkPrintfVar10= verifyNot checkPrintfVar "printf '%s %s %s' foo bar baz"
-prop_checkPrintfVar11= verifyNot checkPrintfVar "printf '%(%s%s)T' -1"
-prop_checkPrintfVar12= verify checkPrintfVar "printf '%s %s\\n' 1 2 3"
-prop_checkPrintfVar13= verifyNot checkPrintfVar "printf '%s %s\\n' 1 2 3 4"
-prop_checkPrintfVar14= verify checkPrintfVar "printf '%*s\\n' 1"
-prop_checkPrintfVar15= verifyNot checkPrintfVar "printf '%*s\\n' 1 2"
-prop_checkPrintfVar16= verifyNot checkPrintfVar "printf $'string'"
-prop_checkPrintfVar17= verify checkPrintfVar "printf '%-*s\\n' 1"
-prop_checkPrintfVar18= verifyNot checkPrintfVar "printf '%-*s\\n' 1 2"
+-- |
+-- >>> prop $ verify checkPrintfVar "printf \"Lol: $s\""
+-- >>> prop $ verifyNot checkPrintfVar "printf 'Lol: $s'"
+-- >>> prop $ verify checkPrintfVar "printf -v cow $(cmd)"
+-- >>> prop $ verifyNot checkPrintfVar "printf \"%${count}s\" var"
+-- >>> prop $ verify checkPrintfVar "printf '%s %s %s' foo bar"
+-- >>> prop $ verify checkPrintfVar "printf foo bar baz"
+-- >>> prop $ verify checkPrintfVar "printf -- foo bar baz"
+-- >>> prop $ verifyNot checkPrintfVar "printf '%s %s %s' \"${var[@]}\""
+-- >>> prop $ verifyNot checkPrintfVar "printf '%s %s %s\\n' *.png"
+-- >>> prop $ verifyNot checkPrintfVar "printf '%s %s %s' foo bar baz"
+-- >>> prop $ verifyNot checkPrintfVar "printf '%(%s%s)T' -1"
+-- >>> prop $ verify checkPrintfVar "printf '%s %s\\n' 1 2 3"
+-- >>> prop $ verifyNot checkPrintfVar "printf '%s %s\\n' 1 2 3 4"
+-- >>> prop $ verify checkPrintfVar "printf '%*s\\n' 1"
+-- >>> prop $ verifyNot checkPrintfVar "printf '%*s\\n' 1 2"
+-- >>> prop $ verifyNot checkPrintfVar "printf $'string'"
+-- >>> prop $ verify checkPrintfVar "printf '%-*s\\n' 1"
+-- >>> prop $ verifyNot checkPrintfVar "printf '%-*s\\n' 1 2"
checkPrintfVar = CommandCheck (Exactly "printf") (f . arguments) where
f (doubledash:rest) | getLiteralString doubledash == Just "--" = f rest
f (dashv:var:rest) | getLiteralString dashv == Just "-v" = f rest
@@ -574,24 +586,26 @@ checkPrintfVar = CommandCheck (Exactly "printf") (f . arguments) where
-prop_checkUuoeCmd1 = verify checkUuoeCmd "echo $(date)"
-prop_checkUuoeCmd2 = verify checkUuoeCmd "echo `date`"
-prop_checkUuoeCmd3 = verify checkUuoeCmd "echo \"$(date)\""
-prop_checkUuoeCmd4 = verify checkUuoeCmd "echo \"`date`\""
-prop_checkUuoeCmd5 = verifyNot checkUuoeCmd "echo \"The time is $(date)\""
-prop_checkUuoeCmd6 = verifyNot checkUuoeCmd "echo \"$(>> prop $ verify checkUuoeCmd "echo $(date)"
+-- >>> prop $ verify checkUuoeCmd "echo `date`"
+-- >>> prop $ verify checkUuoeCmd "echo \"$(date)\""
+-- >>> prop $ verify checkUuoeCmd "echo \"`date`\""
+-- >>> prop $ verifyNot checkUuoeCmd "echo \"The time is $(date)\""
+-- >>> prop $ verifyNot checkUuoeCmd "echo \"$(>> prop $ verify checkSetAssignment "set foo 42"
+-- >>> prop $ verify checkSetAssignment "set foo = 42"
+-- >>> prop $ verify checkSetAssignment "set foo=42"
+-- >>> prop $ verifyNot checkSetAssignment "set -- if=/dev/null"
+-- >>> prop $ verifyNot checkSetAssignment "set 'a=5'"
+-- >>> prop $ verifyNot checkSetAssignment "set"
checkSetAssignment = CommandCheck (Exactly "set") (f . arguments)
where
f (var:value:rest) =
@@ -611,10 +625,11 @@ checkSetAssignment = CommandCheck (Exactly "set") (f . arguments)
literal _ = "*"
-prop_checkExportedExpansions1 = verify checkExportedExpansions "export $foo"
-prop_checkExportedExpansions2 = verify checkExportedExpansions "export \"$foo\""
-prop_checkExportedExpansions3 = verifyNot checkExportedExpansions "export foo"
-prop_checkExportedExpansions4 = verifyNot checkExportedExpansions "export ${foo?}"
+-- |
+-- >>> prop $ verify checkExportedExpansions "export $foo"
+-- >>> prop $ verify checkExportedExpansions "export \"$foo\""
+-- >>> prop $ verifyNot checkExportedExpansions "export foo"
+-- >>> prop $ verifyNot checkExportedExpansions "export ${foo?}"
checkExportedExpansions = CommandCheck (Exactly "export") (mapM_ check . arguments)
where
check t = potentially $ do
@@ -623,14 +638,15 @@ checkExportedExpansions = CommandCheck (Exactly "export") (mapM_ check . argumen
return . warn (getId t) 2163 $
"This does not export '" ++ name ++ "'. Remove $/${} for that, or use ${var?} to quiet."
-prop_checkReadExpansions1 = verify checkReadExpansions "read $var"
-prop_checkReadExpansions2 = verify checkReadExpansions "read -r $var"
-prop_checkReadExpansions3 = verifyNot checkReadExpansions "read -p $var"
-prop_checkReadExpansions4 = verifyNot checkReadExpansions "read -rd $delim name"
-prop_checkReadExpansions5 = verify checkReadExpansions "read \"$var\""
-prop_checkReadExpansions6 = verify checkReadExpansions "read -a $var"
-prop_checkReadExpansions7 = verifyNot checkReadExpansions "read $1"
-prop_checkReadExpansions8 = verifyNot checkReadExpansions "read ${var?}"
+-- |
+-- >>> prop $ verify checkReadExpansions "read $var"
+-- >>> prop $ verify checkReadExpansions "read -r $var"
+-- >>> prop $ verifyNot checkReadExpansions "read -p $var"
+-- >>> prop $ verifyNot checkReadExpansions "read -rd $delim name"
+-- >>> prop $ verify checkReadExpansions "read \"$var\""
+-- >>> prop $ verify checkReadExpansions "read -a $var"
+-- >>> prop $ verifyNot checkReadExpansions "read $1"
+-- >>> prop $ verifyNot checkReadExpansions "read ${var?}"
checkReadExpansions = CommandCheck (Exactly "read") check
where
options = getGnuOpts "sreu:n:N:i:p:a:"
@@ -657,9 +673,10 @@ getSingleUnmodifiedVariable word =
in guard (contents == name) >> return t
_ -> Nothing
-prop_checkAliasesUsesArgs1 = verify checkAliasesUsesArgs "alias a='cp $1 /a'"
-prop_checkAliasesUsesArgs2 = verifyNot checkAliasesUsesArgs "alias $1='foo'"
-prop_checkAliasesUsesArgs3 = verify checkAliasesUsesArgs "alias a=\"echo \\${@}\""
+-- |
+-- >>> prop $ verify checkAliasesUsesArgs "alias a='cp $1 /a'"
+-- >>> prop $ verifyNot checkAliasesUsesArgs "alias $1='foo'"
+-- >>> prop $ verify checkAliasesUsesArgs "alias a=\"echo \\${@}\""
checkAliasesUsesArgs = CommandCheck (Exactly "alias") (f . arguments)
where
re = mkRegex "\\$\\{?[0-9*@]"
@@ -671,9 +688,10 @@ checkAliasesUsesArgs = CommandCheck (Exactly "alias") (f . arguments)
"Aliases can't use positional parameters. Use a function."
-prop_checkAliasesExpandEarly1 = verify checkAliasesExpandEarly "alias foo=\"echo $PWD\""
-prop_checkAliasesExpandEarly2 = verifyNot checkAliasesExpandEarly "alias -p"
-prop_checkAliasesExpandEarly3 = verifyNot checkAliasesExpandEarly "alias foo='echo {1..10}'"
+-- |
+-- >>> prop $ verify checkAliasesExpandEarly "alias foo=\"echo $PWD\""
+-- >>> prop $ verifyNot checkAliasesExpandEarly "alias -p"
+-- >>> prop $ verifyNot checkAliasesExpandEarly "alias foo='echo {1..10}'"
checkAliasesExpandEarly = CommandCheck (Exactly "alias") (f . arguments)
where
f = mapM_ checkArg
@@ -683,8 +701,8 @@ checkAliasesExpandEarly = CommandCheck (Exactly "alias") (f . arguments)
checkArg _ = return ()
-prop_checkUnsetGlobs1 = verify checkUnsetGlobs "unset foo[1]"
-prop_checkUnsetGlobs2 = verifyNot checkUnsetGlobs "unset foo"
+-- >>> prop $ verify checkUnsetGlobs "unset foo[1]"
+-- >>> prop $ verifyNot checkUnsetGlobs "unset foo"
checkUnsetGlobs = CommandCheck (Exactly "unset") (mapM_ check . arguments)
where
check arg =
@@ -692,14 +710,15 @@ checkUnsetGlobs = CommandCheck (Exactly "unset") (mapM_ check . arguments)
warn (getId arg) 2184 "Quote arguments to unset so they're not glob expanded."
-prop_checkFindWithoutPath1 = verify checkFindWithoutPath "find -type f"
-prop_checkFindWithoutPath2 = verify checkFindWithoutPath "find"
-prop_checkFindWithoutPath3 = verifyNot checkFindWithoutPath "find . -type f"
-prop_checkFindWithoutPath4 = verifyNot checkFindWithoutPath "find -H -L \"$path\" -print"
-prop_checkFindWithoutPath5 = verifyNot checkFindWithoutPath "find -O3 ."
-prop_checkFindWithoutPath6 = verifyNot checkFindWithoutPath "find -D exec ."
-prop_checkFindWithoutPath7 = verifyNot checkFindWithoutPath "find --help"
-prop_checkFindWithoutPath8 = verifyNot checkFindWithoutPath "find -Hx . -print"
+-- |
+-- >>> prop $ verify checkFindWithoutPath "find -type f"
+-- >>> prop $ verify checkFindWithoutPath "find"
+-- >>> prop $ verifyNot checkFindWithoutPath "find . -type f"
+-- >>> prop $ verifyNot checkFindWithoutPath "find -H -L \"$path\" -print"
+-- >>> prop $ verifyNot checkFindWithoutPath "find -O3 ."
+-- >>> prop $ verifyNot checkFindWithoutPath "find -D exec ."
+-- >>> prop $ verifyNot checkFindWithoutPath "find --help"
+-- >>> prop $ verifyNot checkFindWithoutPath "find -Hx . -print"
checkFindWithoutPath = CommandCheck (Basename "find") f
where
f t@(T_SimpleCommand _ _ (cmd:args)) =
@@ -718,10 +737,11 @@ checkFindWithoutPath = CommandCheck (Basename "find") f
leadingFlagChars="-EHLPXdfsxO0123456789"
-prop_checkTimeParameters1 = verify checkTimeParameters "time -f lol sleep 10"
-prop_checkTimeParameters2 = verifyNot checkTimeParameters "time sleep 10"
-prop_checkTimeParameters3 = verifyNot checkTimeParameters "time -p foo"
-prop_checkTimeParameters4 = verifyNot checkTimeParameters "command time -f lol sleep 10"
+-- |
+-- >>> prop $ verify checkTimeParameters "time -f lol sleep 10"
+-- >>> prop $ verifyNot checkTimeParameters "time sleep 10"
+-- >>> prop $ verifyNot checkTimeParameters "time -p foo"
+-- >>> prop $ verifyNot checkTimeParameters "command time -f lol sleep 10"
checkTimeParameters = CommandCheck (Exactly "time") f
where
f (T_SimpleCommand _ _ (cmd:args:_)) =
@@ -732,9 +752,10 @@ checkTimeParameters = CommandCheck (Exactly "time") f
f _ = return ()
-prop_checkTimedCommand1 = verify checkTimedCommand "#!/bin/sh\ntime -p foo | bar"
-prop_checkTimedCommand2 = verify checkTimedCommand "#!/bin/dash\ntime ( foo; bar; )"
-prop_checkTimedCommand3 = verifyNot checkTimedCommand "#!/bin/sh\ntime sleep 1"
+-- |
+-- >>> prop $ verify checkTimedCommand "#!/bin/sh\ntime -p foo | bar"
+-- >>> prop $ verify checkTimedCommand "#!/bin/dash\ntime ( foo; bar; )"
+-- >>> prop $ verifyNot checkTimedCommand "#!/bin/sh\ntime sleep 1"
checkTimedCommand = CommandCheck (Exactly "time") f where
f (T_SimpleCommand _ _ (c:args@(_:_))) =
whenShell [Sh, Dash] $ do
@@ -758,32 +779,37 @@ checkTimedCommand = CommandCheck (Exactly "time") f where
T_SimpleCommand {} -> return True
_ -> return False
-prop_checkLocalScope1 = verify checkLocalScope "local foo=3"
-prop_checkLocalScope2 = verifyNot checkLocalScope "f() { local foo=3; }"
+-- |
+-- >>> prop $ verify checkLocalScope "local foo=3"
+-- >>> prop $ verifyNot checkLocalScope "f() { local foo=3; }"
checkLocalScope = CommandCheck (Exactly "local") $ \t ->
whenShell [Bash, Dash] $ do -- Ksh allows it, Sh doesn't support local
path <- getPathM t
unless (any isFunction path) $
err (getId $ getCommandTokenOrThis t) 2168 "'local' is only valid in functions."
-prop_checkDeprecatedTempfile1 = verify checkDeprecatedTempfile "var=$(tempfile)"
-prop_checkDeprecatedTempfile2 = verifyNot checkDeprecatedTempfile "tempfile=$(mktemp)"
+-- |
+-- >>> prop $ verify checkDeprecatedTempfile "var=$(tempfile)"
+-- >>> prop $ verifyNot checkDeprecatedTempfile "tempfile=$(mktemp)"
checkDeprecatedTempfile = CommandCheck (Basename "tempfile") $
\t -> warn (getId $ getCommandTokenOrThis t) 2186 "tempfile is deprecated. Use mktemp instead."
-prop_checkDeprecatedEgrep = verify checkDeprecatedEgrep "egrep '.+'"
+-- |
+-- >>> prop $ verify checkDeprecatedEgrep "egrep '.+'"
checkDeprecatedEgrep = CommandCheck (Basename "egrep") $
\t -> info (getId $ getCommandTokenOrThis t) 2196 "egrep is non-standard and deprecated. Use grep -E instead."
-prop_checkDeprecatedFgrep = verify checkDeprecatedFgrep "fgrep '*' files"
+-- |
+-- >>> prop $ verify checkDeprecatedFgrep "fgrep '*' files"
checkDeprecatedFgrep = CommandCheck (Basename "fgrep") $
\t -> info (getId $ getCommandTokenOrThis t) 2197 "fgrep is non-standard and deprecated. Use grep -F instead."
-prop_checkWhileGetoptsCase1 = verify checkWhileGetoptsCase "while getopts 'a:b' x; do case $x in a) foo;; esac; done"
-prop_checkWhileGetoptsCase2 = verify checkWhileGetoptsCase "while getopts 'a:' x; do case $x in a) foo;; b) bar;; esac; done"
-prop_checkWhileGetoptsCase3 = verifyNot checkWhileGetoptsCase "while getopts 'a:b' x; do case $x in a) foo;; b) bar;; *) :;esac; done"
-prop_checkWhileGetoptsCase4 = verifyNot checkWhileGetoptsCase "while getopts 'a:123' x; do case $x in a) foo;; [0-9]) bar;; esac; done"
-prop_checkWhileGetoptsCase5 = verifyNot checkWhileGetoptsCase "while getopts 'a:' x; do case $x in a) foo;; \\?) bar;; *) baz;; esac; done"
+-- |
+-- >>> prop $ verify checkWhileGetoptsCase "while getopts 'a:b' x; do case $x in a) foo;; esac; done"
+-- >>> prop $ verify checkWhileGetoptsCase "while getopts 'a:' x; do case $x in a) foo;; b) bar;; esac; done"
+-- >>> prop $ verifyNot checkWhileGetoptsCase "while getopts 'a:b' x; do case $x in a) foo;; b) bar;; *) :;esac; done"
+-- >>> prop $ verifyNot checkWhileGetoptsCase "while getopts 'a:123' x; do case $x in a) foo;; [0-9]) bar;; esac; done"
+-- >>> prop $ verifyNot checkWhileGetoptsCase "while getopts 'a:' x; do case $x in a) foo;; \\?) bar;; *) baz;; esac; done"
checkWhileGetoptsCase = CommandCheck (Exactly "getopts") f
where
f :: Token -> Analysis
@@ -848,19 +874,20 @@ checkWhileGetoptsCase = CommandCheck (Exactly "getopts") f
T_Redirecting _ _ x@(T_CaseExpression {}) -> return x
_ -> Nothing
-prop_checkCatastrophicRm1 = verify checkCatastrophicRm "rm -r $1/$2"
-prop_checkCatastrophicRm2 = verify checkCatastrophicRm "rm -r /home/$foo"
-prop_checkCatastrophicRm3 = verifyNot checkCatastrophicRm "rm -r /home/${USER:?}/*"
-prop_checkCatastrophicRm4 = verify checkCatastrophicRm "rm -fr /home/$(whoami)/*"
-prop_checkCatastrophicRm5 = verifyNot checkCatastrophicRm "rm -r /home/${USER:-thing}/*"
-prop_checkCatastrophicRm6 = verify checkCatastrophicRm "rm --recursive /etc/*$config*"
-prop_checkCatastrophicRm8 = verify checkCatastrophicRm "rm -rf /home"
-prop_checkCatastrophicRm10= verifyNot checkCatastrophicRm "rm -r \"${DIR}\"/{.gitignore,.gitattributes,ci}"
-prop_checkCatastrophicRm11= verify checkCatastrophicRm "rm -r /{bin,sbin}/$exec"
-prop_checkCatastrophicRm12= verify checkCatastrophicRm "rm -r /{{usr,},{bin,sbin}}/$exec"
-prop_checkCatastrophicRm13= verifyNot checkCatastrophicRm "rm -r /{{a,b},{c,d}}/$exec"
-prop_checkCatastrophicRmA = verify checkCatastrophicRm "rm -rf /usr /lib/nvidia-current/xorg/xorg"
-prop_checkCatastrophicRmB = verify checkCatastrophicRm "rm -rf \"$STEAMROOT/\"*"
+-- |
+-- >>> prop $ verify checkCatastrophicRm "rm -r $1/$2"
+-- >>> prop $ verify checkCatastrophicRm "rm -r /home/$foo"
+-- >>> prop $ verifyNot checkCatastrophicRm "rm -r /home/${USER:?}/*"
+-- >>> prop $ verify checkCatastrophicRm "rm -fr /home/$(whoami)/*"
+-- >>> prop $ verifyNot checkCatastrophicRm "rm -r /home/${USER:-thing}/*"
+-- >>> prop $ verify checkCatastrophicRm "rm --recursive /etc/*$config*"
+-- >>> prop $ verify checkCatastrophicRm "rm -rf /home"
+-- >>> prop $ verifyNot checkCatastrophicRm "rm -r \"${DIR}\"/{.gitignore,.gitattributes,ci}"
+-- >>> prop $ verify checkCatastrophicRm "rm -r /{bin,sbin}/$exec"
+-- >>> prop $ verify checkCatastrophicRm "rm -r /{{usr,},{bin,sbin}}/$exec"
+-- >>> prop $ verifyNot checkCatastrophicRm "rm -r /{{a,b},{c,d}}/$exec"
+-- >>> prop $ verify checkCatastrophicRm "rm -rf /usr /lib/nvidia-current/xorg/xorg"
+-- >>> prop $ verify checkCatastrophicRm "rm -rf \"$STEAMROOT/\"*"
checkCatastrophicRm = CommandCheck (Basename "rm") $ \t ->
when (isRecursive t) $
mapM_ (mapM_ checkWord . braceExpand) $ arguments t
@@ -909,8 +936,9 @@ checkCatastrophicRm = CommandCheck (Basename "rm") $ \t ->
["", "/", "/*", "/*/*"] >>= (\x -> map (++x) paths)
-prop_checkLetUsage1 = verify checkLetUsage "let a=1"
-prop_checkLetUsage2 = verifyNot checkLetUsage "(( a=1 ))"
+-- |
+-- >>> prop $ verify checkLetUsage "let a=1"
+-- >>> prop $ verifyNot checkLetUsage "(( a=1 ))"
checkLetUsage = CommandCheck (Exactly "let") f
where
f t = whenShell [Bash,Ksh] $ do
@@ -930,15 +958,16 @@ missingDestination handler token = do
any (\x -> x /= "" && x `isPrefixOf` "target-directory") $
map snd args
-prop_checkMvArguments1 = verify checkMvArguments "mv 'foo bar'"
-prop_checkMvArguments2 = verifyNot checkMvArguments "mv foo bar"
-prop_checkMvArguments3 = verifyNot checkMvArguments "mv 'foo bar'{,bak}"
-prop_checkMvArguments4 = verifyNot checkMvArguments "mv \"$@\""
-prop_checkMvArguments5 = verifyNot checkMvArguments "mv -t foo bar"
-prop_checkMvArguments6 = verifyNot checkMvArguments "mv --target-directory=foo bar"
-prop_checkMvArguments7 = verifyNot checkMvArguments "mv --target-direc=foo bar"
-prop_checkMvArguments8 = verifyNot checkMvArguments "mv --version"
-prop_checkMvArguments9 = verifyNot checkMvArguments "mv \"${!var}\""
+-- |
+-- >>> prop $ verify checkMvArguments "mv 'foo bar'"
+-- >>> prop $ verifyNot checkMvArguments "mv foo bar"
+-- >>> prop $ verifyNot checkMvArguments "mv 'foo bar'{,bak}"
+-- >>> prop $ verifyNot checkMvArguments "mv \"$@\""
+-- >>> prop $ verifyNot checkMvArguments "mv -t foo bar"
+-- >>> prop $ verifyNot checkMvArguments "mv --target-directory=foo bar"
+-- >>> prop $ verifyNot checkMvArguments "mv --target-direc=foo bar"
+-- >>> prop $ verifyNot checkMvArguments "mv --version"
+-- >>> prop $ verifyNot checkMvArguments "mv \"${!var}\""
checkMvArguments = CommandCheck (Basename "mv") $ missingDestination f
where
f t = err (getId t) 2224 "This mv has no destination. Check the arguments."
@@ -952,9 +981,10 @@ checkLnArguments = CommandCheck (Basename "ln") $ missingDestination f
f t = warn (getId t) 2226 "This ln has no destination. Check the arguments, or specify '.' explicitly."
-prop_checkFindRedirections1 = verify checkFindRedirections "find . -exec echo {} > file \\;"
-prop_checkFindRedirections2 = verifyNot checkFindRedirections "find . -exec echo {} \\; > file"
-prop_checkFindRedirections3 = verifyNot checkFindRedirections "find . -execdir sh -c 'foo > file' \\;"
+-- |
+-- >>> prop $ verify checkFindRedirections "find . -exec echo {} > file \\;"
+-- >>> prop $ verifyNot checkFindRedirections "find . -exec echo {} \\; > file"
+-- >>> prop $ verifyNot checkFindRedirections "find . -execdir sh -c 'foo > file' \\;"
checkFindRedirections = CommandCheck (Basename "find") f
where
f t = do
@@ -969,17 +999,18 @@ checkFindRedirections = CommandCheck (Basename "find") f
"Redirection applies to the find command itself. Rewrite to work per action (or move to end)."
_ -> return ()
-prop_checkWhich = verify checkWhich "which '.+'"
+-- >>> prop $ verify checkWhich "which '.+'"
checkWhich = CommandCheck (Basename "which") $
\t -> info (getId $ getCommandTokenOrThis t) 2230 "which is non-standard. Use builtin 'command -v' instead."
-prop_checkSudoRedirect1 = verify checkSudoRedirect "sudo echo 3 > /proc/file"
-prop_checkSudoRedirect2 = verify checkSudoRedirect "sudo cmd < input"
-prop_checkSudoRedirect3 = verify checkSudoRedirect "sudo cmd >> file"
-prop_checkSudoRedirect4 = verify checkSudoRedirect "sudo cmd &> file"
-prop_checkSudoRedirect5 = verifyNot checkSudoRedirect "sudo cmd 2>&1"
-prop_checkSudoRedirect6 = verifyNot checkSudoRedirect "sudo cmd 2> log"
-prop_checkSudoRedirect7 = verifyNot checkSudoRedirect "sudo cmd > /dev/null 2>&1"
+-- |
+-- >>> prop $ verify checkSudoRedirect "sudo echo 3 > /proc/file"
+-- >>> prop $ verify checkSudoRedirect "sudo cmd < input"
+-- >>> prop $ verify checkSudoRedirect "sudo cmd >> file"
+-- >>> prop $ verify checkSudoRedirect "sudo cmd &> file"
+-- >>> prop $ verifyNot checkSudoRedirect "sudo cmd 2>&1"
+-- >>> prop $ verifyNot checkSudoRedirect "sudo cmd 2> log"
+-- >>> prop $ verifyNot checkSudoRedirect "sudo cmd > /dev/null 2>&1"
checkSudoRedirect = CommandCheck (Basename "sudo") f
where
f t = do
@@ -1003,13 +1034,14 @@ checkSudoRedirect = CommandCheck (Basename "sudo") f
warnAbout _ = return ()
special file = concat (oversimplify file) == "/dev/null"
-prop_checkSudoArgs1 = verify checkSudoArgs "sudo cd /root"
-prop_checkSudoArgs2 = verify checkSudoArgs "sudo export x=3"
-prop_checkSudoArgs3 = verifyNot checkSudoArgs "sudo ls /usr/local/protected"
-prop_checkSudoArgs4 = verifyNot checkSudoArgs "sudo ls && export x=3"
-prop_checkSudoArgs5 = verifyNot checkSudoArgs "sudo echo ls"
-prop_checkSudoArgs6 = verifyNot checkSudoArgs "sudo -n -u export ls"
-prop_checkSudoArgs7 = verifyNot checkSudoArgs "sudo docker export foo"
+-- |
+-- >>> prop $ verify checkSudoArgs "sudo cd /root"
+-- >>> prop $ verify checkSudoArgs "sudo export x=3"
+-- >>> prop $ verifyNot checkSudoArgs "sudo ls /usr/local/protected"
+-- >>> prop $ verifyNot checkSudoArgs "sudo ls && export x=3"
+-- >>> prop $ verifyNot checkSudoArgs "sudo echo ls"
+-- >>> prop $ verifyNot checkSudoArgs "sudo -n -u export ls"
+-- >>> prop $ verifyNot checkSudoArgs "sudo docker export foo"
checkSudoArgs = CommandCheck (Basename "sudo") f
where
f t = potentially $ do
@@ -1023,9 +1055,10 @@ checkSudoArgs = CommandCheck (Basename "sudo") f
-- This mess is why ShellCheck prefers not to know.
parseOpts = getBsdOpts "vAknSbEHPa:g:h:p:u:c:T:r:"
-prop_checkSourceArgs1 = verify checkSourceArgs "#!/bin/sh\n. script arg"
-prop_checkSourceArgs2 = verifyNot checkSourceArgs "#!/bin/sh\n. script"
-prop_checkSourceArgs3 = verifyNot checkSourceArgs "#!/bin/bash\n. script arg"
+-- |
+-- >>> prop $ verify checkSourceArgs "#!/bin/sh\n. script arg"
+-- >>> prop $ verifyNot checkSourceArgs "#!/bin/sh\n. script"
+-- >>> prop $ verifyNot checkSourceArgs "#!/bin/bash\n. script arg"
checkSourceArgs = CommandCheck (Exactly ".") f
where
f t = whenShell [Sh, Dash] $
@@ -1033,6 +1066,3 @@ checkSourceArgs = CommandCheck (Exactly ".") f
(file:arg1:_) -> warn (getId arg1) 2240 $
"The dot command does not support arguments in sh/dash. Set them as variables."
_ -> return ()
-
-return []
-runTests = $( [| $(forAllProperties) (quickCheckWithResult (stdArgs { maxSuccess = 1 }) ) |])
diff --git a/src/ShellCheck/Checks/ShellSupport.hs b/src/ShellCheck/Checks/ShellSupport.hs
index 1bc425f..6e4dd54 100644
--- a/src/ShellCheck/Checks/ShellSupport.hs
+++ b/src/ShellCheck/Checks/ShellSupport.hs
@@ -17,9 +17,8 @@
You should have received a copy of the GNU General Public License
along with this program. If not, see .
-}
-{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE FlexibleContexts #-}
-module ShellCheck.Checks.ShellSupport (checker , ShellCheck.Checks.ShellSupport.runTests) where
+module ShellCheck.Checks.ShellSupport (checker) where
import ShellCheck.AST
import ShellCheck.ASTLib
@@ -33,8 +32,6 @@ import Data.Char
import Data.List
import Data.Maybe
import qualified Data.Map as Map
-import Test.QuickCheck.All (forAllProperties)
-import Test.QuickCheck.Test (quickCheckWithResult, stdArgs, maxSuccess)
data ForShell = ForShell [Shell] (Token -> Analysis)
@@ -67,9 +64,10 @@ testChecker (ForShell _ t) =
verify c s = producesComments (testChecker c) s == Just True
verifyNot c s = producesComments (testChecker c) s == Just False
-prop_checkForDecimals1 = verify checkForDecimals "((3.14*c))"
-prop_checkForDecimals2 = verify checkForDecimals "foo[1.2]=bar"
-prop_checkForDecimals3 = verifyNot checkForDecimals "declare -A foo; foo[1.2]=bar"
+-- |
+-- >>> prop $ verify checkForDecimals "((3.14*c))"
+-- >>> prop $ verify checkForDecimals "foo[1.2]=bar"
+-- >>> prop $ verifyNot checkForDecimals "declare -A foo; foo[1.2]=bar"
checkForDecimals = ForShell [Sh, Dash, Bash] f
where
f t@(TA_Expansion id _) = potentially $ do
@@ -80,62 +78,63 @@ checkForDecimals = ForShell [Sh, Dash, Bash] f
f _ = return ()
-prop_checkBashisms = verify checkBashisms "while read a; do :; done < <(a)"
-prop_checkBashisms2 = verify checkBashisms "[ foo -nt bar ]"
-prop_checkBashisms3 = verify checkBashisms "echo $((i++))"
-prop_checkBashisms4 = verify checkBashisms "rm !(*.hs)"
-prop_checkBashisms5 = verify checkBashisms "source file"
-prop_checkBashisms6 = verify checkBashisms "[ \"$a\" == 42 ]"
-prop_checkBashisms7 = verify checkBashisms "echo ${var[1]}"
-prop_checkBashisms8 = verify checkBashisms "echo ${!var[@]}"
-prop_checkBashisms9 = verify checkBashisms "echo ${!var*}"
-prop_checkBashisms10= verify checkBashisms "echo ${var:4:12}"
-prop_checkBashisms11= verifyNot checkBashisms "echo ${var:-4}"
-prop_checkBashisms12= verify checkBashisms "echo ${var//foo/bar}"
-prop_checkBashisms13= verify checkBashisms "exec -c env"
-prop_checkBashisms14= verify checkBashisms "echo -n \"Foo: \""
-prop_checkBashisms15= verify checkBashisms "let n++"
-prop_checkBashisms16= verify checkBashisms "echo $RANDOM"
-prop_checkBashisms17= verify checkBashisms "echo $((RANDOM%6+1))"
-prop_checkBashisms18= verify checkBashisms "foo &> /dev/null"
-prop_checkBashisms19= verify checkBashisms "foo > file*.txt"
-prop_checkBashisms20= verify checkBashisms "read -ra foo"
-prop_checkBashisms21= verify checkBashisms "[ -a foo ]"
-prop_checkBashisms22= verifyNot checkBashisms "[ foo -a bar ]"
-prop_checkBashisms23= verify checkBashisms "trap mything ERR INT"
-prop_checkBashisms24= verifyNot checkBashisms "trap mything INT TERM"
-prop_checkBashisms25= verify checkBashisms "cat < /dev/tcp/host/123"
-prop_checkBashisms26= verify checkBashisms "trap mything ERR SIGTERM"
-prop_checkBashisms27= verify checkBashisms "echo *[^0-9]*"
-prop_checkBashisms28= verify checkBashisms "exec {n}>&2"
-prop_checkBashisms29= verify checkBashisms "echo ${!var}"
-prop_checkBashisms30= verify checkBashisms "printf -v '%s' \"$1\""
-prop_checkBashisms31= verify checkBashisms "printf '%q' \"$1\""
-prop_checkBashisms32= verifyNot checkBashisms "#!/bin/dash\n[ foo -nt bar ]"
-prop_checkBashisms33= verify checkBashisms "#!/bin/sh\necho -n foo"
-prop_checkBashisms34= verifyNot checkBashisms "#!/bin/dash\necho -n foo"
-prop_checkBashisms35= verifyNot checkBashisms "#!/bin/dash\nlocal foo"
-prop_checkBashisms36= verifyNot checkBashisms "#!/bin/dash\nread -p foo -r bar"
-prop_checkBashisms37= verifyNot checkBashisms "HOSTNAME=foo; echo $HOSTNAME"
-prop_checkBashisms38= verify checkBashisms "RANDOM=9; echo $RANDOM"
-prop_checkBashisms39= verify checkBashisms "foo-bar() { true; }"
-prop_checkBashisms40= verify checkBashisms "echo $(/dev/null"
-prop_checkBashisms48= verifyNot checkBashisms "#!/bin/sh\necho $LINENO"
-prop_checkBashisms49= verify checkBashisms "#!/bin/dash\necho $MACHTYPE"
-prop_checkBashisms50= verify checkBashisms "#!/bin/sh\ncmd >& file"
-prop_checkBashisms51= verifyNot checkBashisms "#!/bin/sh\ncmd 2>&1"
-prop_checkBashisms52= verifyNot checkBashisms "#!/bin/sh\ncmd >&2"
-prop_checkBashisms53= verifyNot checkBashisms "#!/bin/sh\nprintf -- -f\n"
-prop_checkBashisms54= verify checkBashisms "#!/bin/sh\nfoo+=bar"
-prop_checkBashisms55= verify checkBashisms "#!/bin/sh\necho ${@%foo}"
-prop_checkBashisms56= verifyNot checkBashisms "#!/bin/sh\necho ${##}"
+-- |
+-- >>> prop $ verify checkBashisms "while read a; do :; done < <(a)"
+-- >>> prop $ verify checkBashisms "[ foo -nt bar ]"
+-- >>> prop $ verify checkBashisms "echo $((i++))"
+-- >>> prop $ verify checkBashisms "rm !(*.hs)"
+-- >>> prop $ verify checkBashisms "source file"
+-- >>> prop $ verify checkBashisms "[ \"$a\" == 42 ]"
+-- >>> prop $ verify checkBashisms "echo ${var[1]}"
+-- >>> prop $ verify checkBashisms "echo ${!var[@]}"
+-- >>> prop $ verify checkBashisms "echo ${!var*}"
+-- >>> prop $ verify checkBashisms "echo ${var:4:12}"
+-- >>> prop $ verifyNot checkBashisms "echo ${var:-4}"
+-- >>> prop $ verify checkBashisms "echo ${var//foo/bar}"
+-- >>> prop $ verify checkBashisms "exec -c env"
+-- >>> prop $ verify checkBashisms "echo -n \"Foo: \""
+-- >>> prop $ verify checkBashisms "let n++"
+-- >>> prop $ verify checkBashisms "echo $RANDOM"
+-- >>> prop $ verify checkBashisms "echo $((RANDOM%6+1))"
+-- >>> prop $ verify checkBashisms "foo &> /dev/null"
+-- >>> prop $ verify checkBashisms "foo > file*.txt"
+-- >>> prop $ verify checkBashisms "read -ra foo"
+-- >>> prop $ verify checkBashisms "[ -a foo ]"
+-- >>> prop $ verifyNot checkBashisms "[ foo -a bar ]"
+-- >>> prop $ verify checkBashisms "trap mything ERR INT"
+-- >>> prop $ verifyNot checkBashisms "trap mything INT TERM"
+-- >>> prop $ verify checkBashisms "cat < /dev/tcp/host/123"
+-- >>> prop $ verify checkBashisms "trap mything ERR SIGTERM"
+-- >>> prop $ verify checkBashisms "echo *[^0-9]*"
+-- >>> prop $ verify checkBashisms "exec {n}>&2"
+-- >>> prop $ verify checkBashisms "echo ${!var}"
+-- >>> prop $ verify checkBashisms "printf -v '%s' \"$1\""
+-- >>> prop $ verify checkBashisms "printf '%q' \"$1\""
+-- >>> prop $ verifyNot checkBashisms "#!/bin/dash\n[ foo -nt bar ]"
+-- >>> prop $ verify checkBashisms "#!/bin/sh\necho -n foo"
+-- >>> prop $ verifyNot checkBashisms "#!/bin/dash\necho -n foo"
+-- >>> prop $ verifyNot checkBashisms "#!/bin/dash\nlocal foo"
+-- >>> prop $ verifyNot checkBashisms "#!/bin/dash\nread -p foo -r bar"
+-- >>> prop $ verifyNot checkBashisms "HOSTNAME=foo; echo $HOSTNAME"
+-- >>> prop $ verify checkBashisms "RANDOM=9; echo $RANDOM"
+-- >>> prop $ verify checkBashisms "foo-bar() { true; }"
+-- >>> prop $ verify checkBashisms "echo $(>> prop $ verify checkBashisms "echo `>> prop $ verify checkBashisms "trap foo int"
+-- >>> prop $ verify checkBashisms "trap foo sigint"
+-- >>> prop $ verifyNot checkBashisms "#!/bin/dash\ntrap foo int"
+-- >>> prop $ verifyNot checkBashisms "#!/bin/dash\ntrap foo INT"
+-- >>> prop $ verify checkBashisms "#!/bin/dash\ntrap foo SIGINT"
+-- >>> prop $ verify checkBashisms "#!/bin/dash\necho foo 42>/dev/null"
+-- >>> prop $ verifyNot checkBashisms "#!/bin/sh\necho $LINENO"
+-- >>> prop $ verify checkBashisms "#!/bin/dash\necho $MACHTYPE"
+-- >>> prop $ verify checkBashisms "#!/bin/sh\ncmd >& file"
+-- >>> prop $ verifyNot checkBashisms "#!/bin/sh\ncmd 2>&1"
+-- >>> prop $ verifyNot checkBashisms "#!/bin/sh\ncmd >&2"
+-- >>> prop $ verifyNot checkBashisms "#!/bin/sh\nprintf -- -f\n"
+-- >>> prop $ verify checkBashisms "#!/bin/sh\nfoo+=bar"
+-- >>> prop $ verify checkBashisms "#!/bin/sh\necho ${@%foo}"
+-- >>> prop $ verifyNot checkBashisms "#!/bin/sh\necho ${##}"
checkBashisms = ForShell [Sh, Dash] $ \t -> do
params <- ask
kludge params t
@@ -317,8 +316,9 @@ checkBashisms = ForShell [Sh, Dash] $ \t -> do
Assignment (_, _, name, _) -> name == var
_ -> False
-prop_checkEchoSed1 = verify checkEchoSed "FOO=$(echo \"$cow\" | sed 's/foo/bar/g')"
-prop_checkEchoSed2 = verify checkEchoSed "rm $(echo $cow | sed -e 's,foo,bar,')"
+-- |
+-- >>> prop $ verify checkEchoSed "FOO=$(echo \"$cow\" | sed 's/foo/bar/g')"
+-- >>> prop $ verify checkEchoSed "rm $(echo $cow | sed -e 's,foo,bar,')"
checkEchoSed = ForShell [Bash, Ksh] f
where
f (T_Pipeline id _ [a, b]) =
@@ -344,10 +344,11 @@ checkEchoSed = ForShell [Bash, Ksh] f
f _ = return ()
-prop_checkBraceExpansionVars1 = verify checkBraceExpansionVars "echo {1..$n}"
-prop_checkBraceExpansionVars2 = verifyNot checkBraceExpansionVars "echo {1,3,$n}"
-prop_checkBraceExpansionVars3 = verify checkBraceExpansionVars "eval echo DSC{0001..$n}.jpg"
-prop_checkBraceExpansionVars4 = verify checkBraceExpansionVars "echo {$i..100}"
+-- |
+-- >>> prop $ verify checkBraceExpansionVars "echo {1..$n}"
+-- >>> prop $ verifyNot checkBraceExpansionVars "echo {1,3,$n}"
+-- >>> prop $ verify checkBraceExpansionVars "eval echo DSC{0001..$n}.jpg"
+-- >>> prop $ verify checkBraceExpansionVars "echo {$i..100}"
checkBraceExpansionVars = ForShell [Bash] f
where
f t@(T_BraceExpansion id list) = mapM_ check list
@@ -372,12 +373,13 @@ checkBraceExpansionVars = ForShell [Bash] f
return $ isJust cmd && fromJust cmd `isUnqualifiedCommand` "eval"
-prop_checkMultiDimensionalArrays1 = verify checkMultiDimensionalArrays "foo[a][b]=3"
-prop_checkMultiDimensionalArrays2 = verifyNot checkMultiDimensionalArrays "foo[a]=3"
-prop_checkMultiDimensionalArrays3 = verify checkMultiDimensionalArrays "foo=( [a][b]=c )"
-prop_checkMultiDimensionalArrays4 = verifyNot checkMultiDimensionalArrays "foo=( [a]=c )"
-prop_checkMultiDimensionalArrays5 = verify checkMultiDimensionalArrays "echo ${foo[bar][baz]}"
-prop_checkMultiDimensionalArrays6 = verifyNot checkMultiDimensionalArrays "echo ${foo[bar]}"
+-- |
+-- >>> prop $ verify checkMultiDimensionalArrays "foo[a][b]=3"
+-- >>> prop $ verifyNot checkMultiDimensionalArrays "foo[a]=3"
+-- >>> prop $ verify checkMultiDimensionalArrays "foo=( [a][b]=c )"
+-- >>> prop $ verifyNot checkMultiDimensionalArrays "foo=( [a]=c )"
+-- >>> prop $ verify checkMultiDimensionalArrays "echo ${foo[bar][baz]}"
+-- >>> prop $ verifyNot checkMultiDimensionalArrays "echo ${foo[bar]}"
checkMultiDimensionalArrays = ForShell [Bash] f
where
f token =
@@ -392,16 +394,17 @@ checkMultiDimensionalArrays = ForShell [Bash] f
re = mkRegex "^\\[.*\\]\\[.*\\]" -- Fixme, this matches ${foo:- [][]} and such as well
isMultiDim t = getBracedModifier (bracedString t) `matches` re
-prop_checkPS11 = verify checkPS1Assignments "PS1='\\033[1;35m\\$ '"
-prop_checkPS11a= verify checkPS1Assignments "export PS1='\\033[1;35m\\$ '"
-prop_checkPSf2 = verify checkPS1Assignments "PS1='\\h \\e[0m\\$ '"
-prop_checkPS13 = verify checkPS1Assignments "PS1=$'\\x1b[c '"
-prop_checkPS14 = verify checkPS1Assignments "PS1=$'\\e[3m; '"
-prop_checkPS14a= verify checkPS1Assignments "export PS1=$'\\e[3m; '"
-prop_checkPS15 = verifyNot checkPS1Assignments "PS1='\\[\\033[1;35m\\]\\$ '"
-prop_checkPS16 = verifyNot checkPS1Assignments "PS1='\\[\\e1m\\e[1m\\]\\$ '"
-prop_checkPS17 = verifyNot checkPS1Assignments "PS1='e033x1B'"
-prop_checkPS18 = verifyNot checkPS1Assignments "PS1='\\[\\e\\]'"
+-- |
+-- >>> prop $ verify checkPS1Assignments "PS1='\\033[1;35m\\$ '"
+-- >>> prop $ verify checkPS1Assignments "export PS1='\\033[1;35m\\$ '"
+-- >>> prop $ verify checkPS1Assignments "PS1='\\h \\e[0m\\$ '"
+-- >>> prop $ verify checkPS1Assignments "PS1=$'\\x1b[c '"
+-- >>> prop $ verify checkPS1Assignments "PS1=$'\\e[3m; '"
+-- >>> prop $ verify checkPS1Assignments "export PS1=$'\\e[3m; '"
+-- >>> prop $ verifyNot checkPS1Assignments "PS1='\\[\\033[1;35m\\]\\$ '"
+-- >>> prop $ verifyNot checkPS1Assignments "PS1='\\[\\e1m\\e[1m\\]\\$ '"
+-- >>> prop $ verifyNot checkPS1Assignments "PS1='e033x1B'"
+-- >>> prop $ verifyNot checkPS1Assignments "PS1='\\[\\e\\]'"
checkPS1Assignments = ForShell [Bash] f
where
f token = case token of
@@ -417,7 +420,3 @@ checkPS1Assignments = ForShell [Bash] f
isJust $ matchRegex escapeRegex unenclosed
enclosedRegex = mkRegex "\\\\\\[.*\\\\\\]" -- FIXME: shouldn't be eager
escapeRegex = mkRegex "\\\\x1[Bb]|\\\\e|\x1B|\\\\033"
-
-
-return []
-runTests = $( [| $(forAllProperties) (quickCheckWithResult (stdArgs { maxSuccess = 1 }) ) |])
diff --git a/src/ShellCheck/Parser.hs b/src/ShellCheck/Parser.hs
index 172ef54..98bf17e 100644
--- a/src/ShellCheck/Parser.hs
+++ b/src/ShellCheck/Parser.hs
@@ -17,11 +17,10 @@
You should have received a copy of the GNU General Public License
along with this program. If not, see .
-}
-{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE NoMonomorphismRestriction #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE MultiWayIf #-}
-module ShellCheck.Parser (parseScript, runTests) where
+module ShellCheck.Parser (parseScript) where
import ShellCheck.AST
import ShellCheck.ASTLib
@@ -48,7 +47,9 @@ import qualified Control.Monad.Reader as Mr
import qualified Control.Monad.State as Ms
import qualified Data.Map as Map
-import Test.QuickCheck.All (quickCheckAll)
+prop :: Bool -> IO ()
+prop False = putStrLn "FAIL"
+prop True = return ()
type SCBase m = Mr.ReaderT (Environment m) (Ms.StateT SystemState m)
type SCParser m v = ParsecT String UserState (SCBase m) v
@@ -87,7 +88,8 @@ extglobStart = oneOf extglobStartChars
unicodeDoubleQuotes = "\x201C\x201D\x2033\x2036"
unicodeSingleQuotes = "\x2018\x2019"
-prop_spacing = isOk spacing " \\\n # Comment"
+-- |
+-- >>> prop $ isOk spacing " \\\n # Comment"
spacing = do
x <- many (many1 linewhitespace <|> try (string "\\\n" >> return ""))
optional readComment
@@ -98,9 +100,10 @@ spacing1 = do
when (null spacing) $ fail "Expected whitespace"
return spacing
-prop_allspacing = isOk allspacing "#foo"
-prop_allspacing2 = isOk allspacing " #foo\n # bar\n#baz\n"
-prop_allspacing3 = isOk allspacing "#foo\n#bar\n#baz\n"
+-- |
+-- >>> prop $ isOk allspacing "#foo"
+-- >>> prop $ isOk allspacing " #foo\n # bar\n#baz\n"
+-- >>> prop $ isOk allspacing "#foo\n#bar\n#baz\n"
allspacing = do
s <- spacing
more <- option False (linefeed >> return True)
@@ -673,29 +676,30 @@ readConditionContents single =
readCondContents = readCondOr
-prop_a1 = isOk readArithmeticContents " n++ + ++c"
-prop_a2 = isOk readArithmeticContents "$N*4-(3,2)"
-prop_a3 = isOk readArithmeticContents "n|=2<<1"
-prop_a4 = isOk readArithmeticContents "n &= 2 **3"
-prop_a5 = isOk readArithmeticContents "1 |= 4 && n >>= 4"
-prop_a6 = isOk readArithmeticContents " 1 | 2 ||3|4"
-prop_a7 = isOk readArithmeticContents "3*2**10"
-prop_a8 = isOk readArithmeticContents "3"
-prop_a9 = isOk readArithmeticContents "a^!-b"
-prop_a10= isOk readArithmeticContents "! $?"
-prop_a11= isOk readArithmeticContents "10#08 * 16#f"
-prop_a12= isOk readArithmeticContents "\"$((3+2))\" + '37'"
-prop_a13= isOk readArithmeticContents "foo[9*y+x]++"
-prop_a14= isOk readArithmeticContents "1+`echo 2`"
-prop_a15= isOk readArithmeticContents "foo[`echo foo | sed s/foo/4/g` * 3] + 4"
-prop_a16= isOk readArithmeticContents "$foo$bar"
-prop_a17= isOk readArithmeticContents "i<(0+(1+1))"
-prop_a18= isOk readArithmeticContents "a?b:c"
-prop_a19= isOk readArithmeticContents "\\\n3 +\\\n 2"
-prop_a20= isOk readArithmeticContents "a ? b ? c : d : e"
-prop_a21= isOk readArithmeticContents "a ? b : c ? d : e"
-prop_a22= isOk readArithmeticContents "!!a"
-prop_a23= isOk readArithmeticContents "~0"
+-- |
+-- >>> prop $ isOk readArithmeticContents " n++ + ++c"
+-- >>> prop $ isOk readArithmeticContents "$N*4-(3,2)"
+-- >>> prop $ isOk readArithmeticContents "n|=2<<1"
+-- >>> prop $ isOk readArithmeticContents "n &= 2 **3"
+-- >>> prop $ isOk readArithmeticContents "1 |= 4 && n >>= 4"
+-- >>> prop $ isOk readArithmeticContents " 1 | 2 ||3|4"
+-- >>> prop $ isOk readArithmeticContents "3*2**10"
+-- >>> prop $ isOk readArithmeticContents "3"
+-- >>> prop $ isOk readArithmeticContents "a^!-b"
+-- >>> prop $ isOk readArithmeticContents "! $?"
+-- >>> prop $ isOk readArithmeticContents "10#08 * 16#f"
+-- >>> prop $ isOk readArithmeticContents "\"$((3+2))\" + '37'"
+-- >>> prop $ isOk readArithmeticContents "foo[9*y+x]++"
+-- >>> prop $ isOk readArithmeticContents "1+`echo 2`"
+-- >>> prop $ isOk readArithmeticContents "foo[`echo foo | sed s/foo/4/g` * 3] + 4"
+-- >>> prop $ isOk readArithmeticContents "$foo$bar"
+-- >>> prop $ isOk readArithmeticContents "i<(0+(1+1))"
+-- >>> prop $ isOk readArithmeticContents "a?b:c"
+-- >>> prop $ isOk readArithmeticContents "\\\n3 +\\\n 2"
+-- >>> prop $ isOk readArithmeticContents "a ? b ? c : d : e"
+-- >>> prop $ isOk readArithmeticContents "a ? b : c ? d : e"
+-- >>> prop $ isOk readArithmeticContents "!!a"
+-- >>> prop $ isOk readArithmeticContents "~0"
readArithmeticContents :: Monad m => SCParser m Token
readArithmeticContents =
readSequence
@@ -876,33 +880,34 @@ readArithmeticContents =
-prop_readCondition = isOk readCondition "[ \\( a = b \\) -a \\( c = d \\) ]"
-prop_readCondition2 = isOk readCondition "[[ (a = b) || (c = d) ]]"
-prop_readCondition3 = isOk readCondition "[[ $c = [[:alpha:].~-] ]]"
-prop_readCondition4 = isOk readCondition "[[ $c =~ *foo* ]]"
-prop_readCondition5 = isOk readCondition "[[ $c =~ f( ]] )* ]]"
-prop_readCondition5a = isOk readCondition "[[ $c =~ a(b) ]]"
-prop_readCondition5b = isOk readCondition "[[ $c =~ f( ($var ]]) )* ]]"
-prop_readCondition6 = isOk readCondition "[[ $c =~ ^[yY]$ ]]"
-prop_readCondition7 = isOk readCondition "[[ ${line} =~ ^[[:space:]]*# ]]"
-prop_readCondition8 = isOk readCondition "[[ $l =~ ogg|flac ]]"
-prop_readCondition9 = isOk readCondition "[ foo -a -f bar ]"
-prop_readCondition10 = isOk readCondition "[[\na == b\n||\nc == d ]]"
-prop_readCondition10a= isOk readCondition "[[\na == b ||\nc == d ]]"
-prop_readCondition10b= isOk readCondition "[[ a == b\n||\nc == d ]]"
-prop_readCondition11 = isOk readCondition "[[ a == b ||\n c == d ]]"
-prop_readCondition12 = isWarning readCondition "[ a == b \n -o c == d ]"
-prop_readCondition13 = isOk readCondition "[[ foo =~ ^fo{1,3}$ ]]"
-prop_readCondition14 = isOk readCondition "[ foo '>' bar ]"
-prop_readCondition15 = isOk readCondition "[ foo \">=\" bar ]"
-prop_readCondition16 = isOk readCondition "[ foo \\< bar ]"
-prop_readCondition17 = isOk readCondition "[[ ${file::1} = [-.\\|/\\\\] ]]"
-prop_readCondition18 = isOk readCondition "[ ]"
-prop_readCondition19 = isOk readCondition "[ '(' x \")\" ]"
-prop_readCondition20 = isOk readCondition "[[ echo_rc -eq 0 ]]"
-prop_readCondition21 = isOk readCondition "[[ $1 =~ ^(a\\ b)$ ]]"
-prop_readCondition22 = isOk readCondition "[[ $1 =~ \\.a\\.(\\.b\\.)\\.c\\. ]]"
-prop_readCondition23 = isOk readCondition "[[ -v arr[$var] ]]"
+-- |
+-- >>> prop $ isOk readCondition "[ \\( a = b \\) -a \\( c = d \\) ]"
+-- >>> prop $ isOk readCondition "[[ (a = b) || (c = d) ]]"
+-- >>> prop $ isOk readCondition "[[ $c = [[:alpha:].~-] ]]"
+-- >>> prop $ isOk readCondition "[[ $c =~ *foo* ]]"
+-- >>> prop $ isOk readCondition "[[ $c =~ f( ]] )* ]]"
+-- >>> prop $ isOk readCondition "[[ $c =~ a(b) ]]"
+-- >>> prop $ isOk readCondition "[[ $c =~ f( ($var ]]) )* ]]"
+-- >>> prop $ isOk readCondition "[[ $c =~ ^[yY]$ ]]"
+-- >>> prop $ isOk readCondition "[[ ${line} =~ ^[[:space:]]*# ]]"
+-- >>> prop $ isOk readCondition "[[ $l =~ ogg|flac ]]"
+-- >>> prop $ isOk readCondition "[ foo -a -f bar ]"
+-- >>> prop $ isOk readCondition "[[\na == b\n||\nc == d ]]"
+-- >>> prop $ isOk readCondition "[[\na == b ||\nc == d ]]"
+-- >>> prop $ isOk readCondition "[[ a == b\n||\nc == d ]]"
+-- >>> prop $ isOk readCondition "[[ a == b ||\n c == d ]]"
+-- >>> prop $ isWarning readCondition "[ a == b \n -o c == d ]"
+-- >>> prop $ isOk readCondition "[[ foo =~ ^fo{1,3}$ ]]"
+-- >>> prop $ isOk readCondition "[ foo '>' bar ]"
+-- >>> prop $ isOk readCondition "[ foo \">=\" bar ]"
+-- >>> prop $ isOk readCondition "[ foo \\< bar ]"
+-- >>> prop $ isOk readCondition "[[ ${file::1} = [-.\\|/\\\\] ]]"
+-- >>> prop $ isOk readCondition "[ ]"
+-- >>> prop $ isOk readCondition "[ '(' x \")\" ]"
+-- >>> prop $ isOk readCondition "[[ echo_rc -eq 0 ]]"
+-- >>> prop $ isOk readCondition "[[ $1 =~ ^(a\\ b)$ ]]"
+-- >>> prop $ isOk readCondition "[[ $1 =~ \\.a\\.(\\.b\\.)\\.c\\. ]]"
+-- >>> prop $ isOk readCondition "[[ -v arr[$var] ]]"
readCondition = called "test expression" $ do
opos <- getPosition
start <- startSpan
@@ -940,12 +945,13 @@ readAnnotationPrefix = do
many linewhitespace
string "shellcheck"
-prop_readAnnotation1 = isOk readAnnotation "# shellcheck disable=1234,5678\n"
-prop_readAnnotation2 = isOk readAnnotation "# shellcheck disable=SC1234 disable=SC5678\n"
-prop_readAnnotation3 = isOk readAnnotation "# shellcheck disable=SC1234 source=/dev/null disable=SC5678\n"
-prop_readAnnotation4 = isWarning readAnnotation "# shellcheck cats=dogs disable=SC1234\n"
-prop_readAnnotation5 = isOk readAnnotation "# shellcheck disable=SC2002 # All cats are precious\n"
-prop_readAnnotation6 = isOk readAnnotation "# shellcheck disable=SC1234 # shellcheck foo=bar\n"
+-- |
+-- >>> prop $ isOk readAnnotation "# shellcheck disable=1234,5678\n"
+-- >>> prop $ isOk readAnnotation "# shellcheck disable=SC1234 disable=SC5678\n"
+-- >>> prop $ isOk readAnnotation "# shellcheck disable=SC1234 source=/dev/null disable=SC5678\n"
+-- >>> prop $ isWarning readAnnotation "# shellcheck cats=dogs disable=SC1234\n"
+-- >>> prop $ isOk readAnnotation "# shellcheck disable=SC2002 # All cats are precious\n"
+-- >>> prop $ isOk readAnnotation "# shellcheck disable=SC1234 # shellcheck foo=bar\n"
readAnnotation = called "shellcheck directive" $ do
try readAnnotationPrefix
many1 linewhitespace
@@ -1002,18 +1008,19 @@ readAnyComment = do
char '#'
many $ noneOf "\r\n"
-prop_readNormalWord = isOk readNormalWord "'foo'\"bar\"{1..3}baz$(lol)"
-prop_readNormalWord2 = isOk readNormalWord "foo**(foo)!!!(@@(bar))"
-prop_readNormalWord3 = isOk readNormalWord "foo#"
-prop_readNormalWord4 = isOk readNormalWord "$\"foo\"$'foo\nbar'"
-prop_readNormalWord5 = isWarning readNormalWord "${foo}}"
-prop_readNormalWord6 = isOk readNormalWord "foo/{}"
-prop_readNormalWord7 = isOk readNormalWord "foo\\\nbar"
-prop_readNormalWord8 = isWarning readSubshell "(foo\\ \nbar)"
-prop_readNormalWord9 = isOk readSubshell "(foo\\ ;\nbar)"
-prop_readNormalWord10 = isWarning readNormalWord "\x201Chello\x201D"
-prop_readNormalWord11 = isWarning readNormalWord "\x2018hello\x2019"
-prop_readNormalWord12 = isWarning readNormalWord "hello\x2018"
+-- |
+-- >>> prop $ isOk readNormalWord "'foo'\"bar\"{1..3}baz$(lol)"
+-- >>> prop $ isOk readNormalWord "foo**(foo)!!!(@@(bar))"
+-- >>> prop $ isOk readNormalWord "foo#"
+-- >>> prop $ isOk readNormalWord "$\"foo\"$'foo\nbar'"
+-- >>> prop $ isWarning readNormalWord "${foo}}"
+-- >>> prop $ isOk readNormalWord "foo/{}"
+-- >>> prop $ isOk readNormalWord "foo\\\nbar"
+-- >>> prop $ isWarning readSubshell "(foo\\ \nbar)"
+-- >>> prop $ isOk readSubshell "(foo\\ ;\nbar)"
+-- >>> prop $ isWarning readNormalWord "\x201Chello\x201D"
+-- >>> prop $ isWarning readNormalWord "\x2018hello\x2019"
+-- >>> prop $ isWarning readNormalWord "hello\x2018"
readNormalWord = readNormalishWord ""
readNormalishWord end = do
@@ -1111,9 +1118,10 @@ readParamSubSpecialChar = do
id <- endSpan start
return $ T_ParamSubSpecialChar id x
-prop_readProcSub1 = isOk readProcSub "<(echo test | wc -l)"
-prop_readProcSub2 = isOk readProcSub "<( if true; then true; fi )"
-prop_readProcSub3 = isOk readProcSub "<( # nothing here \n)"
+-- |
+-- >>> prop $ isOk readProcSub "<(echo test | wc -l)"
+-- >>> prop $ isOk readProcSub "<( if true; then true; fi )"
+-- >>> prop $ isOk readProcSub "<( # nothing here \n)"
readProcSub = called "process substitution" $ do
start <- startSpan
dir <- try $ do
@@ -1126,13 +1134,14 @@ readProcSub = called "process substitution" $ do
id <- endSpan start
return $ T_ProcSub id dir list
-prop_readSingleQuoted = isOk readSingleQuoted "'foo bar'"
-prop_readSingleQuoted2 = isWarning readSingleQuoted "'foo bar\\'"
-prop_readSingleQuoted4 = isWarning readNormalWord "'it's"
-prop_readSingleQuoted5 = isWarning readSimpleCommand "foo='bar\ncow 'arg"
-prop_readSingleQuoted6 = isOk readSimpleCommand "foo='bar cow 'arg"
-prop_readSingleQuoted7 = isOk readSingleQuoted "'foo\x201C\&bar'"
-prop_readSingleQuoted8 = isWarning readSingleQuoted "'foo\x2018\&bar'"
+-- |
+-- >>> prop $ isOk readSingleQuoted "'foo bar'"
+-- >>> prop $ isWarning readSingleQuoted "'foo bar\\'"
+-- >>> prop $ isWarning readNormalWord "'it's"
+-- >>> prop $ isWarning readSimpleCommand "foo='bar\ncow 'arg"
+-- >>> prop $ isOk readSimpleCommand "foo='bar cow 'arg"
+-- >>> prop $ isOk readSingleQuoted "'foo\x201C\&bar'"
+-- >>> prop $ isWarning readSingleQuoted "'foo\x2018\&bar'"
readSingleQuoted = called "single quoted string" $ do
start <- startSpan
startPos <- getPosition
@@ -1174,14 +1183,15 @@ readSingleQuotedPart =
return [x]
-prop_readBackTicked = isOk (readBackTicked False) "`ls *.mp3`"
-prop_readBackTicked2 = isOk (readBackTicked False) "`grep \"\\\"\"`"
-prop_readBackTicked3 = isWarning (readBackTicked False) "´grep \"\\\"\"´"
-prop_readBackTicked4 = isOk readSimpleCommand "`echo foo\necho bar`"
-prop_readBackTicked5 = isOk readSimpleCommand "echo `foo`bar"
-prop_readBackTicked6 = isWarning readSimpleCommand "echo `foo\necho `bar"
-prop_readBackTicked7 = isOk readSimpleCommand "`#inline comment`"
-prop_readBackTicked8 = isOk readSimpleCommand "echo `#comment` \\\nbar baz"
+-- |
+-- >>> prop $ isOk (readBackTicked False) "`ls *.mp3`"
+-- >>> prop $ isOk (readBackTicked False) "`grep \"\\\"\"`"
+-- >>> prop $ isWarning (readBackTicked False) "´grep \"\\\"\"´"
+-- >>> prop $ isOk readSimpleCommand "`echo foo\necho bar`"
+-- >>> prop $ isOk readSimpleCommand "echo `foo`bar"
+-- >>> prop $ isWarning readSimpleCommand "echo `foo\necho `bar"
+-- >>> prop $ isOk readSimpleCommand "`#inline comment`"
+-- >>> prop $ isOk readSimpleCommand "echo `#comment` \\\nbar baz"
readQuotedBackTicked = readBackTicked True
readUnquotedBackTicked = readBackTicked False
readBackTicked quoted = called "backtick expansion" $ do
@@ -1247,15 +1257,16 @@ parseForgettingContext alsoOnSuccess parser = do
Ms.put c
fail ""
-prop_readDoubleQuoted = isOk readDoubleQuoted "\"Hello $FOO\""
-prop_readDoubleQuoted2 = isOk readDoubleQuoted "\"$'\""
-prop_readDoubleQuoted3 = isOk readDoubleQuoted "\"\x2018hello\x2019\""
-prop_readDoubleQuoted4 = isWarning readSimpleCommand "\"foo\nbar\"foo"
-prop_readDoubleQuoted5 = isOk readSimpleCommand "lol \"foo\nbar\" etc"
-prop_readDoubleQuoted6 = isOk readSimpleCommand "echo \"${ ls; }\""
-prop_readDoubleQuoted7 = isOk readSimpleCommand "echo \"${ ls;}bar\""
-prop_readDoubleQuoted8 = isWarning readDoubleQuoted "\"\x201Chello\x201D\""
-prop_readDoubleQuoted10 = isOk readDoubleQuoted "\"foo\\\\n\""
+-- |
+-- >>> prop $ isOk readDoubleQuoted "\"Hello $FOO\""
+-- >>> prop $ isOk readDoubleQuoted "\"$'\""
+-- >>> prop $ isOk readDoubleQuoted "\"\x2018hello\x2019\""
+-- >>> prop $ isWarning readSimpleCommand "\"foo\nbar\"foo"
+-- >>> prop $ isOk readSimpleCommand "lol \"foo\nbar\" etc"
+-- >>> prop $ isOk readSimpleCommand "echo \"${ ls; }\""
+-- >>> prop $ isOk readSimpleCommand "echo \"${ ls;}bar\""
+-- >>> prop $ isWarning readDoubleQuoted "\"\x201Chello\x201D\""
+-- >>> prop $ isOk readDoubleQuoted "\"foo\\\\n\""
readDoubleQuoted = called "double quoted string" $ do
start <- startSpan
startPos <- getPosition
@@ -1308,14 +1319,15 @@ readNormalLiteral end = do
id <- endSpan start
return $ T_Literal id (concat s)
-prop_readGlob1 = isOk readGlob "*"
-prop_readGlob2 = isOk readGlob "[^0-9]"
-prop_readGlob3 = isOk readGlob "[a[:alpha:]]"
-prop_readGlob4 = isOk readGlob "[[:alnum:]]"
-prop_readGlob5 = isOk readGlob "[^[:alpha:]1-9]"
-prop_readGlob6 = isOk readGlob "[\\|]"
-prop_readGlob7 = isOk readGlob "[^[]"
-prop_readGlob8 = isOk readGlob "[*?]"
+-- |
+-- >>> prop $ isOk readGlob "*"
+-- >>> prop $ isOk readGlob "[^0-9]"
+-- >>> prop $ isOk readGlob "[a[:alpha:]]"
+-- >>> prop $ isOk readGlob "[[:alnum:]]"
+-- >>> prop $ isOk readGlob "[^[:alpha:]1-9]"
+-- >>> prop $ isOk readGlob "[\\|]"
+-- >>> prop $ isOk readGlob "[^[]"
+-- >>> prop $ isOk readGlob "[*?]"
readGlob = readExtglob <|> readSimple <|> readClass <|> readGlobbyLiteral
where
readSimple = do
@@ -1383,13 +1395,14 @@ readNormalEscaped = called "escaped char" $ do
parseProblemAt pos ErrorC 1101 "Delete trailing spaces after \\ to break line (or use quotes for literal space)."
-prop_readExtglob1 = isOk readExtglob "!(*.mp3)"
-prop_readExtglob2 = isOk readExtglob "!(*.mp3|*.wmv)"
-prop_readExtglob4 = isOk readExtglob "+(foo \\) bar)"
-prop_readExtglob5 = isOk readExtglob "+(!(foo *(bar)))"
-prop_readExtglob6 = isOk readExtglob "*(((||))|())"
-prop_readExtglob7 = isOk readExtglob "*(<>)"
-prop_readExtglob8 = isOk readExtglob "@(|*())"
+-- |
+-- >>> prop $ isOk readExtglob "!(*.mp3)"
+-- >>> prop $ isOk readExtglob "!(*.mp3|*.wmv)"
+-- >>> prop $ isOk readExtglob "+(foo \\) bar)"
+-- >>> prop $ isOk readExtglob "+(!(foo *(bar)))"
+-- >>> prop $ isOk readExtglob "*(((||))|())"
+-- >>> prop $ isOk readExtglob "*(<>)"
+-- >>> prop $ isOk readExtglob "@(|*())"
readExtglob = called "extglob" $ do
start <- startSpan
c <- try $ do
@@ -1465,14 +1478,15 @@ readGenericEscaped = do
x <- anyChar
return $ if x == '\n' then [] else ['\\', x]
-prop_readBraced = isOk readBraced "{1..4}"
-prop_readBraced2 = isOk readBraced "{foo,bar,\"baz lol\"}"
-prop_readBraced3 = isOk readBraced "{1,\\},2}"
-prop_readBraced4 = isOk readBraced "{1,{2,3}}"
-prop_readBraced5 = isOk readBraced "{JP{,E}G,jp{,e}g}"
-prop_readBraced6 = isOk readBraced "{foo,bar,$((${var}))}"
-prop_readBraced7 = isNotOk readBraced "{}"
-prop_readBraced8 = isNotOk readBraced "{foo}"
+-- |
+-- >>> prop $ isOk readBraced "{1..4}"
+-- >>> prop $ isOk readBraced "{foo,bar,\"baz lol\"}"
+-- >>> prop $ isOk readBraced "{1,\\},2}"
+-- >>> prop $ isOk readBraced "{1,{2,3}}"
+-- >>> prop $ isOk readBraced "{JP{,E}G,jp{,e}g}"
+-- >>> prop $ isOk readBraced "{foo,bar,$((${var}))}"
+-- >>> prop $ isNotOk readBraced "{}"
+-- >>> prop $ isNotOk readBraced "{foo}"
readBraced = try braceExpansion
where
braceExpansion =
@@ -1512,9 +1526,10 @@ readDoubleQuotedDollar = do
readDollarExp <|> readDollarLonely
-prop_readDollarExpression1 = isOk readDollarExpression "$(((1) && 3))"
-prop_readDollarExpression2 = isWarning readDollarExpression "$(((1)) && 3)"
-prop_readDollarExpression3 = isWarning readDollarExpression "$((\"$@\" &); foo;)"
+-- |
+-- >>> prop $ isOk readDollarExpression "$(((1) && 3))"
+-- >>> prop $ isWarning readDollarExpression "$(((1)) && 3)"
+-- >>> prop $ isWarning readDollarExpression "$((\"$@\" &); foo;)"
readDollarExpression :: Monad m => SCParser m Token
readDollarExpression = do
ensureDollar
@@ -1525,7 +1540,8 @@ readDollarExp = arithmetic <|> readDollarExpansion <|> readDollarBracket <|> rea
arithmetic = readAmbiguous "$((" readDollarArithmetic readDollarExpansion (\pos ->
parseNoteAt pos WarningC 1102 "Shells disambiguate $(( differently or not at all. For $(command substition), add space after $( . For $((arithmetics)), fix parsing errors.")
-prop_readDollarSingleQuote = isOk readDollarSingleQuote "$'foo\\\'lol'"
+-- |
+-- >>> prop $ isOk readDollarSingleQuote "$'foo\\\'lol'"
readDollarSingleQuote = called "$'..' expression" $ do
start <- startSpan
try $ string "$'"
@@ -1534,7 +1550,8 @@ readDollarSingleQuote = called "$'..' expression" $ do
id <- endSpan start
return $ T_DollarSingleQuoted id str
-prop_readDollarDoubleQuote = isOk readDollarDoubleQuote "$\"hello\""
+-- |
+-- >>> prop $ isOk readDollarDoubleQuote "$\"hello\""
readDollarDoubleQuote = do
lookAhead . try $ string "$\""
start <- startSpan
@@ -1545,8 +1562,9 @@ readDollarDoubleQuote = do
id <- endSpan start
return $ T_DollarDoubleQuoted id x
-prop_readDollarArithmetic = isOk readDollarArithmetic "$(( 3 * 4 +5))"
-prop_readDollarArithmetic2 = isOk readDollarArithmetic "$(((3*4)+(1*2+(3-1))))"
+-- |
+-- >>> prop $ isOk readDollarArithmetic "$(( 3 * 4 +5))"
+-- >>> prop $ isOk readDollarArithmetic "$(((3*4)+(1*2+(3-1))))"
readDollarArithmetic = called "$((..)) expression" $ do
start <- startSpan
try (string "$((")
@@ -1565,7 +1583,8 @@ readDollarBracket = called "$[..] expression" $ do
id <- endSpan start
return (T_DollarBracket id c)
-prop_readArithmeticExpression = isOk readArithmeticExpression "((a?b:c))"
+-- |
+-- >>> prop $ isOk readArithmeticExpression "((a?b:c))"
readArithmeticExpression = called "((..)) command" $ do
start <- startSpan
try (string "((")
@@ -1588,8 +1607,9 @@ readAmbiguous prefix expected alternative warner = do
warner pos
return t
-prop_readDollarBraceCommandExpansion1 = isOk readDollarBraceCommandExpansion "${ ls; }"
-prop_readDollarBraceCommandExpansion2 = isOk readDollarBraceCommandExpansion "${\nls\n}"
+-- |
+-- >>> prop $ isOk readDollarBraceCommandExpansion "${ ls; }"
+-- >>> prop $ isOk readDollarBraceCommandExpansion "${\nls\n}"
readDollarBraceCommandExpansion = called "ksh ${ ..; } command expansion" $ do
start <- startSpan
try $ do
@@ -1601,10 +1621,11 @@ readDollarBraceCommandExpansion = called "ksh ${ ..; } command expansion" $ do
id <- endSpan start
return $ T_DollarBraceCommandExpansion id term
-prop_readDollarBraced1 = isOk readDollarBraced "${foo//bar/baz}"
-prop_readDollarBraced2 = isOk readDollarBraced "${foo/'{cow}'}"
-prop_readDollarBraced3 = isOk readDollarBraced "${foo%%$(echo cow\\})}"
-prop_readDollarBraced4 = isOk readDollarBraced "${foo#\\}}"
+-- |
+-- >>> prop $ isOk readDollarBraced "${foo//bar/baz}"
+-- >>> prop $ isOk readDollarBraced "${foo/'{cow}'}"
+-- >>> prop $ isOk readDollarBraced "${foo%%$(echo cow\\})}"
+-- >>> prop $ isOk readDollarBraced "${foo#\\}}"
readDollarBraced = called "parameter expansion" $ do
start <- startSpan
try (string "${")
@@ -1613,9 +1634,10 @@ readDollarBraced = called "parameter expansion" $ do
id <- endSpan start
return $ T_DollarBraced id word
-prop_readDollarExpansion1= isOk readDollarExpansion "$(echo foo; ls\n)"
-prop_readDollarExpansion2= isOk readDollarExpansion "$( )"
-prop_readDollarExpansion3= isOk readDollarExpansion "$( command \n#comment \n)"
+-- |
+-- >>> prop $ isOk readDollarExpansion "$(echo foo; ls\n)"
+-- >>> prop $ isOk readDollarExpansion "$( )"
+-- >>> prop $ isOk readDollarExpansion "$( command \n#comment \n)"
readDollarExpansion = called "command expansion" $ do
start <- startSpan
try (string "$(")
@@ -1624,12 +1646,12 @@ readDollarExpansion = called "command expansion" $ do
id <- endSpan start
return $ T_DollarExpansion id cmds
-prop_readDollarVariable = isOk readDollarVariable "$@"
-prop_readDollarVariable2 = isOk (readDollarVariable >> anyChar) "$?!"
-prop_readDollarVariable3 = isWarning (readDollarVariable >> anyChar) "$10"
-prop_readDollarVariable4 = isWarning (readDollarVariable >> string "[@]") "$arr[@]"
-prop_readDollarVariable5 = isWarning (readDollarVariable >> string "[f") "$arr[f"
-
+-- |
+-- >>> prop $ isOk readDollarVariable "$@"
+-- >>> prop $ isOk (readDollarVariable >> anyChar) "$?!"
+-- >>> prop $ isWarning (readDollarVariable >> anyChar) "$10"
+-- >>> prop $ isWarning (readDollarVariable >> string "[@]") "$arr[@]"
+-- >>> prop $ isWarning (readDollarVariable >> string "[f") "$arr[f"
readDollarVariable :: Monad m => SCParser m Token
readDollarVariable = do
start <- startSpan
@@ -1678,25 +1700,26 @@ readDollarLonely = do
n <- lookAhead (anyChar <|> (eof >> return '_'))
return $ T_Literal id "$"
-prop_readHereDoc = isOk readScript "cat << foo\nlol\ncow\nfoo"
-prop_readHereDoc2 = isNotOk readScript "cat <<- EOF\n cow\n EOF"
-prop_readHereDoc3 = isOk readScript "cat << foo\n$\"\nfoo"
-prop_readHereDoc4 = isNotOk readScript "cat << foo\n`\nfoo"
-prop_readHereDoc5 = isOk readScript "cat <<- !foo\nbar\n!foo"
-prop_readHereDoc6 = isOk readScript "cat << foo\\ bar\ncow\nfoo bar"
-prop_readHereDoc7 = isOk readScript "cat << foo\n\\$(f ())\nfoo"
-prop_readHereDoc8 = isOk readScript "cat <>bar\netc\nfoo"
-prop_readHereDoc9 = isOk readScript "if true; then cat << foo; fi\nbar\nfoo\n"
-prop_readHereDoc10= isOk readScript "if true; then cat << foo << bar; fi\nfoo\nbar\n"
-prop_readHereDoc11= isOk readScript "cat << foo $(\nfoo\n)lol\nfoo\n"
-prop_readHereDoc12= isOk readScript "cat << foo|cat\nbar\nfoo"
-prop_readHereDoc13= isOk readScript "cat <<'#!'\nHello World\n#!\necho Done"
-prop_readHereDoc14= isWarning readScript "cat << foo\nbar\nfoo \n"
-prop_readHereDoc15= isWarning readScript "cat <>> prop $ isOk readScript "cat << foo\nlol\ncow\nfoo"
+-- >>> prop $ isNotOk readScript "cat <<- EOF\n cow\n EOF"
+-- >>> prop $ isOk readScript "cat << foo\n$\"\nfoo"
+-- >>> prop $ isNotOk readScript "cat << foo\n`\nfoo"
+-- >>> prop $ isOk readScript "cat <<- !foo\nbar\n!foo"
+-- >>> prop $ isOk readScript "cat << foo\\ bar\ncow\nfoo bar"
+-- >>> prop $ isOk readScript "cat << foo\n\\$(f ())\nfoo"
+-- >>> prop $ isOk readScript "cat <>bar\netc\nfoo"
+-- >>> prop $ isOk readScript "if true; then cat << foo; fi\nbar\nfoo\n"
+-- >>> prop $ isOk readScript "if true; then cat << foo << bar; fi\nfoo\nbar\n"
+-- >>> prop $ isOk readScript "cat << foo $(\nfoo\n)lol\nfoo\n"
+-- >>> prop $ isOk readScript "cat << foo|cat\nbar\nfoo"
+-- >>> prop $ isOk readScript "cat <<'#!'\nHello World\n#!\necho Done"
+-- >>> prop $ isWarning readScript "cat << foo\nbar\nfoo \n"
+-- >>> prop $ isWarning readScript "cat <>> prop $ isOk readScript "cat <<- ' foo'\nbar\n foo\n"
+-- >>> prop $ isWarning readScript "cat <<- ' foo'\nbar\n foo\n foo\n"
+-- >>> prop $ isWarning readScript "cat << foo\n foo\n()\nfoo\n"
+-- >>> prop $ isOk readScript "# shellcheck disable=SC1039\ncat << foo\n foo\n()\nfoo\n"
readHereDoc = called "here document" $ do
pos <- getPosition
try $ string "<<"
@@ -1864,7 +1887,8 @@ readIoDuplicate = try $ do
return $ str ++ dash
-prop_readIoFile = isOk readIoFile ">> \"$(date +%YYmmDD)\""
+-- |
+-- >>> prop $ isOk readIoFile ">> \"$(date +%YYmmDD)\""
readIoFile = called "redirection" $ do
start <- startSpan
op <- readIoFileOp
@@ -1884,13 +1908,14 @@ readIoSource = try $ do
lookAhead $ void readIoFileOp <|> void (string "<<")
return x
-prop_readIoRedirect = isOk readIoRedirect "3>&2"
-prop_readIoRedirect2 = isOk readIoRedirect "2> lol"
-prop_readIoRedirect3 = isOk readIoRedirect "4>&-"
-prop_readIoRedirect4 = isOk readIoRedirect "&> lol"
-prop_readIoRedirect5 = isOk readIoRedirect "{foo}>&2"
-prop_readIoRedirect6 = isOk readIoRedirect "{foo}<&-"
-prop_readIoRedirect7 = isOk readIoRedirect "{foo}>&1-"
+-- |
+-- >>> prop $ isOk readIoRedirect "3>&2"
+-- >>> prop $ isOk readIoRedirect "2> lol"
+-- >>> prop $ isOk readIoRedirect "4>&-"
+-- >>> prop $ isOk readIoRedirect "&> lol"
+-- >>> prop $ isOk readIoRedirect "{foo}>&2"
+-- >>> prop $ isOk readIoRedirect "{foo}<&-"
+-- >>> prop $ isOk readIoRedirect "{foo}>&1-"
readIoRedirect = do
start <- startSpan
n <- readIoSource
@@ -1902,7 +1927,8 @@ readIoRedirect = do
readRedirectList = many1 readIoRedirect
-prop_readHereString = isOk readHereString "<<< \"Hello $world\""
+-- |
+-- >>> prop $ isOk readHereString "<<< \"Hello $world\""
readHereString = called "here string" $ do
start <- startSpan
try $ string "<<<"
@@ -1921,11 +1947,12 @@ readNewlineList =
"Unexpected start of line. If breaking lines, |/||/&& should be at the end of the previous one."
readLineBreak = optional readNewlineList
-prop_readSeparator1 = isWarning readScript "a &; b"
-prop_readSeparator2 = isOk readScript "a & b"
-prop_readSeparator3 = isWarning readScript "a & b"
-prop_readSeparator4 = isWarning readScript "a > file; b"
-prop_readSeparator5 = isWarning readScript "curl https://example.com/?foo=moo&bar=cow"
+-- |
+-- >>> prop $ isWarning readScript "a &; b"
+-- >>> prop $ isOk readScript "a & b"
+-- >>> prop $ isWarning readScript "a & b"
+-- >>> prop $ isWarning readScript "a > file; b"
+-- >>> prop $ isWarning readScript "curl https://example.com/?foo=moo&bar=cow"
readSeparatorOp = do
notFollowedBy2 (void g_AND_IF <|> void readCaseSeparator)
notFollowedBy2 (string "&>")
@@ -1969,20 +1996,21 @@ readSeparator =
end <- getPosition
return ('\n', (start, end))
-prop_readSimpleCommand = isOk readSimpleCommand "echo test > file"
-prop_readSimpleCommand2 = isOk readSimpleCommand "cmd &> file"
-prop_readSimpleCommand3 = isOk readSimpleCommand "export foo=(bar baz)"
-prop_readSimpleCommand4 = isOk readSimpleCommand "typeset -a foo=(lol)"
-prop_readSimpleCommand5 = isOk readSimpleCommand "time if true; then echo foo; fi"
-prop_readSimpleCommand6 = isOk readSimpleCommand "time -p ( ls -l; )"
-prop_readSimpleCommand7 = isOk readSimpleCommand "\\ls"
-prop_readSimpleCommand8 = isWarning readSimpleCommand "// Lol"
-prop_readSimpleCommand9 = isWarning readSimpleCommand "/* Lolbert */"
-prop_readSimpleCommand10 = isWarning readSimpleCommand "/**** Lolbert */"
-prop_readSimpleCommand11 = isOk readSimpleCommand "/\\* foo"
-prop_readSimpleCommand12 = isWarning readSimpleCommand "elsif foo"
-prop_readSimpleCommand13 = isWarning readSimpleCommand "ElseIf foo"
-prop_readSimpleCommand14 = isWarning readSimpleCommand "elseif[$i==2]"
+-- |
+-- >>> prop $ isOk readSimpleCommand "echo test > file"
+-- >>> prop $ isOk readSimpleCommand "cmd &> file"
+-- >>> prop $ isOk readSimpleCommand "export foo=(bar baz)"
+-- >>> prop $ isOk readSimpleCommand "typeset -a foo=(lol)"
+-- >>> prop $ isOk readSimpleCommand "time if true; then echo foo; fi"
+-- >>> prop $ isOk readSimpleCommand "time -p ( ls -l; )"
+-- >>> prop $ isOk readSimpleCommand "\\ls"
+-- >>> prop $ isWarning readSimpleCommand "// Lol"
+-- >>> prop $ isWarning readSimpleCommand "/* Lolbert */"
+-- >>> prop $ isWarning readSimpleCommand "/**** Lolbert */"
+-- >>> prop $ isOk readSimpleCommand "/\\* foo"
+-- >>> prop $ isWarning readSimpleCommand "elsif foo"
+-- >>> prop $ isWarning readSimpleCommand "ElseIf foo"
+-- >>> prop $ isWarning readSimpleCommand "elseif[$i==2]"
readSimpleCommand = called "simple command" $ do
prefix <- option [] readCmdPrefix
skipAnnotationAndWarn
@@ -2108,9 +2136,10 @@ readSource t@(T_Redirecting _ _ (T_SimpleCommand cmdId _ (cmd:file:_))) = do
readSource t = return t
-prop_readPipeline = isOk readPipeline "! cat /etc/issue | grep -i ubuntu"
-prop_readPipeline2 = isWarning readPipeline "!cat /etc/issue | grep -i ubuntu"
-prop_readPipeline3 = isOk readPipeline "for f; do :; done|cat"
+-- |
+-- >>> prop $ isOk readPipeline "! cat /etc/issue | grep -i ubuntu"
+-- >>> prop $ isWarning readPipeline "!cat /etc/issue | grep -i ubuntu"
+-- >>> prop $ isOk readPipeline "for f; do :; done|cat"
readPipeline = do
unexpecting "keyword/token" readKeyword
do
@@ -2120,9 +2149,10 @@ readPipeline = do
<|>
readPipeSequence
-prop_readAndOr = isOk readAndOr "grep -i lol foo || exit 1"
-prop_readAndOr1 = isOk readAndOr "# shellcheck disable=1\nfoo"
-prop_readAndOr2 = isOk readAndOr "# shellcheck disable=1\n# lol\n# shellcheck disable=3\nfoo"
+-- |
+-- >>> prop $ isOk readAndOr "grep -i lol foo || exit 1"
+-- >>> prop $ isOk readAndOr "# shellcheck disable=1\nfoo"
+-- >>> prop $ isOk readAndOr "# shellcheck disable=1\n# lol\n# shellcheck disable=3\nfoo"
readAndOr = do
start <- startSpan
apos <- getPosition
@@ -2150,7 +2180,8 @@ readTermOrNone = do
eof
return []
-prop_readTerm = isOk readTerm "time ( foo; bar; )"
+-- |
+-- >>> prop $ isOk readTerm "time ( foo; bar; )"
readTerm = do
allspacing
m <- readAndOr
@@ -2221,11 +2252,12 @@ skipAnnotationAndWarn = optional $ do
parseProblem ErrorC 1126 "Place shellcheck directives before commands, not after."
readAnyComment
-prop_readIfClause = isOk readIfClause "if false; then foo; elif true; then stuff; more stuff; else cows; fi"
-prop_readIfClause2 = isWarning readIfClause "if false; then; echo oo; fi"
-prop_readIfClause3 = isWarning readIfClause "if false; then true; else; echo lol; fi"
-prop_readIfClause4 = isWarning readIfClause "if false; then true; else if true; then echo lol; fi; fi"
-prop_readIfClause5 = isOk readIfClause "if false; then true; else\nif true; then echo lol; fi; fi"
+-- |
+-- >>> prop $ isOk readIfClause "if false; then foo; elif true; then stuff; more stuff; else cows; fi"
+-- >>> prop $ isWarning readIfClause "if false; then; echo oo; fi"
+-- >>> prop $ isWarning readIfClause "if false; then true; else; echo lol; fi"
+-- >>> prop $ isWarning readIfClause "if false; then true; else if true; then echo lol; fi; fi"
+-- >>> prop $ isOk readIfClause "if false; then true; else\nif true; then echo lol; fi; fi"
readIfClause = called "if expression" $ do
start <- startSpan
pos <- getPosition
@@ -2300,7 +2332,8 @@ ifNextToken parser action =
try . lookAhead $ parser
action
-prop_readSubshell = isOk readSubshell "( cd /foo; tar cf stuff.tar * )"
+-- |
+-- >>> prop $ isOk readSubshell "( cd /foo; tar cf stuff.tar * )"
readSubshell = called "explicit subshell" $ do
start <- startSpan
char '('
@@ -2311,9 +2344,10 @@ readSubshell = called "explicit subshell" $ do
id <- endSpan start
return $ T_Subshell id list
-prop_readBraceGroup = isOk readBraceGroup "{ a; b | c | d; e; }"
-prop_readBraceGroup2 = isWarning readBraceGroup "{foo;}"
-prop_readBraceGroup3 = isOk readBraceGroup "{(foo)}"
+-- |
+-- >>> prop $ isOk readBraceGroup "{ a; b | c | d; e; }"
+-- >>> prop $ isWarning readBraceGroup "{foo;}"
+-- >>> prop $ isOk readBraceGroup "{(foo)}"
readBraceGroup = called "brace group" $ do
start <- startSpan
char '{'
@@ -2331,7 +2365,8 @@ readBraceGroup = called "brace group" $ do
id <- endSpan start
return $ T_BraceGroup id list
-prop_readWhileClause = isOk readWhileClause "while [[ -e foo ]]; do sleep 1; done"
+-- |
+-- >>> prop $ isOk readWhileClause "while [[ -e foo ]]; do sleep 1; done"
readWhileClause = called "while loop" $ do
start <- startSpan
kwId <- getId <$> g_While
@@ -2340,7 +2375,8 @@ readWhileClause = called "while loop" $ do
id <- endSpan start
return $ T_WhileExpression id condition statements
-prop_readUntilClause = isOk readUntilClause "until kill -0 $PID; do sleep 1; done"
+-- |
+-- >>> prop $ isOk readUntilClause "until kill -0 $PID; do sleep 1; done"
readUntilClause = called "until loop" $ do
start <- startSpan
kwId <- getId <$> g_Until
@@ -2373,17 +2409,18 @@ readDoGroup kwId = do
return commands
-prop_readForClause = isOk readForClause "for f in *; do rm \"$f\"; done"
-prop_readForClause3 = isOk readForClause "for f; do foo; done"
-prop_readForClause4 = isOk readForClause "for((i=0; i<10; i++)); do echo $i; done"
-prop_readForClause5 = isOk readForClause "for ((i=0;i<10 && n>x;i++,--n))\ndo \necho $i\ndone"
-prop_readForClause6 = isOk readForClause "for ((;;))\ndo echo $i\ndone"
-prop_readForClause7 = isOk readForClause "for ((;;)) do echo $i\ndone"
-prop_readForClause8 = isOk readForClause "for ((;;)) ; do echo $i\ndone"
-prop_readForClause9 = isOk readForClause "for i do true; done"
-prop_readForClause10= isOk readForClause "for ((;;)) { true; }"
-prop_readForClause12= isWarning readForClause "for $a in *; do echo \"$a\"; done"
-prop_readForClause13= isOk readForClause "for foo\nin\\\n bar\\\n baz\ndo true; done"
+-- |
+-- >>> prop $ isOk readForClause "for f in *; do rm \"$f\"; done"
+-- >>> prop $ isOk readForClause "for f; do foo; done"
+-- >>> prop $ isOk readForClause "for((i=0; i<10; i++)); do echo $i; done"
+-- >>> prop $ isOk readForClause "for ((i=0;i<10 && n>x;i++,--n))\ndo \necho $i\ndone"
+-- >>> prop $ isOk readForClause "for ((;;))\ndo echo $i\ndone"
+-- >>> prop $ isOk readForClause "for ((;;)) do echo $i\ndone"
+-- >>> prop $ isOk readForClause "for ((;;)) ; do echo $i\ndone"
+-- >>> prop $ isOk readForClause "for i do true; done"
+-- >>> prop $ isOk readForClause "for ((;;)) { true; }"
+-- >>> prop $ isWarning readForClause "for $a in *; do echo \"$a\"; done"
+-- >>> prop $ isOk readForClause "for foo\nin\\\n bar\\\n baz\ndo true; done"
readForClause = called "for loop" $ do
pos <- getPosition
(T_For id) <- g_For
@@ -2416,8 +2453,9 @@ readForClause = called "for loop" $ do
group <- readDoGroup id
return $ T_ForIn id name values group
-prop_readSelectClause1 = isOk readSelectClause "select foo in *; do echo $foo; done"
-prop_readSelectClause2 = isOk readSelectClause "select foo; do echo $foo; done"
+-- |
+-- >>> prop $ isOk readSelectClause "select foo in *; do echo $foo; done"
+-- >>> prop $ isOk readSelectClause "select foo; do echo $foo; done"
readSelectClause = called "select loop" $ do
(T_Select id) <- g_Select
spacing
@@ -2446,11 +2484,12 @@ readInClause = do
return things
-prop_readCaseClause = isOk readCaseClause "case foo in a ) lol; cow;; b|d) fooo; esac"
-prop_readCaseClause2 = isOk readCaseClause "case foo\n in * ) echo bar;; esac"
-prop_readCaseClause3 = isOk readCaseClause "case foo\n in * ) echo bar & ;; esac"
-prop_readCaseClause4 = isOk readCaseClause "case foo\n in *) echo bar ;& bar) foo; esac"
-prop_readCaseClause5 = isOk readCaseClause "case foo\n in *) echo bar;;& foo) baz;; esac"
+-- |
+-- >>> prop $ isOk readCaseClause "case foo in a ) lol; cow;; b|d) fooo; esac"
+-- >>> prop $ isOk readCaseClause "case foo\n in * ) echo bar;; esac"
+-- >>> prop $ isOk readCaseClause "case foo\n in * ) echo bar & ;; esac"
+-- >>> prop $ isOk readCaseClause "case foo\n in *) echo bar ;& bar) foo; esac"
+-- >>> prop $ isOk readCaseClause "case foo\n in *) echo bar;;& foo) baz;; esac"
readCaseClause = called "case expression" $ do
start <- startSpan
g_Case
@@ -2494,18 +2533,19 @@ readCaseSeparator = choice [
lookAhead (readLineBreak >> g_Esac) >> return CaseBreak
]
-prop_readFunctionDefinition = isOk readFunctionDefinition "foo() { command foo --lol \"$@\"; }"
-prop_readFunctionDefinition1 = isOk readFunctionDefinition "foo (){ command foo --lol \"$@\"; }"
-prop_readFunctionDefinition4 = isWarning readFunctionDefinition "foo(a, b) { true; }"
-prop_readFunctionDefinition5 = isOk readFunctionDefinition ":(){ :|:;}"
-prop_readFunctionDefinition6 = isOk readFunctionDefinition "?(){ foo; }"
-prop_readFunctionDefinition7 = isOk readFunctionDefinition "..(){ cd ..; }"
-prop_readFunctionDefinition8 = isOk readFunctionDefinition "foo() (ls)"
-prop_readFunctionDefinition9 = isOk readFunctionDefinition "function foo { true; }"
-prop_readFunctionDefinition10= isOk readFunctionDefinition "function foo () { true; }"
-prop_readFunctionDefinition11= isWarning readFunctionDefinition "function foo{\ntrue\n}"
-prop_readFunctionDefinition12= isOk readFunctionDefinition "function []!() { true; }"
-prop_readFunctionDefinition13= isOk readFunctionDefinition "@require(){ true; }"
+-- |
+-- >>> prop $ isOk readFunctionDefinition "foo() { command foo --lol \"$@\"; }"
+-- >>> prop $ isOk readFunctionDefinition "foo (){ command foo --lol \"$@\"; }"
+-- >>> prop $ isWarning readFunctionDefinition "foo(a, b) { true; }"
+-- >>> prop $ isOk readFunctionDefinition ":(){ :|:;}"
+-- >>> prop $ isOk readFunctionDefinition "?(){ foo; }"
+-- >>> prop $ isOk readFunctionDefinition "..(){ cd ..; }"
+-- >>> prop $ isOk readFunctionDefinition "foo() (ls)"
+-- >>> prop $ isOk readFunctionDefinition "function foo { true; }"
+-- >>> prop $ isOk readFunctionDefinition "function foo () { true; }"
+-- >>> prop $ isWarning readFunctionDefinition "function foo{\ntrue\n}"
+-- >>> prop $ isOk readFunctionDefinition "function []!() { true; }"
+-- >>> prop $ isOk readFunctionDefinition "@require(){ true; }"
readFunctionDefinition = called "function" $ do
start <- startSpan
functionSignature <- try readFunctionSignature
@@ -2547,9 +2587,10 @@ readFunctionDefinition = called "function" $ do
g_Rparen
return ()
-prop_readCoProc1 = isOk readCoProc "coproc foo { echo bar; }"
-prop_readCoProc2 = isOk readCoProc "coproc { echo bar; }"
-prop_readCoProc3 = isOk readCoProc "coproc echo bar"
+-- |
+-- >>> prop $ isOk readCoProc "coproc foo { echo bar; }"
+-- >>> prop $ isOk readCoProc "coproc { echo bar; }"
+-- >>> prop $ isOk readCoProc "coproc echo bar"
readCoProc = called "coproc" $ do
start <- startSpan
try $ do
@@ -2576,7 +2617,8 @@ readCoProc = called "coproc" $ do
readPattern = (readNormalWord `thenSkip` spacing) `sepBy1` (char '|' `thenSkip` spacing)
-prop_readCompoundCommand = isOk readCompoundCommand "{ echo foo; }>/dev/null"
+-- |
+-- >>> prop $ isOk readCompoundCommand "{ echo foo; }>/dev/null"
readCompoundCommand = do
cmd <- choice [
readBraceGroup,
@@ -2668,24 +2710,26 @@ readLiteralForParser parser = do
id <- endSpan start
return $ T_Literal id str
-prop_readAssignmentWord = isOk readAssignmentWord "a=42"
-prop_readAssignmentWord2 = isOk readAssignmentWord "b=(1 2 3)"
-prop_readAssignmentWord3 = isWarning readAssignmentWord "$b = 13"
-prop_readAssignmentWord4 = isWarning readAssignmentWord "b = $(lol)"
-prop_readAssignmentWord5 = isOk readAssignmentWord "b+=lol"
-prop_readAssignmentWord6 = isWarning readAssignmentWord "b += (1 2 3)"
-prop_readAssignmentWord7 = isOk readAssignmentWord "a[3$n'']=42"
-prop_readAssignmentWord8 = isOk readAssignmentWord "a[4''$(cat foo)]=42"
-prop_readAssignmentWord9 = isOk readAssignmentWord "IFS= "
-prop_readAssignmentWord9a= isOk readAssignmentWord "foo="
-prop_readAssignmentWord9b= isOk readAssignmentWord "foo= "
-prop_readAssignmentWord9c= isOk readAssignmentWord "foo= #bar"
-prop_readAssignmentWord10= isWarning readAssignmentWord "foo$n=42"
-prop_readAssignmentWord11= isOk readAssignmentWord "foo=([a]=b [c] [d]= [e f )"
-prop_readAssignmentWord12= isOk readAssignmentWord "a[b <<= 3 + c]='thing'"
-prop_readAssignmentWord13= isOk readAssignmentWord "var=( (1 2) (3 4) )"
-prop_readAssignmentWord14= isOk readAssignmentWord "var=( 1 [2]=(3 4) )"
-prop_readAssignmentWord15= isOk readAssignmentWord "var=(1 [2]=(3 4))"
+-- |
+-- >>> prop $ isOk readAssignmentWord "a=42"
+-- >>> prop $ isOk readAssignmentWord "b=(1 2 3)"
+-- >>> prop $ isWarning readAssignmentWord "$b = 13"
+-- >>> prop $ isWarning readAssignmentWord "b = $(lol)"
+-- >>> prop $ isOk readAssignmentWord "b+=lol"
+-- >>> prop $ isWarning readAssignmentWord "b += (1 2 3)"
+-- >>> prop $ isOk readAssignmentWord "a[3$n'']=42"
+-- >>> prop $ isOk readAssignmentWord "a[4''$(cat foo)]=42"
+-- >>> prop $ isOk readAssignmentWord "IFS= "
+-- >>> prop $ isOk readAssignmentWord "foo="
+-- >>> prop $ isOk readAssignmentWord "foo= "
+-- >>> prop $ isOk readAssignmentWord "foo= #bar"
+-- >>> prop $ isWarning readAssignmentWord "foo$n=42"
+-- >>> prop $ isOk readAssignmentWord "foo=([a]=b [c] [d]= [e f )"
+-- >>> prop $ isOk readAssignmentWord "a[b <<= 3 + c]='thing'"
+-- >>> prop $ isOk readAssignmentWord "var=( (1 2) (3 4) )"
+-- >>> prop $ isOk readAssignmentWord "var=( 1 [2]=(3 4) )"
+-- >>> prop $ isOk readAssignmentWord "var=(1 [2]=(3 4))"
+
readAssignmentWord = readAssignmentWordExt True
readWellFormedAssignment = readAssignmentWordExt False
readAssignmentWordExt lenient = try $ do
@@ -2879,13 +2923,14 @@ readKeyword = choice [ g_Then, g_Else, g_Elif, g_Fi, g_Do, g_Done, g_Esac, g_Rbr
ifParse p t f =
(lookAhead (try p) >> t) <|> f
-prop_readShebang1 = isOk readShebang "#!/bin/sh\n"
-prop_readShebang2 = isWarning readShebang "!# /bin/sh\n"
-prop_readShebang3 = isNotOk readShebang "#shellcheck shell=/bin/sh\n"
-prop_readShebang4 = isWarning readShebang "! /bin/sh"
-prop_readShebang5 = isWarning readShebang "\n#!/bin/sh"
-prop_readShebang6 = isWarning readShebang " # Copyright \n!#/bin/bash"
-prop_readShebang7 = isNotOk readShebang "# Copyright \nfoo\n#!/bin/bash"
+-- |
+-- >>> prop $ isOk readShebang "#!/bin/sh\n"
+-- >>> prop $ isWarning readShebang "!# /bin/sh\n"
+-- >>> prop $ isNotOk readShebang "#shellcheck shell=/bin/sh\n"
+-- >>> prop $ isWarning readShebang "! /bin/sh"
+-- >>> prop $ isWarning readShebang "\n#!/bin/sh"
+-- >>> prop $ isWarning readShebang " # Copyright \n!#/bin/bash"
+-- >>> prop $ isNotOk readShebang "# Copyright \nfoo\n#!/bin/bash"
readShebang = do
anyShebang <|> try readMissingBang <|> withHeader
many linewhitespace
@@ -2968,11 +3013,12 @@ verifyEof = eof <|> choice [
try (lookAhead p)
action
-prop_readScript1 = isOk readScriptFile "#!/bin/bash\necho hello world\n"
-prop_readScript2 = isWarning readScriptFile "#!/bin/bash\r\necho hello world\n"
-prop_readScript3 = isWarning readScriptFile "#!/bin/bash\necho hello\xA0world"
-prop_readScript4 = isWarning readScriptFile "#!/usr/bin/perl\nfoo=("
-prop_readScript5 = isOk readScriptFile "#!/bin/bash\n#This is an empty script\n\n"
+-- |
+-- >>> prop $ isOk readScriptFile "#!/bin/bash\necho hello world\n"
+-- >>> prop $ isWarning readScriptFile "#!/bin/bash\r\necho hello world\n"
+-- >>> prop $ isWarning readScriptFile "#!/bin/bash\necho hello\xA0world"
+-- >>> prop $ isWarning readScriptFile "#!/usr/bin/perl\nfoo=("
+-- >>> prop $ isOk readScriptFile "#!/bin/bash\n#This is an empty script\n\n"
readScriptFile = do
start <- startSpan
pos <- getPosition
@@ -3295,7 +3341,3 @@ tryWithErrors parser = do
endInput <- getInput
endState <- getState
return (result, endPos, endInput, endState)
-
-return []
-runTests = $quickCheckAll
-
diff --git a/stack.yaml b/stack.yaml
index d39cada..ab2abbd 100644
--- a/stack.yaml
+++ b/stack.yaml
@@ -1,35 +1,3 @@
-# This file was automatically generated by stack init
-# For more information, see: https://docs.haskellstack.org/en/stable/yaml_configuration/
-
-# Specifies the GHC version and set of packages available (e.g., lts-3.5, nightly-2015-09-21, ghc-7.10.2)
-resolver: lts-8.5
-
-# Local packages, usually specified by relative directory name
+resolver: lts-12.9
packages:
- '.'
-# Packages to be pulled from upstream that are not in the resolver (e.g., acme-missiles-0.3)
-extra-deps: []
-
-# Override default flag values for local packages and extra-deps
-flags: {}
-
-# Extra package databases containing global packages
-extra-package-dbs: []
-
-# Control whether we use the GHC we find on the path
-# system-ghc: true
-
-# Require a specific version of stack, using version ranges
-# require-stack-version: -any # Default
-# require-stack-version: >= 1.0.0
-
-# Override the architecture used by stack, especially useful on Windows
-# arch: i386
-# arch: x86_64
-
-# Extra directories used by stack for building
-# extra-include-dirs: [/path/to/dir]
-# extra-lib-dirs: [/path/to/dir]
-
-# Allow a newer minor version of GHC than the snapshot specifies
-# compiler-check: newer-minor
diff --git a/striptests b/striptests
index 8281f78..fc3db44 100755
--- a/striptests
+++ b/striptests
@@ -1,78 +1,2 @@
#!/usr/bin/env bash
-# This file strips all unit tests from ShellCheck, removing
-# the dependency on QuickCheck and Template Haskell and
-# reduces the binary size considerably.
-set -o pipefail
-
-sponge() {
- local data
- data="$(cat)"
- printf '%s\n' "$data" > "$1"
-}
-
-modify() {
- if ! "${@:2}" < "$1" | sponge "$1"
- then
- {
- printf 'Failed to modify %s: ' "$1"
- printf '%q ' "${@:2}"
- printf '\n'
- } >&2
- exit 1
- fi
-}
-
-detestify() {
- printf '%s\n' '-- AUTOGENERATED from ShellCheck by striptests. Do not modify.'
- awk '
- BEGIN {
- state = 0;
- }
-
- /LANGUAGE TemplateHaskell/ { next; }
- /^import.*Test\./ { next; }
-
- /^module/ {
- sub(/,[^,)]*runTests/, "");
- }
-
- # Delete tests
- /^prop_/ { state = 1; next; }
-
- # ..and any blank lines following them.
- state == 1 && /^ / { next; }
-
- # Template Haskell marker
- /^return / {
- exit;
- }
-
- { state = 0; print; }
- '
-}
-
-
-
-if [[ ! -e 'ShellCheck.cabal' ]]
-then
- echo "Run me from the ShellCheck directory." >&2
- exit 1
-fi
-
-if [[ -d '.git' ]] && ! git diff --exit-code > /dev/null 2>&1
-then
- echo "You have local changes! These may be overwritten." >&2
- exit 2
-fi
-
-modify 'ShellCheck.cabal' sed -e '
- /QuickCheck/d
- /^test-suite/{ s/.*//; q; }
- '
-
-find . -name '.git' -prune -o -type f -name '*.hs' -print |
- while IFS= read -r file
- do
- modify "$file" detestify
- done
-
+# This file was deprecated by the doctest build.
diff --git a/test/doctests.hs b/test/doctests.hs
new file mode 100644
index 0000000..bbf7787
--- /dev/null
+++ b/test/doctests.hs
@@ -0,0 +1,12 @@
+module Main where
+
+import Build_doctests (flags, pkgs, module_sources)
+import Data.Foldable (traverse_)
+import Test.DocTest (doctest)
+
+main :: IO ()
+main = do
+ traverse_ putStrLn args
+ doctest args
+ where
+ args = flags ++ pkgs ++ module_sources
diff --git a/test/shellcheck.hs b/test/shellcheck.hs
deleted file mode 100644
index 6106d9a..0000000
--- a/test/shellcheck.hs
+++ /dev/null
@@ -1,24 +0,0 @@
-module Main where
-
-import Control.Monad
-import System.Exit
-import qualified ShellCheck.Checker
-import qualified ShellCheck.Analytics
-import qualified ShellCheck.AnalyzerLib
-import qualified ShellCheck.Parser
-import qualified ShellCheck.Checks.Commands
-import qualified ShellCheck.Checks.ShellSupport
-
-main = do
- putStrLn "Running ShellCheck tests..."
- results <- sequence [
- ShellCheck.Checker.runTests,
- ShellCheck.Checks.Commands.runTests,
- ShellCheck.Checks.ShellSupport.runTests,
- ShellCheck.Analytics.runTests,
- ShellCheck.AnalyzerLib.runTests,
- ShellCheck.Parser.runTests
- ]
- if and results
- then exitSuccess
- else exitFailure