{-# LANGUAGE TemplateHaskell #-}
module ShellCheck.Checker (checkScript, ShellCheck.Checker.runTests) where
import ShellCheck.Interface
import ShellCheck.Parser
import ShellCheck.Analyzer
import Data.Either
import Data.Functor
import Data.List
import Data.Maybe
import Data.Ord
import Control.Monad.Identity
import qualified Data.Map as Map
import qualified System.IO
import Prelude hiding (readFile)
import Control.Monad
import Test.QuickCheck.All
tokenToPosition :: Map Id (Position, Position) -> TokenComment -> PositionedComment
tokenToPosition startMap :: Map Id (Position, Position)
startMap t :: TokenComment
t = PositionedComment -> Maybe PositionedComment -> PositionedComment
forall a. a -> Maybe a -> a
fromMaybe PositionedComment
forall a. a
fail (Maybe PositionedComment -> PositionedComment)
-> Maybe PositionedComment -> PositionedComment
forall a b. (a -> b) -> a -> b
$ do
(Position, Position)
span <- Id -> Map Id (Position, Position) -> Maybe (Position, Position)
forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup (TokenComment -> Id
tcId TokenComment
t) Map Id (Position, Position)
startMap
PositionedComment -> Maybe PositionedComment
forall (m :: * -> *) a. Monad m => a -> m a
return (PositionedComment -> Maybe PositionedComment)
-> PositionedComment -> Maybe PositionedComment
forall a b. (a -> b) -> a -> b
$ PositionedComment
newPositionedComment {
pcStartPos :: Position
pcStartPos = (Position, Position) -> Position
forall a b. (a, b) -> a
fst (Position, Position)
span,
pcEndPos :: Position
pcEndPos = (Position, Position) -> Position
forall a b. (a, b) -> b
snd (Position, Position)
span,
pcComment :: Comment
pcComment = TokenComment -> Comment
tcComment TokenComment
t,
pcFix :: Maybe Fix
pcFix = TokenComment -> Maybe Fix
tcFix TokenComment
t
}
where
fail :: a
fail = [Char] -> a
forall a. HasCallStack => [Char] -> a
error "Internal shellcheck error: id doesn't exist. Please report!"
shellFromFilename :: [Char] -> Maybe Shell
shellFromFilename filename :: [Char]
filename = (Maybe Shell -> Maybe Shell -> Maybe Shell)
-> Maybe Shell -> [Maybe Shell] -> Maybe Shell
forall (t :: * -> *) b a.
Foldable t =>
(b -> a -> b) -> b -> t a -> b
foldl Maybe Shell -> Maybe Shell -> Maybe Shell
forall (m :: * -> *) a. MonadPlus m => m a -> m a -> m a
mplus Maybe Shell
forall a. Maybe a
Nothing [Maybe Shell]
candidates
where
shellExtensions :: [([Char], Shell)]
shellExtensions = [(".ksh", Shell
Ksh)
,(".bash", Shell
Bash)
,(".bats", Shell
Bash)
,(".dash", Shell
Dash)]
candidates :: [Maybe Shell]
candidates =
(([Char], Shell) -> Maybe Shell)
-> [([Char], Shell)] -> [Maybe Shell]
forall a b. (a -> b) -> [a] -> [b]
map (\(ext :: [Char]
ext,sh :: Shell
sh) -> if [Char]
ext [Char] -> [Char] -> Bool
forall a. Eq a => [a] -> [a] -> Bool
`isSuffixOf` [Char]
filename then Shell -> Maybe Shell
forall a. a -> Maybe a
Just Shell
sh else Maybe Shell
forall a. Maybe a
Nothing) [([Char], Shell)]
shellExtensions
checkScript :: Monad m => SystemInterface m -> CheckSpec -> m CheckResult
checkScript :: SystemInterface m -> CheckSpec -> m CheckResult
checkScript sys :: SystemInterface m
sys spec :: CheckSpec
spec = do
[PositionedComment]
results <- [Char] -> m [PositionedComment]
checkScript (CheckSpec -> [Char]
csScript CheckSpec
spec)
CheckResult -> m CheckResult
forall (m :: * -> *) a. Monad m => a -> m a
return CheckResult
emptyCheckResult {
crFilename :: [Char]
crFilename = CheckSpec -> [Char]
csFilename CheckSpec
spec,
crComments :: [PositionedComment]
crComments = [PositionedComment]
results
}
where
checkScript :: [Char] -> m [PositionedComment]
checkScript contents :: [Char]
contents = do
ParseResult
result <- SystemInterface m -> ParseSpec -> m ParseResult
forall (m :: * -> *).
Monad m =>
SystemInterface m -> ParseSpec -> m ParseResult
parseScript SystemInterface m
sys ParseSpec
newParseSpec {
psFilename :: [Char]
psFilename = CheckSpec -> [Char]
csFilename CheckSpec
spec,
psScript :: [Char]
psScript = [Char]
contents,
psCheckSourced :: Bool
psCheckSourced = CheckSpec -> Bool
csCheckSourced CheckSpec
spec,
psIgnoreRC :: Bool
psIgnoreRC = CheckSpec -> Bool
csIgnoreRC CheckSpec
spec,
psShellTypeOverride :: Maybe Shell
psShellTypeOverride = CheckSpec -> Maybe Shell
csShellTypeOverride CheckSpec
spec
}
let parseMessages :: [PositionedComment]
parseMessages = ParseResult -> [PositionedComment]
prComments ParseResult
result
let tokenPositions :: Map Id (Position, Position)
tokenPositions = ParseResult -> Map Id (Position, Position)
prTokenPositions ParseResult
result
let analysisSpec :: Token -> AnalysisSpec
analysisSpec root :: Token
root =
AnalysisSpec
as {
asScript :: Token
asScript = Token
root,
asShellType :: Maybe Shell
asShellType = CheckSpec -> Maybe Shell
csShellTypeOverride CheckSpec
spec,
asFallbackShell :: Maybe Shell
asFallbackShell = [Char] -> Maybe Shell
shellFromFilename ([Char] -> Maybe Shell) -> [Char] -> Maybe Shell
forall a b. (a -> b) -> a -> b
$ CheckSpec -> [Char]
csFilename CheckSpec
spec,
asCheckSourced :: Bool
asCheckSourced = CheckSpec -> Bool
csCheckSourced CheckSpec
spec,
asExecutionMode :: ExecutionMode
asExecutionMode = ExecutionMode
Executed,
asTokenPositions :: Map Id (Position, Position)
asTokenPositions = Map Id (Position, Position)
tokenPositions,
asOptionalChecks :: [[Char]]
asOptionalChecks = CheckSpec -> [[Char]]
csOptionalChecks CheckSpec
spec
} where as :: AnalysisSpec
as = Token -> AnalysisSpec
newAnalysisSpec Token
root
let analysisMessages :: [TokenComment]
analysisMessages =
[TokenComment] -> Maybe [TokenComment] -> [TokenComment]
forall a. a -> Maybe a -> a
fromMaybe [] (Maybe [TokenComment] -> [TokenComment])
-> Maybe [TokenComment] -> [TokenComment]
forall a b. (a -> b) -> a -> b
$
(AnalysisResult -> [TokenComment]
arComments (AnalysisResult -> [TokenComment])
-> (Token -> AnalysisResult) -> Token -> [TokenComment]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. AnalysisSpec -> AnalysisResult
analyzeScript (AnalysisSpec -> AnalysisResult)
-> (Token -> AnalysisSpec) -> Token -> AnalysisResult
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Token -> AnalysisSpec
analysisSpec)
(Token -> [TokenComment]) -> Maybe Token -> Maybe [TokenComment]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> ParseResult -> Maybe Token
prRoot ParseResult
result
let translator :: TokenComment -> PositionedComment
translator = Map Id (Position, Position) -> TokenComment -> PositionedComment
tokenToPosition Map Id (Position, Position)
tokenPositions
[PositionedComment] -> m [PositionedComment]
forall (m :: * -> *) a. Monad m => a -> m a
return ([PositionedComment] -> m [PositionedComment])
-> ([PositionedComment] -> [PositionedComment])
-> [PositionedComment]
-> m [PositionedComment]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [PositionedComment] -> [PositionedComment]
forall a. Eq a => [a] -> [a]
nub ([PositionedComment] -> [PositionedComment])
-> ([PositionedComment] -> [PositionedComment])
-> [PositionedComment]
-> [PositionedComment]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [PositionedComment] -> [PositionedComment]
sortMessages ([PositionedComment] -> [PositionedComment])
-> ([PositionedComment] -> [PositionedComment])
-> [PositionedComment]
-> [PositionedComment]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (PositionedComment -> Bool)
-> [PositionedComment] -> [PositionedComment]
forall a. (a -> Bool) -> [a] -> [a]
filter PositionedComment -> Bool
shouldInclude ([PositionedComment] -> m [PositionedComment])
-> [PositionedComment] -> m [PositionedComment]
forall a b. (a -> b) -> a -> b
$
([PositionedComment]
parseMessages [PositionedComment] -> [PositionedComment] -> [PositionedComment]
forall a. [a] -> [a] -> [a]
++ (TokenComment -> PositionedComment)
-> [TokenComment] -> [PositionedComment]
forall a b. (a -> b) -> [a] -> [b]
map TokenComment -> PositionedComment
translator [TokenComment]
analysisMessages)
shouldInclude :: PositionedComment -> Bool
shouldInclude pc :: PositionedComment
pc =
Severity
severity Severity -> Severity -> Bool
forall a. Ord a => a -> a -> Bool
<= CheckSpec -> Severity
csMinSeverity CheckSpec
spec Bool -> Bool -> Bool
&&
case CheckSpec -> Maybe [Integer]
csIncludedWarnings CheckSpec
spec of
Nothing -> Integer
code Integer -> [Integer] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`notElem` CheckSpec -> [Integer]
csExcludedWarnings CheckSpec
spec
Just includedWarnings :: [Integer]
includedWarnings -> Integer
code Integer -> [Integer] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [Integer]
includedWarnings
where
code :: Integer
code = Comment -> Integer
cCode (PositionedComment -> Comment
pcComment PositionedComment
pc)
severity :: Severity
severity = Comment -> Severity
cSeverity (PositionedComment -> Comment
pcComment PositionedComment
pc)
sortMessages :: [PositionedComment] -> [PositionedComment]
sortMessages = (PositionedComment -> PositionedComment -> Ordering)
-> [PositionedComment] -> [PositionedComment]
forall a. (a -> a -> Ordering) -> [a] -> [a]
sortBy ((PositionedComment
-> ([Char], Integer, Integer, Severity, Integer, [Char]))
-> PositionedComment -> PositionedComment -> Ordering
forall a b. Ord a => (b -> a) -> b -> b -> Ordering
comparing PositionedComment
-> ([Char], Integer, Integer, Severity, Integer, [Char])
order)
order :: PositionedComment
-> ([Char], Integer, Integer, Severity, Integer, [Char])
order pc :: PositionedComment
pc =
let pos :: Position
pos = PositionedComment -> Position
pcStartPos PositionedComment
pc
comment :: Comment
comment = PositionedComment -> Comment
pcComment PositionedComment
pc in
(Position -> [Char]
posFile Position
pos,
Position -> Integer
posLine Position
pos,
Position -> Integer
posColumn Position
pos,
Comment -> Severity
cSeverity Comment
comment,
Comment -> Integer
cCode Comment
comment,
Comment -> [Char]
cMessage Comment
comment)
getPosition :: PositionedComment -> Position
getPosition = PositionedComment -> Position
pcStartPos
getErrors :: SystemInterface Identity -> CheckSpec -> [Integer]
getErrors sys :: SystemInterface Identity
sys spec :: CheckSpec
spec =
[Integer] -> [Integer]
forall a. Ord a => [a] -> [a]
sort ([Integer] -> [Integer])
-> (CheckResult -> [Integer]) -> CheckResult -> [Integer]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (PositionedComment -> Integer) -> [PositionedComment] -> [Integer]
forall a b. (a -> b) -> [a] -> [b]
map PositionedComment -> Integer
getCode ([PositionedComment] -> [Integer])
-> (CheckResult -> [PositionedComment]) -> CheckResult -> [Integer]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. CheckResult -> [PositionedComment]
crComments (CheckResult -> [Integer]) -> CheckResult -> [Integer]
forall a b. (a -> b) -> a -> b
$
Identity CheckResult -> CheckResult
forall a. Identity a -> a
runIdentity (SystemInterface Identity -> CheckSpec -> Identity CheckResult
forall (m :: * -> *).
Monad m =>
SystemInterface m -> CheckSpec -> m CheckResult
checkScript SystemInterface Identity
sys CheckSpec
spec)
where
getCode :: PositionedComment -> Integer
getCode = Comment -> Integer
cCode (Comment -> Integer)
-> (PositionedComment -> Comment) -> PositionedComment -> Integer
forall b c a. (b -> c) -> (a -> b) -> a -> c
. PositionedComment -> Comment
pcComment
check :: [Char] -> [Integer]
check = [([Char], [Char])] -> [Char] -> [Integer]
checkWithIncludes []
checkWithSpec :: [([Char], [Char])] -> CheckSpec -> [Integer]
checkWithSpec includes :: [([Char], [Char])]
includes =
SystemInterface Identity -> CheckSpec -> [Integer]
getErrors ([([Char], [Char])] -> SystemInterface Identity
mockedSystemInterface [([Char], [Char])]
includes)
checkWithIncludes :: [([Char], [Char])] -> [Char] -> [Integer]
checkWithIncludes includes :: [([Char], [Char])]
includes src :: [Char]
src =
[([Char], [Char])] -> CheckSpec -> [Integer]
checkWithSpec [([Char], [Char])]
includes CheckSpec
emptyCheckSpec {
csScript :: [Char]
csScript = [Char]
src,
csExcludedWarnings :: [Integer]
csExcludedWarnings = [2148]
}
checkRecursive :: [([Char], [Char])] -> [Char] -> [Integer]
checkRecursive includes :: [([Char], [Char])]
includes src :: [Char]
src =
[([Char], [Char])] -> CheckSpec -> [Integer]
checkWithSpec [([Char], [Char])]
includes CheckSpec
emptyCheckSpec {
csScript :: [Char]
csScript = [Char]
src,
csExcludedWarnings :: [Integer]
csExcludedWarnings = [2148],
csCheckSourced :: Bool
csCheckSourced = Bool
True
}
checkOptionIncludes :: Maybe [Integer] -> [Char] -> [Integer]
checkOptionIncludes includes :: Maybe [Integer]
includes src :: [Char]
src =
[([Char], [Char])] -> CheckSpec -> [Integer]
checkWithSpec [] CheckSpec
emptyCheckSpec {
csScript :: [Char]
csScript = [Char]
src,
csIncludedWarnings :: Maybe [Integer]
csIncludedWarnings = Maybe [Integer]
includes,
csCheckSourced :: Bool
csCheckSourced = Bool
True
}
checkWithRc :: [Char] -> CheckSpec -> [Integer]
checkWithRc rc :: [Char]
rc = SystemInterface Identity -> CheckSpec -> [Integer]
getErrors
([Char] -> SystemInterface Identity -> SystemInterface Identity
forall (m :: * -> *).
Monad m =>
[Char] -> SystemInterface m -> SystemInterface m
mockRcFile [Char]
rc (SystemInterface Identity -> SystemInterface Identity)
-> SystemInterface Identity -> SystemInterface Identity
forall a b. (a -> b) -> a -> b
$ [([Char], [Char])] -> SystemInterface Identity
mockedSystemInterface [])
checkWithIncludesAndSourcePath :: [([Char], [Char])]
-> ([Char] -> [[Char]] -> [Char] -> Identity [Char])
-> CheckSpec
-> [Integer]
checkWithIncludesAndSourcePath includes :: [([Char], [Char])]
includes mapper :: [Char] -> [[Char]] -> [Char] -> Identity [Char]
mapper = SystemInterface Identity -> CheckSpec -> [Integer]
getErrors
([([Char], [Char])] -> SystemInterface Identity
mockedSystemInterface [([Char], [Char])]
includes) {
siFindSource :: [Char] -> [[Char]] -> [Char] -> Identity [Char]
siFindSource = [Char] -> [[Char]] -> [Char] -> Identity [Char]
mapper
}
prop_findsParseIssue :: Bool
prop_findsParseIssue = [Char] -> [Integer]
check "echo \"$12\"" [Integer] -> [Integer] -> Bool
forall a. Eq a => a -> a -> Bool
== [1037]
=
[Integer] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null ([Integer] -> Bool) -> [Integer] -> Bool
forall a b. (a -> b) -> a -> b
$ [Char] -> [Integer]
check "#shellcheck disable=SC1037\necho \"$12\""
=
[Integer] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null ([Integer] -> Bool) -> [Integer] -> Bool
forall a b. (a -> b) -> a -> b
$ [Char] -> [Integer]
check "#shellcheck disable=SC1037\n#lol\necho \"$12\""
prop_findsAnalysisIssue :: Bool
prop_findsAnalysisIssue =
[Char] -> [Integer]
check "echo $1" [Integer] -> [Integer] -> Bool
forall a. Eq a => a -> a -> Bool
== [2086]
=
[Integer] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null ([Integer] -> Bool) -> [Integer] -> Bool
forall a b. (a -> b) -> a -> b
$ [Char] -> [Integer]
check "#shellcheck disable=SC2086\necho $1"
=
[Integer] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null ([Integer] -> Bool) -> [Integer] -> Bool
forall a b. (a -> b) -> a -> b
$ [Char] -> [Integer]
check "#shellcheck disable=SC2086\n#lol\necho $1"
prop_optionDisablesIssue1 :: Bool
prop_optionDisablesIssue1 =
[Integer] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null ([Integer] -> Bool) -> [Integer] -> Bool
forall a b. (a -> b) -> a -> b
$ SystemInterface Identity -> CheckSpec -> [Integer]
getErrors
([([Char], [Char])] -> SystemInterface Identity
mockedSystemInterface [])
CheckSpec
emptyCheckSpec {
csScript :: [Char]
csScript = "echo $1",
csExcludedWarnings :: [Integer]
csExcludedWarnings = [2148, 2086]
}
prop_optionDisablesIssue2 :: Bool
prop_optionDisablesIssue2 =
[Integer] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null ([Integer] -> Bool) -> [Integer] -> Bool
forall a b. (a -> b) -> a -> b
$ SystemInterface Identity -> CheckSpec -> [Integer]
getErrors
([([Char], [Char])] -> SystemInterface Identity
mockedSystemInterface [])
CheckSpec
emptyCheckSpec {
csScript :: [Char]
csScript = "echo \"$10\"",
csExcludedWarnings :: [Integer]
csExcludedWarnings = [2148, 1037]
}
prop_wontParseBadShell :: Bool
prop_wontParseBadShell =
[1071] [Integer] -> [Integer] -> Bool
forall a. Eq a => a -> a -> Bool
== [Char] -> [Integer]
check "#!/usr/bin/python\ntrue $1\n"
prop_optionDisablesBadShebang :: Bool
prop_optionDisablesBadShebang =
[Integer] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null ([Integer] -> Bool) -> [Integer] -> Bool
forall a b. (a -> b) -> a -> b
$ SystemInterface Identity -> CheckSpec -> [Integer]
getErrors
([([Char], [Char])] -> SystemInterface Identity
mockedSystemInterface [])
CheckSpec
emptyCheckSpec {
csScript :: [Char]
csScript = "#!/usr/bin/python\ntrue\n",
csShellTypeOverride :: Maybe Shell
csShellTypeOverride = Shell -> Maybe Shell
forall a. a -> Maybe a
Just Shell
Sh
}
prop_annotationDisablesBadShebang :: Bool
prop_annotationDisablesBadShebang =
[] [Integer] -> [Integer] -> Bool
forall a. Eq a => a -> a -> Bool
== [Char] -> [Integer]
check "#!/usr/bin/python\n# shellcheck shell=sh\ntrue\n"
prop_canParseDevNull :: Bool
prop_canParseDevNull =
[] [Integer] -> [Integer] -> Bool
forall a. Eq a => a -> a -> Bool
== [Char] -> [Integer]
check "source /dev/null"
prop_failsWhenNotSourcing :: Bool
prop_failsWhenNotSourcing =
[1091, 2154] [Integer] -> [Integer] -> Bool
forall a. Eq a => a -> a -> Bool
== [Char] -> [Integer]
check "source lol; echo \"$bar\""
prop_worksWhenSourcing :: Bool
prop_worksWhenSourcing =
[Integer] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null ([Integer] -> Bool) -> [Integer] -> Bool
forall a b. (a -> b) -> a -> b
$ [([Char], [Char])] -> [Char] -> [Integer]
checkWithIncludes [("lib", "bar=1")] "source lib; echo \"$bar\""
prop_worksWhenSourcingWithDashDash :: Bool
prop_worksWhenSourcingWithDashDash =
[Integer] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null ([Integer] -> Bool) -> [Integer] -> Bool
forall a b. (a -> b) -> a -> b
$ [([Char], [Char])] -> [Char] -> [Integer]
checkWithIncludes [("lib", "bar=1")] "source -- lib; echo \"$bar\""
prop_worksWhenDotting :: Bool
prop_worksWhenDotting =
[Integer] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null ([Integer] -> Bool) -> [Integer] -> Bool
forall a b. (a -> b) -> a -> b
$ [([Char], [Char])] -> [Char] -> [Integer]
checkWithIncludes [("lib", "bar=1")] ". lib; echo \"$bar\""
prop_noInfiniteSourcing :: Bool
prop_noInfiniteSourcing =
[] [Integer] -> [Integer] -> Bool
forall a. Eq a => a -> a -> Bool
== [([Char], [Char])] -> [Char] -> [Integer]
checkWithIncludes [("lib", "source lib")] "source lib"
prop_canSourceBadSyntax :: Bool
prop_canSourceBadSyntax =
[1094, 2086] [Integer] -> [Integer] -> Bool
forall a. Eq a => a -> a -> Bool
== [([Char], [Char])] -> [Char] -> [Integer]
checkWithIncludes [("lib", "for f; do")] "source lib; echo $1"
prop_cantSourceDynamic :: Bool
prop_cantSourceDynamic =
[1090] [Integer] -> [Integer] -> Bool
forall a. Eq a => a -> a -> Bool
== [([Char], [Char])] -> [Char] -> [Integer]
checkWithIncludes [("lib", "")] ". \"$1\""
prop_cantSourceDynamic2 :: Bool
prop_cantSourceDynamic2 =
[1090] [Integer] -> [Integer] -> Bool
forall a. Eq a => a -> a -> Bool
== [([Char], [Char])] -> [Char] -> [Integer]
checkWithIncludes [("lib", "")] "source ~/foo"
prop_canSourceDynamicWhenRedirected :: Bool
prop_canSourceDynamicWhenRedirected =
[Integer] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null ([Integer] -> Bool) -> [Integer] -> Bool
forall a b. (a -> b) -> a -> b
$ [([Char], [Char])] -> [Char] -> [Integer]
checkWithIncludes [("lib", "")] "#shellcheck source=lib\n. \"$1\""
prop_recursiveAnalysis :: Bool
prop_recursiveAnalysis =
[2086] [Integer] -> [Integer] -> Bool
forall a. Eq a => a -> a -> Bool
== [([Char], [Char])] -> [Char] -> [Integer]
checkRecursive [("lib", "echo $1")] "source lib"
prop_recursiveParsing :: Bool
prop_recursiveParsing =
[1037] [Integer] -> [Integer] -> Bool
forall a. Eq a => a -> a -> Bool
== [([Char], [Char])] -> [Char] -> [Integer]
checkRecursive [("lib", "echo \"$10\"")] "source lib"
prop_nonRecursiveAnalysis :: Bool
prop_nonRecursiveAnalysis =
[] [Integer] -> [Integer] -> Bool
forall a. Eq a => a -> a -> Bool
== [([Char], [Char])] -> [Char] -> [Integer]
checkWithIncludes [("lib", "echo $1")] "source lib"
prop_nonRecursiveParsing :: Bool
prop_nonRecursiveParsing =
[] [Integer] -> [Integer] -> Bool
forall a. Eq a => a -> a -> Bool
== [([Char], [Char])] -> [Char] -> [Integer]
checkWithIncludes [("lib", "echo \"$10\"")] "source lib"
prop_sourceDirectiveDoesntFollowFile :: Bool
prop_sourceDirectiveDoesntFollowFile =
[Integer] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null ([Integer] -> Bool) -> [Integer] -> Bool
forall a b. (a -> b) -> a -> b
$ [([Char], [Char])] -> [Char] -> [Integer]
checkWithIncludes
[("foo", "source bar"), ("bar", "baz=3")]
"#shellcheck source=foo\n. \"$1\"; echo \"$baz\""
prop_filewideAnnotationBase :: Bool
prop_filewideAnnotationBase = [2086] [Integer] -> [Integer] -> Bool
forall a. Eq a => a -> a -> Bool
== [Char] -> [Integer]
check "#!/bin/sh\necho $1"
prop_filewideAnnotation1 :: Bool
prop_filewideAnnotation1 = [Integer] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null ([Integer] -> Bool) -> [Integer] -> Bool
forall a b. (a -> b) -> a -> b
$
[Char] -> [Integer]
check "#!/bin/sh\n# shellcheck disable=2086\necho $1"
prop_filewideAnnotation2 :: Bool
prop_filewideAnnotation2 = [Integer] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null ([Integer] -> Bool) -> [Integer] -> Bool
forall a b. (a -> b) -> a -> b
$
[Char] -> [Integer]
check "#!/bin/sh\n# shellcheck disable=2086\ntrue\necho $1"
prop_filewideAnnotation3 :: Bool
prop_filewideAnnotation3 = [Integer] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null ([Integer] -> Bool) -> [Integer] -> Bool
forall a b. (a -> b) -> a -> b
$
[Char] -> [Integer]
check "#!/bin/sh\n#unrelated\n# shellcheck disable=2086\ntrue\necho $1"
prop_filewideAnnotation4 :: Bool
prop_filewideAnnotation4 = [Integer] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null ([Integer] -> Bool) -> [Integer] -> Bool
forall a b. (a -> b) -> a -> b
$
[Char] -> [Integer]
check "#!/bin/sh\n# shellcheck disable=2086\n#unrelated\ntrue\necho $1"
prop_filewideAnnotation5 :: Bool
prop_filewideAnnotation5 = [Integer] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null ([Integer] -> Bool) -> [Integer] -> Bool
forall a b. (a -> b) -> a -> b
$
[Char] -> [Integer]
check "#!/bin/sh\n\n\n\n#shellcheck disable=2086\ntrue\necho $1"
prop_filewideAnnotation6 :: Bool
prop_filewideAnnotation6 = [Integer] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null ([Integer] -> Bool) -> [Integer] -> Bool
forall a b. (a -> b) -> a -> b
$
[Char] -> [Integer]
check "#shellcheck shell=sh\n#unrelated\n#shellcheck disable=2086\ntrue\necho $1"
prop_filewideAnnotation7 :: Bool
prop_filewideAnnotation7 = [Integer] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null ([Integer] -> Bool) -> [Integer] -> Bool
forall a b. (a -> b) -> a -> b
$
[Char] -> [Integer]
check "#!/bin/sh\n# shellcheck disable=2086\n#unrelated\ntrue\necho $1"
prop_filewideAnnotationBase2 :: Bool
prop_filewideAnnotationBase2 = [2086, 2181] [Integer] -> [Integer] -> Bool
forall a. Eq a => a -> a -> Bool
== [Char] -> [Integer]
check "true\n[ $? == 0 ] && echo $1"
prop_filewideAnnotation8 :: Bool
prop_filewideAnnotation8 = [Integer] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null ([Integer] -> Bool) -> [Integer] -> Bool
forall a b. (a -> b) -> a -> b
$
[Char] -> [Integer]
check "# Disable $? warning\n#shellcheck disable=SC2181\n# Disable quoting warning\n#shellcheck disable=2086\ntrue\n[ $? == 0 ] && echo $1"
prop_sourcePartOfOriginalScript :: Bool
prop_sourcePartOfOriginalScript =
2039 Integer -> [Integer] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [([Char], [Char])] -> [Char] -> [Integer]
checkWithIncludes [("./saywhat.sh", "echo foo")] "#!/bin/sh\nsource ./saywhat.sh"
prop_spinBug1413 :: Bool
prop_spinBug1413 = [Integer] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null ([Integer] -> Bool) -> [Integer] -> Bool
forall a b. (a -> b) -> a -> b
$ [Char] -> [Integer]
check "fun() {\n# shellcheck disable=SC2188\n> /dev/null\n}\n"
prop_deducesTypeFromExtension :: Bool
prop_deducesTypeFromExtension = [Integer] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null [Integer]
result
where
result :: [Integer]
result = [([Char], [Char])] -> CheckSpec -> [Integer]
checkWithSpec [] CheckSpec
emptyCheckSpec {
csFilename :: [Char]
csFilename = "file.ksh",
csScript :: [Char]
csScript = "(( 3.14 ))"
}
prop_deducesTypeFromExtension2 :: Bool
prop_deducesTypeFromExtension2 = [Integer]
result [Integer] -> [Integer] -> Bool
forall a. Eq a => a -> a -> Bool
== [2079]
where
result :: [Integer]
result = [([Char], [Char])] -> CheckSpec -> [Integer]
checkWithSpec [] CheckSpec
emptyCheckSpec {
csFilename :: [Char]
csFilename = "file.bash",
csScript :: [Char]
csScript = "(( 3.14 ))"
}
prop_shExtensionDoesntMatter :: Bool
prop_shExtensionDoesntMatter = [Integer]
result [Integer] -> [Integer] -> Bool
forall a. Eq a => a -> a -> Bool
== [2148]
where
result :: [Integer]
result = [([Char], [Char])] -> CheckSpec -> [Integer]
checkWithSpec [] CheckSpec
emptyCheckSpec {
csFilename :: [Char]
csFilename = "file.sh",
csScript :: [Char]
csScript = "echo 'hello world'"
}
prop_sourcedFileUsesOriginalShellExtension :: Bool
prop_sourcedFileUsesOriginalShellExtension = [Integer]
result [Integer] -> [Integer] -> Bool
forall a. Eq a => a -> a -> Bool
== [2079]
where
result :: [Integer]
result = [([Char], [Char])] -> CheckSpec -> [Integer]
checkWithSpec [("file.ksh", "(( 3.14 ))")] CheckSpec
emptyCheckSpec {
csFilename :: [Char]
csFilename = "file.bash",
csScript :: [Char]
csScript = "source file.ksh",
csCheckSourced :: Bool
csCheckSourced = Bool
True
}
prop_canEnableOptionalsWithSpec :: Bool
prop_canEnableOptionalsWithSpec = [Integer]
result [Integer] -> [Integer] -> Bool
forall a. Eq a => a -> a -> Bool
== [2244]
where
result :: [Integer]
result = [([Char], [Char])] -> CheckSpec -> [Integer]
checkWithSpec [] CheckSpec
emptyCheckSpec {
csFilename :: [Char]
csFilename = "file.sh",
csScript :: [Char]
csScript = "#!/bin/sh\n[ \"$1\" ]",
csOptionalChecks :: [[Char]]
csOptionalChecks = ["avoid-nullary-conditions"]
}
prop_optionIncludes1 :: Bool
prop_optionIncludes1 =
[Integer] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null ([Integer] -> Bool) -> [Integer] -> Bool
forall a b. (a -> b) -> a -> b
$ Maybe [Integer] -> [Char] -> [Integer]
checkOptionIncludes ([Integer] -> Maybe [Integer]
forall a. a -> Maybe a
Just [2080]) "#!/bin/sh\n var='a b'\n echo $var"
prop_optionIncludes2 :: Bool
prop_optionIncludes2 =
[2086] [Integer] -> [Integer] -> Bool
forall a. Eq a => a -> a -> Bool
== Maybe [Integer] -> [Char] -> [Integer]
checkOptionIncludes ([Integer] -> Maybe [Integer]
forall a. a -> Maybe a
Just [2086]) "#!/bin/sh\n var='a b'\n echo $var"
prop_optionIncludes3 :: Bool
prop_optionIncludes3 =
[2086] [Integer] -> [Integer] -> Bool
forall a. Eq a => a -> a -> Bool
== Maybe [Integer] -> [Char] -> [Integer]
checkOptionIncludes Maybe [Integer]
forall a. Maybe a
Nothing "#!/bin/sh\n var='a b'\n echo $var"
prop_optionIncludes4 :: Bool
prop_optionIncludes4 =
[2154] [Integer] -> [Integer] -> Bool
forall a. Eq a => a -> a -> Bool
== Maybe [Integer] -> [Char] -> [Integer]
checkOptionIncludes ([Integer] -> Maybe [Integer]
forall a. a -> Maybe a
Just [2154]) "#!/bin/sh\n var='a b'\n echo $var\n echo $bar"
prop_readsRcFile :: Bool
prop_readsRcFile = [Integer]
result [Integer] -> [Integer] -> Bool
forall a. Eq a => a -> a -> Bool
== []
where
result :: [Integer]
result = [Char] -> CheckSpec -> [Integer]
checkWithRc "disable=2086" CheckSpec
emptyCheckSpec {
csScript :: [Char]
csScript = "#!/bin/sh\necho $1",
csIgnoreRC :: Bool
csIgnoreRC = Bool
False
}
prop_canUseNoRC :: Bool
prop_canUseNoRC = [Integer]
result [Integer] -> [Integer] -> Bool
forall a. Eq a => a -> a -> Bool
== [2086]
where
result :: [Integer]
result = [Char] -> CheckSpec -> [Integer]
checkWithRc "disable=2086" CheckSpec
emptyCheckSpec {
csScript :: [Char]
csScript = "#!/bin/sh\necho $1",
csIgnoreRC :: Bool
csIgnoreRC = Bool
True
}
prop_NoRCWontLookAtFile :: Bool
prop_NoRCWontLookAtFile = [Integer]
result [Integer] -> [Integer] -> Bool
forall a. Eq a => a -> a -> Bool
== [2086]
where
result :: [Integer]
result = [Char] -> CheckSpec -> [Integer]
checkWithRc ([Char] -> [Char]
forall a. HasCallStack => [Char] -> a
error "Fail") CheckSpec
emptyCheckSpec {
csScript :: [Char]
csScript = "#!/bin/sh\necho $1",
csIgnoreRC :: Bool
csIgnoreRC = Bool
True
}
prop_brokenRcGetsWarning :: Bool
prop_brokenRcGetsWarning = [Integer]
result [Integer] -> [Integer] -> Bool
forall a. Eq a => a -> a -> Bool
== [1134, 2086]
where
result :: [Integer]
result = [Char] -> CheckSpec -> [Integer]
checkWithRc "rofl" CheckSpec
emptyCheckSpec {
csScript :: [Char]
csScript = "#!/bin/sh\necho $1",
csIgnoreRC :: Bool
csIgnoreRC = Bool
False
}
prop_canEnableOptionalsWithRc :: Bool
prop_canEnableOptionalsWithRc = [Integer]
result [Integer] -> [Integer] -> Bool
forall a. Eq a => a -> a -> Bool
== [2244]
where
result :: [Integer]
result = [Char] -> CheckSpec -> [Integer]
checkWithRc "enable=avoid-nullary-conditions" CheckSpec
emptyCheckSpec {
csScript :: [Char]
csScript = "#!/bin/sh\n[ \"$1\" ]"
}
prop_sourcePathRedirectsName :: Bool
prop_sourcePathRedirectsName = [Integer]
result [Integer] -> [Integer] -> Bool
forall a. Eq a => a -> a -> Bool
== [2086]
where
f :: [Char] -> p -> [Char] -> m [Char]
f "dir/myscript" _ "lib" = [Char] -> m [Char]
forall (m :: * -> *) a. Monad m => a -> m a
return "foo/lib"
result :: [Integer]
result = [([Char], [Char])]
-> ([Char] -> [[Char]] -> [Char] -> Identity [Char])
-> CheckSpec
-> [Integer]
checkWithIncludesAndSourcePath [("foo/lib", "echo $1")] [Char] -> [[Char]] -> [Char] -> Identity [Char]
forall (m :: * -> *) p.
Monad m =>
[Char] -> p -> [Char] -> m [Char]
f CheckSpec
emptyCheckSpec {
csScript :: [Char]
csScript = "#!/bin/bash\nsource lib",
csFilename :: [Char]
csFilename = "dir/myscript",
csCheckSourced :: Bool
csCheckSourced = Bool
True
}
prop_sourcePathAddsAnnotation :: Bool
prop_sourcePathAddsAnnotation = [Integer]
result [Integer] -> [Integer] -> Bool
forall a. Eq a => a -> a -> Bool
== [2086]
where
f :: [Char] -> [[Char]] -> [Char] -> m [Char]
f "dir/myscript" ["mypath"] "lib" = [Char] -> m [Char]
forall (m :: * -> *) a. Monad m => a -> m a
return "foo/lib"
result :: [Integer]
result = [([Char], [Char])]
-> ([Char] -> [[Char]] -> [Char] -> Identity [Char])
-> CheckSpec
-> [Integer]
checkWithIncludesAndSourcePath [("foo/lib", "echo $1")] [Char] -> [[Char]] -> [Char] -> Identity [Char]
forall (m :: * -> *).
Monad m =>
[Char] -> [[Char]] -> [Char] -> m [Char]
f CheckSpec
emptyCheckSpec {
csScript :: [Char]
csScript = "#!/bin/bash\n# shellcheck source-path=mypath\nsource lib",
csFilename :: [Char]
csFilename = "dir/myscript",
csCheckSourced :: Bool
csCheckSourced = Bool
True
}
prop_sourcePathRedirectsDirective :: Bool
prop_sourcePathRedirectsDirective = [Integer]
result [Integer] -> [Integer] -> Bool
forall a. Eq a => a -> a -> Bool
== [2086]
where
f :: [Char] -> p -> [Char] -> m [Char]
f "dir/myscript" _ "lib" = [Char] -> m [Char]
forall (m :: * -> *) a. Monad m => a -> m a
return "foo/lib"
f _ _ _ = [Char] -> m [Char]
forall (m :: * -> *) a. Monad m => a -> m a
return "/dev/null"
result :: [Integer]
result = [([Char], [Char])]
-> ([Char] -> [[Char]] -> [Char] -> Identity [Char])
-> CheckSpec
-> [Integer]
checkWithIncludesAndSourcePath [("foo/lib", "echo $1")] [Char] -> [[Char]] -> [Char] -> Identity [Char]
forall (m :: * -> *) p.
Monad m =>
[Char] -> p -> [Char] -> m [Char]
f CheckSpec
emptyCheckSpec {
csScript :: [Char]
csScript = "#!/bin/bash\n# shellcheck source=lib\nsource kittens",
csFilename :: [Char]
csFilename = "dir/myscript",
csCheckSourced :: Bool
csCheckSourced = Bool
True
}
return []
runTests :: IO Bool
runTests = Bool
Bool -> Property
[([Char], Property)] -> (Property -> IO Result) -> IO Bool
Property -> IO Result
forall prop. Testable prop => prop -> IO Result
forall prop. Testable prop => prop -> Property
runQuickCheckAll :: [([Char], Property)] -> (Property -> IO Result) -> IO Bool
property :: forall prop. Testable prop => prop -> Property
quickCheckResult :: forall prop. Testable prop => prop -> IO Result
prop_sourcePathRedirectsDirective :: Bool
prop_sourcePathAddsAnnotation :: Bool
prop_sourcePathRedirectsName :: Bool
prop_canEnableOptionalsWithRc :: Bool
prop_brokenRcGetsWarning :: Bool
prop_NoRCWontLookAtFile :: Bool
prop_canUseNoRC :: Bool
prop_readsRcFile :: Bool
prop_optionIncludes4 :: Bool
prop_optionIncludes3 :: Bool
prop_optionIncludes2 :: Bool
prop_optionIncludes1 :: Bool
prop_canEnableOptionalsWithSpec :: Bool
prop_sourcedFileUsesOriginalShellExtension :: Bool
prop_shExtensionDoesntMatter :: Bool
prop_deducesTypeFromExtension2 :: Bool
prop_deducesTypeFromExtension :: Bool
prop_spinBug1413 :: Bool
prop_sourcePartOfOriginalScript :: Bool
prop_filewideAnnotation8 :: Bool
prop_filewideAnnotationBase2 :: Bool
prop_filewideAnnotation7 :: Bool
prop_filewideAnnotation6 :: Bool
prop_filewideAnnotation5 :: Bool
prop_filewideAnnotation4 :: Bool
prop_filewideAnnotation3 :: Bool
prop_filewideAnnotation2 :: Bool
prop_filewideAnnotation1 :: Bool
prop_filewideAnnotationBase :: Bool
prop_sourceDirectiveDoesntFollowFile :: Bool
prop_nonRecursiveParsing :: Bool
prop_nonRecursiveAnalysis :: Bool
prop_recursiveParsing :: Bool
prop_recursiveAnalysis :: Bool
prop_canSourceDynamicWhenRedirected :: Bool
prop_cantSourceDynamic2 :: Bool
prop_cantSourceDynamic :: Bool
prop_canSourceBadSyntax :: Bool
prop_noInfiniteSourcing :: Bool
prop_worksWhenDotting :: Bool
prop_worksWhenSourcingWithDashDash :: Bool
prop_worksWhenSourcing :: Bool
prop_failsWhenNotSourcing :: Bool
prop_canParseDevNull :: Bool
prop_annotationDisablesBadShebang :: Bool
prop_optionDisablesBadShebang :: Bool
prop_wontParseBadShell :: Bool
prop_optionDisablesIssue2 :: Bool
prop_optionDisablesIssue1 :: Bool
prop_commentDisablesAnalysisIssue2 :: Bool
prop_commentDisablesAnalysisIssue1 :: Bool
prop_findsAnalysisIssue :: Bool
prop_commentDisablesParseIssue2 :: Bool
prop_commentDisablesParseIssue1 :: Bool
prop_findsParseIssue :: Bool
$quickCheckAll