{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE FlexibleContexts #-}
module ShellCheck.Analytics (runAnalytics, optionalChecks, ShellCheck.Analytics.runTests) where
import ShellCheck.AST
import ShellCheck.ASTLib
import ShellCheck.AnalyzerLib hiding (producesComments)
import ShellCheck.Data
import ShellCheck.Parser
import ShellCheck.Interface
import ShellCheck.Regex
import Control.Arrow (first)
import Control.Monad
import Control.Monad.Identity
import Control.Monad.State
import Control.Monad.Writer hiding ((<>))
import Control.Monad.Reader
import Data.Char
import Data.Functor
import Data.Function (on)
import Data.List
import Data.Maybe
import Data.Ord
import Data.Semigroup
import Debug.Trace
import qualified Data.Map.Strict as Map
import Test.QuickCheck.All (forAllProperties)
import Test.QuickCheck.Test (quickCheckWithResult, stdArgs, maxSuccess)
treeChecks :: [Parameters -> Token -> [TokenComment]]
treeChecks :: [Parameters -> Token -> [TokenComment]]
treeChecks = [
[Parameters -> Token -> WriterT [TokenComment] Identity ()]
-> Parameters -> Token -> [TokenComment]
forall w (t :: * -> *) t b.
(Monoid w, Foldable t) =>
t (t -> Token -> WriterT w Identity b) -> t -> Token -> w
nodeChecksToTreeCheck [Parameters -> Token -> WriterT [TokenComment] Identity ()]
nodeChecks
,Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
subshellAssignmentCheck
,Parameters -> Token -> [TokenComment]
checkSpacefulness
,Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkQuotesInLiterals
,Parameters -> Token -> [TokenComment]
forall t. t -> Token -> [TokenComment]
checkShebangParameters
,Parameters -> Token -> [TokenComment]
forall t. t -> Token -> [TokenComment]
checkFunctionsUsedExternally
,Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkUnusedAssignments
,Parameters -> Token -> [TokenComment]
checkUnpassedInFunctions
,Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkArrayWithoutIndex
,Parameters -> Token -> [TokenComment]
checkShebang
,Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkUnassignedReferences
,Parameters -> Token -> [TokenComment]
checkUncheckedCdPushdPopd
,Parameters -> Token -> [TokenComment]
checkArrayAssignmentIndices
,Parameters -> Token -> [TokenComment]
forall t. t -> Token -> [TokenComment]
checkUseBeforeDefinition
,Parameters -> Token -> [TokenComment]
checkAliasUsedInSameParsingUnit
]
runAnalytics :: AnalysisSpec -> [TokenComment]
runAnalytics :: AnalysisSpec -> [TokenComment]
runAnalytics options :: AnalysisSpec
options =
AnalysisSpec
-> [Parameters -> Token -> [TokenComment]] -> [TokenComment]
runList AnalysisSpec
options [Parameters -> Token -> [TokenComment]]
treeChecks [TokenComment] -> [TokenComment] -> [TokenComment]
forall a. [a] -> [a] -> [a]
++ AnalysisSpec
-> [Parameters -> Token -> [TokenComment]] -> [TokenComment]
runList AnalysisSpec
options [Parameters -> Token -> [TokenComment]]
optionalChecks
where
root :: Token
root = AnalysisSpec -> Token
asScript AnalysisSpec
options
optionals :: [String]
optionals = Token -> [String]
getEnableDirectives Token
root [String] -> [String] -> [String]
forall a. [a] -> [a] -> [a]
++ AnalysisSpec -> [String]
asOptionalChecks AnalysisSpec
options
optionalChecks :: [Parameters -> Token -> [TokenComment]]
optionalChecks =
if "all" String -> [String] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [String]
optionals
then ((CheckDescription, Parameters -> Token -> [TokenComment])
-> Parameters -> Token -> [TokenComment])
-> [(CheckDescription, Parameters -> Token -> [TokenComment])]
-> [Parameters -> Token -> [TokenComment]]
forall a b. (a -> b) -> [a] -> [b]
map (CheckDescription, Parameters -> Token -> [TokenComment])
-> Parameters -> Token -> [TokenComment]
forall a b. (a, b) -> b
snd [(CheckDescription, Parameters -> Token -> [TokenComment])]
optionalTreeChecks
else (String -> Maybe (Parameters -> Token -> [TokenComment]))
-> [String] -> [Parameters -> Token -> [TokenComment]]
forall a b. (a -> Maybe b) -> [a] -> [b]
mapMaybe (\c :: String
c -> String
-> Map String (Parameters -> Token -> [TokenComment])
-> Maybe (Parameters -> Token -> [TokenComment])
forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup String
c Map String (Parameters -> Token -> [TokenComment])
optionalCheckMap) [String]
optionals
runList :: AnalysisSpec -> [Parameters -> Token -> [TokenComment]]
-> [TokenComment]
runList :: AnalysisSpec
-> [Parameters -> Token -> [TokenComment]] -> [TokenComment]
runList spec :: AnalysisSpec
spec list :: [Parameters -> Token -> [TokenComment]]
list = [TokenComment]
notes
where
root :: Token
root = AnalysisSpec -> Token
asScript AnalysisSpec
spec
params :: Parameters
params = AnalysisSpec -> Parameters
makeParameters AnalysisSpec
spec
notes :: [TokenComment]
notes = ((Parameters -> Token -> [TokenComment]) -> [TokenComment])
-> [Parameters -> Token -> [TokenComment]] -> [TokenComment]
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap (\f :: Parameters -> Token -> [TokenComment]
f -> Parameters -> Token -> [TokenComment]
f Parameters
params Token
root) [Parameters -> Token -> [TokenComment]]
list
getEnableDirectives :: Token -> [String]
getEnableDirectives root :: Token
root =
case Token
root of
T_Annotation _ list :: [Annotation]
list _ -> [String
s | EnableComment s :: String
s <- [Annotation]
list]
_ -> []
checkList :: t (t -> [b]) -> t -> [b]
checkList l :: t (t -> [b])
l t :: t
t = ((t -> [b]) -> [b]) -> t (t -> [b]) -> [b]
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap (\f :: t -> [b]
f -> t -> [b]
f t
t) t (t -> [b])
l
runNodeAnalysis :: (t -> Token -> WriterT w Identity ()) -> t -> Token -> w
runNodeAnalysis f :: t -> Token -> WriterT w Identity ()
f p :: t
p t :: Token
t = Writer w Token -> w
forall w a. Writer w a -> w
execWriter ((Token -> WriterT w Identity ()) -> Token -> Writer w Token
forall (m :: * -> *).
Monad m =>
(Token -> m ()) -> Token -> m Token
doAnalysis (t -> Token -> WriterT w Identity ()
f t
p) Token
t)
nodeChecksToTreeCheck :: t (t -> Token -> WriterT w Identity b) -> t -> Token -> w
nodeChecksToTreeCheck checkList :: t (t -> Token -> WriterT w Identity b)
checkList =
(t -> Token -> WriterT w Identity ()) -> t -> Token -> w
forall w t.
Monoid w =>
(t -> Token -> WriterT w Identity ()) -> t -> Token -> w
runNodeAnalysis
(\p :: t
p t :: Token
t -> (((t -> Token -> WriterT w Identity b) -> WriterT w Identity b)
-> t (t -> Token -> WriterT w Identity b) -> WriterT w Identity ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ ((\ f :: Token -> WriterT w Identity b
f -> Token -> WriterT w Identity b
f Token
t) ((Token -> WriterT w Identity b) -> WriterT w Identity b)
-> ((t -> Token -> WriterT w Identity b)
-> Token -> WriterT w Identity b)
-> (t -> Token -> WriterT w Identity b)
-> WriterT w Identity b
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (\ f :: t -> Token -> WriterT w Identity b
f -> t -> Token -> WriterT w Identity b
f t
p))
t (t -> Token -> WriterT w Identity b)
checkList))
nodeChecks :: [Parameters -> Token -> Writer [TokenComment] ()]
nodeChecks :: [Parameters -> Token -> WriterT [TokenComment] Identity ()]
nodeChecks = [
Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkUuoc
,Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkPipePitfalls
,Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkForInQuoted
,Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkForInLs
,Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkShorthandIf
,Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkDollarStar
,Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkUnquotedDollarAt
,Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkStderrRedirect
,Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkUnquotedN
,Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkNumberComparisons
,Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkSingleBracketOperators
,Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkDoubleBracketOperators
,Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkLiteralBreakingTest
,Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkConstantNullary
,Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkDivBeforeMult
,Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkArithmeticDeref
,Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkArithmeticBadOctal
,Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkComparisonAgainstGlob
,Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkCaseAgainstGlob
,Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkCommarrays
,Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkOrNeq
,Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkEchoWc
,Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkConstantIfs
,Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkPipedAssignment
,Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkAssignAteCommand
,Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkUuoeVar
,Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkQuotedCondRegex
,Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkForInCat
,Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkFindExec
,Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkValidCondOps
,Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkGlobbedRegex
,Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkTestRedirects
,Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkIndirectExpansion
,Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkPS1Assignments
,Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkBackticks
,Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkInexplicablyUnquoted
,Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkTildeInQuotes
,Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkLonelyDotDash
,Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkSpuriousExec
,Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkSpuriousExpansion
,Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkDollarBrackets
,Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkSshHereDoc
,Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkGlobsAsOptions
,Parameters -> Token -> WriterT [TokenComment] Identity ()
checkWhileReadPitfalls
,Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkArithmeticOpCommand
,Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkCharRangeGlob
,Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkUnquotedExpansions
,Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkSingleQuotedVariables
,Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkRedirectToSame
,Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkPrefixAssignmentReference
,Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkLoopKeywordScope
,Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkCdAndBack
,Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkWrongArithmeticAssignment
,Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkConditionalAndOrs
,Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkFunctionDeclarations
,Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkStderrPipe
,Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkOverridingPath
,Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkArrayAsString
,Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkUnsupported
,Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkMultipleAppends
,Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkSuspiciousIFS
,Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkShouldUseGrepQ
,Parameters -> Token -> WriterT [TokenComment] Identity ()
checkTestArgumentSplitting
,Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkConcatenatedDollarAt
,Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkTildeInPath
,Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkMaskedReturns
,Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkReadWithoutR
,Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkLoopVariableReassignment
,Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkTrailingBracket
,Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkReturnAgainstZero
,Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkRedirectedNowhere
,Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkUnmatchableCases
,Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkSubshellAsTest
,Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkSplittingInArrays
,Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkRedirectionToNumber
,Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkGlobAsCommand
,Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkFlagAsCommand
,Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkEmptyCondition
,Parameters -> Token -> WriterT [TokenComment] Identity ()
checkPipeToNowhere
,Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkForLoopGlobVariables
,Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkSubshelledTests
,Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkInvertedStringTest
,Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkRedirectionToCommand
,Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkDollarQuoteParen
,Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkUselessBang
,Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkTranslatedStringVariable
,Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkModifiedArithmeticInRedirection
,Parameters -> Token -> WriterT [TokenComment] Identity ()
checkBlatantRecursion
,Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkBadTestAndOr
,Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkAssignToSelf
,Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkEqualsInCommand
,Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkSecondArgIsComparison
,Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkComparisonWithLeadingX
]
optionalChecks :: [CheckDescription]
optionalChecks = ((CheckDescription, Parameters -> Token -> [TokenComment])
-> CheckDescription)
-> [(CheckDescription, Parameters -> Token -> [TokenComment])]
-> [CheckDescription]
forall a b. (a -> b) -> [a] -> [b]
map (CheckDescription, Parameters -> Token -> [TokenComment])
-> CheckDescription
forall a b. (a, b) -> a
fst [(CheckDescription, Parameters -> Token -> [TokenComment])]
optionalTreeChecks
prop_verifyOptionalExamples :: Bool
prop_verifyOptionalExamples = ((CheckDescription, Parameters -> Token -> [TokenComment]) -> Bool)
-> [(CheckDescription, Parameters -> Token -> [TokenComment])]
-> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all (CheckDescription, Parameters -> Token -> [TokenComment]) -> Bool
check [(CheckDescription, Parameters -> Token -> [TokenComment])]
optionalTreeChecks
where
check :: (CheckDescription, Parameters -> Token -> [TokenComment]) -> Bool
check (desc :: CheckDescription
desc, check :: Parameters -> Token -> [TokenComment]
check) =
(Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyTree Parameters -> Token -> [TokenComment]
check (CheckDescription -> String
cdPositive CheckDescription
desc)
Bool -> Bool -> Bool
&& (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
check (CheckDescription -> String
cdNegative CheckDescription
desc)
optionalTreeChecks :: [(CheckDescription, (Parameters -> Token -> [TokenComment]))]
optionalTreeChecks :: [(CheckDescription, Parameters -> Token -> [TokenComment])]
optionalTreeChecks = [
(CheckDescription
newCheckDescription {
cdName :: String
cdName = "quote-safe-variables",
cdDescription :: String
cdDescription = "Suggest quoting variables without metacharacters",
cdPositive :: String
cdPositive = "var=hello; echo $var",
cdNegative :: String
cdNegative = "var=hello; echo \"$var\""
}, Parameters -> Token -> [TokenComment]
checkVerboseSpacefulness)
,(CheckDescription
newCheckDescription {
cdName :: String
cdName = "avoid-nullary-conditions",
cdDescription :: String
cdDescription = "Suggest explicitly using -n in `[ $var ]`",
cdPositive :: String
cdPositive = "[ \"$var\" ]",
cdNegative :: String
cdNegative = "[ -n \"$var\" ]"
}, [Parameters -> Token -> WriterT [TokenComment] Identity ()]
-> Parameters -> Token -> [TokenComment]
forall w (t :: * -> *) t b.
(Monoid w, Foldable t) =>
t (t -> Token -> WriterT w Identity b) -> t -> Token -> w
nodeChecksToTreeCheck [Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkNullaryExpansionTest])
,(CheckDescription
newCheckDescription {
cdName :: String
cdName = "add-default-case",
cdDescription :: String
cdDescription = "Suggest adding a default case in `case` statements",
cdPositive :: String
cdPositive = "case $? in 0) echo 'Success';; esac",
cdNegative :: String
cdNegative = "case $? in 0) echo 'Success';; *) echo 'Fail' ;; esac"
}, [Parameters -> Token -> WriterT [TokenComment] Identity ()]
-> Parameters -> Token -> [TokenComment]
forall w (t :: * -> *) t b.
(Monoid w, Foldable t) =>
t (t -> Token -> WriterT w Identity b) -> t -> Token -> w
nodeChecksToTreeCheck [Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkDefaultCase])
,(CheckDescription
newCheckDescription {
cdName :: String
cdName = "require-variable-braces",
cdDescription :: String
cdDescription = "Suggest putting braces around all variable references",
cdPositive :: String
cdPositive = "var=hello; echo $var",
cdNegative :: String
cdNegative = "var=hello; echo ${var}"
}, [Parameters -> Token -> WriterT [TokenComment] Identity ()]
-> Parameters -> Token -> [TokenComment]
forall w (t :: * -> *) t b.
(Monoid w, Foldable t) =>
t (t -> Token -> WriterT w Identity b) -> t -> Token -> w
nodeChecksToTreeCheck [Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkVariableBraces])
,(CheckDescription
newCheckDescription {
cdName :: String
cdName = "check-unassigned-uppercase",
cdDescription :: String
cdDescription = "Warn when uppercase variables are unassigned",
cdPositive :: String
cdPositive = "echo $VAR",
cdNegative :: String
cdNegative = "VAR=hello; echo $VAR"
}, Bool -> Parameters -> Token -> [TokenComment]
forall p. Bool -> Parameters -> p -> [TokenComment]
checkUnassignedReferences' Bool
True)
]
optionalCheckMap :: Map.Map String (Parameters -> Token -> [TokenComment])
optionalCheckMap :: Map String (Parameters -> Token -> [TokenComment])
optionalCheckMap = [(String, Parameters -> Token -> [TokenComment])]
-> Map String (Parameters -> Token -> [TokenComment])
forall k a. Ord k => [(k, a)] -> Map k a
Map.fromList ([(String, Parameters -> Token -> [TokenComment])]
-> Map String (Parameters -> Token -> [TokenComment]))
-> [(String, Parameters -> Token -> [TokenComment])]
-> Map String (Parameters -> Token -> [TokenComment])
forall a b. (a -> b) -> a -> b
$ ((CheckDescription, Parameters -> Token -> [TokenComment])
-> (String, Parameters -> Token -> [TokenComment]))
-> [(CheckDescription, Parameters -> Token -> [TokenComment])]
-> [(String, Parameters -> Token -> [TokenComment])]
forall a b. (a -> b) -> [a] -> [b]
map (CheckDescription, Parameters -> Token -> [TokenComment])
-> (String, Parameters -> Token -> [TokenComment])
forall b. (CheckDescription, b) -> (String, b)
item [(CheckDescription, Parameters -> Token -> [TokenComment])]
optionalTreeChecks
where
item :: (CheckDescription, b) -> (String, b)
item (desc :: CheckDescription
desc, check :: b
check) = (CheckDescription -> String
cdName CheckDescription
desc, b
check)
wouldHaveBeenGlob :: t Char -> Bool
wouldHaveBeenGlob s :: t Char
s = '*' Char -> t Char -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` t Char
s
verify :: (Parameters -> Token -> Writer [TokenComment] ()) -> String -> Bool
verify :: (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify f :: Parameters -> Token -> WriterT [TokenComment] Identity ()
f s :: String
s = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Maybe Bool
checkNode Parameters -> Token -> WriterT [TokenComment] Identity ()
f String
s Maybe Bool -> Maybe Bool -> Bool
forall a. Eq a => a -> a -> Bool
== Bool -> Maybe Bool
forall a. a -> Maybe a
Just Bool
True
verifyNot :: (Parameters -> Token -> Writer [TokenComment] ()) -> String -> Bool
verifyNot :: (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot f :: Parameters -> Token -> WriterT [TokenComment] Identity ()
f s :: String
s = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Maybe Bool
checkNode Parameters -> Token -> WriterT [TokenComment] Identity ()
f String
s Maybe Bool -> Maybe Bool -> Bool
forall a. Eq a => a -> a -> Bool
== Bool -> Maybe Bool
forall a. a -> Maybe a
Just Bool
False
verifyTree :: (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyTree :: (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyTree f :: Parameters -> Token -> [TokenComment]
f s :: String
s = (Parameters -> Token -> [TokenComment]) -> String -> Maybe Bool
producesComments Parameters -> Token -> [TokenComment]
f String
s Maybe Bool -> Maybe Bool -> Bool
forall a. Eq a => a -> a -> Bool
== Bool -> Maybe Bool
forall a. a -> Maybe a
Just Bool
True
verifyNotTree :: (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree :: (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree f :: Parameters -> Token -> [TokenComment]
f s :: String
s = (Parameters -> Token -> [TokenComment]) -> String -> Maybe Bool
producesComments Parameters -> Token -> [TokenComment]
f String
s Maybe Bool -> Maybe Bool -> Bool
forall a. Eq a => a -> a -> Bool
== Bool -> Maybe Bool
forall a. a -> Maybe a
Just Bool
False
checkCommand :: String -> (Token -> [Token] -> m ()) -> Token -> m ()
checkCommand str :: String
str f :: Token -> [Token] -> m ()
f t :: Token
t@(T_SimpleCommand id :: Id
id _ (cmd :: Token
cmd:rest :: [Token]
rest))
| Token
t Token -> String -> Bool
`isCommand` String
str = Token -> [Token] -> m ()
f Token
cmd [Token]
rest
checkCommand _ _ _ = () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
checkUnqualifiedCommand :: String -> (Token -> [Token] -> m ()) -> Token -> m ()
checkUnqualifiedCommand str :: String
str f :: Token -> [Token] -> m ()
f t :: Token
t@(T_SimpleCommand id :: Id
id _ (cmd :: Token
cmd:rest :: [Token]
rest))
| Token
t Token -> String -> Bool
`isUnqualifiedCommand` String
str = Token -> [Token] -> m ()
f Token
cmd [Token]
rest
checkUnqualifiedCommand _ _ _ = () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
verifyCodes :: (Parameters -> Token -> Writer [TokenComment] ()) -> [Code] -> String -> Bool
verifyCodes :: (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> [Code] -> String -> Bool
verifyCodes f :: Parameters -> Token -> WriterT [TokenComment] Identity ()
f l :: [Code]
l s :: String
s = Maybe [Code]
codes Maybe [Code] -> Maybe [Code] -> Bool
forall a. Eq a => a -> a -> Bool
== [Code] -> Maybe [Code]
forall a. a -> Maybe a
Just [Code]
l
where
treeCheck :: Parameters -> Token -> [TokenComment]
treeCheck = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> Parameters -> Token -> [TokenComment]
forall w t.
Monoid w =>
(t -> Token -> WriterT w Identity ()) -> t -> Token -> w
runNodeAnalysis Parameters -> Token -> WriterT [TokenComment] Identity ()
f
comments :: Maybe [TokenComment]
comments = (Parameters -> Token -> [TokenComment])
-> String -> Maybe [TokenComment]
runAndGetComments Parameters -> Token -> [TokenComment]
treeCheck String
s
codes :: Maybe [Code]
codes = (TokenComment -> Code) -> [TokenComment] -> [Code]
forall a b. (a -> b) -> [a] -> [b]
map (Comment -> Code
cCode (Comment -> Code)
-> (TokenComment -> Comment) -> TokenComment -> Code
forall b c a. (b -> c) -> (a -> b) -> a -> c
. TokenComment -> Comment
tcComment) ([TokenComment] -> [Code]) -> Maybe [TokenComment] -> Maybe [Code]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Maybe [TokenComment]
comments
checkNode :: (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Maybe Bool
checkNode f :: Parameters -> Token -> WriterT [TokenComment] Identity ()
f = (Parameters -> Token -> [TokenComment]) -> String -> Maybe Bool
producesComments ((Parameters -> Token -> WriterT [TokenComment] Identity ())
-> Parameters -> Token -> [TokenComment]
forall w t.
Monoid w =>
(t -> Token -> WriterT w Identity ()) -> t -> Token -> w
runNodeAnalysis Parameters -> Token -> WriterT [TokenComment] Identity ()
f)
producesComments :: (Parameters -> Token -> [TokenComment]) -> String -> Maybe Bool
f :: Parameters -> Token -> [TokenComment]
f s :: String
s = Bool -> Bool
not (Bool -> Bool)
-> ([TokenComment] -> Bool) -> [TokenComment] -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [TokenComment] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null ([TokenComment] -> Bool) -> Maybe [TokenComment] -> Maybe Bool
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> (Parameters -> Token -> [TokenComment])
-> String -> Maybe [TokenComment]
runAndGetComments Parameters -> Token -> [TokenComment]
f String
s
runAndGetComments :: (Parameters -> Token -> [TokenComment])
-> String -> Maybe [TokenComment]
runAndGetComments f :: Parameters -> Token -> [TokenComment]
f s :: String
s = do
let pr :: ParseResult
pr = String -> ParseResult
pScript String
s
ParseResult -> Maybe Token
prRoot ParseResult
pr
let spec :: AnalysisSpec
spec = ParseResult -> AnalysisSpec
defaultSpec ParseResult
pr
let params :: Parameters
params = AnalysisSpec -> Parameters
makeParameters AnalysisSpec
spec
[TokenComment] -> Maybe [TokenComment]
forall (m :: * -> *) a. Monad m => a -> m a
return ([TokenComment] -> Maybe [TokenComment])
-> [TokenComment] -> Maybe [TokenComment]
forall a b. (a -> b) -> a -> b
$
AnalysisSpec -> Parameters -> [TokenComment] -> [TokenComment]
filterByAnnotation AnalysisSpec
spec Parameters
params ([TokenComment] -> [TokenComment])
-> [TokenComment] -> [TokenComment]
forall a b. (a -> b) -> a -> b
$
AnalysisSpec
-> [Parameters -> Token -> [TokenComment]] -> [TokenComment]
runList AnalysisSpec
spec [Parameters -> Token -> [TokenComment]
f]
dist :: Eq a => [a] -> [a] -> Int
dist :: [a] -> [a] -> Int
dist a :: [a]
a b :: [a]
b
= [Int] -> Int
forall a. [a] -> a
last (if Int
lab Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== 0 then [Int]
mainDiag
else if Int
lab Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> 0 then [[Int]]
lowers [[Int]] -> Int -> [Int]
forall a. [a] -> Int -> a
!! (Int
lab Int -> Int -> Int
forall a. Num a => a -> a -> a
- 1)
else [[Int]]
uppers [[Int]] -> Int -> [Int]
forall a. [a] -> Int -> a
!! (-1 Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
lab))
where mainDiag :: [Int]
mainDiag = [a] -> [a] -> [Int] -> [Int] -> [Int]
forall a a. (Num a, Ord a, Eq a) => [a] -> [a] -> [a] -> [a] -> [a]
oneDiag [a]
a [a]
b ([[Int]] -> [Int]
forall a. [a] -> a
head [[Int]]
uppers) (-1 Int -> [Int] -> [Int]
forall a. a -> [a] -> [a]
: [[Int]] -> [Int]
forall a. [a] -> a
head [[Int]]
lowers)
uppers :: [[Int]]
uppers = [a] -> [a] -> [[Int]] -> [[Int]]
forall a a. (Num a, Ord a, Eq a) => [a] -> [a] -> [[a]] -> [[a]]
eachDiag [a]
a [a]
b ([Int]
mainDiag [Int] -> [[Int]] -> [[Int]]
forall a. a -> [a] -> [a]
: [[Int]]
uppers)
lowers :: [[Int]]
lowers = [a] -> [a] -> [[Int]] -> [[Int]]
forall a a. (Num a, Ord a, Eq a) => [a] -> [a] -> [[a]] -> [[a]]
eachDiag [a]
b [a]
a ([Int]
mainDiag [Int] -> [[Int]] -> [[Int]]
forall a. a -> [a] -> [a]
: [[Int]]
lowers)
eachDiag :: [a] -> [a] -> [[a]] -> [[a]]
eachDiag a :: [a]
a [] diags :: [[a]]
diags = []
eachDiag a :: [a]
a (bch :: a
bch:bs :: [a]
bs) (lastDiag :: [a]
lastDiag:diags :: [[a]]
diags) = [a] -> [a] -> [a] -> [a] -> [a]
forall a a. (Num a, Ord a, Eq a) => [a] -> [a] -> [a] -> [a] -> [a]
oneDiag [a]
a [a]
bs [a]
nextDiag [a]
lastDiag [a] -> [[a]] -> [[a]]
forall a. a -> [a] -> [a]
: [a] -> [a] -> [[a]] -> [[a]]
eachDiag [a]
a [a]
bs [[a]]
diags
where nextDiag :: [a]
nextDiag = [[a]] -> [a]
forall a. [a] -> a
head ([[a]] -> [[a]]
forall a. [a] -> [a]
tail [[a]]
diags)
oneDiag :: [a] -> [a] -> [a] -> [a] -> [a]
oneDiag a :: [a]
a b :: [a]
b diagAbove :: [a]
diagAbove diagBelow :: [a]
diagBelow = [a]
thisdiag
where doDiag :: [a] -> [a] -> a -> [a] -> [a] -> [a]
doDiag [] b :: [a]
b nw :: a
nw n :: [a]
n w :: [a]
w = []
doDiag a :: [a]
a [] nw :: a
nw n :: [a]
n w :: [a]
w = []
doDiag (ach :: a
ach:as :: [a]
as) (bch :: a
bch:bs :: [a]
bs) nw :: a
nw n :: [a]
n w :: [a]
w = a
me a -> [a] -> [a]
forall a. a -> [a] -> [a]
: [a] -> [a] -> a -> [a] -> [a] -> [a]
doDiag [a]
as [a]
bs a
me ([a] -> [a]
forall a. [a] -> [a]
tail [a]
n) ([a] -> [a]
forall a. [a] -> [a]
tail [a]
w)
where me :: a
me = if a
ach a -> a -> Bool
forall a. Eq a => a -> a -> Bool
== a
bch then a
nw else 1 a -> a -> a
forall a. Num a => a -> a -> a
+ a -> a -> a -> a
forall a. Ord a => a -> a -> a -> a
min3 ([a] -> a
forall a. [a] -> a
head [a]
w) a
nw ([a] -> a
forall a. [a] -> a
head [a]
n)
firstelt :: a
firstelt = 1 a -> a -> a
forall a. Num a => a -> a -> a
+ [a] -> a
forall a. [a] -> a
head [a]
diagBelow
thisdiag :: [a]
thisdiag = a
firstelt a -> [a] -> [a]
forall a. a -> [a] -> [a]
: [a] -> [a] -> a -> [a] -> [a] -> [a]
forall a a.
(Num a, Ord a, Eq a) =>
[a] -> [a] -> a -> [a] -> [a] -> [a]
doDiag [a]
a [a]
b a
firstelt [a]
diagAbove ([a] -> [a]
forall a. [a] -> [a]
tail [a]
diagBelow)
lab :: Int
lab = [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [a]
a Int -> Int -> Int
forall a. Num a => a -> a -> a
- [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [a]
b
min3 :: a -> a -> a -> a
min3 x :: a
x y :: a
y z :: a
z = if a
x a -> a -> Bool
forall a. Ord a => a -> a -> Bool
< a
y then a
x else a -> a -> a
forall a. Ord a => a -> a -> a
min a
y a
z
hasFloatingPoint :: Parameters -> Bool
hasFloatingPoint params :: Parameters
params = Parameters -> Shell
shellType Parameters
params Shell -> Shell -> Bool
forall a. Eq a => a -> a -> Bool
== Shell
Ksh
isCondition :: [Token] -> Bool
isCondition [] = Bool
False
isCondition [_] = Bool
False
isCondition (child :: Token
child:parent :: Token
parent:rest :: [Token]
rest) =
case Token
child of
T_BatsTest {} -> Bool
True
_ -> Token -> Id
getId Token
child Id -> [Id] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` (Token -> Id) -> [Token] -> [Id]
forall a b. (a -> b) -> [a] -> [b]
map Token -> Id
getId (Token -> [Token]
getConditionChildren Token
parent) Bool -> Bool -> Bool
|| [Token] -> Bool
isCondition (Token
parentToken -> [Token] -> [Token]
forall a. a -> [a] -> [a]
:[Token]
rest)
where
getConditionChildren :: Token -> [Token]
getConditionChildren t :: Token
t =
case Token
t of
T_AndIf _ left :: Token
left right :: Token
right -> [Token
left]
T_OrIf id :: Id
id left :: Token
left right :: Token
right -> [Token
left]
T_IfExpression id :: Id
id conditions :: [([Token], [Token])]
conditions elses :: [Token]
elses -> (([Token], [Token]) -> [Token]) -> [([Token], [Token])] -> [Token]
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap (Int -> [Token] -> [Token]
forall a. Int -> [a] -> [a]
take 1 ([Token] -> [Token])
-> (([Token], [Token]) -> [Token]) -> ([Token], [Token]) -> [Token]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Token] -> [Token]
forall a. [a] -> [a]
reverse ([Token] -> [Token])
-> (([Token], [Token]) -> [Token]) -> ([Token], [Token]) -> [Token]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ([Token], [Token]) -> [Token]
forall a b. (a, b) -> a
fst) [([Token], [Token])]
conditions
T_WhileExpression id :: Id
id c :: [Token]
c l :: [Token]
l -> Int -> [Token] -> [Token]
forall a. Int -> [a] -> [a]
take 1 ([Token] -> [Token]) -> ([Token] -> [Token]) -> [Token] -> [Token]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Token] -> [Token]
forall a. [a] -> [a]
reverse ([Token] -> [Token]) -> [Token] -> [Token]
forall a b. (a -> b) -> a -> b
$ [Token]
c
T_UntilExpression id :: Id
id c :: [Token]
c l :: [Token]
l -> Int -> [Token] -> [Token]
forall a. Int -> [a] -> [a]
take 1 ([Token] -> [Token]) -> ([Token] -> [Token]) -> [Token] -> [Token]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Token] -> [Token]
forall a. [a] -> [a]
reverse ([Token] -> [Token]) -> [Token] -> [Token]
forall a b. (a -> b) -> a -> b
$ [Token]
c
_ -> []
replaceStart :: Id -> Parameters -> Code -> String -> Replacement
replaceStart id :: Id
id params :: Parameters
params n :: Code
n r :: String
r =
let tp :: Map Id (Position, Position)
tp = Parameters -> Map Id (Position, Position)
tokenPositions Parameters
params
(start :: Position
start, _) = Map Id (Position, Position)
tp Map Id (Position, Position) -> Id -> (Position, Position)
forall k a. Ord k => Map k a -> k -> a
Map.! Id
id
new_end :: Position
new_end = Position
start {
posColumn :: Code
posColumn = Position -> Code
posColumn Position
start Code -> Code -> Code
forall a. Num a => a -> a -> a
+ Code
n
}
depth :: Int
depth = [Token] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length ([Token] -> Int) -> [Token] -> Int
forall a b. (a -> b) -> a -> b
$ Map Id Token -> Token -> [Token]
getPath (Parameters -> Map Id Token
parentMap Parameters
params) (Id -> Token
T_EOF Id
id)
in
Replacement
newReplacement {
repStartPos :: Position
repStartPos = Position
start,
repEndPos :: Position
repEndPos = Position
new_end,
repString :: String
repString = String
r,
repPrecedence :: Int
repPrecedence = Int
depth,
repInsertionPoint :: InsertionPoint
repInsertionPoint = InsertionPoint
InsertAfter
}
replaceEnd :: Id -> Parameters -> Code -> String -> Replacement
replaceEnd id :: Id
id params :: Parameters
params n :: Code
n r :: String
r =
let tp :: Map Id (Position, Position)
tp = Parameters -> Map Id (Position, Position)
tokenPositions Parameters
params
(_, end :: Position
end) = Map Id (Position, Position)
tp Map Id (Position, Position) -> Id -> (Position, Position)
forall k a. Ord k => Map k a -> k -> a
Map.! Id
id
new_start :: Position
new_start = Position
end {
posColumn :: Code
posColumn = Position -> Code
posColumn Position
end Code -> Code -> Code
forall a. Num a => a -> a -> a
- Code
n
}
new_end :: Position
new_end = Position
end {
posColumn :: Code
posColumn = Position -> Code
posColumn Position
end
}
depth :: Int
depth = [Token] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length ([Token] -> Int) -> [Token] -> Int
forall a b. (a -> b) -> a -> b
$ Map Id Token -> Token -> [Token]
getPath (Parameters -> Map Id Token
parentMap Parameters
params) (Id -> Token
T_EOF Id
id)
in
Replacement
newReplacement {
repStartPos :: Position
repStartPos = Position
new_start,
repEndPos :: Position
repEndPos = Position
new_end,
repString :: String
repString = String
r,
repPrecedence :: Int
repPrecedence = Int
depth,
repInsertionPoint :: InsertionPoint
repInsertionPoint = InsertionPoint
InsertBefore
}
replaceToken :: Id -> Parameters -> String -> Replacement
replaceToken id :: Id
id params :: Parameters
params r :: String
r =
let tp :: Map Id (Position, Position)
tp = Parameters -> Map Id (Position, Position)
tokenPositions Parameters
params
(start :: Position
start, end :: Position
end) = Map Id (Position, Position)
tp Map Id (Position, Position) -> Id -> (Position, Position)
forall k a. Ord k => Map k a -> k -> a
Map.! Id
id
depth :: Int
depth = [Token] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length ([Token] -> Int) -> [Token] -> Int
forall a b. (a -> b) -> a -> b
$ Map Id Token -> Token -> [Token]
getPath (Parameters -> Map Id Token
parentMap Parameters
params) (Id -> Token
T_EOF Id
id)
in
Replacement
newReplacement {
repStartPos :: Position
repStartPos = Position
start,
repEndPos :: Position
repEndPos = Position
end,
repString :: String
repString = String
r,
repPrecedence :: Int
repPrecedence = Int
depth,
repInsertionPoint :: InsertionPoint
repInsertionPoint = InsertionPoint
InsertBefore
}
surroundWidth :: Id -> Parameters -> String -> Fix
surroundWidth id :: Id
id params :: Parameters
params s :: String
s = [Replacement] -> Fix
fixWith [Id -> Parameters -> Code -> String -> Replacement
replaceStart Id
id Parameters
params 0 String
s, Id -> Parameters -> Code -> String -> Replacement
replaceEnd Id
id Parameters
params 0 String
s]
fixWith :: [Replacement] -> Fix
fixWith fixes :: [Replacement]
fixes = Fix
newFix { fixReplacements :: [Replacement]
fixReplacements = [Replacement]
fixes }
prop_checkEchoWc3 :: Bool
prop_checkEchoWc3 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkEchoWc "n=$(echo $foo | wc -c)"
checkEchoWc :: p -> Token -> f ()
checkEchoWc _ (T_Pipeline id :: Id
id _ [a :: Token
a, b :: Token
b]) =
Bool -> f () -> f ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when ([String]
acmd [String] -> [String] -> Bool
forall a. Eq a => a -> a -> Bool
== ["echo", "${VAR}"]) (f () -> f ()) -> f () -> f ()
forall a b. (a -> b) -> a -> b
$
case [String]
bcmd of
["wc", "-c"] -> f ()
countMsg
["wc", "-m"] -> f ()
countMsg
_ -> () -> f ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
where
acmd :: [String]
acmd = Token -> [String]
oversimplify Token
a
bcmd :: [String]
bcmd = Token -> [String]
oversimplify Token
b
countMsg :: f ()
countMsg = Id -> Code -> String -> f ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
style Id
id 2000 "See if you can use ${#variable} instead."
checkEchoWc _ _ = () -> f ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
prop_checkPipedAssignment1 :: Bool
prop_checkPipedAssignment1 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkPipedAssignment "A=ls | grep foo"
prop_checkPipedAssignment2 :: Bool
prop_checkPipedAssignment2 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkPipedAssignment "A=foo cmd | grep foo"
prop_checkPipedAssignment3 :: Bool
prop_checkPipedAssignment3 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkPipedAssignment "A=foo"
checkPipedAssignment :: p -> Token -> m ()
checkPipedAssignment _ (T_Pipeline _ _ (T_Redirecting _ _ (T_SimpleCommand id :: Id
id (_:_) []):_:_)) =
Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
warn Id
id 2036 "If you wanted to assign the output of the pipeline, use a=$(b | c) ."
checkPipedAssignment _ _ = () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
prop_checkAssignAteCommand1 :: Bool
prop_checkAssignAteCommand1 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkAssignAteCommand "A=ls -l"
prop_checkAssignAteCommand2 :: Bool
prop_checkAssignAteCommand2 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkAssignAteCommand "A=ls --sort=$foo"
prop_checkAssignAteCommand3 :: Bool
prop_checkAssignAteCommand3 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkAssignAteCommand "A=cat foo | grep bar"
prop_checkAssignAteCommand4 :: Bool
prop_checkAssignAteCommand4 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkAssignAteCommand "A=foo ls -l"
prop_checkAssignAteCommand5 :: Bool
prop_checkAssignAteCommand5 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkAssignAteCommand "PAGER=cat grep bar"
prop_checkAssignAteCommand6 :: Bool
prop_checkAssignAteCommand6 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkAssignAteCommand "PAGER=\"cat\" grep bar"
prop_checkAssignAteCommand7 :: Bool
prop_checkAssignAteCommand7 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkAssignAteCommand "here=pwd"
checkAssignAteCommand :: p -> Token -> m ()
checkAssignAteCommand _ (T_SimpleCommand id :: Id
id [T_Assignment _ _ _ _ assignmentTerm :: Token
assignmentTerm] list :: [Token]
list) =
if [Token] -> Bool
firstWordIsArg [Token]
list
then
Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
err Id
id 2037 "To assign the output of a command, use var=$(cmd) ."
else
Bool -> m () -> m ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (Maybe String -> Bool
isCommonCommand (Maybe String -> Bool) -> Maybe String -> Bool
forall a b. (a -> b) -> a -> b
$ Token -> Maybe String
getUnquotedLiteral Token
assignmentTerm) (m () -> m ()) -> m () -> m ()
forall a b. (a -> b) -> a -> b
$
Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
warn Id
id 2209 "Use var=$(command) to assign output (or quote to assign string)."
where
isCommonCommand :: Maybe String -> Bool
isCommonCommand (Just s :: String
s) = String
s String -> [String] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [String]
commonCommands
isCommonCommand _ = Bool
False
firstWordIsArg :: [Token] -> Bool
firstWordIsArg list :: [Token]
list = Bool -> Maybe Bool -> Bool
forall a. a -> Maybe a -> a
fromMaybe Bool
False (Maybe Bool -> Bool) -> Maybe Bool -> Bool
forall a b. (a -> b) -> a -> b
$ do
Token
head <- [Token]
list [Token] -> Int -> Maybe Token
forall a. [a] -> Int -> Maybe a
!!! 0
Bool -> Maybe Bool
forall (m :: * -> *) a. Monad m => a -> m a
return (Bool -> Maybe Bool) -> Bool -> Maybe Bool
forall a b. (a -> b) -> a -> b
$ Token -> Bool
isGlob Token
head Bool -> Bool -> Bool
|| Token -> Bool
isUnquotedFlag Token
head
checkAssignAteCommand _ _ = () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
prop_checkArithmeticOpCommand1 :: Bool
prop_checkArithmeticOpCommand1 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkArithmeticOpCommand "i=i + 1"
prop_checkArithmeticOpCommand2 :: Bool
prop_checkArithmeticOpCommand2 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkArithmeticOpCommand "foo=bar * 2"
prop_checkArithmeticOpCommand3 :: Bool
prop_checkArithmeticOpCommand3 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkArithmeticOpCommand "foo + opts"
checkArithmeticOpCommand :: p -> Token -> m ()
checkArithmeticOpCommand _ (T_SimpleCommand id :: Id
id [T_Assignment {}] (firstWord :: Token
firstWord:_)) =
(String -> m ()) -> Maybe String -> m ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ String -> m ()
forall (f :: * -> *).
MonadWriter [TokenComment] f =>
String -> f ()
check (Maybe String -> m ()) -> Maybe String -> m ()
forall a b. (a -> b) -> a -> b
$ Token -> Maybe String
getGlobOrLiteralString Token
firstWord
where
check :: String -> f ()
check op :: String
op =
Bool -> f () -> f ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (String
op String -> [String] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` ["+", "-", "*", "/"]) (f () -> f ()) -> f () -> f ()
forall a b. (a -> b) -> a -> b
$
Id -> Code -> String -> f ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
warn (Token -> Id
getId Token
firstWord) 2099 (String -> f ()) -> String -> f ()
forall a b. (a -> b) -> a -> b
$
"Use $((..)) for arithmetics, e.g. i=$((i " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
op String -> String -> String
forall a. [a] -> [a] -> [a]
++ " 2))"
checkArithmeticOpCommand _ _ = () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
prop_checkWrongArit :: Bool
prop_checkWrongArit = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkWrongArithmeticAssignment "i=i+1"
prop_checkWrongArit2 :: Bool
prop_checkWrongArit2 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkWrongArithmeticAssignment "n=2; i=n*2"
checkWrongArithmeticAssignment :: Parameters -> Token -> m ()
checkWrongArithmeticAssignment params :: Parameters
params (T_SimpleCommand id :: Id
id [T_Assignment _ _ _ _ val :: Token
val] []) =
Maybe (m ()) -> m ()
forall (t :: * -> *) (m :: * -> *) a.
(Foldable t, Monad m) =>
t (m a) -> m ()
sequence_ (Maybe (m ()) -> m ()) -> Maybe (m ()) -> m ()
forall a b. (a -> b) -> a -> b
$ do
String
str <- Token -> Maybe String
getNormalString Token
val
[String]
match <- Regex -> String -> Maybe [String]
matchRegex Regex
regex String
str
String
var <- [String]
match [String] -> Int -> Maybe String
forall a. [a] -> Int -> Maybe a
!!! 0
String
op <- [String]
match [String] -> Int -> Maybe String
forall a. [a] -> Int -> Maybe a
!!! 1
String -> Map String () -> Maybe ()
forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup String
var Map String ()
references
m () -> Maybe (m ())
forall (m :: * -> *) a. Monad m => a -> m a
return (m () -> Maybe (m ()))
-> (String -> m ()) -> String -> Maybe (m ())
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
warn (Token -> Id
getId Token
val) 2100 (String -> Maybe (m ())) -> String -> Maybe (m ())
forall a b. (a -> b) -> a -> b
$
"Use $((..)) for arithmetics, e.g. i=$((i " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
op String -> String -> String
forall a. [a] -> [a] -> [a]
++ " 2))"
where
regex :: Regex
regex = String -> Regex
mkRegex "^([_a-zA-Z][_a-zA-Z0-9]*)([+*-]).+$"
references :: Map String ()
references = (Map String ()
-> (Map String () -> Map String ()) -> Map String ())
-> Map String ()
-> [Map String () -> Map String ()]
-> Map String ()
forall (t :: * -> *) b a.
Foldable t =>
(b -> a -> b) -> b -> t a -> b
foldl (((Map String () -> Map String ())
-> Map String () -> Map String ())
-> Map String ()
-> (Map String () -> Map String ())
-> Map String ()
forall a b c. (a -> b -> c) -> b -> a -> c
flip (Map String () -> Map String ()) -> Map String () -> Map String ()
forall a b. (a -> b) -> a -> b
($)) Map String ()
forall k a. Map k a
Map.empty ((StackData -> Map String () -> Map String ())
-> [StackData] -> [Map String () -> Map String ()]
forall a b. (a -> b) -> [a] -> [b]
map StackData -> Map String () -> Map String ()
insertRef ([StackData] -> [Map String () -> Map String ()])
-> [StackData] -> [Map String () -> Map String ()]
forall a b. (a -> b) -> a -> b
$ Parameters -> [StackData]
variableFlow Parameters
params)
insertRef :: StackData -> Map String () -> Map String ()
insertRef (Assignment (_, _, name :: String
name, _)) =
String -> () -> Map String () -> Map String ()
forall k a. Ord k => k -> a -> Map k a -> Map k a
Map.insert String
name ()
insertRef _ = Map String () -> Map String ()
forall a. a -> a
Prelude.id
getNormalString :: Token -> Maybe String
getNormalString (T_NormalWord _ words :: [Token]
words) = do
[String]
parts <- (Token -> Maybe String) -> [Token] -> Maybe [String]
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
mapM Token -> Maybe String
getLiterals [Token]
words
String -> Maybe String
forall (m :: * -> *) a. Monad m => a -> m a
return (String -> Maybe String) -> String -> Maybe String
forall a b. (a -> b) -> a -> b
$ [String] -> String
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat [String]
parts
getNormalString _ = Maybe String
forall a. Maybe a
Nothing
getLiterals :: Token -> Maybe String
getLiterals (T_Literal _ s :: String
s) = String -> Maybe String
forall (m :: * -> *) a. Monad m => a -> m a
return String
s
getLiterals (T_Glob _ s :: String
s) = String -> Maybe String
forall (m :: * -> *) a. Monad m => a -> m a
return String
s
getLiterals _ = Maybe String
forall a. Maybe a
Nothing
checkWrongArithmeticAssignment _ _ = () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
prop_checkUuoc1 :: Bool
prop_checkUuoc1 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkUuoc "cat foo | grep bar"
prop_checkUuoc2 :: Bool
prop_checkUuoc2 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkUuoc "cat * | grep bar"
prop_checkUuoc3 :: Bool
prop_checkUuoc3 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkUuoc "cat $var | grep bar"
prop_checkUuoc4 :: Bool
prop_checkUuoc4 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkUuoc "cat $var"
prop_checkUuoc5 :: Bool
prop_checkUuoc5 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkUuoc "cat \"$@\""
prop_checkUuoc6 :: Bool
prop_checkUuoc6 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkUuoc "cat -n | grep bar"
checkUuoc :: p -> Token -> m ()
checkUuoc _ (T_Pipeline _ _ (T_Redirecting _ _ cmd :: Token
cmd:_:_)) =
String -> (Token -> [Token] -> m ()) -> Token -> m ()
forall (m :: * -> *).
Monad m =>
String -> (Token -> [Token] -> m ()) -> Token -> m ()
checkCommand "cat" (([Token] -> m ()) -> Token -> [Token] -> m ()
forall a b. a -> b -> a
const [Token] -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
[Token] -> m ()
f) Token
cmd
where
f :: [Token] -> m ()
f [word :: Token
word] | Bool -> Bool
not (Token -> Bool
mayBecomeMultipleArgs Token
word Bool -> Bool -> Bool
|| Token -> Bool
isOption Token
word) =
Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
style (Token -> Id
getId Token
word) 2002 "Useless cat. Consider 'cmd < file | ..' or 'cmd file | ..' instead."
f _ = () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
isOption :: Token -> Bool
isOption word :: Token
word = "-" String -> String -> Bool
forall a. Eq a => [a] -> [a] -> Bool
`isPrefixOf` Token -> String
onlyLiteralString Token
word
checkUuoc _ _ = () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
prop_checkPipePitfalls3 :: Bool
prop_checkPipePitfalls3 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkPipePitfalls "ls | grep -v mp3"
prop_checkPipePitfalls4 :: Bool
prop_checkPipePitfalls4 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkPipePitfalls "find . -print0 | xargs -0 foo"
prop_checkPipePitfalls5 :: Bool
prop_checkPipePitfalls5 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkPipePitfalls "ls -N | foo"
prop_checkPipePitfalls6 :: Bool
prop_checkPipePitfalls6 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkPipePitfalls "find . | xargs foo"
prop_checkPipePitfalls7 :: Bool
prop_checkPipePitfalls7 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkPipePitfalls "find . -printf '%s\\n' | xargs foo"
prop_checkPipePitfalls8 :: Bool
prop_checkPipePitfalls8 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkPipePitfalls "foo | grep bar | wc -l"
prop_checkPipePitfalls9 :: Bool
prop_checkPipePitfalls9 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkPipePitfalls "foo | grep -o bar | wc -l"
prop_checkPipePitfalls10 :: Bool
prop_checkPipePitfalls10 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkPipePitfalls "foo | grep -o bar | wc"
prop_checkPipePitfalls11 :: Bool
prop_checkPipePitfalls11 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkPipePitfalls "foo | grep bar | wc"
prop_checkPipePitfalls12 :: Bool
prop_checkPipePitfalls12 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkPipePitfalls "foo | grep -o bar | wc -c"
prop_checkPipePitfalls13 :: Bool
prop_checkPipePitfalls13 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkPipePitfalls "foo | grep bar | wc -c"
prop_checkPipePitfalls14 :: Bool
prop_checkPipePitfalls14 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkPipePitfalls "foo | grep -o bar | wc -cmwL"
prop_checkPipePitfalls15 :: Bool
prop_checkPipePitfalls15 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkPipePitfalls "foo | grep bar | wc -cmwL"
prop_checkPipePitfalls16 :: Bool
prop_checkPipePitfalls16 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkPipePitfalls "foo | grep -r bar | wc -l"
checkPipePitfalls :: p -> Token -> m ()
checkPipePitfalls _ (T_Pipeline id :: Id
id _ commands :: [Token]
commands) = do
[String] -> ([Token] -> m ()) -> m Bool
forall (m :: * -> *) b.
Monad m =>
[String] -> ([Token] -> m b) -> m Bool
for ["find", "xargs"] (([Token] -> m ()) -> m Bool) -> ([Token] -> m ()) -> m Bool
forall a b. (a -> b) -> a -> b
$
\(find :: Token
find:xargs :: Token
xargs:_) ->
let args :: [String]
args = Token -> [String]
oversimplify Token
xargs [String] -> [String] -> [String]
forall a. [a] -> [a] -> [a]
++ Token -> [String]
oversimplify Token
find
in
Bool -> m () -> m ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
unless ((([String] -> Bool) -> Bool) -> [[String] -> Bool] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any (([String] -> Bool) -> [String] -> Bool
forall a b. (a -> b) -> a -> b
$ [String]
args) [
Char -> [String] -> Bool
forall (t :: * -> *). Foldable t => Char -> t String -> Bool
hasShortParameter '0',
String -> [String] -> Bool
forall (t :: * -> *). Foldable t => String -> t String -> Bool
hasParameter "null",
String -> [String] -> Bool
forall (t :: * -> *). Foldable t => String -> t String -> Bool
hasParameter "print0",
String -> [String] -> Bool
forall (t :: * -> *). Foldable t => String -> t String -> Bool
hasParameter "printf"
]) (m () -> m ()) -> m () -> m ()
forall a b. (a -> b) -> a -> b
$ Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
warn (Token -> Id
getId Token
find) 2038
"Use -print0/-0 or -exec + to allow for non-alphanumeric filenames."
[String] -> (Id -> m ()) -> m Bool
forall (m :: * -> *). Monad m => [String] -> (Id -> m ()) -> m Bool
for' ["ps", "grep"] ((Id -> m ()) -> m Bool) -> (Id -> m ()) -> m Bool
forall a b. (a -> b) -> a -> b
$
\x :: Id
x -> Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
info Id
x 2009 "Consider using pgrep instead of grepping ps output."
[String] -> ([Token] -> m ()) -> m Bool
forall (m :: * -> *) b.
Monad m =>
[String] -> ([Token] -> m b) -> m Bool
for ["grep", "wc"] (([Token] -> m ()) -> m Bool) -> ([Token] -> m ()) -> m Bool
forall a b. (a -> b) -> a -> b
$
\(grep :: Token
grep:wc :: Token
wc:_) ->
let flagsGrep :: [String]
flagsGrep = [String] -> (Token -> [String]) -> Maybe Token -> [String]
forall b a. b -> (a -> b) -> Maybe a -> b
maybe [] (((Token, String) -> String) -> [(Token, String)] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map (Token, String) -> String
forall a b. (a, b) -> b
snd ([(Token, String)] -> [String])
-> (Token -> [(Token, String)]) -> Token -> [String]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Token -> [(Token, String)]
getAllFlags) (Maybe Token -> [String]) -> Maybe Token -> [String]
forall a b. (a -> b) -> a -> b
$ Token -> Maybe Token
getCommand Token
grep
flagsWc :: [String]
flagsWc = [String] -> (Token -> [String]) -> Maybe Token -> [String]
forall b a. b -> (a -> b) -> Maybe a -> b
maybe [] (((Token, String) -> String) -> [(Token, String)] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map (Token, String) -> String
forall a b. (a, b) -> b
snd ([(Token, String)] -> [String])
-> (Token -> [(Token, String)]) -> Token -> [String]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Token -> [(Token, String)]
getAllFlags) (Maybe Token -> [String]) -> Maybe Token -> [String]
forall a b. (a -> b) -> a -> b
$ Token -> Maybe Token
getCommand Token
wc
in
Bool -> m () -> m ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
unless ((String -> Bool) -> [String] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any (String -> [String] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` ["o", "only-matching", "r", "R", "recursive"]) [String]
flagsGrep Bool -> Bool -> Bool
|| (String -> Bool) -> [String] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any (String -> [String] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` ["m", "chars", "w", "words", "c", "bytes", "L", "max-line-length"]) [String]
flagsWc Bool -> Bool -> Bool
|| [String] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null [String]
flagsWc) (m () -> m ()) -> m () -> m ()
forall a b. (a -> b) -> a -> b
$
Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
style (Token -> Id
getId Token
grep) 2126 "Consider using grep -c instead of grep|wc -l."
Bool
didLs <- ([Bool] -> Bool) -> m [Bool] -> m Bool
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap [Bool] -> Bool
forall (t :: * -> *). Foldable t => t Bool -> Bool
or (m [Bool] -> m Bool)
-> ([m Bool] -> m [Bool]) -> [m Bool] -> m Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [m Bool] -> m [Bool]
forall (t :: * -> *) (m :: * -> *) a.
(Traversable t, Monad m) =>
t (m a) -> m (t a)
sequence ([m Bool] -> m Bool) -> [m Bool] -> m Bool
forall a b. (a -> b) -> a -> b
$ [
[String] -> (Id -> m ()) -> m Bool
forall (m :: * -> *). Monad m => [String] -> (Id -> m ()) -> m Bool
for' ["ls", "grep"] ((Id -> m ()) -> m Bool) -> (Id -> m ()) -> m Bool
forall a b. (a -> b) -> a -> b
$
\x :: Id
x -> Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
warn Id
x 2010 "Don't use ls | grep. Use a glob or a for loop with a condition to allow non-alphanumeric filenames.",
[String] -> (Id -> m ()) -> m Bool
forall (m :: * -> *). Monad m => [String] -> (Id -> m ()) -> m Bool
for' ["ls", "xargs"] ((Id -> m ()) -> m Bool) -> (Id -> m ()) -> m Bool
forall a b. (a -> b) -> a -> b
$
\x :: Id
x -> Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
warn Id
x 2011 "Use 'find .. -print0 | xargs -0 ..' or 'find .. -exec .. +' to allow non-alphanumeric filenames."
]
Bool -> m () -> m ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
unless Bool
didLs (m () -> m ()) -> m () -> m ()
forall a b. (a -> b) -> a -> b
$ m Bool -> m ()
forall (f :: * -> *) a. Functor f => f a -> f ()
void (m Bool -> m ()) -> m Bool -> m ()
forall a b. (a -> b) -> a -> b
$
[String] -> ([Token] -> m ()) -> m Bool
forall (m :: * -> *) b.
Monad m =>
[String] -> ([Token] -> m b) -> m Bool
for ["ls", "?"] (([Token] -> m ()) -> m Bool) -> ([Token] -> m ()) -> m Bool
forall a b. (a -> b) -> a -> b
$
\(ls :: Token
ls:_) -> Bool -> m () -> m ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
unless (Char -> [String] -> Bool
forall (t :: * -> *). Foldable t => Char -> t String -> Bool
hasShortParameter 'N' (Token -> [String]
oversimplify Token
ls)) (m () -> m ()) -> m () -> m ()
forall a b. (a -> b) -> a -> b
$
Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
info (Token -> Id
getId Token
ls) 2012 "Use find instead of ls to better handle non-alphanumeric filenames."
where
for :: [String] -> ([Token] -> m b) -> m Bool
for l :: [String]
l f :: [Token] -> m b
f =
let indices :: [Int]
indices = [String] -> [String] -> [Int]
forall t. Num t => [String] -> [String] -> [t]
indexOfSublists [String]
l ((Token -> String) -> [Token] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map (String -> [String] -> String
forall p. p -> [p] -> p
headOrDefault "" ([String] -> String) -> (Token -> [String]) -> Token -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Token -> [String]
oversimplify) [Token]
commands)
in do
(Int -> m b) -> [Int] -> m ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ ([Token] -> m b
f ([Token] -> m b) -> (Int -> [Token]) -> Int -> m b
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (\ n :: Int
n -> Int -> [Token] -> [Token]
forall a. Int -> [a] -> [a]
take ([String] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [String]
l) ([Token] -> [Token]) -> [Token] -> [Token]
forall a b. (a -> b) -> a -> b
$ Int -> [Token] -> [Token]
forall a. Int -> [a] -> [a]
drop Int
n [Token]
commands)) [Int]
indices
Bool -> m Bool
forall (m :: * -> *) a. Monad m => a -> m a
return (Bool -> m Bool) -> ([Int] -> Bool) -> [Int] -> m Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Bool -> Bool
not (Bool -> Bool) -> ([Int] -> Bool) -> [Int] -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Int] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null ([Int] -> m Bool) -> [Int] -> m Bool
forall a b. (a -> b) -> a -> b
$ [Int]
indices
for' :: [String] -> (Id -> m ()) -> m Bool
for' l :: [String]
l f :: Id -> m ()
f = [String] -> ([Token] -> m ()) -> m Bool
forall (m :: * -> *) b.
Monad m =>
[String] -> ([Token] -> m b) -> m Bool
for [String]
l ((Id -> m ()) -> [Token] -> m ()
forall (m :: * -> *). Monad m => (Id -> m ()) -> [Token] -> m ()
first Id -> m ()
f)
first :: (Id -> m ()) -> [Token] -> m ()
first func :: Id -> m ()
func (x :: Token
x:_) = Id -> m ()
func (Token -> Id
getId (Token -> Id) -> Token -> Id
forall a b. (a -> b) -> a -> b
$ Token -> Token
getCommandTokenOrThis Token
x)
first _ _ = () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
hasShortParameter :: Char -> t String -> Bool
hasShortParameter char :: Char
char = (String -> Bool) -> t String -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any (\x :: String
x -> "-" String -> String -> Bool
forall a. Eq a => [a] -> [a] -> Bool
`isPrefixOf` String
x Bool -> Bool -> Bool
&& Char
char Char -> String -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` String
x)
hasParameter :: String -> t String -> Bool
hasParameter string :: String
string =
(String -> Bool) -> t String -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any (String -> String -> Bool
forall a. Eq a => [a] -> [a] -> Bool
isPrefixOf String
string (String -> Bool) -> (String -> String) -> String -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Char -> Bool) -> String -> String
forall a. (a -> Bool) -> [a] -> [a]
dropWhile (Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
== '-'))
checkPipePitfalls _ _ = () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
indexOfSublists :: [String] -> [String] -> [t]
indexOfSublists sub :: [String]
sub = t -> [String] -> [t]
forall t. Num t => t -> [String] -> [t]
f 0
where
f :: t -> [String] -> [t]
f _ [] = []
f n :: t
n a :: [String]
a@(r :: String
r:rest :: [String]
rest) =
let others :: [t]
others = t -> [String] -> [t]
f (t
nt -> t -> t
forall a. Num a => a -> a -> a
+1) [String]
rest in
if [String] -> [String] -> Bool
match [String]
sub [String]
a
then t
nt -> [t] -> [t]
forall a. a -> [a] -> [a]
:[t]
others
else [t]
others
match :: [String] -> [String] -> Bool
match ("?":r1 :: [String]
r1) (_:r2 :: [String]
r2) = [String] -> [String] -> Bool
match [String]
r1 [String]
r2
match (x1 :: String
x1:r1 :: [String]
r1) (x2 :: String
x2:r2 :: [String]
r2) | String
x1 String -> String -> Bool
forall a. Eq a => a -> a -> Bool
== String
x2 = [String] -> [String] -> Bool
match [String]
r1 [String]
r2
match [] _ = Bool
True
match _ _ = Bool
False
prop_checkShebangParameters1 :: Bool
prop_checkShebangParameters1 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyTree Parameters -> Token -> [TokenComment]
forall t. t -> Token -> [TokenComment]
checkShebangParameters "#!/usr/bin/env bash -x\necho cow"
prop_checkShebangParameters2 :: Bool
prop_checkShebangParameters2 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
forall t. t -> Token -> [TokenComment]
checkShebangParameters "#! /bin/sh -l "
prop_checkShebangParameters3 :: Bool
prop_checkShebangParameters3 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
forall t. t -> Token -> [TokenComment]
checkShebangParameters "#!/usr/bin/env -S bash -x\necho cow"
prop_checkShebangParameters4 :: Bool
prop_checkShebangParameters4 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
forall t. t -> Token -> [TokenComment]
checkShebangParameters "#!/usr/bin/env --split-string bash -x\necho cow"
checkShebangParameters :: t -> Token -> [TokenComment]
checkShebangParameters p :: t
p (T_Annotation _ _ t :: Token
t) = t -> Token -> [TokenComment]
checkShebangParameters t
p Token
t
checkShebangParameters _ (T_Script _ (T_Literal id :: Id
id sb :: String
sb) _) =
[Severity -> Id -> Code -> String -> TokenComment
makeComment Severity
ErrorC Id
id 2096 "On most OS, shebangs can only specify a single parameter." | Bool
isMultiWord]
where
isMultiWord :: Bool
isMultiWord = [String] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length (String -> [String]
words String
sb) Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> 2 Bool -> Bool -> Bool
&& Bool -> Bool
not (String
sb String -> Regex -> Bool
`matches` Regex
re)
re :: Regex
re = String -> Regex
mkRegex "env +(-S|--split-string)"
prop_checkShebang1 :: Bool
prop_checkShebang1 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
checkShebang "#!/usr/bin/env bash -x\necho cow"
prop_checkShebang2 :: Bool
prop_checkShebang2 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
checkShebang "#! /bin/sh -l "
prop_checkShebang3 :: Bool
prop_checkShebang3 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyTree Parameters -> Token -> [TokenComment]
checkShebang "ls -l"
prop_checkShebang4 :: Bool
prop_checkShebang4 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
checkShebang "#shellcheck shell=sh\nfoo"
prop_checkShebang5 :: Bool
prop_checkShebang5 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyTree Parameters -> Token -> [TokenComment]
checkShebang "#!/usr/bin/env ash"
prop_checkShebang6 :: Bool
prop_checkShebang6 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
checkShebang "#!/usr/bin/env ash\n# shellcheck shell=dash\n"
prop_checkShebang7 :: Bool
prop_checkShebang7 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
checkShebang "#!/usr/bin/env ash\n# shellcheck shell=sh\n"
prop_checkShebang8 :: Bool
prop_checkShebang8 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyTree Parameters -> Token -> [TokenComment]
checkShebang "#!bin/sh\ntrue"
prop_checkShebang9 :: Bool
prop_checkShebang9 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
checkShebang "# shellcheck shell=sh\ntrue"
prop_checkShebang10 :: Bool
prop_checkShebang10= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
checkShebang "#!foo\n# shellcheck shell=sh ignore=SC2239\ntrue"
prop_checkShebang11 :: Bool
prop_checkShebang11= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyTree Parameters -> Token -> [TokenComment]
checkShebang "#!/bin/sh/\ntrue"
prop_checkShebang12 :: Bool
prop_checkShebang12= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyTree Parameters -> Token -> [TokenComment]
checkShebang "#!/bin/sh/ -xe\ntrue"
prop_checkShebang13 :: Bool
prop_checkShebang13= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyTree Parameters -> Token -> [TokenComment]
checkShebang "#!/bin/busybox sh"
prop_checkShebang14 :: Bool
prop_checkShebang14= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
checkShebang "#!/bin/busybox sh\n# shellcheck shell=sh\n"
prop_checkShebang15 :: Bool
prop_checkShebang15= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
checkShebang "#!/bin/busybox sh\n# shellcheck shell=dash\n"
prop_checkShebang16 :: Bool
prop_checkShebang16= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyTree Parameters -> Token -> [TokenComment]
checkShebang "#!/bin/busybox ash"
prop_checkShebang17 :: Bool
prop_checkShebang17= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
checkShebang "#!/bin/busybox ash\n# shellcheck shell=dash\n"
prop_checkShebang18 :: Bool
prop_checkShebang18= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
checkShebang "#!/bin/busybox ash\n# shellcheck shell=sh\n"
checkShebang :: Parameters -> Token -> [TokenComment]
checkShebang params :: Parameters
params (T_Annotation _ list :: [Annotation]
list t :: Token
t) =
if (Annotation -> Bool) -> [Annotation] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any Annotation -> Bool
isOverride [Annotation]
list then [] else Parameters -> Token -> [TokenComment]
checkShebang Parameters
params Token
t
where
isOverride :: Annotation -> Bool
isOverride (ShellOverride _) = Bool
True
isOverride _ = Bool
False
checkShebang params :: Parameters
params (T_Script _ (T_Literal id :: Id
id sb :: String
sb) _) = WriterT [TokenComment] Identity () -> [TokenComment]
forall w a. Writer w a -> w
execWriter (WriterT [TokenComment] Identity () -> [TokenComment])
-> WriterT [TokenComment] Identity () -> [TokenComment]
forall a b. (a -> b) -> a -> b
$ do
Bool
-> WriterT [TokenComment] Identity ()
-> WriterT [TokenComment] Identity ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
unless (Parameters -> Bool
shellTypeSpecified Parameters
params) (WriterT [TokenComment] Identity ()
-> WriterT [TokenComment] Identity ())
-> WriterT [TokenComment] Identity ()
-> WriterT [TokenComment] Identity ()
forall a b. (a -> b) -> a -> b
$ do
Bool
-> WriterT [TokenComment] Identity ()
-> WriterT [TokenComment] Identity ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (String -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null String
sb) (WriterT [TokenComment] Identity ()
-> WriterT [TokenComment] Identity ())
-> WriterT [TokenComment] Identity ()
-> WriterT [TokenComment] Identity ()
forall a b. (a -> b) -> a -> b
$
Id -> Code -> String -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
err Id
id 2148 "Tips depend on target shell and yours is unknown. Add a shebang or a 'shell' directive."
Bool
-> WriterT [TokenComment] Identity ()
-> WriterT [TokenComment] Identity ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (String -> String
executableFromShebang String
sb String -> String -> Bool
forall a. Eq a => a -> a -> Bool
== "ash") (WriterT [TokenComment] Identity ()
-> WriterT [TokenComment] Identity ())
-> WriterT [TokenComment] Identity ()
-> WriterT [TokenComment] Identity ()
forall a b. (a -> b) -> a -> b
$
Id -> Code -> String -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
warn Id
id 2187 "Ash scripts will be checked as Dash. Add '# shellcheck shell=dash' to silence."
Bool
-> WriterT [TokenComment] Identity ()
-> WriterT [TokenComment] Identity ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
unless (String -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null String
sb) (WriterT [TokenComment] Identity ()
-> WriterT [TokenComment] Identity ())
-> WriterT [TokenComment] Identity ()
-> WriterT [TokenComment] Identity ()
forall a b. (a -> b) -> a -> b
$ do
Bool
-> WriterT [TokenComment] Identity ()
-> WriterT [TokenComment] Identity ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
unless ("/" String -> String -> Bool
forall a. Eq a => [a] -> [a] -> Bool
`isPrefixOf` String
sb) (WriterT [TokenComment] Identity ()
-> WriterT [TokenComment] Identity ())
-> WriterT [TokenComment] Identity ()
-> WriterT [TokenComment] Identity ()
forall a b. (a -> b) -> a -> b
$
Id -> Code -> String -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
err Id
id 2239 "Ensure the shebang uses an absolute path to the interpreter."
Bool
-> WriterT [TokenComment] Identity ()
-> WriterT [TokenComment] Identity ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when ("/" String -> String -> Bool
forall a. Eq a => [a] -> [a] -> Bool
`isSuffixOf` [String] -> String
forall a. [a] -> a
head (String -> [String]
words String
sb)) (WriterT [TokenComment] Identity ()
-> WriterT [TokenComment] Identity ())
-> WriterT [TokenComment] Identity ()
-> WriterT [TokenComment] Identity ()
forall a b. (a -> b) -> a -> b
$
Id -> Code -> String -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
err Id
id 2246 "This shebang specifies a directory. Ensure the interpreter is a file."
prop_checkForInQuoted :: Bool
prop_checkForInQuoted = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkForInQuoted "for f in \"$(ls)\"; do echo foo; done"
prop_checkForInQuoted2 :: Bool
prop_checkForInQuoted2 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkForInQuoted "for f in \"$@\"; do echo foo; done"
prop_checkForInQuoted2a :: Bool
prop_checkForInQuoted2a = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkForInQuoted "for f in *.mp3; do echo foo; done"
prop_checkForInQuoted2b :: Bool
prop_checkForInQuoted2b = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkForInQuoted "for f in \"*.mp3\"; do echo foo; done"
prop_checkForInQuoted3 :: Bool
prop_checkForInQuoted3 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkForInQuoted "for f in 'find /'; do true; done"
prop_checkForInQuoted4 :: Bool
prop_checkForInQuoted4 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkForInQuoted "for f in 1,2,3; do true; done"
prop_checkForInQuoted4a :: Bool
prop_checkForInQuoted4a = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkForInQuoted "for f in foo{1,2,3}; do true; done"
prop_checkForInQuoted5 :: Bool
prop_checkForInQuoted5 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkForInQuoted "for f in ls; do true; done"
prop_checkForInQuoted6 :: Bool
prop_checkForInQuoted6 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkForInQuoted "for f in \"${!arr}\"; do true; done"
prop_checkForInQuoted7 :: Bool
prop_checkForInQuoted7 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkForInQuoted "for f in ls, grep, mv; do true; done"
prop_checkForInQuoted8 :: Bool
prop_checkForInQuoted8 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkForInQuoted "for f in 'ls', 'grep', 'mv'; do true; done"
prop_checkForInQuoted9 :: Bool
prop_checkForInQuoted9 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkForInQuoted "for f in 'ls,' 'grep,' 'mv'; do true; done"
checkForInQuoted :: Parameters -> Token -> m ()
checkForInQuoted _ (T_ForIn _ f :: String
f [T_NormalWord _ [word :: Token
word@(T_DoubleQuoted id :: Id
id list :: [Token]
list)]] _)
| (Token -> Bool) -> [Token] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any (\x :: Token
x -> Token -> Bool
willSplit Token
x Bool -> Bool -> Bool
&& Bool -> Bool
not (Token -> Bool
mayBecomeMultipleArgs Token
x)) [Token]
list
Bool -> Bool -> Bool
|| Bool -> (String -> Bool) -> Maybe String -> Bool
forall b a. b -> (a -> b) -> Maybe a -> b
maybe Bool
False String -> Bool
forall (t :: * -> *). Foldable t => t Char -> Bool
wouldHaveBeenGlob (Token -> Maybe String
getLiteralString Token
word) =
Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
err Id
id 2066 "Since you double quoted this, it will not word split, and the loop will only run once."
checkForInQuoted _ (T_ForIn _ f :: String
f [T_NormalWord _ [T_SingleQuoted id :: Id
id _]] _) =
Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
warn Id
id 2041 "This is a literal string. To run as a command, use $(..) instead of '..' . "
checkForInQuoted _ (T_ForIn _ _ [single :: Token
single] _)
| Bool -> (String -> Bool) -> Maybe String -> Bool
forall b a. b -> (a -> b) -> Maybe a -> b
maybe Bool
False (',' Char -> String -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem`) (Maybe String -> Bool) -> Maybe String -> Bool
forall a b. (a -> b) -> a -> b
$ Token -> Maybe String
getUnquotedLiteral Token
single =
Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
warn (Token -> Id
getId Token
single) 2042 "Use spaces, not commas, to separate loop elements."
| Bool -> Bool
not (Token -> Bool
willSplit Token
single Bool -> Bool -> Bool
|| Token -> Bool
mayBecomeMultipleArgs Token
single) =
Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
warn (Token -> Id
getId Token
single) 2043 "This loop will only ever run once. Bad quoting or missing glob/expansion?"
checkForInQuoted params :: Parameters
params (T_ForIn _ _ multiple :: [Token]
multiple _) =
[Token] -> (Token -> m ()) -> m ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
t a -> (a -> m b) -> m ()
forM_ [Token]
multiple ((Token -> m ()) -> m ()) -> (Token -> m ()) -> m ()
forall a b. (a -> b) -> a -> b
$ \arg :: Token
arg -> Maybe (m ()) -> m ()
forall (t :: * -> *) (m :: * -> *) a.
(Foldable t, Monad m) =>
t (m a) -> m ()
sequence_ (Maybe (m ()) -> m ()) -> Maybe (m ()) -> m ()
forall a b. (a -> b) -> a -> b
$ do
Token
suffix <- Token -> Maybe Token
getTrailingUnquotedLiteral Token
arg
String
string <- Token -> Maybe String
getLiteralString Token
suffix
Bool -> Maybe ()
forall (f :: * -> *). Alternative f => Bool -> f ()
guard (Bool -> Maybe ()) -> Bool -> Maybe ()
forall a b. (a -> b) -> a -> b
$ "," String -> String -> Bool
forall a. Eq a => [a] -> [a] -> Bool
`isSuffixOf` String
string
m () -> Maybe (m ())
forall (m :: * -> *) a. Monad m => a -> m a
return (m () -> Maybe (m ())) -> m () -> Maybe (m ())
forall a b. (a -> b) -> a -> b
$
Id -> Code -> String -> Fix -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> Fix -> m ()
warnWithFix (Token -> Id
getId Token
arg) 2258
"The trailing comma is part of the value, not a separator. Delete or quote it."
([Replacement] -> Fix
fixWith [Id -> Parameters -> Code -> String -> Replacement
replaceEnd (Token -> Id
getId Token
suffix) Parameters
params 1 ""])
checkForInQuoted _ _ = () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
prop_checkForInCat1 :: Bool
prop_checkForInCat1 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkForInCat "for f in $(cat foo); do stuff; done"
prop_checkForInCat1a :: Bool
prop_checkForInCat1a= (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkForInCat "for f in `cat foo`; do stuff; done"
prop_checkForInCat2 :: Bool
prop_checkForInCat2 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkForInCat "for f in $(cat foo | grep lol); do stuff; done"
prop_checkForInCat2a :: Bool
prop_checkForInCat2a= (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkForInCat "for f in `cat foo | grep lol`; do stuff; done"
prop_checkForInCat3 :: Bool
prop_checkForInCat3 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkForInCat "for f in $(cat foo | grep bar | wc -l); do stuff; done"
checkForInCat :: p -> Token -> m ()
checkForInCat _ (T_ForIn _ f :: String
f [T_NormalWord _ w :: [Token]
w] _) = (Token -> m ()) -> [Token] -> m ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ Token -> m ()
forall (m :: * -> *). MonadWriter [TokenComment] m => Token -> m ()
checkF [Token]
w
where
checkF :: Token -> m ()
checkF (T_DollarExpansion id :: Id
id [T_Pipeline _ _ r :: [Token]
r])
| (Token -> Bool) -> [Token] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all Token -> Bool
isLineBased [Token]
r =
Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
info Id
id 2013 "To read lines rather than words, pipe/redirect to a 'while read' loop."
checkF (T_Backticked id :: Id
id cmds :: [Token]
cmds) = Token -> m ()
checkF (Id -> [Token] -> Token
T_DollarExpansion Id
id [Token]
cmds)
checkF _ = () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
isLineBased :: Token -> Bool
isLineBased cmd :: Token
cmd = (String -> Bool) -> [String] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any (Token
cmd Token -> String -> Bool
`isCommand`)
["grep", "fgrep", "egrep", "sed", "cat", "awk", "cut", "sort"]
checkForInCat _ _ = () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
prop_checkForInLs :: Bool
prop_checkForInLs = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkForInLs "for f in $(ls *.mp3); do mplayer \"$f\"; done"
prop_checkForInLs2 :: Bool
prop_checkForInLs2 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkForInLs "for f in `ls *.mp3`; do mplayer \"$f\"; done"
prop_checkForInLs3 :: Bool
prop_checkForInLs3 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkForInLs "for f in `find / -name '*.mp3'`; do mplayer \"$f\"; done"
checkForInLs :: p -> Token -> m ()
checkForInLs _ = Token -> m ()
forall (m :: * -> *). MonadWriter [TokenComment] m => Token -> m ()
try
where
try :: Token -> m ()
try (T_ForIn _ f :: String
f [T_NormalWord _ [T_DollarExpansion id :: Id
id [x :: Token
x]]] _) =
Id -> String -> Token -> m ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
Id -> p -> Token -> m ()
check Id
id String
f Token
x
try (T_ForIn _ f :: String
f [T_NormalWord _ [T_Backticked id :: Id
id [x :: Token
x]]] _) =
Id -> String -> Token -> m ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
Id -> p -> Token -> m ()
check Id
id String
f Token
x
try _ = () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
check :: Id -> p -> Token -> m ()
check id :: Id
id f :: p
f x :: Token
x =
case Token -> [String]
oversimplify Token
x of
("ls":n :: [String]
n) ->
let warntype :: Id -> Code -> String -> m ()
warntype = if (String -> Bool) -> [String] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any ("-" String -> String -> Bool
forall a. Eq a => [a] -> [a] -> Bool
`isPrefixOf`) [String]
n then Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
warn else Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
err in
Id -> Code -> String -> m ()
warntype Id
id 2045 "Iterating over ls output is fragile. Use globs."
("find":_) -> Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
warn Id
id 2044 "For loops over find output are fragile. Use find -exec or a while read loop."
_ -> () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
prop_checkFindExec1 :: Bool
prop_checkFindExec1 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkFindExec "find / -name '*.php' -exec rm {};"
prop_checkFindExec2 :: Bool
prop_checkFindExec2 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkFindExec "find / -exec touch {} && ls {} \\;"
prop_checkFindExec3 :: Bool
prop_checkFindExec3 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkFindExec "find / -execdir cat {} | grep lol +"
prop_checkFindExec4 :: Bool
prop_checkFindExec4 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkFindExec "find / -name '*.php' -exec foo {} +"
prop_checkFindExec5 :: Bool
prop_checkFindExec5 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkFindExec "find / -execdir bash -c 'a && b' \\;"
prop_checkFindExec6 :: Bool
prop_checkFindExec6 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkFindExec "find / -type d -execdir rm *.jpg \\;"
checkFindExec :: p -> Token -> m ()
checkFindExec _ cmd :: Token
cmd@(T_SimpleCommand _ _ t :: [Token]
t@(h :: Token
h:r :: [Token]
r)) | Token
cmd Token -> String -> Bool
`isCommand` "find" = do
Bool
c <- [Token] -> Bool -> m Bool
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
[Token] -> Bool -> m Bool
broken [Token]
r Bool
False
Bool -> m () -> m ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when Bool
c (m () -> m ()) -> m () -> m ()
forall a b. (a -> b) -> a -> b
$
let wordId :: Id
wordId = Token -> Id
getId (Token -> Id) -> Token -> Id
forall a b. (a -> b) -> a -> b
$ [Token] -> Token
forall a. [a] -> a
last [Token]
t in
Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
err Id
wordId 2067 "Missing ';' or + terminating -exec. You can't use |/||/&&, and ';' has to be a separate, quoted argument."
where
broken :: [Token] -> Bool -> m Bool
broken [] v :: Bool
v = Bool -> m Bool
forall (m :: * -> *) a. Monad m => a -> m a
return Bool
v
broken (w :: Token
w:r :: [Token]
r) v :: Bool
v = do
Bool -> m () -> m ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when Bool
v ((Token -> m ()) -> [Token] -> m ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ Token -> m ()
forall (m :: * -> *). MonadWriter [TokenComment] m => Token -> m ()
warnFor ([Token] -> m ()) -> [Token] -> m ()
forall a b. (a -> b) -> a -> b
$ Token -> [Token]
fromWord Token
w)
case Token -> Maybe String
getLiteralString Token
w of
Just "-exec" -> [Token] -> Bool -> m Bool
broken [Token]
r Bool
True
Just "-execdir" -> [Token] -> Bool -> m Bool
broken [Token]
r Bool
True
Just "+" -> [Token] -> Bool -> m Bool
broken [Token]
r Bool
False
Just ";" -> [Token] -> Bool -> m Bool
broken [Token]
r Bool
False
_ -> [Token] -> Bool -> m Bool
broken [Token]
r Bool
v
shouldWarn :: Token -> Bool
shouldWarn x :: Token
x =
case Token
x of
T_DollarExpansion _ _ -> Bool
True
T_Backticked _ _ -> Bool
True
T_Glob _ _ -> Bool
True
T_Extglob {} -> Bool
True
_ -> Bool
False
warnFor :: Token -> f ()
warnFor x :: Token
x =
Bool -> f () -> f ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when(Token -> Bool
shouldWarn Token
x) (f () -> f ()) -> f () -> f ()
forall a b. (a -> b) -> a -> b
$
Id -> Code -> String -> f ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
info (Token -> Id
getId Token
x) 2014 "This will expand once before find runs, not per file found."
fromWord :: Token -> [Token]
fromWord (T_NormalWord _ l :: [Token]
l) = [Token]
l
fromWord _ = []
checkFindExec _ _ = () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
prop_checkUnquotedExpansions1 :: Bool
prop_checkUnquotedExpansions1 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkUnquotedExpansions "rm $(ls)"
prop_checkUnquotedExpansions1a :: Bool
prop_checkUnquotedExpansions1a= (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkUnquotedExpansions "rm `ls`"
prop_checkUnquotedExpansions2 :: Bool
prop_checkUnquotedExpansions2 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkUnquotedExpansions "rm foo$(date)"
prop_checkUnquotedExpansions3 :: Bool
prop_checkUnquotedExpansions3 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkUnquotedExpansions "[ $(foo) == cow ]"
prop_checkUnquotedExpansions3a :: Bool
prop_checkUnquotedExpansions3a= (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkUnquotedExpansions "[ ! $(foo) ]"
prop_checkUnquotedExpansions4 :: Bool
prop_checkUnquotedExpansions4 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkUnquotedExpansions "[[ $(foo) == cow ]]"
prop_checkUnquotedExpansions5 :: Bool
prop_checkUnquotedExpansions5 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkUnquotedExpansions "for f in $(cmd); do echo $f; done"
prop_checkUnquotedExpansions6 :: Bool
prop_checkUnquotedExpansions6 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkUnquotedExpansions "$(cmd)"
prop_checkUnquotedExpansions7 :: Bool
prop_checkUnquotedExpansions7 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkUnquotedExpansions "cat << foo\n$(ls)\nfoo"
prop_checkUnquotedExpansions8 :: Bool
prop_checkUnquotedExpansions8 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkUnquotedExpansions "set -- $(seq 1 4)"
prop_checkUnquotedExpansions9 :: Bool
prop_checkUnquotedExpansions9 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkUnquotedExpansions "echo foo `# inline comment`"
checkUnquotedExpansions :: Parameters -> Token -> f ()
checkUnquotedExpansions params :: Parameters
params =
Token -> f ()
forall (m :: * -> *). MonadWriter [TokenComment] m => Token -> m ()
check
where
check :: Token -> f ()
check t :: Token
t@(T_DollarExpansion _ c :: [Token]
c) = Token -> [Token] -> f ()
forall (f :: * -> *) (t :: * -> *) a.
(Foldable t, MonadWriter [TokenComment] f) =>
Token -> t a -> f ()
examine Token
t [Token]
c
check t :: Token
t@(T_Backticked _ c :: [Token]
c) = Token -> [Token] -> f ()
forall (f :: * -> *) (t :: * -> *) a.
(Foldable t, MonadWriter [TokenComment] f) =>
Token -> t a -> f ()
examine Token
t [Token]
c
check t :: Token
t@(T_DollarBraceCommandExpansion _ c :: [Token]
c) = Token -> [Token] -> f ()
forall (f :: * -> *) (t :: * -> *) a.
(Foldable t, MonadWriter [TokenComment] f) =>
Token -> t a -> f ()
examine Token
t [Token]
c
check _ = () -> f ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
tree :: Map Id Token
tree = Parameters -> Map Id Token
parentMap Parameters
params
examine :: Token -> t a -> f ()
examine t :: Token
t contents :: t a
contents =
Bool -> f () -> f ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
unless (t a -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null t a
contents Bool -> Bool -> Bool
|| Token -> Bool
shouldBeSplit Token
t Bool -> Bool -> Bool
|| Map Id Token -> Token -> Bool
isQuoteFree Map Id Token
tree Token
t Bool -> Bool -> Bool
|| Map Id Token -> Token -> Bool
usedAsCommandName Map Id Token
tree Token
t) (f () -> f ()) -> f () -> f ()
forall a b. (a -> b) -> a -> b
$
Id -> Code -> String -> f ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
warn (Token -> Id
getId Token
t) 2046 "Quote this to prevent word splitting."
shouldBeSplit :: Token -> Bool
shouldBeSplit t :: Token
t =
Token -> Maybe String
getCommandNameFromExpansion Token
t Maybe String -> Maybe String -> Bool
forall a. Eq a => a -> a -> Bool
== String -> Maybe String
forall a. a -> Maybe a
Just "seq"
prop_checkRedirectToSame :: Bool
prop_checkRedirectToSame = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkRedirectToSame "cat foo > foo"
prop_checkRedirectToSame2 :: Bool
prop_checkRedirectToSame2 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkRedirectToSame "cat lol | sed -e 's/a/b/g' > lol"
prop_checkRedirectToSame3 :: Bool
prop_checkRedirectToSame3 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkRedirectToSame "cat lol | sed -e 's/a/b/g' > foo.bar && mv foo.bar lol"
prop_checkRedirectToSame4 :: Bool
prop_checkRedirectToSame4 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkRedirectToSame "foo /dev/null > /dev/null"
prop_checkRedirectToSame5 :: Bool
prop_checkRedirectToSame5 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkRedirectToSame "foo > bar 2> bar"
prop_checkRedirectToSame6 :: Bool
prop_checkRedirectToSame6 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkRedirectToSame "echo foo > foo"
prop_checkRedirectToSame7 :: Bool
prop_checkRedirectToSame7 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkRedirectToSame "sed 's/foo/bar/g' file | sponge file"
prop_checkRedirectToSame8 :: Bool
prop_checkRedirectToSame8 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkRedirectToSame "while read -r line; do _=\"$fname\"; done <\"$fname\""
checkRedirectToSame :: Parameters -> Token -> m ()
checkRedirectToSame params :: Parameters
params s :: Token
s@(T_Pipeline _ _ list :: [Token]
list) =
(Token -> m ()) -> [Token] -> m ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ (\l :: Token
l -> ((Token -> m Token) -> [Token] -> m ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ (\x :: Token
x -> (Token -> m ()) -> Token -> m Token
forall (m :: * -> *).
Monad m =>
(Token -> m ()) -> Token -> m Token
doAnalysis (Token -> Token -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Token -> Token -> m ()
checkOccurrences Token
x) Token
l) ([Token] -> [Token]
getAllRedirs [Token]
list))) [Token]
list
where
note :: Id -> TokenComment
note x :: Id
x = Severity -> Id -> Code -> String -> TokenComment
makeComment Severity
InfoC Id
x 2094
"Make sure not to read and write the same file in the same pipeline."
checkOccurrences :: Token -> Token -> m ()
checkOccurrences t :: Token
t@(T_NormalWord exceptId :: Id
exceptId x :: [Token]
x) u :: Token
u@(T_NormalWord newId :: Id
newId y :: [Token]
y) |
Id
exceptId Id -> Id -> Bool
forall a. Eq a => a -> a -> Bool
/= Id
newId
Bool -> Bool -> Bool
&& [Token]
x [Token] -> [Token] -> Bool
forall a. Eq a => a -> a -> Bool
== [Token]
y
Bool -> Bool -> Bool
&& Bool -> Bool
not (Token -> Bool
isOutput Token
t Bool -> Bool -> Bool
&& Token -> Bool
isOutput Token
u)
Bool -> Bool -> Bool
&& Bool -> Bool
not (Token -> Bool
special Token
t)
Bool -> Bool -> Bool
&& Bool -> Bool
not ((Token -> Bool) -> [Token] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any Token -> Bool
isHarmlessCommand [Token
t,Token
u])
Bool -> Bool -> Bool
&& Bool -> Bool
not ((Token -> Bool) -> [Token] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any Token -> Bool
containsAssignment [Token
u]) = do
TokenComment -> m ()
forall a (m :: * -> *). (NFData a, MonadWriter [a] m) => a -> m ()
addComment (TokenComment -> m ()) -> TokenComment -> m ()
forall a b. (a -> b) -> a -> b
$ Id -> TokenComment
note Id
newId
TokenComment -> m ()
forall a (m :: * -> *). (NFData a, MonadWriter [a] m) => a -> m ()
addComment (TokenComment -> m ()) -> TokenComment -> m ()
forall a b. (a -> b) -> a -> b
$ Id -> TokenComment
note Id
exceptId
checkOccurrences _ _ = () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
getAllRedirs :: [Token] -> [Token]
getAllRedirs = (Token -> [Token]) -> [Token] -> [Token]
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap (\t :: Token
t ->
case Token
t of
T_Redirecting _ ls :: [Token]
ls _ -> (Token -> [Token]) -> [Token] -> [Token]
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap Token -> [Token]
getRedirs [Token]
ls
_ -> [])
getRedirs :: Token -> [Token]
getRedirs (T_FdRedirect _ _ (T_IoFile _ op :: Token
op file :: Token
file)) =
case Token
op of T_Greater _ -> [Token
file]
T_Less _ -> [Token
file]
T_DGREAT _ -> [Token
file]
_ -> []
getRedirs _ = []
special :: Token -> Bool
special x :: Token
x = "/dev/" String -> String -> Bool
forall a. Eq a => [a] -> [a] -> Bool
`isPrefixOf` [String] -> String
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat (Token -> [String]
oversimplify Token
x)
isOutput :: Token -> Bool
isOutput t :: Token
t =
case Int -> [Token] -> [Token]
forall a. Int -> [a] -> [a]
drop 1 ([Token] -> [Token]) -> [Token] -> [Token]
forall a b. (a -> b) -> a -> b
$ Map Id Token -> Token -> [Token]
getPath (Parameters -> Map Id Token
parentMap Parameters
params) Token
t of
T_IoFile _ op :: Token
op _:_ ->
case Token
op of
T_Greater _ -> Bool
True
T_DGREAT _ -> Bool
True
_ -> Bool
False
_ -> Bool
False
isHarmlessCommand :: Token -> Bool
isHarmlessCommand arg :: Token
arg = Bool -> Maybe Bool -> Bool
forall a. a -> Maybe a -> a
fromMaybe Bool
False (Maybe Bool -> Bool) -> Maybe Bool -> Bool
forall a b. (a -> b) -> a -> b
$ do
Token
cmd <- Map Id Token -> Token -> Maybe Token
getClosestCommand (Parameters -> Map Id Token
parentMap Parameters
params) Token
arg
String
name <- Token -> Maybe String
getCommandBasename Token
cmd
Bool -> Maybe Bool
forall (m :: * -> *) a. Monad m => a -> m a
return (Bool -> Maybe Bool) -> Bool -> Maybe Bool
forall a b. (a -> b) -> a -> b
$ String
name String -> [String] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` ["echo", "printf", "sponge"]
containsAssignment :: Token -> Bool
containsAssignment arg :: Token
arg = Bool -> Maybe Bool -> Bool
forall a. a -> Maybe a -> a
fromMaybe Bool
False (Maybe Bool -> Bool) -> Maybe Bool -> Bool
forall a b. (a -> b) -> a -> b
$ do
Token
cmd <- Map Id Token -> Token -> Maybe Token
getClosestCommand (Parameters -> Map Id Token
parentMap Parameters
params) Token
arg
Bool -> Maybe Bool
forall (m :: * -> *) a. Monad m => a -> m a
return (Bool -> Maybe Bool) -> Bool -> Maybe Bool
forall a b. (a -> b) -> a -> b
$ Token -> Bool
isAssignment Token
cmd
checkRedirectToSame _ _ = () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
prop_checkShorthandIf :: Bool
prop_checkShorthandIf = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkShorthandIf "[[ ! -z file ]] && scp file host || rm file"
prop_checkShorthandIf2 :: Bool
prop_checkShorthandIf2 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkShorthandIf "[[ ! -z file ]] && { scp file host || echo 'Eek'; }"
prop_checkShorthandIf3 :: Bool
prop_checkShorthandIf3 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkShorthandIf "foo && bar || echo baz"
prop_checkShorthandIf4 :: Bool
prop_checkShorthandIf4 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkShorthandIf "foo && a=b || a=c"
prop_checkShorthandIf5 :: Bool
prop_checkShorthandIf5 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkShorthandIf "foo && rm || printf b"
prop_checkShorthandIf6 :: Bool
prop_checkShorthandIf6 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkShorthandIf "if foo && bar || baz; then true; fi"
prop_checkShorthandIf7 :: Bool
prop_checkShorthandIf7 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkShorthandIf "while foo && bar || baz; do true; done"
prop_checkShorthandIf8 :: Bool
prop_checkShorthandIf8 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkShorthandIf "if true; then foo && bar || baz; fi"
checkShorthandIf :: Parameters -> Token -> m ()
checkShorthandIf params :: Parameters
params x :: Token
x@(T_AndIf id :: Id
id _ (T_OrIf _ _ (T_Pipeline _ _ t :: [Token]
t)))
| Bool -> Bool
not ([Token] -> Bool
isOk [Token]
t Bool -> Bool -> Bool
|| Bool
inCondition) =
Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
info Id
id 2015 "Note that A && B || C is not if-then-else. C may run when A is true."
where
isOk :: [Token] -> Bool
isOk [t :: Token
t] = Token -> Bool
isAssignment Token
t Bool -> Bool -> Bool
|| Bool -> Maybe Bool -> Bool
forall a. a -> Maybe a -> a
fromMaybe Bool
False (do
String
name <- Token -> Maybe String
getCommandBasename Token
t
Bool -> Maybe Bool
forall (m :: * -> *) a. Monad m => a -> m a
return (Bool -> Maybe Bool) -> Bool -> Maybe Bool
forall a b. (a -> b) -> a -> b
$ String
name String -> [String] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` ["echo", "exit", "return", "printf"])
isOk _ = Bool
False
inCondition :: Bool
inCondition = [Token] -> Bool
isCondition ([Token] -> Bool) -> [Token] -> Bool
forall a b. (a -> b) -> a -> b
$ Map Id Token -> Token -> [Token]
getPath (Parameters -> Map Id Token
parentMap Parameters
params) Token
x
checkShorthandIf _ _ = () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
prop_checkDollarStar :: Bool
prop_checkDollarStar = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkDollarStar "for f in $*; do ..; done"
prop_checkDollarStar2 :: Bool
prop_checkDollarStar2 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkDollarStar "a=$*"
prop_checkDollarStar3 :: Bool
prop_checkDollarStar3 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkDollarStar "[[ $* = 'a b' ]]"
checkDollarStar :: Parameters -> Token -> m ()
checkDollarStar p :: Parameters
p t :: Token
t@(T_NormalWord _ [T_DollarBraced id :: Id
id _ l :: Token
l])
| [String] -> String
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat (Token -> [String]
oversimplify Token
l) String -> String -> Bool
forall a. Eq a => a -> a -> Bool
== "*" Bool -> Bool -> Bool
&&
Bool -> Bool
not (Map Id Token -> Token -> Bool
isStrictlyQuoteFree (Parameters -> Map Id Token
parentMap Parameters
p) Token
t) =
Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
warn Id
id 2048 "Use \"$@\" (with quotes) to prevent whitespace problems."
checkDollarStar _ _ = () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
prop_checkUnquotedDollarAt :: Bool
prop_checkUnquotedDollarAt = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkUnquotedDollarAt "ls $@"
prop_checkUnquotedDollarAt1 :: Bool
prop_checkUnquotedDollarAt1= (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkUnquotedDollarAt "ls ${#@}"
prop_checkUnquotedDollarAt2 :: Bool
prop_checkUnquotedDollarAt2 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkUnquotedDollarAt "ls ${foo[@]}"
prop_checkUnquotedDollarAt3 :: Bool
prop_checkUnquotedDollarAt3 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkUnquotedDollarAt "ls ${#foo[@]}"
prop_checkUnquotedDollarAt4 :: Bool
prop_checkUnquotedDollarAt4 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkUnquotedDollarAt "ls \"$@\""
prop_checkUnquotedDollarAt5 :: Bool
prop_checkUnquotedDollarAt5 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkUnquotedDollarAt "ls ${foo/@/ at }"
prop_checkUnquotedDollarAt6 :: Bool
prop_checkUnquotedDollarAt6 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkUnquotedDollarAt "a=$@"
prop_checkUnquotedDollarAt7 :: Bool
prop_checkUnquotedDollarAt7 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkUnquotedDollarAt "for f in ${var[@]}; do true; done"
prop_checkUnquotedDollarAt8 :: Bool
prop_checkUnquotedDollarAt8 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkUnquotedDollarAt "echo \"${args[@]:+${args[@]}}\""
prop_checkUnquotedDollarAt9 :: Bool
prop_checkUnquotedDollarAt9 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkUnquotedDollarAt "echo ${args[@]:+\"${args[@]}\"}"
prop_checkUnquotedDollarAt10 :: Bool
prop_checkUnquotedDollarAt10 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkUnquotedDollarAt "echo ${@+\"$@\"}"
checkUnquotedDollarAt :: Parameters -> Token -> m ()
checkUnquotedDollarAt p :: Parameters
p word :: Token
word@(T_NormalWord _ parts :: [Token]
parts) | Bool -> Bool
not (Bool -> Bool) -> Bool -> Bool
forall a b. (a -> b) -> a -> b
$ Map Id Token -> Token -> Bool
isStrictlyQuoteFree (Parameters -> Map Id Token
parentMap Parameters
p) Token
word =
Maybe Token -> (Token -> m ()) -> m ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
t a -> (a -> m b) -> m ()
forM_ ((Token -> Bool) -> [Token] -> Maybe Token
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Maybe a
find Token -> Bool
isArrayExpansion [Token]
parts) ((Token -> m ()) -> m ()) -> (Token -> m ()) -> m ()
forall a b. (a -> b) -> a -> b
$ \x :: Token
x ->
Bool -> m () -> m ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
unless (Token -> Bool
isQuotedAlternativeReference Token
x) (m () -> m ()) -> m () -> m ()
forall a b. (a -> b) -> a -> b
$
Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
err (Token -> Id
getId Token
x) 2068
"Double quote array expansions to avoid re-splitting elements."
checkUnquotedDollarAt _ _ = () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
prop_checkConcatenatedDollarAt1 :: Bool
prop_checkConcatenatedDollarAt1 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkConcatenatedDollarAt "echo \"foo$@\""
prop_checkConcatenatedDollarAt2 :: Bool
prop_checkConcatenatedDollarAt2 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkConcatenatedDollarAt "echo ${arr[@]}lol"
prop_checkConcatenatedDollarAt3 :: Bool
prop_checkConcatenatedDollarAt3 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkConcatenatedDollarAt "echo $a$@"
prop_checkConcatenatedDollarAt4 :: Bool
prop_checkConcatenatedDollarAt4 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkConcatenatedDollarAt "echo $@"
prop_checkConcatenatedDollarAt5 :: Bool
prop_checkConcatenatedDollarAt5 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkConcatenatedDollarAt "echo \"${arr[@]}\""
checkConcatenatedDollarAt :: Parameters -> Token -> m ()
checkConcatenatedDollarAt p :: Parameters
p word :: Token
word@T_NormalWord {}
| Bool -> Bool
not (Bool -> Bool) -> Bool -> Bool
forall a b. (a -> b) -> a -> b
$ Map Id Token -> Token -> Bool
isQuoteFree (Parameters -> Map Id Token
parentMap Parameters
p) Token
word
Bool -> Bool -> Bool
|| [Token] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null (Int -> [Token] -> [Token]
forall a. Int -> [a] -> [a]
drop 1 [Token]
parts) =
(Token -> m ()) -> Maybe Token -> m ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ Token -> m ()
forall (m :: * -> *). MonadWriter [TokenComment] m => Token -> m ()
for Maybe Token
array
where
parts :: [Token]
parts = Token -> [Token]
getWordParts Token
word
array :: Maybe Token
array = (Token -> Bool) -> [Token] -> Maybe Token
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Maybe a
find Token -> Bool
isArrayExpansion [Token]
parts
for :: Token -> m ()
for t :: Token
t = Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
err (Token -> Id
getId Token
t) 2145 "Argument mixes string and array. Use * or separate argument."
checkConcatenatedDollarAt _ _ = () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
prop_checkArrayAsString1 :: Bool
prop_checkArrayAsString1 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkArrayAsString "a=$@"
prop_checkArrayAsString2 :: Bool
prop_checkArrayAsString2 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkArrayAsString "a=\"${arr[@]}\""
prop_checkArrayAsString3 :: Bool
prop_checkArrayAsString3 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkArrayAsString "a=*.png"
prop_checkArrayAsString4 :: Bool
prop_checkArrayAsString4 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkArrayAsString "a={1..10}"
prop_checkArrayAsString5 :: Bool
prop_checkArrayAsString5 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkArrayAsString "a='*.gif'"
prop_checkArrayAsString6 :: Bool
prop_checkArrayAsString6 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkArrayAsString "a=$*"
prop_checkArrayAsString7 :: Bool
prop_checkArrayAsString7 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkArrayAsString "a=( $@ )"
checkArrayAsString :: p -> Token -> m ()
checkArrayAsString _ (T_Assignment id :: Id
id _ _ _ word :: Token
word) =
if Token -> Bool
willConcatInAssignment Token
word
then
Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
warn (Token -> Id
getId Token
word) 2124
"Assigning an array to a string! Assign as array, or use * instead of @ to concatenate."
else
Bool -> m () -> m ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (Token -> Bool
willBecomeMultipleArgs Token
word) (m () -> m ()) -> m () -> m ()
forall a b. (a -> b) -> a -> b
$
Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
warn (Token -> Id
getId Token
word) 2125
"Brace expansions and globs are literal in assignments. Quote it or use an array."
checkArrayAsString _ _ = () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
prop_checkArrayWithoutIndex1 :: Bool
prop_checkArrayWithoutIndex1 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkArrayWithoutIndex "foo=(a b); echo $foo"
prop_checkArrayWithoutIndex2 :: Bool
prop_checkArrayWithoutIndex2 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkArrayWithoutIndex "foo='bar baz'; foo=($foo); echo ${foo[0]}"
prop_checkArrayWithoutIndex3 :: Bool
prop_checkArrayWithoutIndex3 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkArrayWithoutIndex "coproc foo while true; do echo cow; done; echo $foo"
prop_checkArrayWithoutIndex4 :: Bool
prop_checkArrayWithoutIndex4 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkArrayWithoutIndex "coproc tail -f log; echo $COPROC"
prop_checkArrayWithoutIndex5 :: Bool
prop_checkArrayWithoutIndex5 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkArrayWithoutIndex "a[0]=foo; echo $a"
prop_checkArrayWithoutIndex6 :: Bool
prop_checkArrayWithoutIndex6 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkArrayWithoutIndex "echo $PIPESTATUS"
prop_checkArrayWithoutIndex7 :: Bool
prop_checkArrayWithoutIndex7 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkArrayWithoutIndex "a=(a b); a+=c"
prop_checkArrayWithoutIndex8 :: Bool
prop_checkArrayWithoutIndex8 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkArrayWithoutIndex "declare -a foo; foo=bar;"
prop_checkArrayWithoutIndex9 :: Bool
prop_checkArrayWithoutIndex9 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkArrayWithoutIndex "read -r -a arr <<< 'foo bar'; echo \"$arr\""
prop_checkArrayWithoutIndex10 :: Bool
prop_checkArrayWithoutIndex10 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkArrayWithoutIndex "read -ra arr <<< 'foo bar'; echo \"$arr\""
prop_checkArrayWithoutIndex11 :: Bool
prop_checkArrayWithoutIndex11 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkArrayWithoutIndex "read -rpfoobar r; r=42"
checkArrayWithoutIndex :: Parameters -> p -> [TokenComment]
checkArrayWithoutIndex params :: Parameters
params _ =
(Token -> Token -> String -> State (Map String ()) [TokenComment])
-> (Token
-> Token
-> String
-> DataType
-> State (Map String ()) [TokenComment])
-> Map String ()
-> [StackData]
-> [TokenComment]
forall t v.
(Token -> Token -> String -> State t [v])
-> (Token -> Token -> String -> DataType -> State t [v])
-> t
-> [StackData]
-> [v]
doVariableFlowAnalysis Token -> Token -> String -> State (Map String ()) [TokenComment]
forall (m :: * -> *) a p p.
MonadState (Map String a) m =>
p -> Token -> p -> m [TokenComment]
readF Token
-> Token
-> String
-> DataType
-> State (Map String ()) [TokenComment]
forall (m :: * -> *) p.
MonadState (Map String ()) m =>
p -> Token -> String -> DataType -> m [TokenComment]
writeF Map String ()
defaultMap (Parameters -> [StackData]
variableFlow Parameters
params)
where
defaultMap :: Map String ()
defaultMap = [(String, ())] -> Map String ()
forall k a. Ord k => [(k, a)] -> Map k a
Map.fromList ([(String, ())] -> Map String ())
-> [(String, ())] -> Map String ()
forall a b. (a -> b) -> a -> b
$ (String -> (String, ())) -> [String] -> [(String, ())]
forall a b. (a -> b) -> [a] -> [b]
map (\x :: String
x -> (String
x,())) [String]
arrayVariables
readF :: p -> Token -> p -> m [TokenComment]
readF _ (T_DollarBraced id :: Id
id _ token :: Token
token) _ = do
Map String a
map <- m (Map String a)
forall s (m :: * -> *). MonadState s m => m s
get
[TokenComment] -> m [TokenComment]
forall (m :: * -> *) a. Monad m => a -> m a
return ([TokenComment] -> m [TokenComment])
-> (Maybe TokenComment -> [TokenComment])
-> Maybe TokenComment
-> m [TokenComment]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Maybe TokenComment -> [TokenComment]
forall a. Maybe a -> [a]
maybeToList (Maybe TokenComment -> m [TokenComment])
-> Maybe TokenComment -> m [TokenComment]
forall a b. (a -> b) -> a -> b
$ do
String
name <- Token -> Maybe String
getLiteralString Token
token
a
assigned <- String -> Map String a -> Maybe a
forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup String
name Map String a
map
TokenComment -> Maybe TokenComment
forall (m :: * -> *) a. Monad m => a -> m a
return (TokenComment -> Maybe TokenComment)
-> TokenComment -> Maybe TokenComment
forall a b. (a -> b) -> a -> b
$ Severity -> Id -> Code -> String -> TokenComment
makeComment Severity
WarningC Id
id 2128
"Expanding an array without an index only gives the first element."
readF _ _ _ = [TokenComment] -> m [TokenComment]
forall (m :: * -> *) a. Monad m => a -> m a
return []
writeF :: p -> Token -> String -> DataType -> m [TokenComment]
writeF _ (T_Assignment id :: Id
id mode :: AssignmentMode
mode name :: String
name [] _) _ (DataString _) = do
Bool
isArray <- (Map String () -> Bool) -> m Bool
forall s (m :: * -> *) a. MonadState s m => (s -> a) -> m a
gets (String -> Map String () -> Bool
forall k a. Ord k => k -> Map k a -> Bool
Map.member String
name)
[TokenComment] -> m [TokenComment]
forall (m :: * -> *) a. Monad m => a -> m a
return ([TokenComment] -> m [TokenComment])
-> [TokenComment] -> m [TokenComment]
forall a b. (a -> b) -> a -> b
$ if Bool -> Bool
not Bool
isArray then [] else
case AssignmentMode
mode of
Assign -> [Severity -> Id -> Code -> String -> TokenComment
makeComment Severity
WarningC Id
id 2178 "Variable was used as an array but is now assigned a string."]
Append -> [Severity -> Id -> Code -> String -> TokenComment
makeComment Severity
WarningC Id
id 2179 "Use array+=(\"item\") to append items to an array."]
writeF _ t :: Token
t name :: String
name (DataArray _) = do
(Map String () -> Map String ()) -> m ()
forall s (m :: * -> *). MonadState s m => (s -> s) -> m ()
modify (String -> () -> Map String () -> Map String ()
forall k a. Ord k => k -> a -> Map k a -> Map k a
Map.insert String
name ())
[TokenComment] -> m [TokenComment]
forall (m :: * -> *) a. Monad m => a -> m a
return []
writeF _ expr :: Token
expr name :: String
name _ = do
if Token -> Bool
isIndexed Token
expr
then (Map String () -> Map String ()) -> m ()
forall s (m :: * -> *). MonadState s m => (s -> s) -> m ()
modify (String -> () -> Map String () -> Map String ()
forall k a. Ord k => k -> a -> Map k a -> Map k a
Map.insert String
name ())
else (Map String () -> Map String ()) -> m ()
forall s (m :: * -> *). MonadState s m => (s -> s) -> m ()
modify (String -> Map String () -> Map String ()
forall k a. Ord k => k -> Map k a -> Map k a
Map.delete String
name)
[TokenComment] -> m [TokenComment]
forall (m :: * -> *) a. Monad m => a -> m a
return []
isIndexed :: Token -> Bool
isIndexed expr :: Token
expr =
case Token
expr of
T_Assignment _ _ _ (_:_) _ -> Bool
True
_ -> Bool
False
prop_checkStderrRedirect :: Bool
prop_checkStderrRedirect = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkStderrRedirect "test 2>&1 > cow"
prop_checkStderrRedirect2 :: Bool
prop_checkStderrRedirect2 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkStderrRedirect "test > cow 2>&1"
prop_checkStderrRedirect3 :: Bool
prop_checkStderrRedirect3 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkStderrRedirect "test 2>&1 > file | grep stderr"
prop_checkStderrRedirect4 :: Bool
prop_checkStderrRedirect4 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkStderrRedirect "errors=$(test 2>&1 > file)"
prop_checkStderrRedirect5 :: Bool
prop_checkStderrRedirect5 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkStderrRedirect "read < <(test 2>&1 > file)"
prop_checkStderrRedirect6 :: Bool
prop_checkStderrRedirect6 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkStderrRedirect "foo | bar 2>&1 > /dev/null"
prop_checkStderrRedirect7 :: Bool
prop_checkStderrRedirect7 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkStderrRedirect "{ cmd > file; } 2>&1"
checkStderrRedirect :: Parameters -> Token -> f ()
checkStderrRedirect params :: Parameters
params redir :: Token
redir@(T_Redirecting _ [
T_FdRedirect id :: Id
id "2" (T_IoDuplicate _ (T_GREATAND _) "1"),
T_FdRedirect _ _ (T_IoFile _ op :: Token
op _)
] _) = case Token
op of
T_Greater _ -> f ()
error
T_DGREAT _ -> f ()
error
_ -> () -> f ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
where
usesOutput :: Token -> Bool
usesOutput t :: Token
t =
case Token
t of
(T_Pipeline _ _ list :: [Token]
list) -> [Token] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [Token]
list Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> 1 Bool -> Bool -> Bool
&& Bool -> Bool
not (Map Id Token -> Token -> Token -> Bool
isParentOf (Parameters -> Map Id Token
parentMap Parameters
params) ([Token] -> Token
forall a. [a] -> a
last [Token]
list) Token
redir)
T_ProcSub {} -> Bool
True
T_DollarExpansion {} -> Bool
True
T_Backticked {} -> Bool
True
_ -> Bool
False
isCaptured :: Bool
isCaptured = (Token -> Bool) -> [Token] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any Token -> Bool
usesOutput ([Token] -> Bool) -> [Token] -> Bool
forall a b. (a -> b) -> a -> b
$ Map Id Token -> Token -> [Token]
getPath (Parameters -> Map Id Token
parentMap Parameters
params) Token
redir
error :: f ()
error = Bool -> f () -> f ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
unless Bool
isCaptured (f () -> f ()) -> f () -> f ()
forall a b. (a -> b) -> a -> b
$
Id -> Code -> String -> f ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
warn Id
id 2069 "To redirect stdout+stderr, 2>&1 must be last (or use '{ cmd > file; } 2>&1' to clarify)."
checkStderrRedirect _ _ = () -> f ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
lt :: a -> a
lt x :: a
x = String -> a -> a
forall a. String -> a -> a
trace ("Tracing " String -> String -> String
forall a. [a] -> [a] -> [a]
++ a -> String
forall a. Show a => a -> String
show a
x) a
x
ltt :: a -> a -> a
ltt t :: a
t = String -> a -> a
forall a. String -> a -> a
trace ("Tracing " String -> String -> String
forall a. [a] -> [a] -> [a]
++ a -> String
forall a. Show a => a -> String
show a
t)
prop_checkSingleQuotedVariables :: Bool
prop_checkSingleQuotedVariables = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkSingleQuotedVariables "echo '$foo'"
prop_checkSingleQuotedVariables2 :: Bool
prop_checkSingleQuotedVariables2 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkSingleQuotedVariables "echo 'lol$1.jpg'"
prop_checkSingleQuotedVariables3 :: Bool
prop_checkSingleQuotedVariables3 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkSingleQuotedVariables "sed 's/foo$/bar/'"
prop_checkSingleQuotedVariables3a :: Bool
prop_checkSingleQuotedVariables3a= (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkSingleQuotedVariables "sed 's/${foo}/bar/'"
prop_checkSingleQuotedVariables3b :: Bool
prop_checkSingleQuotedVariables3b= (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkSingleQuotedVariables "sed 's/$(echo cow)/bar/'"
prop_checkSingleQuotedVariables3c :: Bool
prop_checkSingleQuotedVariables3c= (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkSingleQuotedVariables "sed 's/$((1+foo))/bar/'"
prop_checkSingleQuotedVariables4 :: Bool
prop_checkSingleQuotedVariables4 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkSingleQuotedVariables "awk '{print $1}'"
prop_checkSingleQuotedVariables5 :: Bool
prop_checkSingleQuotedVariables5 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkSingleQuotedVariables "trap 'echo $SECONDS' EXIT"
prop_checkSingleQuotedVariables6 :: Bool
prop_checkSingleQuotedVariables6 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkSingleQuotedVariables "sed -n '$p'"
prop_checkSingleQuotedVariables6a :: Bool
prop_checkSingleQuotedVariables6a= (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkSingleQuotedVariables "sed -n '$pattern'"
prop_checkSingleQuotedVariables7 :: Bool
prop_checkSingleQuotedVariables7 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkSingleQuotedVariables "PS1='$PWD \\$ '"
prop_checkSingleQuotedVariables8 :: Bool
prop_checkSingleQuotedVariables8 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkSingleQuotedVariables "find . -exec echo '$1' {} +"
prop_checkSingleQuotedVariables9 :: Bool
prop_checkSingleQuotedVariables9 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkSingleQuotedVariables "find . -exec awk '{print $1}' {} \\;"
prop_checkSingleQuotedVariables10 :: Bool
prop_checkSingleQuotedVariables10= (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkSingleQuotedVariables "echo '`pwd`'"
prop_checkSingleQuotedVariables11 :: Bool
prop_checkSingleQuotedVariables11= (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkSingleQuotedVariables "sed '${/lol/d}'"
prop_checkSingleQuotedVariables12 :: Bool
prop_checkSingleQuotedVariables12= (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkSingleQuotedVariables "eval 'echo $1'"
prop_checkSingleQuotedVariables13 :: Bool
prop_checkSingleQuotedVariables13= (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkSingleQuotedVariables "busybox awk '{print $1}'"
prop_checkSingleQuotedVariables14 :: Bool
prop_checkSingleQuotedVariables14= (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkSingleQuotedVariables "[ -v 'bar[$foo]' ]"
prop_checkSingleQuotedVariables15 :: Bool
prop_checkSingleQuotedVariables15= (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkSingleQuotedVariables "git filter-branch 'test $GIT_COMMIT'"
prop_checkSingleQuotedVariables16 :: Bool
prop_checkSingleQuotedVariables16= (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkSingleQuotedVariables "git '$a'"
prop_checkSingleQuotedVariables17 :: Bool
prop_checkSingleQuotedVariables17= (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkSingleQuotedVariables "rename 's/(.)a/$1/g' *"
prop_checkSingleQuotedVariables18 :: Bool
prop_checkSingleQuotedVariables18= (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkSingleQuotedVariables "echo '``'"
prop_checkSingleQuotedVariables19 :: Bool
prop_checkSingleQuotedVariables19= (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkSingleQuotedVariables "echo '```'"
prop_checkSingleQuotedVariables20 :: Bool
prop_checkSingleQuotedVariables20= (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkSingleQuotedVariables "mumps -run %XCMD 'W $O(^GLOBAL(5))'"
prop_checkSingleQuotedVariables21 :: Bool
prop_checkSingleQuotedVariables21= (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkSingleQuotedVariables "mumps -run LOOP%XCMD --xec 'W $O(^GLOBAL(6))'"
prop_checkSingleQuotedVariables22 :: Bool
prop_checkSingleQuotedVariables22= (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkSingleQuotedVariables "jq '$__loc__'"
prop_checkSingleQuotedVariables23 :: Bool
prop_checkSingleQuotedVariables23= (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkSingleQuotedVariables "command jq '$__loc__'"
prop_checkSingleQuotedVariables24 :: Bool
prop_checkSingleQuotedVariables24= (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkSingleQuotedVariables "exec jq '$__loc__'"
prop_checkSingleQuotedVariables25 :: Bool
prop_checkSingleQuotedVariables25= (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkSingleQuotedVariables "exec -c -a foo jq '$__loc__'"
checkSingleQuotedVariables :: Parameters -> Token -> f ()
checkSingleQuotedVariables params :: Parameters
params t :: Token
t@(T_SingleQuoted id :: Id
id s :: String
s) =
Bool -> f () -> f ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (String
s String -> Regex -> Bool
`matches` Regex
re) (f () -> f ()) -> f () -> f ()
forall a b. (a -> b) -> a -> b
$
if "sed" String -> String -> Bool
forall a. Eq a => a -> a -> Bool
== String
commandName
then Bool -> f () -> f ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
unless (String
s String -> Regex -> Bool
`matches` Regex
sedContra) f ()
showMessage
else Bool -> f () -> f ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
unless Bool
isProbablyOk f ()
showMessage
where
parents :: Map Id Token
parents = Parameters -> Map Id Token
parentMap Parameters
params
showMessage :: f ()
showMessage = Id -> Code -> String -> f ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
info Id
id 2016
"Expressions don't expand in single quotes, use double quotes for that."
commandName :: String
commandName = String -> Maybe String -> String
forall a. a -> Maybe a -> a
fromMaybe "" (Maybe String -> String) -> Maybe String -> String
forall a b. (a -> b) -> a -> b
$ do
Token
cmd <- Map Id Token -> Token -> Maybe Token
getClosestCommand Map Id Token
parents Token
t
String
name <- Token -> Maybe String
getCommandBasename Token
cmd
String -> Maybe String
forall (m :: * -> *) a. Monad m => a -> m a
return (String -> Maybe String) -> String -> Maybe String
forall a b. (a -> b) -> a -> b
$ if String
name String -> String -> Bool
forall a. Eq a => a -> a -> Bool
== "find" then Token -> String
getFindCommand Token
cmd else if String
name String -> String -> Bool
forall a. Eq a => a -> a -> Bool
== "git" then Token -> String
getGitCommand Token
cmd else if String
name String -> String -> Bool
forall a. Eq a => a -> a -> Bool
== "mumps" then Token -> String
getMumpsCommand Token
cmd else String
name
isProbablyOk :: Bool
isProbablyOk =
(Token -> Bool) -> [Token] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any Token -> Bool
isOkAssignment (Int -> [Token] -> [Token]
forall a. Int -> [a] -> [a]
take 3 ([Token] -> [Token]) -> [Token] -> [Token]
forall a b. (a -> b) -> a -> b
$ Map Id Token -> Token -> [Token]
getPath Map Id Token
parents Token
t)
Bool -> Bool -> Bool
|| String
commandName String -> [String] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [
"trap"
,"sh"
,"bash"
,"ksh"
,"zsh"
,"ssh"
,"eval"
,"xprop"
,"alias"
,"sudo"
,"docker"
,"podman"
,"dpkg-query"
,"jq"
,"rename"
,"unset"
,"git filter-branch"
,"mumps -run %XCMD"
,"mumps -run LOOP%XCMD"
]
Bool -> Bool -> Bool
|| "awk" String -> String -> Bool
forall a. Eq a => [a] -> [a] -> Bool
`isSuffixOf` String
commandName
Bool -> Bool -> Bool
|| "perl" String -> String -> Bool
forall a. Eq a => [a] -> [a] -> Bool
`isPrefixOf` String
commandName
commonlyQuoted :: [String]
commonlyQuoted = ["PS1", "PS2", "PS3", "PS4", "PROMPT_COMMAND"]
isOkAssignment :: Token -> Bool
isOkAssignment t :: Token
t =
case Token
t of
T_Assignment _ _ name :: String
name _ _ -> String
name String -> [String] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [String]
commonlyQuoted
TC_Unary _ _ "-v" _ -> Bool
True
_ -> Bool
False
re :: Regex
re = String -> Regex
mkRegex "\\$[{(0-9a-zA-Z_]|`[^`]+`"
sedContra :: Regex
sedContra = String -> Regex
mkRegex "\\$[{dpsaic]($|[^a-zA-Z])"
getFindCommand :: Token -> String
getFindCommand (T_SimpleCommand _ _ words :: [Token]
words) =
let list :: [Maybe String]
list = (Token -> Maybe String) -> [Token] -> [Maybe String]
forall a b. (a -> b) -> [a] -> [b]
map Token -> Maybe String
getLiteralString [Token]
words
cmd :: [Maybe String]
cmd = (Maybe String -> Bool) -> [Maybe String] -> [Maybe String]
forall a. (a -> Bool) -> [a] -> [a]
dropWhile (\x :: Maybe String
x -> Maybe String
x Maybe String -> Maybe String -> Bool
forall a. Eq a => a -> a -> Bool
/= String -> Maybe String
forall a. a -> Maybe a
Just "-exec" Bool -> Bool -> Bool
&& Maybe String
x Maybe String -> Maybe String -> Bool
forall a. Eq a => a -> a -> Bool
/= String -> Maybe String
forall a. a -> Maybe a
Just "-execdir") [Maybe String]
list
in
case [Maybe String]
cmd of
(flag :: Maybe String
flag:cmd :: Maybe String
cmd:rest :: [Maybe String]
rest) -> String -> Maybe String -> String
forall a. a -> Maybe a -> a
fromMaybe "find" Maybe String
cmd
_ -> "find"
getFindCommand (T_Redirecting _ _ cmd :: Token
cmd) = Token -> String
getFindCommand Token
cmd
getFindCommand _ = "find"
getGitCommand :: Token -> String
getGitCommand (T_SimpleCommand _ _ words :: [Token]
words) =
case (Token -> Maybe String) -> [Token] -> [Maybe String]
forall a b. (a -> b) -> [a] -> [b]
map Token -> Maybe String
getLiteralString [Token]
words of
Just "git":Just "filter-branch":_ -> "git filter-branch"
_ -> "git"
getGitCommand (T_Redirecting _ _ cmd :: Token
cmd) = Token -> String
getGitCommand Token
cmd
getGitCommand _ = "git"
getMumpsCommand :: Token -> String
getMumpsCommand (T_SimpleCommand _ _ words :: [Token]
words) =
case (Token -> Maybe String) -> [Token] -> [Maybe String]
forall a b. (a -> b) -> [a] -> [b]
map Token -> Maybe String
getLiteralString [Token]
words of
Just "mumps":Just "-run":Just "%XCMD":_ -> "mumps -run %XCMD"
Just "mumps":Just "-run":Just "LOOP%XCMD":_ -> "mumps -run LOOP%XCMD"
_ -> "mumps"
getMumpsCommand (T_Redirecting _ _ cmd :: Token
cmd) = Token -> String
getMumpsCommand Token
cmd
getMumpsCommand _ = "mumps"
checkSingleQuotedVariables _ _ = () -> f ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
prop_checkUnquotedN :: Bool
prop_checkUnquotedN = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkUnquotedN "if [ -n $foo ]; then echo cow; fi"
prop_checkUnquotedN2 :: Bool
prop_checkUnquotedN2 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkUnquotedN "[ -n $cow ]"
prop_checkUnquotedN3 :: Bool
prop_checkUnquotedN3 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkUnquotedN "[[ -n $foo ]] && echo cow"
prop_checkUnquotedN4 :: Bool
prop_checkUnquotedN4 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkUnquotedN "[ -n $cow -o -t 1 ]"
prop_checkUnquotedN5 :: Bool
prop_checkUnquotedN5 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkUnquotedN "[ -n \"$@\" ]"
checkUnquotedN :: p -> Token -> f ()
checkUnquotedN _ (TC_Unary _ SingleBracket "-n" t :: Token
t) | Token -> Bool
willSplit Token
t =
Bool -> f () -> f ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
unless ((Token -> Bool) -> [Token] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any Token -> Bool
isArrayExpansion ([Token] -> Bool) -> [Token] -> Bool
forall a b. (a -> b) -> a -> b
$ Token -> [Token]
getWordParts Token
t) (f () -> f ()) -> f () -> f ()
forall a b. (a -> b) -> a -> b
$
Id -> Code -> String -> f ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
err (Token -> Id
getId Token
t) 2070 "-n doesn't work with unquoted arguments. Quote or use [[ ]]."
checkUnquotedN _ _ = () -> f ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
prop_checkNumberComparisons1 :: Bool
prop_checkNumberComparisons1 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkNumberComparisons "[[ $foo < 3 ]]"
prop_checkNumberComparisons2 :: Bool
prop_checkNumberComparisons2 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkNumberComparisons "[[ 0 >= $(cmd) ]]"
prop_checkNumberComparisons3 :: Bool
prop_checkNumberComparisons3 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkNumberComparisons "[[ $foo ]] > 3"
prop_checkNumberComparisons4 :: Bool
prop_checkNumberComparisons4 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkNumberComparisons "[[ $foo > 2.72 ]]"
prop_checkNumberComparisons5 :: Bool
prop_checkNumberComparisons5 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkNumberComparisons "[[ $foo -le 2.72 ]]"
prop_checkNumberComparisons6 :: Bool
prop_checkNumberComparisons6 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkNumberComparisons "[[ 3.14 -eq $foo ]]"
prop_checkNumberComparisons7 :: Bool
prop_checkNumberComparisons7 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkNumberComparisons "[[ 3.14 == $foo ]]"
prop_checkNumberComparisons8 :: Bool
prop_checkNumberComparisons8 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkNumberComparisons "[ foo <= bar ]"
prop_checkNumberComparisons9 :: Bool
prop_checkNumberComparisons9 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkNumberComparisons "[ foo \\>= bar ]"
prop_checkNumberComparisons11 :: Bool
prop_checkNumberComparisons11 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkNumberComparisons "[ $foo -eq 'N' ]"
prop_checkNumberComparisons12 :: Bool
prop_checkNumberComparisons12 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkNumberComparisons "[ x$foo -gt x${N} ]"
prop_checkNumberComparisons13 :: Bool
prop_checkNumberComparisons13 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkNumberComparisons "[ $foo > $bar ]"
prop_checkNumberComparisons14 :: Bool
prop_checkNumberComparisons14 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkNumberComparisons "[[ foo < bar ]]"
prop_checkNumberComparisons15 :: Bool
prop_checkNumberComparisons15 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkNumberComparisons "[ $foo '>' $bar ]"
prop_checkNumberComparisons16 :: Bool
prop_checkNumberComparisons16 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkNumberComparisons "[ foo -eq 'y' ]"
checkNumberComparisons :: Parameters -> Token -> m ()
checkNumberComparisons params :: Parameters
params (TC_Binary id :: Id
id typ :: ConditionType
typ op :: String
op lhs :: Token
lhs rhs :: Token
rhs) = do
if Token -> Bool
isNum Token
lhs Bool -> Bool -> Bool
|| Token -> Bool
isNum Token
rhs
then do
Bool -> m () -> m ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (String -> Bool
isLtGt String
op) (m () -> m ()) -> m () -> m ()
forall a b. (a -> b) -> a -> b
$
Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
err Id
id 2071 (String -> m ()) -> String -> m ()
forall a b. (a -> b) -> a -> b
$
String
op String -> String -> String
forall a. [a] -> [a] -> [a]
++ " is for string comparisons. Use " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String -> String
eqv String
op String -> String -> String
forall a. [a] -> [a] -> [a]
++ " instead."
Bool -> m () -> m ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (String -> Bool
isLeGe String
op Bool -> Bool -> Bool
&& Bool
hasStringComparison) (m () -> m ()) -> m () -> m ()
forall a b. (a -> b) -> a -> b
$
Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
err Id
id 2071 (String -> m ()) -> String -> m ()
forall a b. (a -> b) -> a -> b
$ String
op String -> String -> String
forall a. [a] -> [a] -> [a]
++ " is not a valid operator. " String -> String -> String
forall a. [a] -> [a] -> [a]
++
"Use " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String -> String
eqv String
op String -> String -> String
forall a. [a] -> [a] -> [a]
++ " ."
else do
Bool -> m () -> m ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (String -> Bool
isLeGe String
op Bool -> Bool -> Bool
|| String -> Bool
isLtGt String
op) (m () -> m ()) -> m () -> m ()
forall a b. (a -> b) -> a -> b
$
(Token -> m ()) -> [Token] -> m ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ Token -> m ()
forall (m :: * -> *). MonadWriter [TokenComment] m => Token -> m ()
checkDecimals [Token
lhs, Token
rhs]
Bool -> m () -> m ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (String -> Bool
isLeGe String
op Bool -> Bool -> Bool
&& Bool
hasStringComparison) (m () -> m ()) -> m () -> m ()
forall a b. (a -> b) -> a -> b
$
Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
err Id
id 2122 (String -> m ()) -> String -> m ()
forall a b. (a -> b) -> a -> b
$ String
op String -> String -> String
forall a. [a] -> [a] -> [a]
++ " is not a valid operator. " String -> String -> String
forall a. [a] -> [a] -> [a]
++
"Use '! a " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
esc String -> String -> String
forall a. [a] -> [a] -> [a]
++ String -> String
invert String
op String -> String -> String
forall a. [a] -> [a] -> [a]
++ " b' instead."
Bool -> m () -> m ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (ConditionType
typ ConditionType -> ConditionType -> Bool
forall a. Eq a => a -> a -> Bool
== ConditionType
SingleBracket Bool -> Bool -> Bool
&& String
op String -> [String] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` ["<", ">"]) (m () -> m ()) -> m () -> m ()
forall a b. (a -> b) -> a -> b
$
case Parameters -> Shell
shellType Parameters
params of
Sh -> () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
Dash -> Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
err Id
id 2073 (String -> m ()) -> String -> m ()
forall a b. (a -> b) -> a -> b
$ "Escape \\" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
op String -> String -> String
forall a. [a] -> [a] -> [a]
++ " to prevent it redirecting."
_ -> Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
err Id
id 2073 (String -> m ()) -> String -> m ()
forall a b. (a -> b) -> a -> b
$ "Escape \\" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
op String -> String -> String
forall a. [a] -> [a] -> [a]
++ " to prevent it redirecting (or switch to [[ .. ]])."
Bool -> m () -> m ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (String
op String -> [String] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [String]
arithmeticBinaryTestOps) (m () -> m ()) -> m () -> m ()
forall a b. (a -> b) -> a -> b
$ do
(Token -> m ()) -> [Token] -> m ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ Token -> m ()
forall (m :: * -> *). MonadWriter [TokenComment] m => Token -> m ()
checkDecimals [Token
lhs, Token
rhs]
Bool -> m () -> m ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (ConditionType
typ ConditionType -> ConditionType -> Bool
forall a. Eq a => a -> a -> Bool
== ConditionType
SingleBracket) (m () -> m ()) -> m () -> m ()
forall a b. (a -> b) -> a -> b
$
[Token] -> m ()
checkStrings [Token
lhs, Token
rhs]
where
hasStringComparison :: Bool
hasStringComparison = Parameters -> Shell
shellType Parameters
params Shell -> Shell -> Bool
forall a. Eq a => a -> a -> Bool
/= Shell
Sh
isLtGt :: String -> Bool
isLtGt = (String -> [String] -> Bool) -> [String] -> String -> Bool
forall a b c. (a -> b -> c) -> b -> a -> c
flip String -> [String] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
elem ["<", "\\<", ">", "\\>"]
isLeGe :: String -> Bool
isLeGe = (String -> [String] -> Bool) -> [String] -> String -> Bool
forall a b c. (a -> b -> c) -> b -> a -> c
flip String -> [String] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
elem ["<=", "\\<=", ">=", "\\>="]
checkDecimals :: Token -> f ()
checkDecimals hs :: Token
hs =
Bool -> f () -> f ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (Token -> Bool
isFraction Token
hs Bool -> Bool -> Bool
&& Bool -> Bool
not (Parameters -> Bool
hasFloatingPoint Parameters
params)) (f () -> f ()) -> f () -> f ()
forall a b. (a -> b) -> a -> b
$
Id -> Code -> String -> f ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
err (Token -> Id
getId Token
hs) 2072 String
decimalError
decimalError :: String
decimalError = "Decimals are not supported. " String -> String -> String
forall a. [a] -> [a] -> [a]
++
"Either use integers only, or use bc or awk to compare."
checkStrings :: [Token] -> m ()
checkStrings =
(Token -> m ()) -> Maybe Token -> m ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ Token -> m ()
forall (m :: * -> *). MonadWriter [TokenComment] m => Token -> m ()
stringError (Maybe Token -> m ())
-> ([Token] -> Maybe Token) -> [Token] -> m ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Token -> Bool) -> [Token] -> Maybe Token
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Maybe a
find Token -> Bool
isNonNum
isNonNum :: Token -> Bool
isNonNum t :: Token
t = Bool -> Bool
not (Bool -> Bool) -> (String -> Bool) -> String -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Char -> Bool) -> String -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all Char -> Bool
numChar (String -> Bool) -> String -> Bool
forall a b. (a -> b) -> a -> b
$ Token -> String
onlyLiteralString Token
t
numChar :: Char -> Bool
numChar x :: Char
x = Char -> Bool
isDigit Char
x Bool -> Bool -> Bool
|| Char
x Char -> String -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` "+-. "
stringError :: Token -> m ()
stringError t :: Token
t = Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
err (Token -> Id
getId Token
t) 2170 (String -> m ()) -> String -> m ()
forall a b. (a -> b) -> a -> b
$
"Numerical " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
op String -> String -> String
forall a. [a] -> [a] -> [a]
++ " does not dereference in [..]. Expand or use string operator."
isNum :: Token -> Bool
isNum t :: Token
t =
case Token -> [String]
oversimplify Token
t of
[v :: String
v] -> (Char -> Bool) -> String -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all Char -> Bool
isDigit String
v
_ -> Bool
False
isFraction :: Token -> Bool
isFraction t :: Token
t =
case Token -> [String]
oversimplify Token
t of
[v :: String
v] -> Maybe [String] -> Bool
forall a. Maybe a -> Bool
isJust (Maybe [String] -> Bool) -> Maybe [String] -> Bool
forall a b. (a -> b) -> a -> b
$ Regex -> String -> Maybe [String]
matchRegex Regex
floatRegex String
v
_ -> Bool
False
eqv :: String -> String
eqv ('\\':s :: String
s) = String -> String
eqv String
s
eqv "<" = "-lt"
eqv ">" = "-gt"
eqv "<=" = "-le"
eqv ">=" = "-ge"
eqv _ = "the numerical equivalent"
esc :: String
esc = if ConditionType
typ ConditionType -> ConditionType -> Bool
forall a. Eq a => a -> a -> Bool
== ConditionType
SingleBracket then "\\" else ""
seqv :: String -> String
seqv "-ge" = "! a " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
esc String -> String -> String
forall a. [a] -> [a] -> [a]
++ "< b"
seqv "-gt" = String
esc String -> String -> String
forall a. [a] -> [a] -> [a]
++ ">"
seqv "-le" = "! a " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
esc String -> String -> String
forall a. [a] -> [a] -> [a]
++ "> b"
seqv "-lt" = String
esc String -> String -> String
forall a. [a] -> [a] -> [a]
++ "<"
seqv "-eq" = "="
seqv "-ne" = "!="
seqv _ = "the string equivalent"
invert :: String -> String
invert ('\\':s :: String
s) = String -> String
invert String
s
invert "<=" = ">"
invert ">=" = "<"
floatRegex :: Regex
floatRegex = String -> Regex
mkRegex "^[-+]?[0-9]+\\.[0-9]+$"
checkNumberComparisons _ _ = () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
prop_checkSingleBracketOperators1 :: Bool
prop_checkSingleBracketOperators1 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkSingleBracketOperators "[ test =~ foo ]"
checkSingleBracketOperators :: Parameters -> Token -> m ()
checkSingleBracketOperators params :: Parameters
params (TC_Binary id :: Id
id SingleBracket "=~" lhs :: Token
lhs rhs :: Token
rhs)
| Parameters -> Shell
shellType Parameters
params Shell -> [Shell] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [Shell
Bash, Shell
Ksh] =
Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
err Id
id 2074 (String -> m ()) -> String -> m ()
forall a b. (a -> b) -> a -> b
$ "Can't use =~ in [ ]. Use [[..]] instead."
checkSingleBracketOperators _ _ = () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
prop_checkDoubleBracketOperators1 :: Bool
prop_checkDoubleBracketOperators1 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkDoubleBracketOperators "[[ 3 \\< 4 ]]"
prop_checkDoubleBracketOperators3 :: Bool
prop_checkDoubleBracketOperators3 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkDoubleBracketOperators "[[ foo < bar ]]"
checkDoubleBracketOperators :: p -> Token -> m ()
checkDoubleBracketOperators _ x :: Token
x@(TC_Binary id :: Id
id typ :: ConditionType
typ op :: String
op lhs :: Token
lhs rhs :: Token
rhs)
| ConditionType
typ ConditionType -> ConditionType -> Bool
forall a. Eq a => a -> a -> Bool
== ConditionType
DoubleBracket Bool -> Bool -> Bool
&& String
op String -> [String] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` ["\\<", "\\>"] =
Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
err Id
id 2075 (String -> m ()) -> String -> m ()
forall a b. (a -> b) -> a -> b
$ "Escaping " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
op String -> String -> String
forall a. [a] -> [a] -> [a]
++" is required in [..], but invalid in [[..]]"
checkDoubleBracketOperators _ _ = () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
prop_checkConditionalAndOrs1 :: Bool
prop_checkConditionalAndOrs1 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkConditionalAndOrs "[ foo && bar ]"
prop_checkConditionalAndOrs2 :: Bool
prop_checkConditionalAndOrs2 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkConditionalAndOrs "[[ foo -o bar ]]"
prop_checkConditionalAndOrs3 :: Bool
prop_checkConditionalAndOrs3 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkConditionalAndOrs "[[ foo || bar ]]"
prop_checkConditionalAndOrs4 :: Bool
prop_checkConditionalAndOrs4 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkConditionalAndOrs "[ foo -a bar ]"
prop_checkConditionalAndOrs5 :: Bool
prop_checkConditionalAndOrs5 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkConditionalAndOrs "[ -z 3 -o a = b ]"
checkConditionalAndOrs :: p -> Token -> m ()
checkConditionalAndOrs _ t :: Token
t =
case Token
t of
(TC_And id :: Id
id SingleBracket "&&" _ _) ->
Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
err Id
id 2107 "Instead of [ a && b ], use [ a ] && [ b ]."
(TC_And id :: Id
id DoubleBracket "-a" _ _) ->
Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
err Id
id 2108 "In [[..]], use && instead of -a."
(TC_Or id :: Id
id SingleBracket "||" _ _) ->
Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
err Id
id 2109 "Instead of [ a || b ], use [ a ] || [ b ]."
(TC_Or id :: Id
id DoubleBracket "-o" _ _) ->
Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
err Id
id 2110 "In [[..]], use || instead of -o."
(TC_And id :: Id
id SingleBracket "-a" _ _) ->
Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
warn Id
id 2166 "Prefer [ p ] && [ q ] as [ p -a q ] is not well defined."
(TC_Or id :: Id
id SingleBracket "-o" _ _) ->
Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
warn Id
id 2166 "Prefer [ p ] || [ q ] as [ p -o q ] is not well defined."
_ -> () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
prop_checkQuotedCondRegex1 :: Bool
prop_checkQuotedCondRegex1 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkQuotedCondRegex "[[ $foo =~ \"bar.*\" ]]"
prop_checkQuotedCondRegex2 :: Bool
prop_checkQuotedCondRegex2 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkQuotedCondRegex "[[ $foo =~ '(cow|bar)' ]]"
prop_checkQuotedCondRegex3 :: Bool
prop_checkQuotedCondRegex3 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkQuotedCondRegex "[[ $foo =~ $foo ]]"
prop_checkQuotedCondRegex4 :: Bool
prop_checkQuotedCondRegex4 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkQuotedCondRegex "[[ $foo =~ \"bar\" ]]"
prop_checkQuotedCondRegex5 :: Bool
prop_checkQuotedCondRegex5 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkQuotedCondRegex "[[ $foo =~ 'cow bar' ]]"
prop_checkQuotedCondRegex6 :: Bool
prop_checkQuotedCondRegex6 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkQuotedCondRegex "[[ $foo =~ 'cow|bar' ]]"
checkQuotedCondRegex :: p -> Token -> f ()
checkQuotedCondRegex _ (TC_Binary _ _ "=~" _ rhs :: Token
rhs) =
case Token
rhs of
T_NormalWord id :: Id
id [T_DoubleQuoted _ _] -> Token -> f ()
forall (m :: * -> *). MonadWriter [TokenComment] m => Token -> m ()
error Token
rhs
T_NormalWord id :: Id
id [T_SingleQuoted _ _] -> Token -> f ()
forall (m :: * -> *). MonadWriter [TokenComment] m => Token -> m ()
error Token
rhs
_ -> () -> f ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
where
error :: Token -> f ()
error t :: Token
t =
Bool -> f () -> f ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
unless (Token -> Bool
isConstantNonRe Token
t) (f () -> f ()) -> f () -> f ()
forall a b. (a -> b) -> a -> b
$
Id -> Code -> String -> f ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
warn (Token -> Id
getId Token
t) 2076
"Remove quotes from right-hand side of =~ to match as a regex rather than literally."
re :: Regex
re = String -> Regex
mkRegex "[][*.+()|]"
hasMetachars :: String -> Bool
hasMetachars s :: String
s = String
s String -> Regex -> Bool
`matches` Regex
re
isConstantNonRe :: Token -> Bool
isConstantNonRe t :: Token
t = Bool -> Maybe Bool -> Bool
forall a. a -> Maybe a -> a
fromMaybe Bool
False (Maybe Bool -> Bool) -> Maybe Bool -> Bool
forall a b. (a -> b) -> a -> b
$ do
String
s <- Token -> Maybe String
getLiteralString Token
t
Bool -> Maybe Bool
forall (m :: * -> *) a. Monad m => a -> m a
return (Bool -> Maybe Bool) -> (Bool -> Bool) -> Bool -> Maybe Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Bool -> Bool
not (Bool -> Maybe Bool) -> Bool -> Maybe Bool
forall a b. (a -> b) -> a -> b
$ String -> Bool
hasMetachars String
s
checkQuotedCondRegex _ _ = () -> f ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
prop_checkGlobbedRegex1 :: Bool
prop_checkGlobbedRegex1 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkGlobbedRegex "[[ $foo =~ *foo* ]]"
prop_checkGlobbedRegex2 :: Bool
prop_checkGlobbedRegex2 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkGlobbedRegex "[[ $foo =~ f* ]]"
prop_checkGlobbedRegex3 :: Bool
prop_checkGlobbedRegex3 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkGlobbedRegex "[[ $foo =~ $foo ]]"
prop_checkGlobbedRegex4 :: Bool
prop_checkGlobbedRegex4 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkGlobbedRegex "[[ $foo =~ ^c.* ]]"
prop_checkGlobbedRegex5 :: Bool
prop_checkGlobbedRegex5 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkGlobbedRegex "[[ $foo =~ \\* ]]"
prop_checkGlobbedRegex6 :: Bool
prop_checkGlobbedRegex6 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkGlobbedRegex "[[ $foo =~ (o*) ]]"
prop_checkGlobbedRegex7 :: Bool
prop_checkGlobbedRegex7 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkGlobbedRegex "[[ $foo =~ \\*foo ]]"
prop_checkGlobbedRegex8 :: Bool
prop_checkGlobbedRegex8 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkGlobbedRegex "[[ $foo =~ x\\* ]]"
checkGlobbedRegex :: p -> Token -> m ()
checkGlobbedRegex _ (TC_Binary _ DoubleBracket "=~" _ rhs :: Token
rhs)
| String -> Bool
isConfusedGlobRegex String
s =
Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
warn (Token -> Id
getId Token
rhs) 2049 "=~ is for regex, but this looks like a glob. Use = instead."
where s :: String
s = [String] -> String
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat ([String] -> String) -> [String] -> String
forall a b. (a -> b) -> a -> b
$ Token -> [String]
oversimplify Token
rhs
checkGlobbedRegex _ _ = () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
prop_checkConstantIfs1 :: Bool
prop_checkConstantIfs1 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkConstantIfs "[[ foo != bar ]]"
prop_checkConstantIfs2a :: Bool
prop_checkConstantIfs2a= (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkConstantIfs "[ n -le 4 ]"
prop_checkConstantIfs2b :: Bool
prop_checkConstantIfs2b= (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkConstantIfs "[[ n -le 4 ]]"
prop_checkConstantIfs3 :: Bool
prop_checkConstantIfs3 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkConstantIfs "[[ $n -le 4 && n != 2 ]]"
prop_checkConstantIfs4 :: Bool
prop_checkConstantIfs4 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkConstantIfs "[[ $n -le 3 ]]"
prop_checkConstantIfs5 :: Bool
prop_checkConstantIfs5 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkConstantIfs "[[ $n -le $n ]]"
prop_checkConstantIfs6 :: Bool
prop_checkConstantIfs6 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkConstantIfs "[[ a -ot b ]]"
prop_checkConstantIfs7 :: Bool
prop_checkConstantIfs7 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkConstantIfs "[ a -nt b ]"
prop_checkConstantIfs8 :: Bool
prop_checkConstantIfs8 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkConstantIfs "[[ ~foo == '~foo' ]]"
prop_checkConstantIfs9 :: Bool
prop_checkConstantIfs9 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkConstantIfs "[[ *.png == [a-z] ]]"
prop_checkConstantIfs10 :: Bool
prop_checkConstantIfs10 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkConstantIfs "[[ ~me == ~+ ]]"
prop_checkConstantIfs11 :: Bool
prop_checkConstantIfs11 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkConstantIfs "[[ ~ == ~+ ]]"
prop_checkConstantIfs12 :: Bool
prop_checkConstantIfs12 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkConstantIfs "[[ '~' == x ]]"
checkConstantIfs :: p -> Token -> m ()
checkConstantIfs _ (TC_Binary id :: Id
id typ :: ConditionType
typ op :: String
op lhs :: Token
lhs rhs :: Token
rhs) | Bool -> Bool
not Bool
isDynamic =
if Token -> Bool
isConstant Token
lhs Bool -> Bool -> Bool
&& Token -> Bool
isConstant Token
rhs
then Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
warn Id
id 2050 "This expression is constant. Did you forget the $ on a variable?"
else Id -> String -> Token -> Token -> m ()
forall (f :: * -> *).
MonadWriter [TokenComment] f =>
Id -> String -> Token -> Token -> f ()
checkUnmatchable Id
id String
op Token
lhs Token
rhs
where
isDynamic :: Bool
isDynamic =
String
op String -> [String] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [String]
arithmeticBinaryTestOps
Bool -> Bool -> Bool
&& ConditionType
typ ConditionType -> ConditionType -> Bool
forall a. Eq a => a -> a -> Bool
== ConditionType
DoubleBracket
Bool -> Bool -> Bool
|| String
op String -> [String] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [ "-nt", "-ot", "-ef"]
checkUnmatchable :: Id -> String -> Token -> Token -> f ()
checkUnmatchable id :: Id
id op :: String
op lhs :: Token
lhs rhs :: Token
rhs =
Bool -> f () -> f ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (String
op String -> [String] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` ["=", "==", "!="] Bool -> Bool -> Bool
&& Bool -> Bool
not (Token -> Token -> Bool
wordsCanBeEqual Token
lhs Token
rhs)) (f () -> f ()) -> f () -> f ()
forall a b. (a -> b) -> a -> b
$
Id -> Code -> String -> f ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
warn Id
id 2193 "The arguments to this comparison can never be equal. Make sure your syntax is correct."
checkConstantIfs _ _ = () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
prop_checkLiteralBreakingTest :: Bool
prop_checkLiteralBreakingTest = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkLiteralBreakingTest "[[ a==$foo ]]"
prop_checkLiteralBreakingTest2 :: Bool
prop_checkLiteralBreakingTest2 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkLiteralBreakingTest "[ $foo=3 ]"
prop_checkLiteralBreakingTest3 :: Bool
prop_checkLiteralBreakingTest3 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkLiteralBreakingTest "[ $foo!=3 ]"
prop_checkLiteralBreakingTest4 :: Bool
prop_checkLiteralBreakingTest4 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkLiteralBreakingTest "[ \"$(ls) \" ]"
prop_checkLiteralBreakingTest5 :: Bool
prop_checkLiteralBreakingTest5 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkLiteralBreakingTest "[ -n \"$(true) \" ]"
prop_checkLiteralBreakingTest6 :: Bool
prop_checkLiteralBreakingTest6 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkLiteralBreakingTest "[ -z $(true)z ]"
prop_checkLiteralBreakingTest7 :: Bool
prop_checkLiteralBreakingTest7 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkLiteralBreakingTest "[ -z $(true) ]"
prop_checkLiteralBreakingTest8 :: Bool
prop_checkLiteralBreakingTest8 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkLiteralBreakingTest "[ $(true)$(true) ]"
prop_checkLiteralBreakingTest10 :: Bool
prop_checkLiteralBreakingTest10 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkLiteralBreakingTest "[ -z foo ]"
checkLiteralBreakingTest :: p -> Token -> m ()
checkLiteralBreakingTest _ t :: Token
t = Maybe (m ()) -> m ()
forall (t :: * -> *) (m :: * -> *) a.
(Foldable t, Monad m) =>
t (m a) -> m ()
sequence_ (Maybe (m ()) -> m ()) -> Maybe (m ()) -> m ()
forall a b. (a -> b) -> a -> b
$
case Token
t of
(TC_Nullary _ _ w :: Token
w@(T_NormalWord _ l :: [Token]
l)) -> do
Bool -> Maybe ()
forall (f :: * -> *). Alternative f => Bool -> f ()
guard (Bool -> Maybe ()) -> (Bool -> Bool) -> Bool -> Maybe ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Bool -> Bool
not (Bool -> Maybe ()) -> Bool -> Maybe ()
forall a b. (a -> b) -> a -> b
$ Token -> Bool
isConstant Token
w
[Token] -> Maybe (m ())
forall (t :: * -> *) (m :: * -> *).
(Foldable t, MonadWriter [TokenComment] m) =>
t Token -> Maybe (m ())
comparisonWarning [Token]
l Maybe (m ()) -> Maybe (m ()) -> Maybe (m ())
forall (m :: * -> *) a. MonadPlus m => m a -> m a -> m a
`mplus` Token -> String -> Maybe (m ())
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Token -> String -> Maybe (m ())
tautologyWarning Token
w "Argument to implicit -n is always true due to literal strings."
(TC_Unary _ _ op :: String
op w :: Token
w@(T_NormalWord _ l :: [Token]
l)) ->
case String
op of
"-n" -> Token -> String -> Maybe (m ())
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Token -> String -> Maybe (m ())
tautologyWarning Token
w "Argument to -n is always true due to literal strings."
"-z" -> Token -> String -> Maybe (m ())
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Token -> String -> Maybe (m ())
tautologyWarning Token
w "Argument to -z is always false due to literal strings."
_ -> String -> Maybe (m ())
forall (m :: * -> *) a. MonadFail m => String -> m a
fail "not relevant"
_ -> String -> Maybe (m ())
forall (m :: * -> *) a. MonadFail m => String -> m a
fail "not my problem"
where
hasEquals :: Token -> Bool
hasEquals = (String -> Bool) -> Token -> Bool
matchToken ('=' Char -> String -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem`)
isNonEmpty :: Token -> Bool
isNonEmpty = (String -> Bool) -> Token -> Bool
matchToken (Bool -> Bool
not (Bool -> Bool) -> (String -> Bool) -> String -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null)
matchToken :: (String -> Bool) -> Token -> Bool
matchToken m :: String -> Bool
m t :: Token
t = Bool -> (String -> Bool) -> Maybe String -> Bool
forall b a. b -> (a -> b) -> Maybe a -> b
maybe Bool
False String -> Bool
m (Token -> Maybe String
getLiteralString Token
t)
comparisonWarning :: t Token -> Maybe (m ())
comparisonWarning list :: t Token
list = do
Token
token <- (Token -> Bool) -> t Token -> Maybe Token
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Maybe a
find Token -> Bool
hasEquals t Token
list
m () -> Maybe (m ())
forall (m :: * -> *) a. Monad m => a -> m a
return (m () -> Maybe (m ())) -> m () -> Maybe (m ())
forall a b. (a -> b) -> a -> b
$ Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
err (Token -> Id
getId Token
token) 2077 "You need spaces around the comparison operator."
tautologyWarning :: Token -> String -> Maybe (m ())
tautologyWarning t :: Token
t s :: String
s = do
Token
token <- (Token -> Bool) -> [Token] -> Maybe Token
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Maybe a
find Token -> Bool
isNonEmpty ([Token] -> Maybe Token) -> [Token] -> Maybe Token
forall a b. (a -> b) -> a -> b
$ Token -> [Token]
getWordParts Token
t
m () -> Maybe (m ())
forall (m :: * -> *) a. Monad m => a -> m a
return (m () -> Maybe (m ())) -> m () -> Maybe (m ())
forall a b. (a -> b) -> a -> b
$ Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
err (Token -> Id
getId Token
token) 2157 String
s
prop_checkConstantNullary :: Bool
prop_checkConstantNullary = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkConstantNullary "[[ '$(foo)' ]]"
prop_checkConstantNullary2 :: Bool
prop_checkConstantNullary2 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkConstantNullary "[ \"-f lol\" ]"
prop_checkConstantNullary3 :: Bool
prop_checkConstantNullary3 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkConstantNullary "[[ cmd ]]"
prop_checkConstantNullary4 :: Bool
prop_checkConstantNullary4 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkConstantNullary "[[ ! cmd ]]"
prop_checkConstantNullary5 :: Bool
prop_checkConstantNullary5 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkConstantNullary "[[ true ]]"
prop_checkConstantNullary6 :: Bool
prop_checkConstantNullary6 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkConstantNullary "[ 1 ]"
prop_checkConstantNullary7 :: Bool
prop_checkConstantNullary7 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkConstantNullary "[ false ]"
checkConstantNullary :: p -> Token -> m ()
checkConstantNullary _ (TC_Nullary _ _ t :: Token
t) | Token -> Bool
isConstant Token
t =
case String -> Maybe String -> String
forall a. a -> Maybe a -> a
fromMaybe "" (Maybe String -> String) -> Maybe String -> String
forall a b. (a -> b) -> a -> b
$ Token -> Maybe String
getLiteralString Token
t of
"false" -> Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
err (Token -> Id
getId Token
t) 2158 "[ false ] is true. Remove the brackets."
"0" -> Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
err (Token -> Id
getId Token
t) 2159 "[ 0 ] is true. Use 'false' instead."
"true" -> Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
style (Token -> Id
getId Token
t) 2160 "Instead of '[ true ]', just use 'true'."
"1" -> Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
style (Token -> Id
getId Token
t) 2161 "Instead of '[ 1 ]', use 'true'."
_ -> Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
err (Token -> Id
getId Token
t) 2078 "This expression is constant. Did you forget a $ somewhere?"
where
string :: String
string = String -> Maybe String -> String
forall a. a -> Maybe a -> a
fromMaybe "" (Maybe String -> String) -> Maybe String -> String
forall a b. (a -> b) -> a -> b
$ Token -> Maybe String
getLiteralString Token
t
checkConstantNullary _ _ = () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
prop_checkForDecimals1 :: Bool
prop_checkForDecimals1 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkForDecimals "((3.14*c))"
prop_checkForDecimals2 :: Bool
prop_checkForDecimals2 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkForDecimals "foo[1.2]=bar"
prop_checkForDecimals3 :: Bool
prop_checkForDecimals3 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkForDecimals "declare -A foo; foo[1.2]=bar"
checkForDecimals :: Parameters -> Token -> m ()
checkForDecimals params :: Parameters
params t :: Token
t@(TA_Expansion id :: Id
id _) = Maybe (m ()) -> m ()
forall (t :: * -> *) (m :: * -> *) a.
(Foldable t, Monad m) =>
t (m a) -> m ()
sequence_ (Maybe (m ()) -> m ()) -> Maybe (m ()) -> m ()
forall a b. (a -> b) -> a -> b
$ do
Bool -> Maybe ()
forall (f :: * -> *). Alternative f => Bool -> f ()
guard (Bool -> Maybe ()) -> Bool -> Maybe ()
forall a b. (a -> b) -> a -> b
$ Bool -> Bool
not (Parameters -> Bool
hasFloatingPoint Parameters
params)
String
str <- Token -> Maybe String
getLiteralString Token
t
Char
first <- String
str String -> Int -> Maybe Char
forall a. [a] -> Int -> Maybe a
!!! 0
Bool -> Maybe ()
forall (f :: * -> *). Alternative f => Bool -> f ()
guard (Bool -> Maybe ()) -> Bool -> Maybe ()
forall a b. (a -> b) -> a -> b
$ Char -> Bool
isDigit Char
first Bool -> Bool -> Bool
&& '.' Char -> String -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` String
str
m () -> Maybe (m ())
forall (m :: * -> *) a. Monad m => a -> m a
return (m () -> Maybe (m ())) -> m () -> Maybe (m ())
forall a b. (a -> b) -> a -> b
$ Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
err Id
id 2079 "(( )) doesn't support decimals. Use bc or awk."
checkForDecimals _ _ = () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
prop_checkDivBeforeMult :: Bool
prop_checkDivBeforeMult = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkDivBeforeMult "echo $((c/n*100))"
prop_checkDivBeforeMult2 :: Bool
prop_checkDivBeforeMult2 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkDivBeforeMult "echo $((c*100/n))"
prop_checkDivBeforeMult3 :: Bool
prop_checkDivBeforeMult3 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkDivBeforeMult "echo $((c/10*10))"
checkDivBeforeMult :: Parameters -> Token -> m ()
checkDivBeforeMult params :: Parameters
params (TA_Binary _ "*" (TA_Binary id :: Id
id "/" _ x :: Token
x) y :: Token
y)
| Bool -> Bool
not (Parameters -> Bool
hasFloatingPoint Parameters
params) Bool -> Bool -> Bool
&& Token
x Token -> Token -> Bool
forall a. Eq a => a -> a -> Bool
/= Token
y =
Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
info Id
id 2017 "Increase precision by replacing a/b*c with a*c/b."
checkDivBeforeMult _ _ = () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
prop_checkArithmeticDeref :: Bool
prop_checkArithmeticDeref = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkArithmeticDeref "echo $((3+$foo))"
prop_checkArithmeticDeref2 :: Bool
prop_checkArithmeticDeref2 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkArithmeticDeref "cow=14; (( s+= $cow ))"
prop_checkArithmeticDeref3 :: Bool
prop_checkArithmeticDeref3 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkArithmeticDeref "cow=1/40; (( s+= ${cow%%/*} ))"
prop_checkArithmeticDeref4 :: Bool
prop_checkArithmeticDeref4 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkArithmeticDeref "(( ! $? ))"
prop_checkArithmeticDeref5 :: Bool
prop_checkArithmeticDeref5 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkArithmeticDeref "(($1))"
prop_checkArithmeticDeref6 :: Bool
prop_checkArithmeticDeref6 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkArithmeticDeref "(( a[$i] ))"
prop_checkArithmeticDeref7 :: Bool
prop_checkArithmeticDeref7 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkArithmeticDeref "(( 10#$n ))"
prop_checkArithmeticDeref8 :: Bool
prop_checkArithmeticDeref8 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkArithmeticDeref "let i=$i+1"
prop_checkArithmeticDeref9 :: Bool
prop_checkArithmeticDeref9 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkArithmeticDeref "(( a[foo] ))"
prop_checkArithmeticDeref10 :: Bool
prop_checkArithmeticDeref10= (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkArithmeticDeref "(( a[\\$foo] ))"
prop_checkArithmeticDeref11 :: Bool
prop_checkArithmeticDeref11= (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkArithmeticDeref "a[$foo]=wee"
prop_checkArithmeticDeref12 :: Bool
prop_checkArithmeticDeref12= (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkArithmeticDeref "for ((i=0; $i < 3; i)); do true; done"
prop_checkArithmeticDeref13 :: Bool
prop_checkArithmeticDeref13= (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkArithmeticDeref "(( $$ ))"
prop_checkArithmeticDeref14 :: Bool
prop_checkArithmeticDeref14= (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkArithmeticDeref "(( $! ))"
prop_checkArithmeticDeref15 :: Bool
prop_checkArithmeticDeref15= (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkArithmeticDeref "(( ${!var} ))"
prop_checkArithmeticDeref16 :: Bool
prop_checkArithmeticDeref16= (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkArithmeticDeref "(( ${x+1} + ${x=42} ))"
checkArithmeticDeref :: Parameters -> Token -> m ()
checkArithmeticDeref params :: Parameters
params t :: Token
t@(TA_Expansion _ [T_DollarBraced id :: Id
id _ l :: Token
l]) =
Bool -> m () -> m ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
unless (String -> Bool
isException (String -> Bool) -> String -> Bool
forall a b. (a -> b) -> a -> b
$ [String] -> String
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat ([String] -> String) -> [String] -> String
forall a b. (a -> b) -> a -> b
$ Token -> [String]
oversimplify Token
l) m ()
getWarning
where
isException :: String -> Bool
isException [] = Bool
True
isException s :: String
s@(h :: Char
h:_) = (Char -> Bool) -> String -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any (Char -> String -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` "/.:#%?*@$-!+=^,") String
s Bool -> Bool -> Bool
|| Char -> Bool
isDigit Char
h
getWarning :: m ()
getWarning = m () -> Maybe (m ()) -> m ()
forall a. a -> Maybe a -> a
fromMaybe m ()
noWarning (Maybe (m ()) -> m ())
-> ([Token] -> Maybe (m ())) -> [Token] -> m ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Maybe (m ())] -> Maybe (m ())
forall (t :: * -> *) (m :: * -> *) a.
(Foldable t, MonadPlus m) =>
t (m a) -> m a
msum ([Maybe (m ())] -> Maybe (m ()))
-> ([Token] -> [Maybe (m ())]) -> [Token] -> Maybe (m ())
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Token -> Maybe (m ())) -> [Token] -> [Maybe (m ())]
forall a b. (a -> b) -> [a] -> [b]
map Token -> Maybe (m ())
warningFor ([Token] -> m ()) -> [Token] -> m ()
forall a b. (a -> b) -> a -> b
$ Parameters -> Token -> [Token]
parents Parameters
params Token
t
warningFor :: Token -> Maybe (m ())
warningFor t :: Token
t =
case Token
t of
T_Arithmetic {} -> m () -> Maybe (m ())
forall (m :: * -> *) a. Monad m => a -> m a
return m ()
normalWarning
T_DollarArithmetic {} -> m () -> Maybe (m ())
forall (m :: * -> *) a. Monad m => a -> m a
return m ()
normalWarning
T_ForArithmetic {} -> m () -> Maybe (m ())
forall (m :: * -> *) a. Monad m => a -> m a
return m ()
normalWarning
T_SimpleCommand {} -> m () -> Maybe (m ())
forall (m :: * -> *) a. Monad m => a -> m a
return m ()
noWarning
_ -> Maybe (m ())
forall a. Maybe a
Nothing
normalWarning :: m ()
normalWarning = Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
style Id
id 2004 "$/${} is unnecessary on arithmetic variables."
noWarning :: m ()
noWarning = () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
checkArithmeticDeref _ _ = () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
prop_checkArithmeticBadOctal1 :: Bool
prop_checkArithmeticBadOctal1 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkArithmeticBadOctal "(( 0192 ))"
prop_checkArithmeticBadOctal2 :: Bool
prop_checkArithmeticBadOctal2 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkArithmeticBadOctal "(( 0x192 ))"
prop_checkArithmeticBadOctal3 :: Bool
prop_checkArithmeticBadOctal3 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkArithmeticBadOctal "(( 1 ^ 0777 ))"
checkArithmeticBadOctal :: p -> Token -> m ()
checkArithmeticBadOctal _ t :: Token
t@(TA_Expansion id :: Id
id _) = Maybe (m ()) -> m ()
forall (t :: * -> *) (m :: * -> *) a.
(Foldable t, Monad m) =>
t (m a) -> m ()
sequence_ (Maybe (m ()) -> m ()) -> Maybe (m ()) -> m ()
forall a b. (a -> b) -> a -> b
$ do
String
str <- Token -> Maybe String
getLiteralString Token
t
Bool -> Maybe ()
forall (f :: * -> *). Alternative f => Bool -> f ()
guard (Bool -> Maybe ()) -> Bool -> Maybe ()
forall a b. (a -> b) -> a -> b
$ String
str String -> Regex -> Bool
`matches` Regex
octalRE
m () -> Maybe (m ())
forall (m :: * -> *) a. Monad m => a -> m a
return (m () -> Maybe (m ())) -> m () -> Maybe (m ())
forall a b. (a -> b) -> a -> b
$ Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
err Id
id 2080 "Numbers with leading 0 are considered octal."
where
octalRE :: Regex
octalRE = String -> Regex
mkRegex "^0[0-7]*[8-9]"
checkArithmeticBadOctal _ _ = () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
prop_checkComparisonAgainstGlob :: Bool
prop_checkComparisonAgainstGlob = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkComparisonAgainstGlob "[[ $cow == $bar ]]"
prop_checkComparisonAgainstGlob2 :: Bool
prop_checkComparisonAgainstGlob2 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkComparisonAgainstGlob "[[ $cow == \"$bar\" ]]"
prop_checkComparisonAgainstGlob3 :: Bool
prop_checkComparisonAgainstGlob3 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkComparisonAgainstGlob "[ $cow = *foo* ]"
prop_checkComparisonAgainstGlob4 :: Bool
prop_checkComparisonAgainstGlob4 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkComparisonAgainstGlob "[ $cow = foo ]"
prop_checkComparisonAgainstGlob5 :: Bool
prop_checkComparisonAgainstGlob5 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkComparisonAgainstGlob "[[ $cow != $bar ]]"
prop_checkComparisonAgainstGlob6 :: Bool
prop_checkComparisonAgainstGlob6 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkComparisonAgainstGlob "[ $f != /* ]"
checkComparisonAgainstGlob :: Parameters -> Token -> m ()
checkComparisonAgainstGlob _ (TC_Binary _ DoubleBracket op :: String
op _ (T_NormalWord id :: Id
id [T_DollarBraced _ _ _]))
| String
op String -> [String] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` ["=", "==", "!="] =
Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
warn Id
id 2053 (String -> m ()) -> String -> m ()
forall a b. (a -> b) -> a -> b
$ "Quote the right-hand side of " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
op String -> String -> String
forall a. [a] -> [a] -> [a]
++ " in [[ ]] to prevent glob matching."
checkComparisonAgainstGlob params :: Parameters
params (TC_Binary _ SingleBracket op :: String
op _ word :: Token
word)
| String
op String -> [String] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` ["=", "==", "!="] Bool -> Bool -> Bool
&& Token -> Bool
isGlob Token
word =
Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
err (Token -> Id
getId Token
word) 2081 String
msg
where
msg :: String
msg = if Parameters -> Bool
isBashLike Parameters
params
then "[ .. ] can't match globs. Use [[ .. ]] or case statement."
else "[ .. ] can't match globs. Use a case statement."
checkComparisonAgainstGlob _ _ = () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
prop_checkCaseAgainstGlob1 :: Bool
prop_checkCaseAgainstGlob1 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkCaseAgainstGlob "case foo in lol$n) foo;; esac"
prop_checkCaseAgainstGlob2 :: Bool
prop_checkCaseAgainstGlob2 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkCaseAgainstGlob "case foo in $(foo)) foo;; esac"
prop_checkCaseAgainstGlob3 :: Bool
prop_checkCaseAgainstGlob3 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkCaseAgainstGlob "case foo in *$bar*) foo;; esac"
checkCaseAgainstGlob :: p -> Token -> m ()
checkCaseAgainstGlob _ t :: Token
t =
case Token
t of
(T_CaseExpression _ _ cases :: [(CaseType, [Token], [Token])]
cases) -> ((CaseType, [Token], [Token]) -> m ())
-> [(CaseType, [Token], [Token])] -> m ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ (CaseType, [Token], [Token]) -> m ()
forall (t :: * -> *) (m :: * -> *) a c.
(Foldable t, MonadWriter [TokenComment] m) =>
(a, t Token, c) -> m ()
check [(CaseType, [Token], [Token])]
cases
_ -> () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
where
check :: (a, t Token, c) -> m ()
check (_, list :: t Token
list, _) = (Token -> m ()) -> t Token -> m ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ Token -> m ()
forall (m :: * -> *). MonadWriter [TokenComment] m => Token -> m ()
check' t Token
list
check' :: Token -> m ()
check' expr :: Token
expr@(T_NormalWord _ list :: [Token]
list)
| Bool -> Bool
not (Token -> Bool
isGlob Token
expr) Bool -> Bool -> Bool
&& (Token -> Bool) -> [Token] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any Token -> Bool
isQuoteableExpansion [Token]
list =
Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
warn (Token -> Id
getId Token
expr) 2254 "Quote expansions in case patterns to match literally rather than as a glob."
check' _ = () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
prop_checkCommarrays1 :: Bool
prop_checkCommarrays1 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkCommarrays "a=(1, 2)"
prop_checkCommarrays2 :: Bool
prop_checkCommarrays2 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkCommarrays "a+=(1,2,3)"
prop_checkCommarrays3 :: Bool
prop_checkCommarrays3 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkCommarrays "cow=(1 \"foo,bar\" 3)"
prop_checkCommarrays4 :: Bool
prop_checkCommarrays4 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkCommarrays "cow=('one,' 'two')"
prop_checkCommarrays5 :: Bool
prop_checkCommarrays5 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkCommarrays "a=([a]=b, [c]=d)"
prop_checkCommarrays6 :: Bool
prop_checkCommarrays6 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkCommarrays "a=([a]=b,[c]=d,[e]=f)"
prop_checkCommarrays7 :: Bool
prop_checkCommarrays7 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkCommarrays "a=(1,2)"
checkCommarrays :: p -> Token -> f ()
checkCommarrays _ (T_Array id :: Id
id l :: [Token]
l) =
Bool -> f () -> f ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when ((Token -> Bool) -> [Token] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any (String -> Bool
isCommaSeparated (String -> Bool) -> (Token -> String) -> Token -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Token -> String
literal) [Token]
l) (f () -> f ()) -> f () -> f ()
forall a b. (a -> b) -> a -> b
$
Id -> Code -> String -> f ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
warn Id
id 2054 "Use spaces, not commas, to separate array elements."
where
literal :: Token -> String
literal (T_IndexedElement _ _ l :: Token
l) = Token -> String
literal Token
l
literal (T_NormalWord _ l :: [Token]
l) = (Token -> String) -> [Token] -> String
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap Token -> String
literal [Token]
l
literal (T_Literal _ str :: String
str) = String
str
literal _ = ""
isCommaSeparated :: String -> Bool
isCommaSeparated = Char -> String -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
elem ','
checkCommarrays _ _ = () -> f ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
prop_checkOrNeq1 :: Bool
prop_checkOrNeq1 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkOrNeq "if [[ $lol -ne cow || $lol -ne foo ]]; then echo foo; fi"
prop_checkOrNeq2 :: Bool
prop_checkOrNeq2 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkOrNeq "(( a!=lol || a!=foo ))"
prop_checkOrNeq3 :: Bool
prop_checkOrNeq3 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkOrNeq "[ \"$a\" != lol || \"$a\" != foo ]"
prop_checkOrNeq4 :: Bool
prop_checkOrNeq4 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkOrNeq "[ a != $cow || b != $foo ]"
prop_checkOrNeq5 :: Bool
prop_checkOrNeq5 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkOrNeq "[[ $a != /home || $a != */public_html/* ]]"
prop_checkOrNeq6 :: Bool
prop_checkOrNeq6 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkOrNeq "[ $a != a ] || [ $a != b ]"
prop_checkOrNeq7 :: Bool
prop_checkOrNeq7 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkOrNeq "[ $a != a ] || [ $a != b ] || true"
prop_checkOrNeq8 :: Bool
prop_checkOrNeq8 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkOrNeq "[[ $a != x || $a != x ]]"
prop_checkOrNeq9 :: Bool
prop_checkOrNeq9 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkOrNeq "[ 0 -ne $FOO ] || [ 0 -ne $BAR ]"
checkOrNeq :: p -> Token -> m ()
checkOrNeq _ (TC_Or id :: Id
id typ :: ConditionType
typ op :: String
op (TC_Binary _ _ op1 :: String
op1 lhs1 :: Token
lhs1 rhs1 :: Token
rhs1 ) (TC_Binary _ _ op2 :: String
op2 lhs2 :: Token
lhs2 rhs2 :: Token
rhs2))
| (String
op1 String -> String -> Bool
forall a. Eq a => a -> a -> Bool
== String
op2 Bool -> Bool -> Bool
&& (String
op1 String -> String -> Bool
forall a. Eq a => a -> a -> Bool
== "-ne" Bool -> Bool -> Bool
|| String
op1 String -> String -> Bool
forall a. Eq a => a -> a -> Bool
== "!=")) Bool -> Bool -> Bool
&& Token
lhs1 Token -> Token -> Bool
forall a. Eq a => a -> a -> Bool
== Token
lhs2 Bool -> Bool -> Bool
&& Token
rhs1 Token -> Token -> Bool
forall a. Eq a => a -> a -> Bool
/= Token
rhs2 Bool -> Bool -> Bool
&& Bool -> Bool
not ((Token -> Bool) -> [Token] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any Token -> Bool
isGlob [Token
rhs1,Token
rhs2]) =
Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
warn Id
id 2055 (String -> m ()) -> String -> m ()
forall a b. (a -> b) -> a -> b
$ "You probably wanted " String -> String -> String
forall a. [a] -> [a] -> [a]
++ (if ConditionType
typ ConditionType -> ConditionType -> Bool
forall a. Eq a => a -> a -> Bool
== ConditionType
SingleBracket then "-a" else "&&") String -> String -> String
forall a. [a] -> [a] -> [a]
++ " here, otherwise it's always true."
checkOrNeq _ (TA_Binary id :: Id
id "||" (TA_Binary _ "!=" word1 :: Token
word1 _) (TA_Binary _ "!=" word2 :: Token
word2 _))
| Token
word1 Token -> Token -> Bool
forall a. Eq a => a -> a -> Bool
== Token
word2 =
Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
warn Id
id 2056 "You probably wanted && here, otherwise it's always true."
checkOrNeq _ (T_OrIf id :: Id
id lhs :: Token
lhs rhs :: Token
rhs) = Maybe (m ()) -> m ()
forall (t :: * -> *) (m :: * -> *) a.
(Foldable t, Monad m) =>
t (m a) -> m ()
sequence_ (Maybe (m ()) -> m ()) -> Maybe (m ()) -> m ()
forall a b. (a -> b) -> a -> b
$ do
(lhs1 :: Token
lhs1, op1 :: String
op1, rhs1 :: Token
rhs1) <- Token -> Maybe (Token, String, Token)
getExpr Token
lhs
(lhs2 :: Token
lhs2, op2 :: String
op2, rhs2 :: Token
rhs2) <- Token -> Maybe (Token, String, Token)
getExpr Token
rhs
Bool -> Maybe ()
forall (f :: * -> *). Alternative f => Bool -> f ()
guard (Bool -> Maybe ()) -> Bool -> Maybe ()
forall a b. (a -> b) -> a -> b
$ String
op1 String -> String -> Bool
forall a. Eq a => a -> a -> Bool
== String
op2 Bool -> Bool -> Bool
&& String
op1 String -> [String] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` ["-ne", "!="]
Bool -> Maybe ()
forall (f :: * -> *). Alternative f => Bool -> f ()
guard (Bool -> Maybe ()) -> Bool -> Maybe ()
forall a b. (a -> b) -> a -> b
$ Token
lhs1 Token -> Token -> Bool
forall a. Eq a => a -> a -> Bool
== Token
lhs2 Bool -> Bool -> Bool
&& Token
rhs1 Token -> Token -> Bool
forall a. Eq a => a -> a -> Bool
/= Token
rhs2
Bool -> Maybe ()
forall (f :: * -> *). Alternative f => Bool -> f ()
guard (Bool -> Maybe ()) -> (Bool -> Bool) -> Bool -> Maybe ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Bool -> Bool
not (Bool -> Maybe ()) -> Bool -> Maybe ()
forall a b. (a -> b) -> a -> b
$ (Token -> Bool) -> [Token] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any Token -> Bool
isGlob [Token
rhs1, Token
rhs2]
m () -> Maybe (m ())
forall (m :: * -> *) a. Monad m => a -> m a
return (m () -> Maybe (m ())) -> m () -> Maybe (m ())
forall a b. (a -> b) -> a -> b
$ Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
warn Id
id 2252 "You probably wanted && here, otherwise it's always true."
where
getExpr :: Token -> Maybe (Token, String, Token)
getExpr x :: Token
x =
case Token
x of
T_OrIf _ lhs :: Token
lhs _ -> Token -> Maybe (Token, String, Token)
getExpr Token
lhs
T_Pipeline _ _ [x :: Token
x] -> Token -> Maybe (Token, String, Token)
getExpr Token
x
T_Redirecting _ _ c :: Token
c -> Token -> Maybe (Token, String, Token)
getExpr Token
c
T_Condition _ _ c :: Token
c -> Token -> Maybe (Token, String, Token)
getExpr Token
c
TC_Binary _ _ op :: String
op lhs :: Token
lhs rhs :: Token
rhs -> (Token, String, Token) -> Maybe (Token, String, Token)
forall b. (Token, b, Token) -> Maybe (Token, b, Token)
orient (Token
lhs, String
op, Token
rhs)
_ -> Maybe (Token, String, Token)
forall a. Maybe a
Nothing
orient :: (Token, b, Token) -> Maybe (Token, b, Token)
orient (lhs :: Token
lhs, op :: b
op, rhs :: Token
rhs) =
case (Token -> Bool
isConstant Token
lhs, Token -> Bool
isConstant Token
rhs) of
(True, False) -> (Token, b, Token) -> Maybe (Token, b, Token)
forall (m :: * -> *) a. Monad m => a -> m a
return (Token
rhs, b
op, Token
lhs)
(False, True) -> (Token, b, Token) -> Maybe (Token, b, Token)
forall (m :: * -> *) a. Monad m => a -> m a
return (Token
lhs, b
op, Token
rhs)
_ -> Maybe (Token, b, Token)
forall a. Maybe a
Nothing
checkOrNeq _ _ = () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
prop_checkValidCondOps1 :: Bool
prop_checkValidCondOps1 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkValidCondOps "[[ a -xz b ]]"
prop_checkValidCondOps2 :: Bool
prop_checkValidCondOps2 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkValidCondOps "[ -M a ]"
prop_checkValidCondOps2a :: Bool
prop_checkValidCondOps2a= (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkValidCondOps "[ 3 \\> 2 ]"
prop_checkValidCondOps3 :: Bool
prop_checkValidCondOps3 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkValidCondOps "[ 1 = 2 -a 3 -ge 4 ]"
prop_checkValidCondOps4 :: Bool
prop_checkValidCondOps4 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkValidCondOps "[[ ! -v foo ]]"
checkValidCondOps :: p -> Token -> m ()
checkValidCondOps _ (TC_Binary id :: Id
id _ s :: String
s _ _)
| String
s String -> [String] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`notElem` [String]
binaryTestOps =
Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
warn Id
id 2057 "Unknown binary operator."
checkValidCondOps _ (TC_Unary id :: Id
id _ s :: String
s _)
| String
s String -> [String] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`notElem` [String]
unaryTestOps =
Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
warn Id
id 2058 "Unknown unary operator."
checkValidCondOps _ _ = () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
prop_checkUuoeVar1 :: Bool
prop_checkUuoeVar1 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkUuoeVar "for f in $(echo $tmp); do echo lol; done"
prop_checkUuoeVar2 :: Bool
prop_checkUuoeVar2 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkUuoeVar "date +`echo \"$format\"`"
prop_checkUuoeVar3 :: Bool
prop_checkUuoeVar3 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkUuoeVar "foo \"$(echo -e '\r')\""
prop_checkUuoeVar4 :: Bool
prop_checkUuoeVar4 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkUuoeVar "echo $tmp"
prop_checkUuoeVar5 :: Bool
prop_checkUuoeVar5 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkUuoeVar "foo \"$(echo \"$(date) value:\" $value)\""
prop_checkUuoeVar6 :: Bool
prop_checkUuoeVar6 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkUuoeVar "foo \"$(echo files: *.png)\""
prop_checkUuoeVar7 :: Bool
prop_checkUuoeVar7 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkUuoeVar "foo $(echo $(bar))"
prop_checkUuoeVar8 :: Bool
prop_checkUuoeVar8 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkUuoeVar "#!/bin/sh\nz=$(echo)"
prop_checkUuoeVar9 :: Bool
prop_checkUuoeVar9 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkUuoeVar "foo $(echo $(<file))"
checkUuoeVar :: p -> Token -> m ()
checkUuoeVar _ p :: Token
p =
case Token
p of
T_Backticked id :: Id
id [cmd :: Token
cmd] -> Id -> Token -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Token -> m ()
check Id
id Token
cmd
T_DollarExpansion id :: Id
id [cmd :: Token
cmd] -> Id -> Token -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Token -> m ()
check Id
id Token
cmd
_ -> () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
where
couldBeOptimized :: Token -> Bool
couldBeOptimized f :: Token
f = case Token
f of
T_Glob {} -> Bool
False
T_Extglob {} -> Bool
False
T_BraceExpansion {} -> Bool
False
T_NormalWord _ l :: [Token]
l -> (Token -> Bool) -> [Token] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all Token -> Bool
couldBeOptimized [Token]
l
T_DoubleQuoted _ l :: [Token]
l -> (Token -> Bool) -> [Token] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all Token -> Bool
couldBeOptimized [Token]
l
_ -> Bool
True
check :: Id -> Token -> m ()
check id :: Id
id (T_Pipeline _ _ [T_Redirecting _ _ c :: Token
c]) = Id -> Token -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Token -> m ()
warnForEcho Id
id Token
c
check _ _ = () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
isCovered :: Token -> t a -> Bool
isCovered first :: Token
first rest :: t a
rest = t a -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null t a
rest Bool -> Bool -> Bool
&& Token -> Bool
tokenIsJustCommandOutput Token
first
warnForEcho :: Id -> Token -> m ()
warnForEcho id :: Id
id = String -> (Token -> [Token] -> m ()) -> Token -> m ()
forall (m :: * -> *).
Monad m =>
String -> (Token -> [Token] -> m ()) -> Token -> m ()
checkUnqualifiedCommand "echo" ((Token -> [Token] -> m ()) -> Token -> m ())
-> (Token -> [Token] -> m ()) -> Token -> m ()
forall a b. (a -> b) -> a -> b
$ \_ vars :: [Token]
vars ->
case [Token]
vars of
(first :: Token
first:rest :: [Token]
rest) ->
Bool -> m () -> m ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
unless (Token -> [Token] -> Bool
forall (t :: * -> *) a. Foldable t => Token -> t a -> Bool
isCovered Token
first [Token]
rest Bool -> Bool -> Bool
|| "-" String -> String -> Bool
forall a. Eq a => [a] -> [a] -> Bool
`isPrefixOf` Token -> String
onlyLiteralString Token
first) (m () -> m ()) -> m () -> m ()
forall a b. (a -> b) -> a -> b
$
Bool -> m () -> m ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when ((Token -> Bool) -> [Token] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all Token -> Bool
couldBeOptimized [Token]
vars) (m () -> m ()) -> m () -> m ()
forall a b. (a -> b) -> a -> b
$ Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
style Id
id 2116
"Useless echo? Instead of 'cmd $(echo foo)', just use 'cmd foo'."
_ -> () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
prop_checkTestRedirects1 :: Bool
prop_checkTestRedirects1 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkTestRedirects "test 3 > 1"
prop_checkTestRedirects2 :: Bool
prop_checkTestRedirects2 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkTestRedirects "test 3 \\> 1"
prop_checkTestRedirects3 :: Bool
prop_checkTestRedirects3 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkTestRedirects "/usr/bin/test $var > $foo"
prop_checkTestRedirects4 :: Bool
prop_checkTestRedirects4 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkTestRedirects "test 1 -eq 2 2> file"
checkTestRedirects :: p -> Token -> m ()
checkTestRedirects _ (T_Redirecting id :: Id
id redirs :: [Token]
redirs cmd :: Token
cmd) | Token
cmd Token -> String -> Bool
`isCommand` "test" =
(Token -> m ()) -> [Token] -> m ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ Token -> m ()
forall (m :: * -> *). MonadWriter [TokenComment] m => Token -> m ()
check [Token]
redirs
where
check :: Token -> f ()
check t :: Token
t =
Bool -> f () -> f ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (Token -> Bool
suspicious Token
t) (f () -> f ()) -> f () -> f ()
forall a b. (a -> b) -> a -> b
$
Id -> Code -> String -> f ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
warn (Token -> Id
getId Token
t) 2065 "This is interpreted as a shell file redirection, not a comparison."
suspicious :: Token -> Bool
suspicious t :: Token
t =
case Token
t of
T_FdRedirect _ fd :: String
fd (T_IoFile _ op :: Token
op _) -> String
fd String -> String -> Bool
forall a. Eq a => a -> a -> Bool
/= "2" Bool -> Bool -> Bool
&& Token -> Bool
isComparison Token
op
_ -> Bool
False
isComparison :: Token -> Bool
isComparison t :: Token
t =
case Token
t of
T_Greater _ -> Bool
True
T_Less _ -> Bool
True
_ -> Bool
False
checkTestRedirects _ _ = () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
prop_checkPS11 :: Bool
prop_checkPS11 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkPS1Assignments "PS1='\\033[1;35m\\$ '"
prop_checkPS11a :: Bool
prop_checkPS11a= (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkPS1Assignments "export PS1='\\033[1;35m\\$ '"
prop_checkPSf2 :: Bool
prop_checkPSf2 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkPS1Assignments "PS1='\\h \\e[0m\\$ '"
prop_checkPS13 :: Bool
prop_checkPS13 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkPS1Assignments "PS1=$'\\x1b[c '"
prop_checkPS14 :: Bool
prop_checkPS14 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkPS1Assignments "PS1=$'\\e[3m; '"
prop_checkPS14a :: Bool
prop_checkPS14a= (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkPS1Assignments "export PS1=$'\\e[3m; '"
prop_checkPS15 :: Bool
prop_checkPS15 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkPS1Assignments "PS1='\\[\\033[1;35m\\]\\$ '"
prop_checkPS16 :: Bool
prop_checkPS16 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkPS1Assignments "PS1='\\[\\e1m\\e[1m\\]\\$ '"
prop_checkPS17 :: Bool
prop_checkPS17 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkPS1Assignments "PS1='e033x1B'"
prop_checkPS18 :: Bool
prop_checkPS18 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkPS1Assignments "PS1='\\[\\e\\]'"
checkPS1Assignments :: p -> Token -> f ()
checkPS1Assignments _ (T_Assignment _ _ "PS1" _ word :: Token
word) = Token -> f ()
forall (m :: * -> *). MonadWriter [TokenComment] m => Token -> m ()
warnFor Token
word
where
warnFor :: Token -> f ()
warnFor word :: Token
word =
let contents :: String
contents = [String] -> String
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat ([String] -> String) -> [String] -> String
forall a b. (a -> b) -> a -> b
$ Token -> [String]
oversimplify Token
word in
Bool -> f () -> f ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (String -> Bool
containsUnescaped String
contents) (f () -> f ()) -> f () -> f ()
forall a b. (a -> b) -> a -> b
$
Id -> Code -> String -> f ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
info (Token -> Id
getId Token
word) 2025 "Make sure all escape sequences are enclosed in \\[..\\] to prevent line wrapping issues"
containsUnescaped :: String -> Bool
containsUnescaped s :: String
s =
let unenclosed :: String
unenclosed = Regex -> String -> String -> String
subRegex Regex
enclosedRegex String
s "" in
Maybe [String] -> Bool
forall a. Maybe a -> Bool
isJust (Maybe [String] -> Bool) -> Maybe [String] -> Bool
forall a b. (a -> b) -> a -> b
$ Regex -> String -> Maybe [String]
matchRegex Regex
escapeRegex String
unenclosed
enclosedRegex :: Regex
enclosedRegex = String -> Regex
mkRegex "\\\\\\[.*\\\\\\]"
escapeRegex :: Regex
escapeRegex = String -> Regex
mkRegex "\\\\x1[Bb]|\\\\e|\x1B|\\\\033"
checkPS1Assignments _ _ = () -> f ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
prop_checkBackticks1 :: Bool
prop_checkBackticks1 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkBackticks "echo `foo`"
prop_checkBackticks2 :: Bool
prop_checkBackticks2 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkBackticks "echo $(foo)"
prop_checkBackticks3 :: Bool
prop_checkBackticks3 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkBackticks "echo `#inlined comment` foo"
checkBackticks :: Parameters -> Token -> m ()
checkBackticks params :: Parameters
params (T_Backticked id :: Id
id list :: [Token]
list) | Bool -> Bool
not ([Token] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null [Token]
list) =
TokenComment -> m ()
forall a (m :: * -> *). (NFData a, MonadWriter [a] m) => a -> m ()
addComment (TokenComment -> m ()) -> TokenComment -> m ()
forall a b. (a -> b) -> a -> b
$
Severity -> Id -> Code -> String -> Fix -> TokenComment
makeCommentWithFix Severity
StyleC Id
id 2006 "Use $(...) notation instead of legacy backticked `...`."
([Replacement] -> Fix
fixWith [Id -> Parameters -> Code -> String -> Replacement
replaceStart Id
id Parameters
params 1 "$(", Id -> Parameters -> Code -> String -> Replacement
replaceEnd Id
id Parameters
params 1 ")"])
checkBackticks _ _ = () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
prop_checkIndirectExpansion1 :: Bool
prop_checkIndirectExpansion1 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkIndirectExpansion "${foo$n}"
prop_checkIndirectExpansion2 :: Bool
prop_checkIndirectExpansion2 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkIndirectExpansion "${foo//$n/lol}"
prop_checkIndirectExpansion3 :: Bool
prop_checkIndirectExpansion3 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkIndirectExpansion "${$#}"
prop_checkIndirectExpansion4 :: Bool
prop_checkIndirectExpansion4 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkIndirectExpansion "${var${n}_$((i%2))}"
prop_checkIndirectExpansion5 :: Bool
prop_checkIndirectExpansion5 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkIndirectExpansion "${bar}"
checkIndirectExpansion :: p -> Token -> m ()
checkIndirectExpansion _ (T_DollarBraced i :: Id
i _ (T_NormalWord _ contents :: [Token]
contents))
| [Token] -> Bool
isIndirection [Token]
contents =
Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
err Id
i 2082 "To expand via indirection, use arrays, ${!name} or (for sh only) eval."
where
isIndirection :: [Token] -> Bool
isIndirection vars :: [Token]
vars =
let list :: [Bool]
list = (Token -> Maybe Bool) -> [Token] -> [Bool]
forall a b. (a -> Maybe b) -> [a] -> [b]
mapMaybe Token -> Maybe Bool
isIndirectionPart [Token]
vars in
Bool -> Bool
not ([Bool] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null [Bool]
list) Bool -> Bool -> Bool
&& [Bool] -> Bool
forall (t :: * -> *). Foldable t => t Bool -> Bool
and [Bool]
list
isIndirectionPart :: Token -> Maybe Bool
isIndirectionPart t :: Token
t =
case Token
t of T_DollarExpansion _ _ -> Bool -> Maybe Bool
forall a. a -> Maybe a
Just Bool
True
T_Backticked _ _ -> Bool -> Maybe Bool
forall a. a -> Maybe a
Just Bool
True
T_DollarBraced _ _ _ -> Bool -> Maybe Bool
forall a. a -> Maybe a
Just Bool
True
T_DollarArithmetic _ _ -> Bool -> Maybe Bool
forall a. a -> Maybe a
Just Bool
True
T_Literal _ s :: String
s -> if (Char -> Bool) -> String -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all Char -> Bool
isVariableChar String
s
then Maybe Bool
forall a. Maybe a
Nothing
else Bool -> Maybe Bool
forall a. a -> Maybe a
Just Bool
False
_ -> Bool -> Maybe Bool
forall a. a -> Maybe a
Just Bool
False
checkIndirectExpansion _ _ = () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
prop_checkInexplicablyUnquoted1 :: Bool
prop_checkInexplicablyUnquoted1 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkInexplicablyUnquoted "echo 'var='value';'"
prop_checkInexplicablyUnquoted2 :: Bool
prop_checkInexplicablyUnquoted2 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkInexplicablyUnquoted "'foo'*"
prop_checkInexplicablyUnquoted3 :: Bool
prop_checkInexplicablyUnquoted3 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkInexplicablyUnquoted "wget --user-agent='something'"
prop_checkInexplicablyUnquoted4 :: Bool
prop_checkInexplicablyUnquoted4 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkInexplicablyUnquoted "echo \"VALUES (\"id\")\""
prop_checkInexplicablyUnquoted5 :: Bool
prop_checkInexplicablyUnquoted5 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkInexplicablyUnquoted "\"$dir\"/\"$file\""
prop_checkInexplicablyUnquoted6 :: Bool
prop_checkInexplicablyUnquoted6 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkInexplicablyUnquoted "\"$dir\"some_stuff\"$file\""
prop_checkInexplicablyUnquoted7 :: Bool
prop_checkInexplicablyUnquoted7 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkInexplicablyUnquoted "${dir/\"foo\"/\"bar\"}"
prop_checkInexplicablyUnquoted8 :: Bool
prop_checkInexplicablyUnquoted8 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkInexplicablyUnquoted " 'foo'\\\n 'bar'"
prop_checkInexplicablyUnquoted9 :: Bool
prop_checkInexplicablyUnquoted9 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkInexplicablyUnquoted "[[ $x =~ \"foo\"(\"bar\"|\"baz\") ]]"
checkInexplicablyUnquoted :: Parameters -> Token -> m ()
checkInexplicablyUnquoted params :: Parameters
params (T_NormalWord id :: Id
id tokens :: [Token]
tokens) = ([Token] -> m ()) -> [[Token]] -> m ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ [Token] -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
[Token] -> m ()
check ([Token] -> [[Token]]
forall a. [a] -> [[a]]
tails [Token]
tokens)
where
check :: [Token] -> m ()
check (T_SingleQuoted _ _:T_Literal id :: Id
id str :: String
str:_)
| Bool -> Bool
not (String -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null String
str) Bool -> Bool -> Bool
&& (Char -> Bool) -> String -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all Char -> Bool
isAlphaNum String
str =
Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
info Id
id 2026 "This word is outside of quotes. Did you intend to 'nest '\"'single quotes'\"' instead'? "
check (T_DoubleQuoted _ a :: [Token]
a:trapped :: Token
trapped:T_DoubleQuoted _ b :: [Token]
b:_) =
case Token
trapped of
T_DollarExpansion id :: Id
id _ -> Id -> m ()
forall (m :: * -> *). MonadWriter [TokenComment] m => Id -> m ()
warnAboutExpansion Id
id
T_DollarBraced id :: Id
id _ _ -> Id -> m ()
forall (m :: * -> *). MonadWriter [TokenComment] m => Id -> m ()
warnAboutExpansion Id
id
T_Literal id :: Id
id s :: String
s
| Bool -> Bool
not ([Token] -> Bool
quotesSingleThing [Token]
a Bool -> Bool -> Bool
&& [Token] -> Bool
quotesSingleThing [Token]
b Bool -> Bool -> Bool
|| [Token] -> Bool
isRegex (Map Id Token -> Token -> [Token]
getPath (Parameters -> Map Id Token
parentMap Parameters
params) Token
trapped)) ->
Id -> m ()
forall (m :: * -> *). MonadWriter [TokenComment] m => Id -> m ()
warnAboutLiteral Id
id
_ -> () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
check _ = () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
isRegex :: [Token] -> Bool
isRegex t :: [Token]
t =
case [Token]
t of
(T_Redirecting {} : _) -> Bool
False
(a :: Token
a:(TC_Binary _ _ "=~" lhs :: Token
lhs rhs :: Token
rhs):rest :: [Token]
rest) -> Token -> Id
getId Token
a Id -> Id -> Bool
forall a. Eq a => a -> a -> Bool
== Token -> Id
getId Token
rhs
_:rest :: [Token]
rest -> [Token] -> Bool
isRegex [Token]
rest
_ -> Bool
False
quotesSingleThing :: [Token] -> Bool
quotesSingleThing x :: [Token]
x = case [Token]
x of
[T_DollarExpansion _ _] -> Bool
True
[T_DollarBraced _ _ _] -> Bool
True
[T_Backticked _ _] -> Bool
True
_ -> Bool
False
warnAboutExpansion :: Id -> m ()
warnAboutExpansion id :: Id
id =
Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
warn Id
id 2027 "The surrounding quotes actually unquote this. Remove or escape them."
warnAboutLiteral :: Id -> m ()
warnAboutLiteral id :: Id
id =
Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
warn Id
id 2140 "Word is of the form \"A\"B\"C\" (B indicated). Did you mean \"ABC\" or \"A\\\"B\\\"C\"?"
checkInexplicablyUnquoted _ _ = () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
prop_checkTildeInQuotes1 :: Bool
prop_checkTildeInQuotes1 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkTildeInQuotes "var=\"~/out.txt\""
prop_checkTildeInQuotes2 :: Bool
prop_checkTildeInQuotes2 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkTildeInQuotes "foo > '~/dir'"
prop_checkTildeInQuotes4 :: Bool
prop_checkTildeInQuotes4 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkTildeInQuotes "~/file"
prop_checkTildeInQuotes5 :: Bool
prop_checkTildeInQuotes5 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkTildeInQuotes "echo '/~foo/cow'"
prop_checkTildeInQuotes6 :: Bool
prop_checkTildeInQuotes6 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkTildeInQuotes "awk '$0 ~ /foo/'"
checkTildeInQuotes :: p -> Token -> m ()
checkTildeInQuotes _ = Token -> m ()
forall (m :: * -> *). MonadWriter [TokenComment] m => Token -> m ()
check
where
verify :: Id -> String -> m ()
verify id :: Id
id ('~':'/':_) = Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
warn Id
id 2088 "Tilde does not expand in quotes. Use $HOME."
verify _ _ = () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
check :: Token -> m ()
check (T_NormalWord _ (T_SingleQuoted id :: Id
id str :: String
str:_)) =
Id -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> String -> m ()
verify Id
id String
str
check (T_NormalWord _ (T_DoubleQuoted _ (T_Literal id :: Id
id str :: String
str:_):_)) =
Id -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> String -> m ()
verify Id
id String
str
check _ = () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
prop_checkLonelyDotDash1 :: Bool
prop_checkLonelyDotDash1 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkLonelyDotDash "./ file"
prop_checkLonelyDotDash2 :: Bool
prop_checkLonelyDotDash2 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkLonelyDotDash "./file"
checkLonelyDotDash :: p -> Token -> m ()
checkLonelyDotDash _ t :: Token
t@(T_Redirecting id :: Id
id _ _)
| Token -> String -> Bool
isUnqualifiedCommand Token
t "./" =
Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
err Id
id 2083 "Don't add spaces after the slash in './file'."
checkLonelyDotDash _ _ = () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
prop_checkSpuriousExec1 :: Bool
prop_checkSpuriousExec1 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkSpuriousExec "exec foo; true"
prop_checkSpuriousExec2 :: Bool
prop_checkSpuriousExec2 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkSpuriousExec "if a; then exec b; exec c; fi"
prop_checkSpuriousExec3 :: Bool
prop_checkSpuriousExec3 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkSpuriousExec "echo cow; exec foo"
prop_checkSpuriousExec4 :: Bool
prop_checkSpuriousExec4 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkSpuriousExec "if a; then exec b; fi"
prop_checkSpuriousExec5 :: Bool
prop_checkSpuriousExec5 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkSpuriousExec "exec > file; cmd"
prop_checkSpuriousExec6 :: Bool
prop_checkSpuriousExec6 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkSpuriousExec "exec foo > file; cmd"
prop_checkSpuriousExec7 :: Bool
prop_checkSpuriousExec7 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkSpuriousExec "exec file; echo failed; exit 3"
prop_checkSpuriousExec8 :: Bool
prop_checkSpuriousExec8 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkSpuriousExec "exec {origout}>&1- >tmp.log 2>&1; bar"
prop_checkSpuriousExec9 :: Bool
prop_checkSpuriousExec9 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkSpuriousExec "for file in rc.d/*; do exec \"$file\"; done"
checkSpuriousExec :: p -> Token -> m ()
checkSpuriousExec _ = Token -> m ()
doLists
where
doLists :: Token -> m ()
doLists (T_Script _ _ cmds :: [Token]
cmds) = [Token] -> Bool -> m ()
doList [Token]
cmds Bool
False
doLists (T_BraceGroup _ cmds :: [Token]
cmds) = [Token] -> Bool -> m ()
doList [Token]
cmds Bool
False
doLists (T_WhileExpression _ _ cmds :: [Token]
cmds) = [Token] -> Bool -> m ()
doList [Token]
cmds Bool
True
doLists (T_UntilExpression _ _ cmds :: [Token]
cmds) = [Token] -> Bool -> m ()
doList [Token]
cmds Bool
True
doLists (T_ForIn _ _ _ cmds :: [Token]
cmds) = [Token] -> Bool -> m ()
doList [Token]
cmds Bool
True
doLists (T_ForArithmetic _ _ _ _ cmds :: [Token]
cmds) = [Token] -> Bool -> m ()
doList [Token]
cmds Bool
True
doLists (T_IfExpression _ thens :: [([Token], [Token])]
thens elses :: [Token]
elses) = do
(([Token], [Token]) -> m ()) -> [([Token], [Token])] -> m ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ (\(_, l :: [Token]
l) -> [Token] -> Bool -> m ()
doList [Token]
l Bool
False) [([Token], [Token])]
thens
[Token] -> Bool -> m ()
doList [Token]
elses Bool
False
doLists _ = () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
stripCleanup :: [Token] -> [Token]
stripCleanup = [Token] -> [Token]
forall a. [a] -> [a]
reverse ([Token] -> [Token]) -> ([Token] -> [Token]) -> [Token] -> [Token]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Token -> Bool) -> [Token] -> [Token]
forall a. (a -> Bool) -> [a] -> [a]
dropWhile Token -> Bool
cleanup ([Token] -> [Token]) -> ([Token] -> [Token]) -> [Token] -> [Token]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Token] -> [Token]
forall a. [a] -> [a]
reverse
cleanup :: Token -> Bool
cleanup (T_Pipeline _ _ [cmd :: Token
cmd]) =
Token -> (String -> Bool) -> Bool
isCommandMatch Token
cmd (String -> [String] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` ["echo", "exit"])
cleanup _ = Bool
False
doList :: [Token] -> Bool -> m ()
doList = [Token] -> Bool -> m ()
doList' ([Token] -> Bool -> m ())
-> ([Token] -> [Token]) -> [Token] -> Bool -> m ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Token] -> [Token]
stripCleanup
doList' :: [Token] -> Bool -> m ()
doList' (current :: Token
current:t :: [Token]
t@(following :: Token
following:_)) False = do
Token -> m ()
forall (m :: * -> *). MonadWriter [TokenComment] m => Token -> m ()
commentIfExec Token
current
[Token] -> Bool -> m ()
doList [Token]
t Bool
False
doList' (current :: Token
current:tail :: [Token]
tail) True = do
Token -> m ()
forall (m :: * -> *). MonadWriter [TokenComment] m => Token -> m ()
commentIfExec Token
current
[Token] -> Bool -> m ()
doList [Token]
tail Bool
True
doList' _ _ = () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
commentIfExec :: Token -> m ()
commentIfExec (T_Pipeline id :: Id
id _ [c :: Token
c]) = Token -> m ()
commentIfExec Token
c
commentIfExec (T_Redirecting _ _ (T_SimpleCommand id :: Id
id _ (cmd :: Token
cmd:additionalArg :: Token
additionalArg:_))) |
Token -> Maybe String
getLiteralString Token
cmd Maybe String -> Maybe String -> Bool
forall a. Eq a => a -> a -> Bool
== String -> Maybe String
forall a. a -> Maybe a
Just "exec" =
Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
warn Id
id 2093 "Remove \"exec \" if script should continue after this command."
commentIfExec _ = () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
prop_checkSpuriousExpansion1 :: Bool
prop_checkSpuriousExpansion1 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkSpuriousExpansion "if $(true); then true; fi"
prop_checkSpuriousExpansion3 :: Bool
prop_checkSpuriousExpansion3 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkSpuriousExpansion "$(cmd) --flag1 --flag2"
prop_checkSpuriousExpansion4 :: Bool
prop_checkSpuriousExpansion4 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkSpuriousExpansion "$((i++))"
checkSpuriousExpansion :: p -> Token -> m ()
checkSpuriousExpansion _ (T_SimpleCommand _ _ [T_NormalWord _ [word :: Token
word]]) = Token -> m ()
forall (m :: * -> *). MonadWriter [TokenComment] m => Token -> m ()
check Token
word
where
check :: Token -> m ()
check word :: Token
word = case Token
word of
T_DollarExpansion id :: Id
id _ ->
Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
warn Id
id 2091 "Remove surrounding $() to avoid executing output (or use eval if intentional)."
T_Backticked id :: Id
id _ ->
Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
warn Id
id 2092 "Remove backticks to avoid executing output (or use eval if intentional)."
T_DollarArithmetic id :: Id
id _ ->
Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
err Id
id 2084 "Remove '$' or use '_=$((expr))' to avoid executing output."
_ -> () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
checkSpuriousExpansion _ _ = () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
prop_checkDollarBrackets1 :: Bool
prop_checkDollarBrackets1 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkDollarBrackets "echo $[1+2]"
prop_checkDollarBrackets2 :: Bool
prop_checkDollarBrackets2 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkDollarBrackets "echo $((1+2))"
checkDollarBrackets :: p -> Token -> m ()
checkDollarBrackets _ (T_DollarBracket id :: Id
id _) =
Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
style Id
id 2007 "Use $((..)) instead of deprecated $[..]"
checkDollarBrackets _ _ = () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
prop_checkSshHereDoc1 :: Bool
prop_checkSshHereDoc1 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkSshHereDoc "ssh host << foo\necho $PATH\nfoo"
prop_checkSshHereDoc2 :: Bool
prop_checkSshHereDoc2 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkSshHereDoc "ssh host << 'foo'\necho $PATH\nfoo"
checkSshHereDoc :: p -> Token -> m ()
checkSshHereDoc _ (T_Redirecting _ redirs :: [Token]
redirs cmd :: Token
cmd)
| Token
cmd Token -> String -> Bool
`isCommand` "ssh" =
(Token -> m ()) -> [Token] -> m ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ Token -> m ()
forall (m :: * -> *). MonadWriter [TokenComment] m => Token -> m ()
checkHereDoc [Token]
redirs
where
hasVariables :: Regex
hasVariables = String -> Regex
mkRegex "[`$]"
checkHereDoc :: Token -> m ()
checkHereDoc (T_FdRedirect _ _ (T_HereDoc id :: Id
id _ Unquoted token :: String
token tokens :: [Token]
tokens))
| Bool -> Bool
not ((Token -> Bool) -> [Token] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all Token -> Bool
isConstant [Token]
tokens) =
Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
warn Id
id 2087 (String -> m ()) -> String -> m ()
forall a b. (a -> b) -> a -> b
$ "Quote '" String -> String -> String
forall a. [a] -> [a] -> [a]
++ (String -> String
e4m String
token) String -> String -> String
forall a. [a] -> [a] -> [a]
++ "' to make here document expansions happen on the server side rather than on the client."
checkHereDoc _ = () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
checkSshHereDoc _ _ = () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
prop_subshellAssignmentCheck :: Bool
prop_subshellAssignmentCheck = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
subshellAssignmentCheck "cat foo | while read bar; do a=$bar; done; echo \"$a\""
prop_subshellAssignmentCheck2 :: Bool
prop_subshellAssignmentCheck2 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
subshellAssignmentCheck "while read bar; do a=$bar; done < file; echo \"$a\""
prop_subshellAssignmentCheck3 :: Bool
prop_subshellAssignmentCheck3 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
subshellAssignmentCheck "( A=foo; ); rm $A"
prop_subshellAssignmentCheck4 :: Bool
prop_subshellAssignmentCheck4 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
subshellAssignmentCheck "( A=foo; rm $A; )"
prop_subshellAssignmentCheck5 :: Bool
prop_subshellAssignmentCheck5 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
subshellAssignmentCheck "cat foo | while read cow; do true; done; echo $cow;"
prop_subshellAssignmentCheck6 :: Bool
prop_subshellAssignmentCheck6 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
subshellAssignmentCheck "( export lol=$(ls); ); echo $lol;"
prop_subshellAssignmentCheck6a :: Bool
prop_subshellAssignmentCheck6a= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
subshellAssignmentCheck "( typeset -a lol=a; ); echo $lol;"
prop_subshellAssignmentCheck7 :: Bool
prop_subshellAssignmentCheck7 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
subshellAssignmentCheck "cmd | while read foo; do (( n++ )); done; echo \"$n lines\""
prop_subshellAssignmentCheck8 :: Bool
prop_subshellAssignmentCheck8 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
subshellAssignmentCheck "n=3 & echo $((n++))"
prop_subshellAssignmentCheck9 :: Bool
prop_subshellAssignmentCheck9 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
subshellAssignmentCheck "read n & n=foo$n"
prop_subshellAssignmentCheck10 :: Bool
prop_subshellAssignmentCheck10 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
subshellAssignmentCheck "(( n <<= 3 )) & (( n |= 4 )) &"
prop_subshellAssignmentCheck11 :: Bool
prop_subshellAssignmentCheck11 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
subshellAssignmentCheck "cat /etc/passwd | while read line; do let n=n+1; done\necho $n"
prop_subshellAssignmentCheck12 :: Bool
prop_subshellAssignmentCheck12 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
subshellAssignmentCheck "cat /etc/passwd | while read line; do let ++n; done\necho $n"
prop_subshellAssignmentCheck13 :: Bool
prop_subshellAssignmentCheck13 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
subshellAssignmentCheck "#!/bin/bash\necho foo | read bar; echo $bar"
prop_subshellAssignmentCheck14 :: Bool
prop_subshellAssignmentCheck14 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
subshellAssignmentCheck "#!/bin/ksh93\necho foo | read bar; echo $bar"
prop_subshellAssignmentCheck15 :: Bool
prop_subshellAssignmentCheck15 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
subshellAssignmentCheck "#!/bin/ksh\ncat foo | while read bar; do a=$bar; done\necho \"$a\""
prop_subshellAssignmentCheck16 :: Bool
prop_subshellAssignmentCheck16 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
subshellAssignmentCheck "(set -e); echo $@"
prop_subshellAssignmentCheck17 :: Bool
prop_subshellAssignmentCheck17 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
subshellAssignmentCheck "foo=${ { bar=$(baz); } 2>&1; }; echo $foo $bar"
prop_subshellAssignmentCheck18 :: Bool
prop_subshellAssignmentCheck18 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
subshellAssignmentCheck "( exec {n}>&2; ); echo $n"
prop_subshellAssignmentCheck19 :: Bool
prop_subshellAssignmentCheck19 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
subshellAssignmentCheck "#!/bin/bash\nshopt -s lastpipe; echo a | read -r b; echo \"$b\""
prop_subshellAssignmentCheck20 :: Bool
prop_subshellAssignmentCheck20 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
subshellAssignmentCheck "@test 'foo' { a=1; }\n@test 'bar' { echo $a; }\n"
subshellAssignmentCheck :: Parameters -> p -> [TokenComment]
subshellAssignmentCheck params :: Parameters
params t :: p
t =
let flow :: [StackData]
flow = Parameters -> [StackData]
variableFlow Parameters
params
check :: WriterT [TokenComment] Identity ()
check = [StackData]
-> [(String, [(Token, Token, String, DataType)])]
-> Map String VariableState
-> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
[StackData]
-> [(String, [(Token, Token, String, DataType)])]
-> Map String VariableState
-> m ()
findSubshelled [StackData]
flow [("oops",[])] Map String VariableState
forall k a. Map k a
Map.empty
in WriterT [TokenComment] Identity () -> [TokenComment]
forall w a. Writer w a -> w
execWriter WriterT [TokenComment] Identity ()
check
findSubshelled :: [StackData]
-> [(String, [(Token, Token, String, DataType)])]
-> Map String VariableState
-> m ()
findSubshelled [] _ _ = () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
findSubshelled (Assignment x :: (Token, Token, String, DataType)
x@(_, _, str :: String
str, _):rest :: [StackData]
rest) ((reason :: String
reason,scope :: [(Token, Token, String, DataType)]
scope):lol :: [(String, [(Token, Token, String, DataType)])]
lol) deadVars :: Map String VariableState
deadVars =
[StackData]
-> [(String, [(Token, Token, String, DataType)])]
-> Map String VariableState
-> m ()
findSubshelled [StackData]
rest ((String
reason, (Token, Token, String, DataType)
x(Token, Token, String, DataType)
-> [(Token, Token, String, DataType)]
-> [(Token, Token, String, DataType)]
forall a. a -> [a] -> [a]
:[(Token, Token, String, DataType)]
scope)(String, [(Token, Token, String, DataType)])
-> [(String, [(Token, Token, String, DataType)])]
-> [(String, [(Token, Token, String, DataType)])]
forall a. a -> [a] -> [a]
:[(String, [(Token, Token, String, DataType)])]
lol) (Map String VariableState -> m ())
-> Map String VariableState -> m ()
forall a b. (a -> b) -> a -> b
$ String
-> VariableState
-> Map String VariableState
-> Map String VariableState
forall k a. Ord k => k -> a -> Map k a -> Map k a
Map.insert String
str VariableState
Alive Map String VariableState
deadVars
findSubshelled (Reference (_, readToken :: Token
readToken, str :: String
str):rest :: [StackData]
rest) scopes :: [(String, [(Token, Token, String, DataType)])]
scopes deadVars :: Map String VariableState
deadVars = do
Bool -> m () -> m ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
unless (String -> Bool
shouldIgnore String
str) (m () -> m ()) -> m () -> m ()
forall a b. (a -> b) -> a -> b
$ case VariableState
-> String -> Map String VariableState -> VariableState
forall k a. Ord k => a -> k -> Map k a -> a
Map.findWithDefault VariableState
Alive String
str Map String VariableState
deadVars of
Alive -> () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
Dead writeToken :: Token
writeToken reason :: String
reason -> do
Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
info (Token -> Id
getId Token
writeToken) 2030 (String -> m ()) -> String -> m ()
forall a b. (a -> b) -> a -> b
$ "Modification of " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
str String -> String -> String
forall a. [a] -> [a] -> [a]
++ " is local (to subshell caused by "String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
reason String -> String -> String
forall a. [a] -> [a] -> [a]
++")."
Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
info (Token -> Id
getId Token
readToken) 2031 (String -> m ()) -> String -> m ()
forall a b. (a -> b) -> a -> b
$ String
str String -> String -> String
forall a. [a] -> [a] -> [a]
++ " was modified in a subshell. That change might be lost."
[StackData]
-> [(String, [(Token, Token, String, DataType)])]
-> Map String VariableState
-> m ()
findSubshelled [StackData]
rest [(String, [(Token, Token, String, DataType)])]
scopes Map String VariableState
deadVars
where
shouldIgnore :: String -> Bool
shouldIgnore str :: String
str =
String
str String -> [String] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` ["@", "*", "IFS"]
findSubshelled (StackScope (SubshellScope reason :: String
reason):rest :: [StackData]
rest) scopes :: [(String, [(Token, Token, String, DataType)])]
scopes deadVars :: Map String VariableState
deadVars =
[StackData]
-> [(String, [(Token, Token, String, DataType)])]
-> Map String VariableState
-> m ()
findSubshelled [StackData]
rest ((String
reason,[])(String, [(Token, Token, String, DataType)])
-> [(String, [(Token, Token, String, DataType)])]
-> [(String, [(Token, Token, String, DataType)])]
forall a. a -> [a] -> [a]
:[(String, [(Token, Token, String, DataType)])]
scopes) Map String VariableState
deadVars
findSubshelled (StackScopeEnd:rest :: [StackData]
rest) ((reason :: String
reason, scope :: [(Token, Token, String, DataType)]
scope):oldScopes :: [(String, [(Token, Token, String, DataType)])]
oldScopes) deadVars :: Map String VariableState
deadVars =
[StackData]
-> [(String, [(Token, Token, String, DataType)])]
-> Map String VariableState
-> m ()
findSubshelled [StackData]
rest [(String, [(Token, Token, String, DataType)])]
oldScopes (Map String VariableState -> m ())
-> Map String VariableState -> m ()
forall a b. (a -> b) -> a -> b
$
(Map String VariableState
-> (Token, Token, String, DataType) -> Map String VariableState)
-> Map String VariableState
-> [(Token, Token, String, DataType)]
-> Map String VariableState
forall (t :: * -> *) b a.
Foldable t =>
(b -> a -> b) -> b -> t a -> b
foldl (\m :: Map String VariableState
m (_, token :: Token
token, var :: String
var, _) ->
String
-> VariableState
-> Map String VariableState
-> Map String VariableState
forall k a. Ord k => k -> a -> Map k a -> Map k a
Map.insert String
var (Token -> String -> VariableState
Dead Token
token String
reason) Map String VariableState
m) Map String VariableState
deadVars [(Token, Token, String, DataType)]
scope
doVariableFlowAnalysis ::
(Token -> Token -> String -> State t [v])
-> (Token -> Token -> String -> DataType -> State t [v])
-> t
-> [StackData]
-> [v]
doVariableFlowAnalysis :: (Token -> Token -> String -> State t [v])
-> (Token -> Token -> String -> DataType -> State t [v])
-> t
-> [StackData]
-> [v]
doVariableFlowAnalysis readFunc :: Token -> Token -> String -> State t [v]
readFunc writeFunc :: Token -> Token -> String -> DataType -> State t [v]
writeFunc empty :: t
empty flow :: [StackData]
flow = State t [v] -> t -> [v]
forall s a. State s a -> s -> a
evalState (
([v] -> StackData -> State t [v])
-> [v] -> [StackData] -> State t [v]
forall (t :: * -> *) (m :: * -> *) b a.
(Foldable t, Monad m) =>
(b -> a -> m b) -> b -> t a -> m b
foldM (\list :: [v]
list x :: StackData
x -> do { [v]
l <- StackData -> State t [v]
doFlow StackData
x; [v] -> State t [v]
forall (m :: * -> *) a. Monad m => a -> m a
return ([v] -> State t [v]) -> [v] -> State t [v]
forall a b. (a -> b) -> a -> b
$ [v]
l [v] -> [v] -> [v]
forall a. [a] -> [a] -> [a]
++ [v]
list; }) [] [StackData]
flow
) t
empty
where
doFlow :: StackData -> State t [v]
doFlow (Reference (base :: Token
base, token :: Token
token, name :: String
name)) =
Token -> Token -> String -> State t [v]
readFunc Token
base Token
token String
name
doFlow (Assignment (base :: Token
base, token :: Token
token, name :: String
name, values :: DataType
values)) =
Token -> Token -> String -> DataType -> State t [v]
writeFunc Token
base Token
token String
name DataType
values
doFlow _ = [v] -> State t [v]
forall (m :: * -> *) a. Monad m => a -> m a
return []
prop_checkSpacefulness1 :: Bool
prop_checkSpacefulness1 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyTree Parameters -> Token -> [TokenComment]
checkSpacefulness "a='cow moo'; echo $a"
prop_checkSpacefulness2 :: Bool
prop_checkSpacefulness2 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
checkSpacefulness "a='cow moo'; [[ $a ]]"
prop_checkSpacefulness3 :: Bool
prop_checkSpacefulness3 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
checkSpacefulness "a='cow*.mp3'; echo \"$a\""
prop_checkSpacefulness4 :: Bool
prop_checkSpacefulness4 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyTree Parameters -> Token -> [TokenComment]
checkSpacefulness "for f in *.mp3; do echo $f; done"
prop_checkSpacefulness4a :: Bool
prop_checkSpacefulness4a= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
checkSpacefulness "foo=3; foo=$(echo $foo)"
prop_checkSpacefulness5 :: Bool
prop_checkSpacefulness5 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyTree Parameters -> Token -> [TokenComment]
checkSpacefulness "a='*'; b=$a; c=lol${b//foo/bar}; echo $c"
prop_checkSpacefulness6 :: Bool
prop_checkSpacefulness6 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyTree Parameters -> Token -> [TokenComment]
checkSpacefulness "a=foo$(lol); echo $a"
prop_checkSpacefulness7 :: Bool
prop_checkSpacefulness7 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyTree Parameters -> Token -> [TokenComment]
checkSpacefulness "a=foo\\ bar; rm $a"
prop_checkSpacefulness8 :: Bool
prop_checkSpacefulness8 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
checkSpacefulness "a=foo\\ bar; a=foo; rm $a"
prop_checkSpacefulness10 :: Bool
prop_checkSpacefulness10= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyTree Parameters -> Token -> [TokenComment]
checkSpacefulness "rm $1"
prop_checkSpacefulness11 :: Bool
prop_checkSpacefulness11= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyTree Parameters -> Token -> [TokenComment]
checkSpacefulness "rm ${10//foo/bar}"
prop_checkSpacefulness12 :: Bool
prop_checkSpacefulness12= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
checkSpacefulness "(( $1 + 3 ))"
prop_checkSpacefulness13 :: Bool
prop_checkSpacefulness13= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
checkSpacefulness "if [[ $2 -gt 14 ]]; then true; fi"
prop_checkSpacefulness14 :: Bool
prop_checkSpacefulness14= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
checkSpacefulness "foo=$3 env"
prop_checkSpacefulness15 :: Bool
prop_checkSpacefulness15= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
checkSpacefulness "local foo=$1"
prop_checkSpacefulness16 :: Bool
prop_checkSpacefulness16= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
checkSpacefulness "declare foo=$1"
prop_checkSpacefulness17 :: Bool
prop_checkSpacefulness17= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyTree Parameters -> Token -> [TokenComment]
checkSpacefulness "echo foo=$1"
prop_checkSpacefulness18 :: Bool
prop_checkSpacefulness18= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
checkSpacefulness "$1 --flags"
prop_checkSpacefulness19 :: Bool
prop_checkSpacefulness19= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyTree Parameters -> Token -> [TokenComment]
checkSpacefulness "echo $PWD"
prop_checkSpacefulness20 :: Bool
prop_checkSpacefulness20= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
checkSpacefulness "n+='foo bar'"
prop_checkSpacefulness21 :: Bool
prop_checkSpacefulness21= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
checkSpacefulness "select foo in $bar; do true; done"
prop_checkSpacefulness22 :: Bool
prop_checkSpacefulness22= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
checkSpacefulness "echo $\"$1\""
prop_checkSpacefulness23 :: Bool
prop_checkSpacefulness23= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
checkSpacefulness "a=(1); echo ${a[@]}"
prop_checkSpacefulness24 :: Bool
prop_checkSpacefulness24= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyTree Parameters -> Token -> [TokenComment]
checkSpacefulness "a='a b'; cat <<< $a"
prop_checkSpacefulness25 :: Bool
prop_checkSpacefulness25= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyTree Parameters -> Token -> [TokenComment]
checkSpacefulness "a='s/[0-9]//g'; sed $a"
prop_checkSpacefulness26 :: Bool
prop_checkSpacefulness26= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyTree Parameters -> Token -> [TokenComment]
checkSpacefulness "a='foo bar'; echo {1,2,$a}"
prop_checkSpacefulness27 :: Bool
prop_checkSpacefulness27= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
checkSpacefulness "echo ${a:+'foo'}"
prop_checkSpacefulness28 :: Bool
prop_checkSpacefulness28= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
checkSpacefulness "exec {n}>&1; echo $n"
prop_checkSpacefulness29 :: Bool
prop_checkSpacefulness29= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
checkSpacefulness "n=$(stuff); exec {n}>&-;"
prop_checkSpacefulness30 :: Bool
prop_checkSpacefulness30= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyTree Parameters -> Token -> [TokenComment]
checkSpacefulness "file='foo bar'; echo foo > $file;"
prop_checkSpacefulness31 :: Bool
prop_checkSpacefulness31= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
checkSpacefulness "echo \"`echo \\\"$1\\\"`\""
prop_checkSpacefulness32 :: Bool
prop_checkSpacefulness32= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
checkSpacefulness "var=$1; [ -v var ]"
prop_checkSpacefulness33 :: Bool
prop_checkSpacefulness33= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyTree Parameters -> Token -> [TokenComment]
checkSpacefulness "for file; do echo $file; done"
prop_checkSpacefulness34 :: Bool
prop_checkSpacefulness34= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyTree Parameters -> Token -> [TokenComment]
checkSpacefulness "declare foo$n=$1"
prop_checkSpacefulness35 :: Bool
prop_checkSpacefulness35= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
checkSpacefulness "echo ${1+\"$1\"}"
prop_checkSpacefulness36 :: Bool
prop_checkSpacefulness36= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
checkSpacefulness "arg=$#; echo $arg"
prop_checkSpacefulness37 :: Bool
prop_checkSpacefulness37= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
checkSpacefulness "@test 'status' {\n [ $status -eq 0 ]\n}"
prop_checkSpacefulness37v :: Bool
prop_checkSpacefulness37v = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyTree Parameters -> Token -> [TokenComment]
checkVerboseSpacefulness "@test 'status' {\n [ $status -eq 0 ]\n}"
prop_checkSpacefulness38 :: Bool
prop_checkSpacefulness38= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyTree Parameters -> Token -> [TokenComment]
checkSpacefulness "a=; echo $a"
prop_checkSpacefulness39 :: Bool
prop_checkSpacefulness39= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
checkSpacefulness "a=''\"\"''; b=x$a; echo $b"
prop_checkSpacefulness40 :: Bool
prop_checkSpacefulness40= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
checkSpacefulness "a=$((x+1)); echo $a"
prop_checkSpacefulness41 :: Bool
prop_checkSpacefulness41= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
checkSpacefulness "exec $1 --flags"
prop_checkSpacefulness42 :: Bool
prop_checkSpacefulness42= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
checkSpacefulness "run $1 --flags"
prop_checkSpacefulness43 :: Bool
prop_checkSpacefulness43= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
checkSpacefulness "$foo=42"
data SpaceStatus = SpaceSome | SpaceNone | SpaceEmpty deriving (SpaceStatus -> SpaceStatus -> Bool
(SpaceStatus -> SpaceStatus -> Bool)
-> (SpaceStatus -> SpaceStatus -> Bool) -> Eq SpaceStatus
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: SpaceStatus -> SpaceStatus -> Bool
$c/= :: SpaceStatus -> SpaceStatus -> Bool
== :: SpaceStatus -> SpaceStatus -> Bool
$c== :: SpaceStatus -> SpaceStatus -> Bool
Eq)
instance Semigroup SpaceStatus where
SpaceNone <> :: SpaceStatus -> SpaceStatus -> SpaceStatus
<> SpaceNone = SpaceStatus
SpaceNone
SpaceSome <> _ = SpaceStatus
SpaceSome
_ <> SpaceSome = SpaceStatus
SpaceSome
SpaceEmpty <> x :: SpaceStatus
x = SpaceStatus
x
x :: SpaceStatus
x <> SpaceEmpty = SpaceStatus
x
instance Monoid SpaceStatus where
mempty :: SpaceStatus
mempty = SpaceStatus
SpaceEmpty
mappend :: SpaceStatus -> SpaceStatus -> SpaceStatus
mappend = SpaceStatus -> SpaceStatus -> SpaceStatus
forall a. Semigroup a => a -> a -> a
(<>)
checkSpacefulness :: Parameters -> Token -> [TokenComment]
checkSpacefulness params :: Parameters
params = (SpaceStatus
-> Token -> String -> WriterT [TokenComment] Identity ())
-> Parameters -> Token -> [TokenComment]
checkSpacefulness' SpaceStatus
-> Token -> String -> WriterT [TokenComment] Identity ()
forall (f :: * -> *) p.
MonadWriter [TokenComment] f =>
SpaceStatus -> Token -> p -> f ()
onFind Parameters
params
where
emit :: a -> m ()
emit x :: a
x = [a] -> m ()
forall w (m :: * -> *). MonadWriter w m => w -> m ()
tell [a
x]
onFind :: SpaceStatus -> Token -> p -> f ()
onFind spaces :: SpaceStatus
spaces token :: Token
token _ =
Bool -> f () -> f ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (SpaceStatus
spaces SpaceStatus -> SpaceStatus -> Bool
forall a. Eq a => a -> a -> Bool
/= SpaceStatus
SpaceNone) (f () -> f ()) -> f () -> f ()
forall a b. (a -> b) -> a -> b
$
if Map Id Token -> Token -> Bool
isDefaultAssignment (Parameters -> Map Id Token
parentMap Parameters
params) Token
token
then
TokenComment -> f ()
forall a (m :: * -> *). MonadWriter [a] m => a -> m ()
emit (TokenComment -> f ()) -> TokenComment -> f ()
forall a b. (a -> b) -> a -> b
$ Severity -> Id -> Code -> String -> TokenComment
makeComment Severity
InfoC (Token -> Id
getId Token
token) 2223
"This default assignment may cause DoS due to globbing. Quote it."
else
Bool -> f () -> f ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
unless (Parameters -> Token -> Bool
quotesMayConflictWithSC2281 Parameters
params Token
token) (f () -> f ()) -> f () -> f ()
forall a b. (a -> b) -> a -> b
$
TokenComment -> f ()
forall a (m :: * -> *). MonadWriter [a] m => a -> m ()
emit (TokenComment -> f ()) -> TokenComment -> f ()
forall a b. (a -> b) -> a -> b
$ Severity -> Id -> Code -> String -> Fix -> TokenComment
makeCommentWithFix Severity
InfoC (Token -> Id
getId Token
token) 2086
"Double quote to prevent globbing and word splitting."
(Parameters -> Token -> Fix
addDoubleQuotesAround Parameters
params Token
token)
isDefaultAssignment :: Map Id Token -> Token -> Bool
isDefaultAssignment parents :: Map Id Token
parents token :: Token
token =
let modifier :: String
modifier = String -> String
getBracedModifier (String -> String) -> String -> String
forall a b. (a -> b) -> a -> b
$ Token -> String
bracedString Token
token in
(String -> Bool) -> [String] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any (String -> String -> Bool
forall a. Eq a => [a] -> [a] -> Bool
`isPrefixOf` String
modifier) ["=", ":="]
Bool -> Bool -> Bool
&& Map Id Token -> String -> Token -> Bool
isParamTo Map Id Token
parents ":" Token
token
bracedString :: Token -> String
bracedString (T_DollarBraced _ _ l :: Token
l) = [String] -> String
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat ([String] -> String) -> [String] -> String
forall a b. (a -> b) -> a -> b
$ Token -> [String]
oversimplify Token
l
bracedString _ = String -> String
forall a. HasCallStack => String -> a
error "Internal shellcheck error, please report! (bracedString on non-variable)"
prop_checkSpacefulness4v :: Bool
prop_checkSpacefulness4v= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyTree Parameters -> Token -> [TokenComment]
checkVerboseSpacefulness "foo=3; foo=$(echo $foo)"
prop_checkSpacefulness8v :: Bool
prop_checkSpacefulness8v= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyTree Parameters -> Token -> [TokenComment]
checkVerboseSpacefulness "a=foo\\ bar; a=foo; rm $a"
prop_checkSpacefulness28v :: Bool
prop_checkSpacefulness28v = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyTree Parameters -> Token -> [TokenComment]
checkVerboseSpacefulness "exec {n}>&1; echo $n"
prop_checkSpacefulness36v :: Bool
prop_checkSpacefulness36v = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyTree Parameters -> Token -> [TokenComment]
checkVerboseSpacefulness "arg=$#; echo $arg"
prop_checkSpacefulness44v :: Bool
prop_checkSpacefulness44v = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
checkVerboseSpacefulness "foo=3; $foo=4"
checkVerboseSpacefulness :: Parameters -> Token -> [TokenComment]
checkVerboseSpacefulness params :: Parameters
params = (SpaceStatus
-> Token -> String -> WriterT [TokenComment] Identity ())
-> Parameters -> Token -> [TokenComment]
checkSpacefulness' SpaceStatus
-> Token -> String -> WriterT [TokenComment] Identity ()
forall (f :: * -> *).
MonadWriter [TokenComment] f =>
SpaceStatus -> Token -> String -> f ()
onFind Parameters
params
where
onFind :: SpaceStatus -> Token -> String -> f ()
onFind spaces :: SpaceStatus
spaces token :: Token
token name :: String
name =
Bool -> f () -> f ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (SpaceStatus
spaces SpaceStatus -> SpaceStatus -> Bool
forall a. Eq a => a -> a -> Bool
== SpaceStatus
SpaceNone
Bool -> Bool -> Bool
&& String
name String -> [String] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`notElem` [String]
specialVariablesWithoutSpaces
Bool -> Bool -> Bool
&& Bool -> Bool
not (Parameters -> Token -> Bool
quotesMayConflictWithSC2281 Parameters
params Token
token)) (f () -> f ()) -> f () -> f ()
forall a b. (a -> b) -> a -> b
$
[TokenComment] -> f ()
forall w (m :: * -> *). MonadWriter w m => w -> m ()
tell [Severity -> Id -> Code -> String -> Fix -> TokenComment
makeCommentWithFix Severity
StyleC (Token -> Id
getId Token
token) 2248
"Prefer double quoting even when variables don't contain special characters."
(Parameters -> Token -> Fix
addDoubleQuotesAround Parameters
params Token
token)]
quotesMayConflictWithSC2281 :: Parameters -> Token -> Bool
quotesMayConflictWithSC2281 params :: Parameters
params t :: Token
t =
case Map Id Token -> Token -> [Token]
getPath (Parameters -> Map Id Token
parentMap Parameters
params) Token
t of
_ : T_NormalWord parentId :: Id
parentId (me :: Token
me:T_Literal _ ('=':_):_) : T_SimpleCommand _ _ (cmd :: Token
cmd:_) : _ ->
(Token -> Id
getId Token
t) Id -> Id -> Bool
forall a. Eq a => a -> a -> Bool
== (Token -> Id
getId Token
me) Bool -> Bool -> Bool
&& (Id
parentId Id -> Id -> Bool
forall a. Eq a => a -> a -> Bool
== Token -> Id
getId Token
cmd)
_ -> Bool
False
addDoubleQuotesAround :: Parameters -> Token -> Fix
addDoubleQuotesAround params :: Parameters
params token :: Token
token = (Id -> Parameters -> String -> Fix
surroundWidth (Token -> Id
getId Token
token) Parameters
params "\"")
checkSpacefulness'
:: (SpaceStatus -> Token -> String -> Writer [TokenComment] ()) ->
Parameters -> Token -> [TokenComment]
checkSpacefulness' :: (SpaceStatus
-> Token -> String -> WriterT [TokenComment] Identity ())
-> Parameters -> Token -> [TokenComment]
checkSpacefulness' onFind :: SpaceStatus
-> Token -> String -> WriterT [TokenComment] Identity ()
onFind params :: Parameters
params t :: Token
t =
(Token
-> Token
-> String
-> State (Map String SpaceStatus) [TokenComment])
-> (Token
-> Token
-> String
-> DataType
-> State (Map String SpaceStatus) [TokenComment])
-> Map String SpaceStatus
-> [StackData]
-> [TokenComment]
forall t v.
(Token -> Token -> String -> State t [v])
-> (Token -> Token -> String -> DataType -> State t [v])
-> t
-> [StackData]
-> [v]
doVariableFlowAnalysis Token
-> Token -> String -> State (Map String SpaceStatus) [TokenComment]
forall (m :: * -> *) p.
MonadState (Map String SpaceStatus) m =>
p -> Token -> String -> m [TokenComment]
readF Token
-> Token
-> String
-> DataType
-> State (Map String SpaceStatus) [TokenComment]
forall (m :: * -> *) p p a.
MonadState (Map String SpaceStatus) m =>
p -> p -> String -> DataType -> m [a]
writeF ([(String, SpaceStatus)] -> Map String SpaceStatus
forall k a. Ord k => [(k, a)] -> Map k a
Map.fromList [(String, SpaceStatus)]
defaults) (Parameters -> [StackData]
variableFlow Parameters
params)
where
defaults :: [(String, SpaceStatus)]
defaults = [String] -> [SpaceStatus] -> [(String, SpaceStatus)]
forall a b. [a] -> [b] -> [(a, b)]
zip [String]
variablesWithoutSpaces (SpaceStatus -> [SpaceStatus]
forall a. a -> [a]
repeat SpaceStatus
SpaceNone)
hasSpaces :: k -> m SpaceStatus
hasSpaces name :: k
name = (Map k SpaceStatus -> SpaceStatus) -> m SpaceStatus
forall s (m :: * -> *) a. MonadState s m => (s -> a) -> m a
gets (SpaceStatus -> k -> Map k SpaceStatus -> SpaceStatus
forall k a. Ord k => a -> k -> Map k a -> a
Map.findWithDefault SpaceStatus
SpaceSome k
name)
setSpaces :: k -> a -> m ()
setSpaces name :: k
name status :: a
status =
(Map k a -> Map k a) -> m ()
forall s (m :: * -> *). MonadState s m => (s -> s) -> m ()
modify ((Map k a -> Map k a) -> m ()) -> (Map k a -> Map k a) -> m ()
forall a b. (a -> b) -> a -> b
$ k -> a -> Map k a -> Map k a
forall k a. Ord k => k -> a -> Map k a -> Map k a
Map.insert k
name a
status
readF :: p -> Token -> String -> m [TokenComment]
readF _ token :: Token
token name :: String
name = do
SpaceStatus
spaces <- String -> m SpaceStatus
forall k (m :: * -> *).
(MonadState (Map k SpaceStatus) m, Ord k) =>
k -> m SpaceStatus
hasSpaces String
name
let needsQuoting :: Bool
needsQuoting =
Token -> Bool
isExpansion Token
token
Bool -> Bool -> Bool
&& Bool -> Bool
not (Token -> Bool
isArrayExpansion Token
token)
Bool -> Bool -> Bool
&& Bool -> Bool
not (Token -> Bool
isCountingReference Token
token)
Bool -> Bool -> Bool
&& Bool -> Bool
not (Map Id Token -> Token -> Bool
isQuoteFree Map Id Token
parents Token
token)
Bool -> Bool -> Bool
&& Bool -> Bool
not (Token -> Bool
isQuotedAlternativeReference Token
token)
Bool -> Bool -> Bool
&& Bool -> Bool
not (Map Id Token -> Token -> Bool
usedAsCommandName Map Id Token
parents Token
token)
[TokenComment] -> m [TokenComment]
forall (m :: * -> *) a. Monad m => a -> m a
return ([TokenComment] -> m [TokenComment])
-> (WriterT [TokenComment] Identity () -> [TokenComment])
-> WriterT [TokenComment] Identity ()
-> m [TokenComment]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. WriterT [TokenComment] Identity () -> [TokenComment]
forall w a. Writer w a -> w
execWriter (WriterT [TokenComment] Identity () -> m [TokenComment])
-> WriterT [TokenComment] Identity () -> m [TokenComment]
forall a b. (a -> b) -> a -> b
$ Bool
-> WriterT [TokenComment] Identity ()
-> WriterT [TokenComment] Identity ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when Bool
needsQuoting (WriterT [TokenComment] Identity ()
-> WriterT [TokenComment] Identity ())
-> WriterT [TokenComment] Identity ()
-> WriterT [TokenComment] Identity ()
forall a b. (a -> b) -> a -> b
$ SpaceStatus
-> Token -> String -> WriterT [TokenComment] Identity ()
onFind SpaceStatus
spaces Token
token String
name
where
emit :: a -> m ()
emit x :: a
x = [a] -> m ()
forall w (m :: * -> *). MonadWriter w m => w -> m ()
tell [a
x]
writeF :: p -> p -> String -> DataType -> m [a]
writeF _ _ name :: String
name (DataString SourceExternal) = String -> SpaceStatus -> m ()
forall k a (m :: * -> *).
(MonadState (Map k a) m, Ord k) =>
k -> a -> m ()
setSpaces String
name SpaceStatus
SpaceSome m () -> m [a] -> m [a]
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> [a] -> m [a]
forall (m :: * -> *) a. Monad m => a -> m a
return []
writeF _ _ name :: String
name (DataString SourceInteger) = String -> SpaceStatus -> m ()
forall k a (m :: * -> *).
(MonadState (Map k a) m, Ord k) =>
k -> a -> m ()
setSpaces String
name SpaceStatus
SpaceNone m () -> m [a] -> m [a]
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> [a] -> m [a]
forall (m :: * -> *) a. Monad m => a -> m a
return []
writeF _ _ name :: String
name (DataString (SourceFrom vals :: [Token]
vals)) = do
Map String SpaceStatus
map <- m (Map String SpaceStatus)
forall s (m :: * -> *). MonadState s m => m s
get
String -> SpaceStatus -> m ()
forall k a (m :: * -> *).
(MonadState (Map k a) m, Ord k) =>
k -> a -> m ()
setSpaces String
name
((String -> SpaceStatus) -> [Token] -> SpaceStatus
isSpacefulWord (\x :: String
x -> SpaceStatus -> String -> Map String SpaceStatus -> SpaceStatus
forall k a. Ord k => a -> k -> Map k a -> a
Map.findWithDefault SpaceStatus
SpaceSome String
x Map String SpaceStatus
map) [Token]
vals)
[a] -> m [a]
forall (m :: * -> *) a. Monad m => a -> m a
return []
writeF _ _ _ _ = [a] -> m [a]
forall (m :: * -> *) a. Monad m => a -> m a
return []
parents :: Map Id Token
parents = Parameters -> Map Id Token
parentMap Parameters
params
isExpansion :: Token -> Bool
isExpansion t :: Token
t =
case Token
t of
(T_DollarBraced _ _ _ ) -> Bool
True
_ -> Bool
False
isSpacefulWord :: (String -> SpaceStatus) -> [Token] -> SpaceStatus
isSpacefulWord :: (String -> SpaceStatus) -> [Token] -> SpaceStatus
isSpacefulWord f :: String -> SpaceStatus
f = [SpaceStatus] -> SpaceStatus
forall a. Monoid a => [a] -> a
mconcat ([SpaceStatus] -> SpaceStatus)
-> ([Token] -> [SpaceStatus]) -> [Token] -> SpaceStatus
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Token -> SpaceStatus) -> [Token] -> [SpaceStatus]
forall a b. (a -> b) -> [a] -> [b]
map ((String -> SpaceStatus) -> Token -> SpaceStatus
isSpaceful String -> SpaceStatus
f)
isSpaceful :: (String -> SpaceStatus) -> Token -> SpaceStatus
isSpaceful :: (String -> SpaceStatus) -> Token -> SpaceStatus
isSpaceful spacefulF :: String -> SpaceStatus
spacefulF x :: Token
x =
case Token
x of
T_DollarExpansion _ _ -> SpaceStatus
SpaceSome
T_Backticked _ _ -> SpaceStatus
SpaceSome
T_Glob _ _ -> SpaceStatus
SpaceSome
T_Extglob {} -> SpaceStatus
SpaceSome
T_DollarArithmetic _ _ -> SpaceStatus
SpaceNone
T_Literal _ s :: String
s -> String -> SpaceStatus
fromLiteral String
s
T_SingleQuoted _ s :: String
s -> String -> SpaceStatus
fromLiteral String
s
T_DollarBraced _ _ l :: Token
l -> String -> SpaceStatus
spacefulF (String -> SpaceStatus) -> String -> SpaceStatus
forall a b. (a -> b) -> a -> b
$ String -> String
getBracedReference (String -> String) -> String -> String
forall a b. (a -> b) -> a -> b
$ [String] -> String
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat ([String] -> String) -> [String] -> String
forall a b. (a -> b) -> a -> b
$ Token -> [String]
oversimplify Token
l
T_NormalWord _ w :: [Token]
w -> (String -> SpaceStatus) -> [Token] -> SpaceStatus
isSpacefulWord String -> SpaceStatus
spacefulF [Token]
w
T_DoubleQuoted _ w :: [Token]
w -> (String -> SpaceStatus) -> [Token] -> SpaceStatus
isSpacefulWord String -> SpaceStatus
spacefulF [Token]
w
_ -> SpaceStatus
SpaceEmpty
where
globspace :: String
globspace = "*?[] \t\n"
containsAny :: t a -> t a -> Bool
containsAny s :: t a
s = (a -> Bool) -> t a -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any (a -> t a -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` t a
s)
fromLiteral :: String -> SpaceStatus
fromLiteral "" = SpaceStatus
SpaceEmpty
fromLiteral s :: String
s | String
s String -> String -> Bool
forall (t :: * -> *) (t :: * -> *) a.
(Foldable t, Foldable t, Eq a) =>
t a -> t a -> Bool
`containsAny` String
globspace = SpaceStatus
SpaceSome
fromLiteral _ = SpaceStatus
SpaceNone
prop_CheckVariableBraces1 :: Bool
prop_CheckVariableBraces1 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkVariableBraces "a='123'; echo $a"
prop_CheckVariableBraces2 :: Bool
prop_CheckVariableBraces2 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkVariableBraces "a='123'; echo ${a}"
prop_CheckVariableBraces3 :: Bool
prop_CheckVariableBraces3 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkVariableBraces "#shellcheck disable=SC2016\necho '$a'"
prop_CheckVariableBraces4 :: Bool
prop_CheckVariableBraces4 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkVariableBraces "echo $* $1"
prop_CheckVariableBraces5 :: Bool
prop_CheckVariableBraces5 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkVariableBraces "$foo=42"
checkVariableBraces :: Parameters -> Token -> m ()
checkVariableBraces params :: Parameters
params t :: Token
t@(T_DollarBraced id :: Id
id False l :: Token
l)
| String
name String -> [String] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`notElem` [String]
unbracedVariables Bool -> Bool -> Bool
&& Bool -> Bool
not (Parameters -> Token -> Bool
quotesMayConflictWithSC2281 Parameters
params Token
t) =
Id -> Code -> String -> Fix -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> Fix -> m ()
styleWithFix Id
id 2250
"Prefer putting braces around variable references even when not strictly required."
(Token -> Fix
fixFor Token
t)
where
name :: String
name = String -> String
getBracedReference (String -> String) -> String -> String
forall a b. (a -> b) -> a -> b
$ [String] -> String
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat ([String] -> String) -> [String] -> String
forall a b. (a -> b) -> a -> b
$ Token -> [String]
oversimplify Token
l
fixFor :: Token -> Fix
fixFor token :: Token
token = [Replacement] -> Fix
fixWith [Id -> Parameters -> Code -> String -> Replacement
replaceStart (Token -> Id
getId Token
token) Parameters
params 1 "${"
,Id -> Parameters -> Code -> String -> Replacement
replaceEnd (Token -> Id
getId Token
token) Parameters
params 0 "}"]
checkVariableBraces _ _ = () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
prop_checkQuotesInLiterals1 :: Bool
prop_checkQuotesInLiterals1 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkQuotesInLiterals "param='--foo=\"bar\"'; app $param"
prop_checkQuotesInLiterals1a :: Bool
prop_checkQuotesInLiterals1a= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkQuotesInLiterals "param=\"--foo='lolbar'\"; app $param"
prop_checkQuotesInLiterals2 :: Bool
prop_checkQuotesInLiterals2 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkQuotesInLiterals "param='--foo=\"bar\"'; app \"$param\""
prop_checkQuotesInLiterals3 :: Bool
prop_checkQuotesInLiterals3 =(Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkQuotesInLiterals "param=('--foo='); app \"${param[@]}\""
prop_checkQuotesInLiterals4 :: Bool
prop_checkQuotesInLiterals4 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkQuotesInLiterals "param=\"don't bother with this one\"; app $param"
prop_checkQuotesInLiterals5 :: Bool
prop_checkQuotesInLiterals5 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkQuotesInLiterals "param=\"--foo='lolbar'\"; eval app $param"
prop_checkQuotesInLiterals6 :: Bool
prop_checkQuotesInLiterals6 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkQuotesInLiterals "param='my\\ file'; cmd=\"rm $param\"; $cmd"
prop_checkQuotesInLiterals6a :: Bool
prop_checkQuotesInLiterals6a= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkQuotesInLiterals "param='my\\ file'; cmd=\"rm ${#param}\"; $cmd"
prop_checkQuotesInLiterals7 :: Bool
prop_checkQuotesInLiterals7 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkQuotesInLiterals "param='my\\ file'; rm $param"
prop_checkQuotesInLiterals8 :: Bool
prop_checkQuotesInLiterals8 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkQuotesInLiterals "param=\"/foo/'bar baz'/etc\"; rm $param"
prop_checkQuotesInLiterals9 :: Bool
prop_checkQuotesInLiterals9 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkQuotesInLiterals "param=\"/foo/'bar baz'/etc\"; rm ${#param}"
checkQuotesInLiterals :: Parameters -> p -> [TokenComment]
checkQuotesInLiterals params :: Parameters
params t :: p
t =
(Token -> Token -> String -> State (Map String Id) [TokenComment])
-> (Token
-> Token
-> String
-> DataType
-> State (Map String Id) [TokenComment])
-> Map String Id
-> [StackData]
-> [TokenComment]
forall t v.
(Token -> Token -> String -> State t [v])
-> (Token -> Token -> String -> DataType -> State t [v])
-> t
-> [StackData]
-> [v]
doVariableFlowAnalysis Token -> Token -> String -> State (Map String Id) [TokenComment]
forall (m :: * -> *) k p.
(MonadState (Map k Id) m, Ord k) =>
p -> Token -> k -> m [TokenComment]
readF Token
-> Token
-> String
-> DataType
-> State (Map String Id) [TokenComment]
forall p p a.
p -> p -> String -> DataType -> StateT (Map String Id) Identity [a]
writeF Map String Id
forall k a. Map k a
Map.empty (Parameters -> [StackData]
variableFlow Parameters
params)
where
getQuotes :: k -> m (Maybe a)
getQuotes name :: k
name = (Map k a -> Maybe a) -> m (Maybe a)
forall s (m :: * -> *) a. MonadState s m => (s -> a) -> m a
gets (k -> Map k a -> Maybe a
forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup k
name)
setQuotes :: k -> a -> m ()
setQuotes name :: k
name ref :: a
ref = (Map k a -> Map k a) -> m ()
forall s (m :: * -> *). MonadState s m => (s -> s) -> m ()
modify ((Map k a -> Map k a) -> m ()) -> (Map k a -> Map k a) -> m ()
forall a b. (a -> b) -> a -> b
$ k -> a -> Map k a -> Map k a
forall k a. Ord k => k -> a -> Map k a -> Map k a
Map.insert k
name a
ref
deleteQuotes :: String -> StateT (Map String Id) Identity ()
deleteQuotes = (Map String Id -> Map String Id)
-> StateT (Map String Id) Identity ()
forall s (m :: * -> *). MonadState s m => (s -> s) -> m ()
modify ((Map String Id -> Map String Id)
-> StateT (Map String Id) Identity ())
-> (String -> Map String Id -> Map String Id)
-> String
-> StateT (Map String Id) Identity ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> Map String Id -> Map String Id
forall k a. Ord k => k -> Map k a -> Map k a
Map.delete
parents :: Map Id Token
parents = Parameters -> Map Id Token
parentMap Parameters
params
quoteRegex :: Regex
quoteRegex = String -> Regex
mkRegex "\"|([/= ]|^)'|'( |$)|\\\\ "
containsQuotes :: String -> Bool
containsQuotes s :: String
s = String
s String -> Regex -> Bool
`matches` Regex
quoteRegex
writeF :: p -> p -> String -> DataType -> StateT (Map String Id) Identity [a]
writeF _ _ name :: String
name (DataString (SourceFrom values :: [Token]
values)) = do
Map String Id
quoteMap <- StateT (Map String Id) Identity (Map String Id)
forall s (m :: * -> *). MonadState s m => m s
get
let quotedVars :: Maybe Id
quotedVars = [Maybe Id] -> Maybe Id
forall (t :: * -> *) (m :: * -> *) a.
(Foldable t, MonadPlus m) =>
t (m a) -> m a
msum ([Maybe Id] -> Maybe Id) -> [Maybe Id] -> Maybe Id
forall a b. (a -> b) -> a -> b
$ (Token -> Maybe Id) -> [Token] -> [Maybe Id]
forall a b. (a -> b) -> [a] -> [b]
map (Map String Id -> Token -> Maybe Id
forToken Map String Id
quoteMap) [Token]
values
case Maybe Id
quotedVars of
Nothing -> String -> StateT (Map String Id) Identity ()
deleteQuotes String
name
Just x :: Id
x -> String -> Id -> StateT (Map String Id) Identity ()
forall k a (m :: * -> *).
(MonadState (Map k a) m, Ord k) =>
k -> a -> m ()
setQuotes String
name Id
x
[a] -> StateT (Map String Id) Identity [a]
forall (m :: * -> *) a. Monad m => a -> m a
return []
writeF _ _ _ _ = [a] -> StateT (Map String Id) Identity [a]
forall (m :: * -> *) a. Monad m => a -> m a
return []
forToken :: Map String Id -> Token -> Maybe Id
forToken map :: Map String Id
map (T_DollarBraced id :: Id
id _ t :: Token
t) =
String -> Map String Id -> Maybe Id
forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup ([String] -> String
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat ([String] -> String) -> (Token -> [String]) -> Token -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Token -> [String]
oversimplify (Token -> String) -> Token -> String
forall a b. (a -> b) -> a -> b
$ Token
t) Map String Id
map
forToken quoteMap :: Map String Id
quoteMap (T_DoubleQuoted id :: Id
id tokens :: [Token]
tokens) =
[Maybe Id] -> Maybe Id
forall (t :: * -> *) (m :: * -> *) a.
(Foldable t, MonadPlus m) =>
t (m a) -> m a
msum ([Maybe Id] -> Maybe Id) -> [Maybe Id] -> Maybe Id
forall a b. (a -> b) -> a -> b
$ (Token -> Maybe Id) -> [Token] -> [Maybe Id]
forall a b. (a -> b) -> [a] -> [b]
map (Map String Id -> Token -> Maybe Id
forToken Map String Id
quoteMap) [Token]
tokens
forToken quoteMap :: Map String Id
quoteMap (T_NormalWord id :: Id
id tokens :: [Token]
tokens) =
[Maybe Id] -> Maybe Id
forall (t :: * -> *) (m :: * -> *) a.
(Foldable t, MonadPlus m) =>
t (m a) -> m a
msum ([Maybe Id] -> Maybe Id) -> [Maybe Id] -> Maybe Id
forall a b. (a -> b) -> a -> b
$ (Token -> Maybe Id) -> [Token] -> [Maybe Id]
forall a b. (a -> b) -> [a] -> [b]
map (Map String Id -> Token -> Maybe Id
forToken Map String Id
quoteMap) [Token]
tokens
forToken _ t :: Token
t =
if String -> Bool
containsQuotes ([String] -> String
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat ([String] -> String) -> [String] -> String
forall a b. (a -> b) -> a -> b
$ Token -> [String]
oversimplify Token
t)
then Id -> Maybe Id
forall (m :: * -> *) a. Monad m => a -> m a
return (Id -> Maybe Id) -> Id -> Maybe Id
forall a b. (a -> b) -> a -> b
$ Token -> Id
getId Token
t
else Maybe Id
forall a. Maybe a
Nothing
squashesQuotes :: Token -> Bool
squashesQuotes t :: Token
t =
case Token
t of
T_DollarBraced id :: Id
id _ l :: Token
l -> "#" String -> String -> Bool
forall a. Eq a => [a] -> [a] -> Bool
`isPrefixOf` [String] -> String
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat (Token -> [String]
oversimplify Token
l)
_ -> Bool
False
readF :: p -> Token -> k -> m [TokenComment]
readF _ expr :: Token
expr name :: k
name = do
Maybe Id
assignment <- k -> m (Maybe Id)
forall k a (m :: * -> *).
(MonadState (Map k a) m, Ord k) =>
k -> m (Maybe a)
getQuotes k
name
[TokenComment] -> m [TokenComment]
forall (m :: * -> *) a. Monad m => a -> m a
return ([TokenComment] -> m [TokenComment])
-> [TokenComment] -> m [TokenComment]
forall a b. (a -> b) -> a -> b
$ case Maybe Id
assignment of
Just j :: Id
j
| Bool -> Bool
not (Map Id Token -> String -> Token -> Bool
isParamTo Map Id Token
parents "eval" Token
expr)
Bool -> Bool -> Bool
&& Bool -> Bool
not (Map Id Token -> Token -> Bool
isQuoteFree Map Id Token
parents Token
expr)
Bool -> Bool -> Bool
&& Bool -> Bool
not (Token -> Bool
squashesQuotes Token
expr)
-> [
Severity -> Id -> Code -> String -> TokenComment
makeComment Severity
WarningC Id
j 2089 (String -> TokenComment) -> String -> TokenComment
forall a b. (a -> b) -> a -> b
$
"Quotes/backslashes will be treated literally. " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
suggestion,
Severity -> Id -> Code -> String -> TokenComment
makeComment Severity
WarningC (Token -> Id
getId Token
expr) 2090
"Quotes/backslashes in this variable will not be respected."
]
_ -> []
suggestion :: String
suggestion =
if Shell -> Bool
supportsArrays (Parameters -> Shell
shellType Parameters
params)
then "Use an array."
else "Rewrite using set/\"$@\" or functions."
prop_checkFunctionsUsedExternally1 :: Bool
prop_checkFunctionsUsedExternally1 =
(Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyTree Parameters -> Token -> [TokenComment]
forall t. t -> Token -> [TokenComment]
checkFunctionsUsedExternally "foo() { :; }; sudo foo"
prop_checkFunctionsUsedExternally2 :: Bool
prop_checkFunctionsUsedExternally2 =
(Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyTree Parameters -> Token -> [TokenComment]
forall t. t -> Token -> [TokenComment]
checkFunctionsUsedExternally "alias f='a'; xargs -0 f"
prop_checkFunctionsUsedExternally2b :: Bool
prop_checkFunctionsUsedExternally2b=
(Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
forall t. t -> Token -> [TokenComment]
checkFunctionsUsedExternally "alias f='a'; find . -type f"
prop_checkFunctionsUsedExternally2c :: Bool
prop_checkFunctionsUsedExternally2c=
(Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyTree Parameters -> Token -> [TokenComment]
forall t. t -> Token -> [TokenComment]
checkFunctionsUsedExternally "alias f='a'; find . -type f -exec f +"
prop_checkFunctionsUsedExternally3 :: Bool
prop_checkFunctionsUsedExternally3 =
(Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
forall t. t -> Token -> [TokenComment]
checkFunctionsUsedExternally "f() { :; }; echo f"
prop_checkFunctionsUsedExternally4 :: Bool
prop_checkFunctionsUsedExternally4 =
(Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
forall t. t -> Token -> [TokenComment]
checkFunctionsUsedExternally "foo() { :; }; sudo \"foo\""
prop_checkFunctionsUsedExternally5 :: Bool
prop_checkFunctionsUsedExternally5 =
(Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyTree Parameters -> Token -> [TokenComment]
forall t. t -> Token -> [TokenComment]
checkFunctionsUsedExternally "foo() { :; }; ssh host foo"
prop_checkFunctionsUsedExternally6 :: Bool
prop_checkFunctionsUsedExternally6 =
(Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
forall t. t -> Token -> [TokenComment]
checkFunctionsUsedExternally "foo() { :; }; ssh host echo foo"
prop_checkFunctionsUsedExternally7 :: Bool
prop_checkFunctionsUsedExternally7 =
(Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
forall t. t -> Token -> [TokenComment]
checkFunctionsUsedExternally "install() { :; }; sudo apt-get install foo"
prop_checkFunctionsUsedExternally8 :: Bool
prop_checkFunctionsUsedExternally8 =
(Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyTree Parameters -> Token -> [TokenComment]
forall t. t -> Token -> [TokenComment]
checkFunctionsUsedExternally "foo() { :; }; command sudo foo"
prop_checkFunctionsUsedExternally9 :: Bool
prop_checkFunctionsUsedExternally9 =
(Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyTree Parameters -> Token -> [TokenComment]
forall t. t -> Token -> [TokenComment]
checkFunctionsUsedExternally "foo() { :; }; exec -c sudo foo"
checkFunctionsUsedExternally :: p -> Token -> [TokenComment]
checkFunctionsUsedExternally params :: p
params t :: Token
t =
(p -> Token -> WriterT [TokenComment] Identity ())
-> p -> Token -> [TokenComment]
forall w t.
Monoid w =>
(t -> Token -> WriterT w Identity ()) -> t -> Token -> w
runNodeAnalysis p -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkCommand p
params Token
t
where
checkCommand :: p -> Token -> m ()
checkCommand _ t :: Token
t@(T_SimpleCommand _ _ argv :: [Token]
argv) =
case Bool -> Token -> (Maybe String, Token)
getCommandNameAndToken Bool
False Token
t of
(Just str :: String
str, t :: Token
t) -> do
let name :: String
name = String -> String
basename String
str
let args :: [Token]
args = Token -> [Token] -> [Token]
skipOver Token
t [Token]
argv
let argStrings :: [(String, Token)]
argStrings = (Token -> (String, Token)) -> [Token] -> [(String, Token)]
forall a b. (a -> b) -> [a] -> [b]
map (\x :: Token
x -> (String -> Maybe String -> String
forall a. a -> Maybe a -> a
fromMaybe "" (Maybe String -> String) -> Maybe String -> String
forall a b. (a -> b) -> a -> b
$ Token -> Maybe String
getLiteralString Token
x, Token
x)) [Token]
args
let candidates :: [(String, Token)]
candidates = String -> [(String, Token)] -> [(String, Token)]
forall b. String -> [(String, b)] -> [(String, b)]
getPotentialCommands String
name [(String, Token)]
argStrings
((String, Token) -> m ()) -> [(String, Token)] -> m ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ (String -> (String, Token) -> m ()
forall (m :: * -> *) a.
MonadWriter [TokenComment] m =>
String -> (a, Token) -> m ()
checkArg String
name) [(String, Token)]
candidates
_ -> () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
checkCommand _ _ = () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
skipOver :: Token -> [Token] -> [Token]
skipOver t :: Token
t list :: [Token]
list = Int -> [Token] -> [Token]
forall a. Int -> [a] -> [a]
drop 1 ([Token] -> [Token]) -> [Token] -> [Token]
forall a b. (a -> b) -> a -> b
$ (Token -> Bool) -> [Token] -> [Token]
forall a. (a -> Bool) -> [a] -> [a]
dropWhile (\c :: Token
c -> Token -> Id
getId Token
c Id -> Id -> Bool
forall a. Eq a => a -> a -> Bool
/= Id
id) ([Token] -> [Token]) -> [Token] -> [Token]
forall a b. (a -> b) -> a -> b
$ [Token]
list
where id :: Id
id = Token -> Id
getId Token
t
getPotentialCommands :: String -> [(String, b)] -> [(String, b)]
getPotentialCommands name :: String
name argAndString :: [(String, b)]
argAndString =
case String
name of
"chroot" -> [(String, b)]
firstNonFlag
"screen" -> [(String, b)]
firstNonFlag
"sudo" -> [(String, b)]
firstNonFlag
"xargs" -> [(String, b)]
firstNonFlag
"tmux" -> [(String, b)]
firstNonFlag
"ssh" -> Int -> [(String, b)] -> [(String, b)]
forall a. Int -> [a] -> [a]
take 1 ([(String, b)] -> [(String, b)]) -> [(String, b)] -> [(String, b)]
forall a b. (a -> b) -> a -> b
$ Int -> [(String, b)] -> [(String, b)]
forall a. Int -> [a] -> [a]
drop 1 ([(String, b)] -> [(String, b)]) -> [(String, b)] -> [(String, b)]
forall a b. (a -> b) -> a -> b
$ [(String, b)] -> [(String, b)]
forall b. [(String, b)] -> [(String, b)]
dropFlags [(String, b)]
argAndString
"find" -> Int -> [(String, b)] -> [(String, b)]
forall a. Int -> [a] -> [a]
take 1 ([(String, b)] -> [(String, b)]) -> [(String, b)] -> [(String, b)]
forall a b. (a -> b) -> a -> b
$ Int -> [(String, b)] -> [(String, b)]
forall a. Int -> [a] -> [a]
drop 1 ([(String, b)] -> [(String, b)]) -> [(String, b)] -> [(String, b)]
forall a b. (a -> b) -> a -> b
$
((String, b) -> Bool) -> [(String, b)] -> [(String, b)]
forall a. (a -> Bool) -> [a] -> [a]
dropWhile (\x :: (String, b)
x -> (String, b) -> String
forall a b. (a, b) -> a
fst (String, b)
x String -> [String] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`notElem` [String]
findExecFlags) [(String, b)]
argAndString
_ -> []
where
firstNonFlag :: [(String, b)]
firstNonFlag = Int -> [(String, b)] -> [(String, b)]
forall a. Int -> [a] -> [a]
take 1 ([(String, b)] -> [(String, b)]) -> [(String, b)] -> [(String, b)]
forall a b. (a -> b) -> a -> b
$ [(String, b)] -> [(String, b)]
forall b. [(String, b)] -> [(String, b)]
dropFlags [(String, b)]
argAndString
findExecFlags :: [String]
findExecFlags = ["-exec", "-execdir", "-ok"]
dropFlags :: [(String, b)] -> [(String, b)]
dropFlags = ((String, b) -> Bool) -> [(String, b)] -> [(String, b)]
forall a. (a -> Bool) -> [a] -> [a]
dropWhile (\x :: (String, b)
x -> "-" String -> String -> Bool
forall a. Eq a => [a] -> [a] -> Bool
`isPrefixOf` (String, b) -> String
forall a b. (a, b) -> a
fst (String, b)
x)
analyse :: (Token -> StateT [a] Identity ()) -> Token -> [a]
analyse f :: Token -> StateT [a] Identity ()
f t :: Token
t = State [a] Token -> [a] -> [a]
forall s a. State s a -> s -> s
execState ((Token -> StateT [a] Identity ()) -> Token -> State [a] Token
forall (m :: * -> *).
Monad m =>
(Token -> m ()) -> Token -> m Token
doAnalysis Token -> StateT [a] Identity ()
f Token
t) []
functions :: Map String Id
functions = [(String, Id)] -> Map String Id
forall k a. Ord k => [(k, a)] -> Map k a
Map.fromList ([(String, Id)] -> Map String Id)
-> [(String, Id)] -> Map String Id
forall a b. (a -> b) -> a -> b
$ (Token -> StateT [(String, Id)] Identity ())
-> Token -> [(String, Id)]
forall a. (Token -> StateT [a] Identity ()) -> Token -> [a]
analyse Token -> StateT [(String, Id)] Identity ()
forall (m :: * -> *). MonadState [(String, Id)] m => Token -> m ()
findFunctions Token
t
findFunctions :: Token -> m ()
findFunctions (T_Function id :: Id
id _ _ name :: String
name _) = ([(String, Id)] -> [(String, Id)]) -> m ()
forall s (m :: * -> *). MonadState s m => (s -> s) -> m ()
modify ((String
name, Id
id)(String, Id) -> [(String, Id)] -> [(String, Id)]
forall a. a -> [a] -> [a]
:)
findFunctions t :: Token
t@(T_SimpleCommand id :: Id
id _ (_:args :: [Token]
args))
| Token
t Token -> String -> Bool
`isUnqualifiedCommand` "alias" = (Token -> m ()) -> [Token] -> m ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ Token -> m ()
forall (m :: * -> *). MonadState [(String, Id)] m => Token -> m ()
getAlias [Token]
args
findFunctions _ = () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
getAlias :: Token -> f ()
getAlias arg :: Token
arg =
let string :: String
string = Token -> String
onlyLiteralString Token
arg
in Bool -> f () -> f ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when ('=' Char -> String -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` String
string) (f () -> f ()) -> f () -> f ()
forall a b. (a -> b) -> a -> b
$
([(String, Id)] -> [(String, Id)]) -> f ()
forall s (m :: * -> *). MonadState s m => (s -> s) -> m ()
modify (((Char -> Bool) -> String -> String
forall a. (a -> Bool) -> [a] -> [a]
takeWhile (Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
/= '=') String
string, Token -> Id
getId Token
arg)(String, Id) -> [(String, Id)] -> [(String, Id)]
forall a. a -> [a] -> [a]
:)
checkArg :: String -> (a, Token) -> m ()
checkArg cmd :: String
cmd (_, arg :: Token
arg) = Maybe (m ()) -> m ()
forall (t :: * -> *) (m :: * -> *) a.
(Foldable t, Monad m) =>
t (m a) -> m ()
sequence_ (Maybe (m ()) -> m ()) -> Maybe (m ()) -> m ()
forall a b. (a -> b) -> a -> b
$ do
String
literalArg <- Token -> Maybe String
getUnquotedLiteral Token
arg
Id
definitionId <- String -> Map String Id -> Maybe Id
forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup String
literalArg Map String Id
functions
m () -> Maybe (m ())
forall (m :: * -> *) a. Monad m => a -> m a
return (m () -> Maybe (m ())) -> m () -> Maybe (m ())
forall a b. (a -> b) -> a -> b
$ do
Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
warn (Token -> Id
getId Token
arg) 2033
"Shell functions can't be passed to external commands."
Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
info Id
definitionId 2032 (String -> m ()) -> String -> m ()
forall a b. (a -> b) -> a -> b
$
"Use own script or sh -c '..' to run this from " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
cmd String -> String -> String
forall a. [a] -> [a] -> [a]
++ "."
prop_checkUnused0 :: Bool
prop_checkUnused0 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkUnusedAssignments "var=foo; echo $var"
prop_checkUnused1 :: Bool
prop_checkUnused1 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkUnusedAssignments "var=foo; echo $bar"
prop_checkUnused2 :: Bool
prop_checkUnused2 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkUnusedAssignments "var=foo; export var;"
prop_checkUnused3 :: Bool
prop_checkUnused3 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkUnusedAssignments "for f in *; do echo '$f'; done"
prop_checkUnused4 :: Bool
prop_checkUnused4 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkUnusedAssignments "local i=0"
prop_checkUnused5 :: Bool
prop_checkUnused5 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkUnusedAssignments "read lol; echo $lol"
prop_checkUnused6 :: Bool
prop_checkUnused6 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkUnusedAssignments "var=4; (( var++ ))"
prop_checkUnused7 :: Bool
prop_checkUnused7 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkUnusedAssignments "var=2; $((var))"
prop_checkUnused8 :: Bool
prop_checkUnused8 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkUnusedAssignments "var=2; var=3;"
prop_checkUnused9 :: Bool
prop_checkUnused9 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkUnusedAssignments "read ''"
prop_checkUnused10 :: Bool
prop_checkUnused10= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkUnusedAssignments "read -p 'test: '"
prop_checkUnused11 :: Bool
prop_checkUnused11= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkUnusedAssignments "bar=5; export foo[$bar]=3"
prop_checkUnused12 :: Bool
prop_checkUnused12= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkUnusedAssignments "read foo; echo ${!foo}"
prop_checkUnused13 :: Bool
prop_checkUnused13= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkUnusedAssignments "x=(1); (( x[0] ))"
prop_checkUnused14 :: Bool
prop_checkUnused14= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkUnusedAssignments "x=(1); n=0; echo ${x[n]}"
prop_checkUnused15 :: Bool
prop_checkUnused15= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkUnusedAssignments "x=(1); n=0; (( x[n] ))"
prop_checkUnused16 :: Bool
prop_checkUnused16= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkUnusedAssignments "foo=5; declare -x foo"
prop_checkUnused16b :: Bool
prop_checkUnused16b= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkUnusedAssignments "f() { local -x foo; foo=42; bar; }; f"
prop_checkUnused17 :: Bool
prop_checkUnused17= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkUnusedAssignments "read -i 'foo' -e -p 'Input: ' bar; $bar;"
prop_checkUnused18 :: Bool
prop_checkUnused18= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkUnusedAssignments "a=1; arr=( [$a]=42 ); echo \"${arr[@]}\""
prop_checkUnused19 :: Bool
prop_checkUnused19= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkUnusedAssignments "a=1; let b=a+1; echo $b"
prop_checkUnused20 :: Bool
prop_checkUnused20= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkUnusedAssignments "a=1; PS1='$a'"
prop_checkUnused21 :: Bool
prop_checkUnused21= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkUnusedAssignments "a=1; trap 'echo $a' INT"
prop_checkUnused22 :: Bool
prop_checkUnused22= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkUnusedAssignments "a=1; [ -v a ]"
prop_checkUnused23 :: Bool
prop_checkUnused23= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkUnusedAssignments "a=1; [ -R a ]"
prop_checkUnused24 :: Bool
prop_checkUnused24= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkUnusedAssignments "mapfile -C a b; echo ${b[@]}"
prop_checkUnused25 :: Bool
prop_checkUnused25= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkUnusedAssignments "readarray foo; echo ${foo[@]}"
prop_checkUnused26 :: Bool
prop_checkUnused26= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkUnusedAssignments "declare -F foo"
prop_checkUnused27 :: Bool
prop_checkUnused27= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkUnusedAssignments "var=3; [ var -eq 3 ]"
prop_checkUnused28 :: Bool
prop_checkUnused28= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkUnusedAssignments "var=3; [[ var -eq 3 ]]"
prop_checkUnused29 :: Bool
prop_checkUnused29= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkUnusedAssignments "var=(a b); declare -p var"
prop_checkUnused30 :: Bool
prop_checkUnused30= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkUnusedAssignments "let a=1"
prop_checkUnused31 :: Bool
prop_checkUnused31= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkUnusedAssignments "let 'a=1'"
prop_checkUnused32 :: Bool
prop_checkUnused32= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkUnusedAssignments "let a=b=c; echo $a"
prop_checkUnused33 :: Bool
prop_checkUnused33= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkUnusedAssignments "a=foo; [[ foo =~ ^{$a}$ ]]"
prop_checkUnused34 :: Bool
prop_checkUnused34= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkUnusedAssignments "foo=1; (( t = foo )); echo $t"
prop_checkUnused35 :: Bool
prop_checkUnused35= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkUnusedAssignments "a=foo; b=2; echo ${a:b}"
prop_checkUnused36 :: Bool
prop_checkUnused36= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkUnusedAssignments "if [[ -v foo ]]; then true; fi"
prop_checkUnused37 :: Bool
prop_checkUnused37= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkUnusedAssignments "fd=2; exec {fd}>&-"
prop_checkUnused38 :: Bool
prop_checkUnused38= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkUnusedAssignments "(( a=42 ))"
prop_checkUnused39 :: Bool
prop_checkUnused39= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkUnusedAssignments "declare -x -f foo"
prop_checkUnused40 :: Bool
prop_checkUnused40= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkUnusedAssignments "arr=(1 2); num=2; echo \"${arr[@]:num}\""
prop_checkUnused41 :: Bool
prop_checkUnused41= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkUnusedAssignments "@test 'foo' {\ntrue\n}\n"
prop_checkUnused42 :: Bool
prop_checkUnused42= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkUnusedAssignments "DEFINE_string foo '' ''; echo \"${FLAGS_foo}\""
prop_checkUnused43 :: Bool
prop_checkUnused43= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkUnusedAssignments "DEFINE_string foo '' ''"
prop_checkUnused44 :: Bool
prop_checkUnused44= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkUnusedAssignments "DEFINE_string \"foo$ibar\" x y"
prop_checkUnused45 :: Bool
prop_checkUnused45= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkUnusedAssignments "readonly foo=bar"
prop_checkUnused46 :: Bool
prop_checkUnused46= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkUnusedAssignments "readonly foo=(bar)"
prop_checkUnused47 :: Bool
prop_checkUnused47= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkUnusedAssignments "a=1; alias hello='echo $a'"
checkUnusedAssignments :: Parameters -> p -> [TokenComment]
checkUnusedAssignments params :: Parameters
params t :: p
t = WriterT [TokenComment] Identity () -> [TokenComment]
forall w a. Writer w a -> w
execWriter (((String, Token) -> WriterT [TokenComment] Identity ())
-> [(String, Token)] -> WriterT [TokenComment] Identity ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ (String, Token) -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
(String, Token) -> m ()
warnFor [(String, Token)]
unused)
where
flow :: [StackData]
flow = Parameters -> [StackData]
variableFlow Parameters
params
references :: Map String ()
references = (Map String ()
-> (Map String () -> Map String ()) -> Map String ())
-> Map String ()
-> [Map String () -> Map String ()]
-> Map String ()
forall (t :: * -> *) b a.
Foldable t =>
(b -> a -> b) -> b -> t a -> b
foldl (((Map String () -> Map String ())
-> Map String () -> Map String ())
-> Map String ()
-> (Map String () -> Map String ())
-> Map String ()
forall a b c. (a -> b -> c) -> b -> a -> c
flip (Map String () -> Map String ()) -> Map String () -> Map String ()
forall a b. (a -> b) -> a -> b
($)) Map String ()
defaultMap ((StackData -> Map String () -> Map String ())
-> [StackData] -> [Map String () -> Map String ()]
forall a b. (a -> b) -> [a] -> [b]
map StackData -> Map String () -> Map String ()
insertRef [StackData]
flow)
insertRef :: StackData -> Map String () -> Map String ()
insertRef (Reference (base :: Token
base, token :: Token
token, name :: String
name)) =
String -> () -> Map String () -> Map String ()
forall k a. Ord k => k -> a -> Map k a -> Map k a
Map.insert (String -> String
stripSuffix String
name) ()
insertRef _ = Map String () -> Map String ()
forall a. a -> a
id
assignments :: Map String Token
assignments = (Map String Token
-> (Map String Token -> Map String Token) -> Map String Token)
-> Map String Token
-> [Map String Token -> Map String Token]
-> Map String Token
forall (t :: * -> *) b a.
Foldable t =>
(b -> a -> b) -> b -> t a -> b
foldl (((Map String Token -> Map String Token)
-> Map String Token -> Map String Token)
-> Map String Token
-> (Map String Token -> Map String Token)
-> Map String Token
forall a b c. (a -> b -> c) -> b -> a -> c
flip (Map String Token -> Map String Token)
-> Map String Token -> Map String Token
forall a b. (a -> b) -> a -> b
($)) Map String Token
forall k a. Map k a
Map.empty ((StackData -> Map String Token -> Map String Token)
-> [StackData] -> [Map String Token -> Map String Token]
forall a b. (a -> b) -> [a] -> [b]
map StackData -> Map String Token -> Map String Token
insertAssignment [StackData]
flow)
insertAssignment :: StackData -> Map String Token -> Map String Token
insertAssignment (Assignment (_, token :: Token
token, name :: String
name, _)) | String -> Bool
isVariableName String
name =
String -> Token -> Map String Token -> Map String Token
forall k a. Ord k => k -> a -> Map k a -> Map k a
Map.insert String
name Token
token
insertAssignment _ = Map String Token -> Map String Token
forall a. a -> a
id
unused :: [(String, Token)]
unused = Map String Token -> [(String, Token)]
forall k a. Map k a -> [(k, a)]
Map.assocs (Map String Token -> [(String, Token)])
-> Map String Token -> [(String, Token)]
forall a b. (a -> b) -> a -> b
$ Map String Token -> Map String () -> Map String Token
forall k a b. Ord k => Map k a -> Map k b -> Map k a
Map.difference Map String Token
assignments Map String ()
references
warnFor :: (String, Token) -> m ()
warnFor (name :: String
name, token :: Token
token) =
Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
warn (Token -> Id
getId Token
token) 2034 (String -> m ()) -> String -> m ()
forall a b. (a -> b) -> a -> b
$
String
name String -> String -> String
forall a. [a] -> [a] -> [a]
++ " appears unused. Verify use (or export if used externally)."
stripSuffix :: String -> String
stripSuffix = (Char -> Bool) -> String -> String
forall a. (a -> Bool) -> [a] -> [a]
takeWhile Char -> Bool
isVariableChar
defaultMap :: Map String ()
defaultMap = [(String, ())] -> Map String ()
forall k a. Ord k => [(k, a)] -> Map k a
Map.fromList ([(String, ())] -> Map String ())
-> [(String, ())] -> Map String ()
forall a b. (a -> b) -> a -> b
$ [String] -> [()] -> [(String, ())]
forall a b. [a] -> [b] -> [(a, b)]
zip [String]
internalVariables ([()] -> [(String, ())]) -> [()] -> [(String, ())]
forall a b. (a -> b) -> a -> b
$ () -> [()]
forall a. a -> [a]
repeat ()
prop_checkUnassignedReferences1 :: Bool
prop_checkUnassignedReferences1 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkUnassignedReferences "echo $foo"
prop_checkUnassignedReferences2 :: Bool
prop_checkUnassignedReferences2 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkUnassignedReferences "foo=hello; echo $foo"
prop_checkUnassignedReferences3 :: Bool
prop_checkUnassignedReferences3 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkUnassignedReferences "MY_VALUE=3; echo $MYVALUE"
prop_checkUnassignedReferences4 :: Bool
prop_checkUnassignedReferences4 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkUnassignedReferences "RANDOM2=foo; echo $RANDOM"
prop_checkUnassignedReferences5 :: Bool
prop_checkUnassignedReferences5 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkUnassignedReferences "declare -A foo=([bar]=baz); echo ${foo[bar]}"
prop_checkUnassignedReferences6 :: Bool
prop_checkUnassignedReferences6 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkUnassignedReferences "foo=..; echo ${foo-bar}"
prop_checkUnassignedReferences7 :: Bool
prop_checkUnassignedReferences7 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkUnassignedReferences "getopts ':h' foo; echo $foo"
prop_checkUnassignedReferences8 :: Bool
prop_checkUnassignedReferences8 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkUnassignedReferences "let 'foo = 1'; echo $foo"
prop_checkUnassignedReferences9 :: Bool
prop_checkUnassignedReferences9 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkUnassignedReferences "echo ${foo-bar}"
prop_checkUnassignedReferences10 :: Bool
prop_checkUnassignedReferences10= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkUnassignedReferences "echo ${foo:?}"
prop_checkUnassignedReferences11 :: Bool
prop_checkUnassignedReferences11= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkUnassignedReferences "declare -A foo; echo \"${foo[@]}\""
prop_checkUnassignedReferences12 :: Bool
prop_checkUnassignedReferences12= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkUnassignedReferences "typeset -a foo; echo \"${foo[@]}\""
prop_checkUnassignedReferences13 :: Bool
prop_checkUnassignedReferences13= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkUnassignedReferences "f() { local foo; echo $foo; }"
prop_checkUnassignedReferences14 :: Bool
prop_checkUnassignedReferences14= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkUnassignedReferences "foo=; echo $foo"
prop_checkUnassignedReferences15 :: Bool
prop_checkUnassignedReferences15= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkUnassignedReferences "f() { true; }; export -f f"
prop_checkUnassignedReferences16 :: Bool
prop_checkUnassignedReferences16= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkUnassignedReferences "declare -A foo=( [a b]=bar ); echo ${foo[a b]}"
prop_checkUnassignedReferences17 :: Bool
prop_checkUnassignedReferences17= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkUnassignedReferences "USERS=foo; echo $USER"
prop_checkUnassignedReferences18 :: Bool
prop_checkUnassignedReferences18= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkUnassignedReferences "FOOBAR=42; export FOOBAR="
prop_checkUnassignedReferences19 :: Bool
prop_checkUnassignedReferences19= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkUnassignedReferences "readonly foo=bar; echo $foo"
prop_checkUnassignedReferences20 :: Bool
prop_checkUnassignedReferences20= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkUnassignedReferences "printf -v foo bar; echo $foo"
prop_checkUnassignedReferences21 :: Bool
prop_checkUnassignedReferences21= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkUnassignedReferences "echo ${#foo}"
prop_checkUnassignedReferences22 :: Bool
prop_checkUnassignedReferences22= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkUnassignedReferences "echo ${!os*}"
prop_checkUnassignedReferences23 :: Bool
prop_checkUnassignedReferences23= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkUnassignedReferences "declare -a foo; foo[bar]=42;"
prop_checkUnassignedReferences24 :: Bool
prop_checkUnassignedReferences24= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkUnassignedReferences "declare -A foo; foo[bar]=42;"
prop_checkUnassignedReferences25 :: Bool
prop_checkUnassignedReferences25= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkUnassignedReferences "declare -A foo=(); foo[bar]=42;"
prop_checkUnassignedReferences26 :: Bool
prop_checkUnassignedReferences26= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkUnassignedReferences "a::b() { foo; }; readonly -f a::b"
prop_checkUnassignedReferences27 :: Bool
prop_checkUnassignedReferences27= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkUnassignedReferences ": ${foo:=bar}"
prop_checkUnassignedReferences28 :: Bool
prop_checkUnassignedReferences28= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkUnassignedReferences "#!/bin/ksh\necho \"${.sh.version}\"\n"
prop_checkUnassignedReferences29 :: Bool
prop_checkUnassignedReferences29= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkUnassignedReferences "if [[ -v foo ]]; then echo $foo; fi"
prop_checkUnassignedReferences30 :: Bool
prop_checkUnassignedReferences30= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkUnassignedReferences "if [[ -v foo[3] ]]; then echo ${foo[3]}; fi"
prop_checkUnassignedReferences31 :: Bool
prop_checkUnassignedReferences31= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkUnassignedReferences "X=1; if [[ -v foo[$X+42] ]]; then echo ${foo[$X+42]}; fi"
prop_checkUnassignedReferences32 :: Bool
prop_checkUnassignedReferences32= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkUnassignedReferences "if [[ -v \"foo[1]\" ]]; then echo ${foo[@]}; fi"
prop_checkUnassignedReferences33 :: Bool
prop_checkUnassignedReferences33= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkUnassignedReferences "f() { local -A foo; echo \"${foo[@]}\"; }"
prop_checkUnassignedReferences34 :: Bool
prop_checkUnassignedReferences34= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkUnassignedReferences "declare -A foo; (( foo[bar] ))"
prop_checkUnassignedReferences35 :: Bool
prop_checkUnassignedReferences35= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkUnassignedReferences "echo ${arr[foo-bar]:?fail}"
prop_checkUnassignedReferences36 :: Bool
prop_checkUnassignedReferences36= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkUnassignedReferences "read -a foo -r <<<\"foo bar\"; echo \"$foo\""
prop_checkUnassignedReferences37 :: Bool
prop_checkUnassignedReferences37= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkUnassignedReferences "var=howdy; printf -v 'array[0]' %s \"$var\"; printf %s \"${array[0]}\";"
prop_checkUnassignedReferences38 :: Bool
prop_checkUnassignedReferences38= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyTree (Bool -> Parameters -> Token -> [TokenComment]
forall p. Bool -> Parameters -> p -> [TokenComment]
checkUnassignedReferences' Bool
True) "echo $VAR"
prop_checkUnassignedReferences39 :: Bool
prop_checkUnassignedReferences39= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkUnassignedReferences "builtin export var=4; echo $var"
prop_checkUnassignedReferences40 :: Bool
prop_checkUnassignedReferences40= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkUnassignedReferences ": ${foo=bar}"
prop_checkUnassignedReferences41 :: Bool
prop_checkUnassignedReferences41= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkUnassignedReferences "mapfile -t files 123; echo \"${files[@]}\""
prop_checkUnassignedReferences42 :: Bool
prop_checkUnassignedReferences42= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkUnassignedReferences "mapfile files -t; echo \"${files[@]}\""
prop_checkUnassignedReferences43 :: Bool
prop_checkUnassignedReferences43= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkUnassignedReferences "mapfile --future files; echo \"${files[@]}\""
prop_checkUnassignedReferences_minusNPlain :: Bool
prop_checkUnassignedReferences_minusNPlain = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkUnassignedReferences "if [ -n \"$x\" ]; then echo $x; fi"
prop_checkUnassignedReferences_minusZPlain :: Bool
prop_checkUnassignedReferences_minusZPlain = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkUnassignedReferences "if [ -z \"$x\" ]; then echo \"\"; fi"
prop_checkUnassignedReferences_minusNBraced :: Bool
prop_checkUnassignedReferences_minusNBraced = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkUnassignedReferences "if [ -n \"${x}\" ]; then echo $x; fi"
prop_checkUnassignedReferences_minusZBraced :: Bool
prop_checkUnassignedReferences_minusZBraced = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkUnassignedReferences "if [ -z \"${x}\" ]; then echo \"\"; fi"
prop_checkUnassignedReferences_minusNDefault :: Bool
prop_checkUnassignedReferences_minusNDefault = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkUnassignedReferences "if [ -n \"${x:-}\" ]; then echo $x; fi"
prop_checkUnassignedReferences_minusZDefault :: Bool
prop_checkUnassignedReferences_minusZDefault = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
forall p. Parameters -> p -> [TokenComment]
checkUnassignedReferences "if [ -z \"${x:-}\" ]; then echo \"\"; fi"
checkUnassignedReferences :: Parameters -> p -> [TokenComment]
checkUnassignedReferences = Bool -> Parameters -> p -> [TokenComment]
forall p. Bool -> Parameters -> p -> [TokenComment]
checkUnassignedReferences' Bool
False
checkUnassignedReferences' :: Bool -> Parameters -> p -> [TokenComment]
checkUnassignedReferences' includeGlobals :: Bool
includeGlobals params :: Parameters
params t :: p
t = [TokenComment]
warnings
where
(readMap :: Map String Token
readMap, writeMap :: Map String ()
writeMap) = State (Map String Token, Map String ()) [()]
-> (Map String Token, Map String ())
-> (Map String Token, Map String ())
forall s a. State s a -> s -> s
execState ((StackData -> StateT (Map String Token, Map String ()) Identity ())
-> [StackData] -> State (Map String Token, Map String ()) [()]
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
mapM StackData -> StateT (Map String Token, Map String ()) Identity ()
forall (m :: * -> *).
MonadState (Map String Token, Map String ()) m =>
StackData -> m ()
tally ([StackData] -> State (Map String Token, Map String ()) [()])
-> [StackData] -> State (Map String Token, Map String ()) [()]
forall a b. (a -> b) -> a -> b
$ Parameters -> [StackData]
variableFlow Parameters
params) (Map String Token
forall k a. Map k a
Map.empty, Map String ()
forall k a. Map k a
Map.empty)
defaultAssigned :: Map String ()
defaultAssigned = [(String, ())] -> Map String ()
forall k a. Ord k => [(k, a)] -> Map k a
Map.fromList ([(String, ())] -> Map String ())
-> [(String, ())] -> Map String ()
forall a b. (a -> b) -> a -> b
$ (String -> (String, ())) -> [String] -> [(String, ())]
forall a b. (a -> b) -> [a] -> [b]
map (\a :: String
a -> (String
a, ())) ([String] -> [(String, ())]) -> [String] -> [(String, ())]
forall a b. (a -> b) -> a -> b
$ (String -> Bool) -> [String] -> [String]
forall a. (a -> Bool) -> [a] -> [a]
filter (Bool -> Bool
not (Bool -> Bool) -> (String -> Bool) -> String -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null) [String]
internalVariables
tally :: StackData -> m ()
tally (Assignment (_, _, name :: String
name, _)) =
((Map String Token, Map String ())
-> (Map String Token, Map String ()))
-> m ()
forall s (m :: * -> *). MonadState s m => (s -> s) -> m ()
modify (\(read :: Map String Token
read, written :: Map String ()
written) -> (Map String Token
read, String -> () -> Map String () -> Map String ()
forall k a. Ord k => k -> a -> Map k a -> Map k a
Map.insert String
name () Map String ()
written))
tally (Reference (_, place :: Token
place, name :: String
name)) =
((Map String Token, Map String ())
-> (Map String Token, Map String ()))
-> m ()
forall s (m :: * -> *). MonadState s m => (s -> s) -> m ()
modify (\(read :: Map String Token
read, written :: Map String ()
written) -> ((Token -> Token -> Token)
-> String -> Token -> Map String Token -> Map String Token
forall k a. Ord k => (a -> a -> a) -> k -> a -> Map k a -> Map k a
Map.insertWith ((Token -> Token) -> Token -> Token -> Token
forall a b. a -> b -> a
const Token -> Token
forall a. a -> a
id) String
name Token
place Map String Token
read, Map String ()
written))
tally _ = () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
unassigned :: [(String, Token)]
unassigned = Map String Token -> [(String, Token)]
forall k a. Map k a -> [(k, a)]
Map.toList (Map String Token -> [(String, Token)])
-> Map String Token -> [(String, Token)]
forall a b. (a -> b) -> a -> b
$ Map String Token -> Map String () -> Map String Token
forall k a b. Ord k => Map k a -> Map k b -> Map k a
Map.difference (Map String Token -> Map String () -> Map String Token
forall k a b. Ord k => Map k a -> Map k b -> Map k a
Map.difference Map String Token
readMap Map String ()
writeMap) Map String ()
defaultAssigned
writtenVars :: [String]
writtenVars = (String -> Bool) -> [String] -> [String]
forall a. (a -> Bool) -> [a] -> [a]
filter String -> Bool
isVariableName ([String] -> [String]) -> [String] -> [String]
forall a b. (a -> b) -> a -> b
$ Map String () -> [String]
forall k a. Map k a -> [k]
Map.keys Map String ()
writeMap
getBestMatch :: String -> Maybe String
getBestMatch var :: String
var = do
(match :: String
match, score :: Int
score) <- [(String, Int)] -> Maybe (String, Int)
forall a. [a] -> Maybe a
listToMaybe [(String, Int)]
best
Bool -> Maybe ()
forall (f :: * -> *). Alternative f => Bool -> f ()
guard (Bool -> Maybe ()) -> Bool -> Maybe ()
forall a b. (a -> b) -> a -> b
$ String -> String -> Int -> Bool
forall a (t :: * -> *) p a.
(Ord a, Num a, Foldable t) =>
p -> t a -> a -> Bool
goodMatch String
var String
match Int
score
String -> Maybe String
forall (m :: * -> *) a. Monad m => a -> m a
return String
match
where
matches :: [(String, Int)]
matches = (String -> (String, Int)) -> [String] -> [(String, Int)]
forall a b. (a -> b) -> [a] -> [b]
map (\x :: String
x -> (String
x, String -> String -> Int
match String
var String
x)) [String]
writtenVars
best :: [(String, Int)]
best = ((String, Int) -> (String, Int) -> Ordering)
-> [(String, Int)] -> [(String, Int)]
forall a. (a -> a -> Ordering) -> [a] -> [a]
sortBy (((String, Int) -> Int)
-> (String, Int) -> (String, Int) -> Ordering
forall a b. Ord a => (b -> a) -> b -> b -> Ordering
comparing (String, Int) -> Int
forall a b. (a, b) -> b
snd) [(String, Int)]
matches
goodMatch :: p -> t a -> a -> Bool
goodMatch var :: p
var match :: t a
match score :: a
score =
let l :: Int
l = t a -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length t a
match in
Int
l Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> 3 Bool -> Bool -> Bool
&& a
score a -> a -> Bool
forall a. Ord a => a -> a -> Bool
<= 1
Bool -> Bool -> Bool
|| Int
l Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> 7 Bool -> Bool -> Bool
&& a
score a -> a -> Bool
forall a. Ord a => a -> a -> Bool
<= 2
isLocal :: String -> Bool
isLocal = (Char -> Bool) -> String -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any Char -> Bool
isLower
warningForGlobals :: String -> Token -> Maybe (m ())
warningForGlobals var :: String
var place :: Token
place = do
String
match <- String -> Maybe String
getBestMatch String
var
m () -> Maybe (m ())
forall (m :: * -> *) a. Monad m => a -> m a
return (m () -> Maybe (m ())) -> m () -> Maybe (m ())
forall a b. (a -> b) -> a -> b
$ Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
warn (Token -> Id
getId Token
place) 2153 (String -> m ()) -> String -> m ()
forall a b. (a -> b) -> a -> b
$
"Possible misspelling: " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
var String -> String -> String
forall a. [a] -> [a] -> [a]
++ " may not be assigned, but " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
match String -> String -> String
forall a. [a] -> [a] -> [a]
++ " is."
warningForLocals :: String -> Token -> m (m ())
warningForLocals var :: String
var place :: Token
place =
m () -> m (m ())
forall (m :: * -> *) a. Monad m => a -> m a
return (m () -> m (m ())) -> m () -> m (m ())
forall a b. (a -> b) -> a -> b
$ Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
warn (Token -> Id
getId Token
place) 2154 (String -> m ()) -> String -> m ()
forall a b. (a -> b) -> a -> b
$
String
var String -> String -> String
forall a. [a] -> [a] -> [a]
++ " is referenced but not assigned" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
optionalTip String -> String -> String
forall a. [a] -> [a] -> [a]
++ "."
where
optionalTip :: String
optionalTip =
if String
var String -> [String] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [String]
commonCommands
then " (for output from commands, use \"$(" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
var String -> String -> String
forall a. [a] -> [a] -> [a]
++ " ..." String -> String -> String
forall a. [a] -> [a] -> [a]
++ ")\" )"
else String -> Maybe String -> String
forall a. a -> Maybe a -> a
fromMaybe "" (Maybe String -> String) -> Maybe String -> String
forall a b. (a -> b) -> a -> b
$ do
String
match <- String -> Maybe String
getBestMatch String
var
String -> Maybe String
forall (m :: * -> *) a. Monad m => a -> m a
return (String -> Maybe String) -> String -> Maybe String
forall a b. (a -> b) -> a -> b
$ " (did you mean '" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
match String -> String -> String
forall a. [a] -> [a] -> [a]
++ "'?)"
warningFor :: (String, Token) -> Maybe (m ())
warningFor (var :: String
var, place :: Token
place) = do
Bool -> Maybe ()
forall (f :: * -> *). Alternative f => Bool -> f ()
guard (Bool -> Maybe ()) -> Bool -> Maybe ()
forall a b. (a -> b) -> a -> b
$ String -> Bool
isVariableName String
var
Bool -> Maybe ()
forall (f :: * -> *). Alternative f => Bool -> f ()
guard (Bool -> Maybe ()) -> (Bool -> Bool) -> Bool -> Maybe ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Bool -> Bool
not (Bool -> Maybe ()) -> Bool -> Maybe ()
forall a b. (a -> b) -> a -> b
$ String -> Token -> Bool
isInArray String
var Token
place Bool -> Bool -> Bool
|| Token -> Bool
isGuarded Token
place
(if Bool
includeGlobals Bool -> Bool -> Bool
|| String -> Bool
isLocal String
var
then String -> Token -> Maybe (m ())
forall (m :: * -> *) (m :: * -> *).
(MonadWriter [TokenComment] m, Monad m) =>
String -> Token -> m (m ())
warningForLocals
else String -> Token -> Maybe (m ())
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
String -> Token -> Maybe (m ())
warningForGlobals) String
var Token
place
warnings :: [TokenComment]
warnings = Writer [TokenComment] [()] -> [TokenComment]
forall w a. Writer w a -> w
execWriter (Writer [TokenComment] [()] -> [TokenComment])
-> ([WriterT [TokenComment] Identity ()]
-> Writer [TokenComment] [()])
-> [WriterT [TokenComment] Identity ()]
-> [TokenComment]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [WriterT [TokenComment] Identity ()] -> Writer [TokenComment] [()]
forall (t :: * -> *) (m :: * -> *) a.
(Traversable t, Monad m) =>
t (m a) -> m (t a)
sequence ([WriterT [TokenComment] Identity ()] -> [TokenComment])
-> [WriterT [TokenComment] Identity ()] -> [TokenComment]
forall a b. (a -> b) -> a -> b
$ ((String, Token) -> Maybe (WriterT [TokenComment] Identity ()))
-> [(String, Token)] -> [WriterT [TokenComment] Identity ()]
forall a b. (a -> Maybe b) -> [a] -> [b]
mapMaybe (String, Token) -> Maybe (WriterT [TokenComment] Identity ())
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
(String, Token) -> Maybe (m ())
warningFor [(String, Token)]
unassigned
isInArray :: String -> Token -> Bool
isInArray var :: String
var t :: Token
t = (Token -> Bool) -> [Token] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any Token -> Bool
isArray ([Token] -> Bool) -> [Token] -> Bool
forall a b. (a -> b) -> a -> b
$ Map Id Token -> Token -> [Token]
getPath (Parameters -> Map Id Token
parentMap Parameters
params) Token
t
where
isArray :: Token -> Bool
isArray T_Array {} = Bool
True
isArray (T_DollarBraced _ _ l :: Token
l) | String
var String -> String -> Bool
forall a. Eq a => a -> a -> Bool
/= String -> String
getBracedReference ([String] -> String
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat ([String] -> String) -> [String] -> String
forall a b. (a -> b) -> a -> b
$ Token -> [String]
oversimplify Token
l) = Bool
True
isArray _ = Bool
False
isGuarded :: Token -> Bool
isGuarded (T_DollarBraced _ _ v :: Token
v) =
String
rest String -> Regex -> Bool
`matches` Regex
guardRegex
where
name :: String
name = [String] -> String
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat ([String] -> String) -> [String] -> String
forall a b. (a -> b) -> a -> b
$ Token -> [String]
oversimplify Token
v
rest :: String
rest = (Char -> Bool) -> String -> String
forall a. (a -> Bool) -> [a] -> [a]
dropWhile Char -> Bool
isVariableChar (String -> String) -> String -> String
forall a b. (a -> b) -> a -> b
$ (Char -> Bool) -> String -> String
forall a. (a -> Bool) -> [a] -> [a]
dropWhile (Char -> String -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` "#!") String
name
isGuarded _ = Bool
False
guardRegex :: Regex
guardRegex = String -> Regex
mkRegex "^(\\[.*\\])?:?[-?]"
match :: String -> String -> Int
match var :: String
var candidate :: String
candidate =
if String
var String -> String -> Bool
forall a. Eq a => a -> a -> Bool
/= String
candidate Bool -> Bool -> Bool
&& (Char -> Char) -> String -> String
forall a b. (a -> b) -> [a] -> [b]
map Char -> Char
toLower String
var String -> String -> Bool
forall a. Eq a => a -> a -> Bool
== (Char -> Char) -> String -> String
forall a b. (a -> b) -> [a] -> [b]
map Char -> Char
toLower String
candidate
then 1
else String -> String -> Int
forall a. Eq a => [a] -> [a] -> Int
dist String
var String
candidate
prop_checkGlobsAsOptions1 :: Bool
prop_checkGlobsAsOptions1 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkGlobsAsOptions "rm *.txt"
prop_checkGlobsAsOptions2 :: Bool
prop_checkGlobsAsOptions2 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkGlobsAsOptions "ls ??.*"
prop_checkGlobsAsOptions3 :: Bool
prop_checkGlobsAsOptions3 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkGlobsAsOptions "rm -- *.txt"
prop_checkGlobsAsOptions4 :: Bool
prop_checkGlobsAsOptions4 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkGlobsAsOptions "*.txt"
prop_checkGlobsAsOptions5 :: Bool
prop_checkGlobsAsOptions5 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkGlobsAsOptions "echo 'Files:' *.txt"
prop_checkGlobsAsOptions6 :: Bool
prop_checkGlobsAsOptions6 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkGlobsAsOptions "printf '%s\\n' *"
checkGlobsAsOptions :: p -> Token -> f ()
checkGlobsAsOptions _ cmd :: Token
cmd@(T_SimpleCommand _ _ args :: [Token]
args) =
Bool -> f () -> f ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
unless ((String -> Maybe String -> String
forall a. a -> Maybe a -> a
fromMaybe "" (Maybe String -> String) -> Maybe String -> String
forall a b. (a -> b) -> a -> b
$ Token -> Maybe String
getCommandBasename Token
cmd) String -> [String] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` ["echo", "printf"]) (f () -> f ()) -> f () -> f ()
forall a b. (a -> b) -> a -> b
$
(Token -> f ()) -> [Token] -> f ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ Token -> f ()
forall (m :: * -> *). MonadWriter [TokenComment] m => Token -> m ()
check ([Token] -> f ()) -> [Token] -> f ()
forall a b. (a -> b) -> a -> b
$ (Token -> Bool) -> [Token] -> [Token]
forall a. (a -> Bool) -> [a] -> [a]
takeWhile (Bool -> Bool
not (Bool -> Bool) -> (Token -> Bool) -> Token -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Token -> Bool
isEndOfArgs) (Int -> [Token] -> [Token]
forall a. Int -> [a] -> [a]
drop 1 [Token]
args)
where
check :: Token -> m ()
check v :: Token
v@(T_NormalWord _ (T_Glob id :: Id
id s :: String
s:_)) | String
s String -> String -> Bool
forall a. Eq a => a -> a -> Bool
== "*" Bool -> Bool -> Bool
|| String
s String -> String -> Bool
forall a. Eq a => a -> a -> Bool
== "?" =
Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
info Id
id 2035 "Use ./*glob* or -- *glob* so names with dashes won't become options."
check _ = () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
isEndOfArgs :: Token -> Bool
isEndOfArgs t :: Token
t =
case [String] -> String
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat ([String] -> String) -> [String] -> String
forall a b. (a -> b) -> a -> b
$ Token -> [String]
oversimplify Token
t of
"--" -> Bool
True
":::" -> Bool
True
"::::" -> Bool
True
_ -> Bool
False
checkGlobsAsOptions _ _ = () -> f ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
prop_checkWhileReadPitfalls1 :: Bool
prop_checkWhileReadPitfalls1 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
checkWhileReadPitfalls "while read foo; do ssh $foo uptime; done < file"
prop_checkWhileReadPitfalls2 :: Bool
prop_checkWhileReadPitfalls2 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
checkWhileReadPitfalls "while read -u 3 foo; do ssh $foo uptime; done 3< file"
prop_checkWhileReadPitfalls3 :: Bool
prop_checkWhileReadPitfalls3 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
checkWhileReadPitfalls "while true; do ssh host uptime; done"
prop_checkWhileReadPitfalls4 :: Bool
prop_checkWhileReadPitfalls4 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
checkWhileReadPitfalls "while read foo; do ssh $foo hostname < /dev/null; done"
prop_checkWhileReadPitfalls5 :: Bool
prop_checkWhileReadPitfalls5 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
checkWhileReadPitfalls "while read foo; do echo ls | ssh $foo; done"
prop_checkWhileReadPitfalls6 :: Bool
prop_checkWhileReadPitfalls6 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
checkWhileReadPitfalls "while read foo <&3; do ssh $foo; done 3< foo"
prop_checkWhileReadPitfalls7 :: Bool
prop_checkWhileReadPitfalls7 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
checkWhileReadPitfalls "while read foo; do if true; then ssh $foo uptime; fi; done < file"
prop_checkWhileReadPitfalls8 :: Bool
prop_checkWhileReadPitfalls8 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
checkWhileReadPitfalls "while read foo; do ssh -n $foo uptime; done < file"
prop_checkWhileReadPitfalls9 :: Bool
prop_checkWhileReadPitfalls9 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
checkWhileReadPitfalls "while read foo; do ffmpeg -i foo.mkv bar.mkv -an; done"
prop_checkWhileReadPitfalls10 :: Bool
prop_checkWhileReadPitfalls10 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
checkWhileReadPitfalls "while read foo; do mplayer foo.ogv > file; done"
prop_checkWhileReadPitfalls11 :: Bool
prop_checkWhileReadPitfalls11 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
checkWhileReadPitfalls "while read foo; do mplayer foo.ogv <<< q; done"
prop_checkWhileReadPitfalls12 :: Bool
prop_checkWhileReadPitfalls12 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
checkWhileReadPitfalls "while read foo\ndo\nmplayer foo.ogv << EOF\nq\nEOF\ndone"
prop_checkWhileReadPitfalls13 :: Bool
prop_checkWhileReadPitfalls13 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
checkWhileReadPitfalls "while read foo; do x=$(ssh host cmd); done"
prop_checkWhileReadPitfalls14 :: Bool
prop_checkWhileReadPitfalls14 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
checkWhileReadPitfalls "while read foo; do echo $(ssh host cmd) < /dev/null; done"
prop_checkWhileReadPitfalls15 :: Bool
prop_checkWhileReadPitfalls15 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
checkWhileReadPitfalls "while read foo; do ssh $foo cmd & done"
checkWhileReadPitfalls :: Parameters -> Token -> WriterT [TokenComment] Identity ()
checkWhileReadPitfalls params :: Parameters
params (T_WhileExpression id :: Id
id [command :: Token
command] contents :: [Token]
contents)
| Token -> Bool
isStdinReadCommand Token
command =
(Token -> WriterT [TokenComment] Identity ())
-> [Token] -> WriterT [TokenComment] Identity ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ Token -> WriterT [TokenComment] Identity ()
checkMuncher [Token]
contents
where
munchers :: Map
String (String -> Token -> Bool, String -> Token -> Fix, String)
munchers = [(String,
(String -> Token -> Bool, String -> Token -> Fix, String))]
-> Map
String (String -> Token -> Bool, String -> Token -> Fix, String)
forall k a. Ord k => [(k, a)] -> Map k a
Map.fromList [
("ssh", (String -> Token -> Bool
hasFlag, String -> Token -> Fix
addFlag, "-n")),
("ffmpeg", (String -> Token -> Bool
hasArgument, String -> Token -> Fix
addFlag, "-nostdin")),
("mplayer", (String -> Token -> Bool
hasArgument, String -> Token -> Fix
addFlag, "-noconsolecontrols")),
("HandBrakeCLI", (\_ _ -> Bool
False, String -> Token -> Fix
addRedirect, "< /dev/null"))
]
hasFlag :: String -> Token -> Bool
hasFlag ('-':flag :: String
flag) = String -> [String] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
elem String
flag ([String] -> Bool) -> (Token -> [String]) -> Token -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ((Token, String) -> String) -> [(Token, String)] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map (Token, String) -> String
forall a b. (a, b) -> b
snd ([(Token, String)] -> [String])
-> (Token -> [(Token, String)]) -> Token -> [String]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Token -> [(Token, String)]
getAllFlags
hasArgument :: String -> Token -> Bool
hasArgument arg :: String
arg = String -> [String] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
elem String
arg ([String] -> Bool) -> (Token -> [String]) -> Token -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Token -> Maybe String) -> [Token] -> [String]
forall a b. (a -> Maybe b) -> [a] -> [b]
mapMaybe Token -> Maybe String
getLiteralString ([Token] -> [String]) -> (Token -> [Token]) -> Token -> [String]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Maybe [Token] -> [Token]
forall a. HasCallStack => Maybe a -> a
fromJust (Maybe [Token] -> [Token])
-> (Token -> Maybe [Token]) -> Token -> [Token]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Token -> Maybe [Token]
getCommandArgv
addFlag :: String -> Token -> Fix
addFlag string :: String
string cmd :: Token
cmd = [Replacement] -> Fix
fixWith [Id -> Parameters -> Code -> String -> Replacement
replaceEnd (Token -> Id
getId (Token -> Id) -> Token -> Id
forall a b. (a -> b) -> a -> b
$ Token -> Token
getCommandTokenOrThis Token
cmd) Parameters
params 0 (' 'Char -> String -> String
forall a. a -> [a] -> [a]
:String
string)]
addRedirect :: String -> Token -> Fix
addRedirect string :: String
string cmd :: Token
cmd = [Replacement] -> Fix
fixWith [Id -> Parameters -> Code -> String -> Replacement
replaceEnd (Token -> Id
getId Token
cmd) Parameters
params 0 (' 'Char -> String -> String
forall a. a -> [a] -> [a]
:String
string)]
isStdinReadCommand :: Token -> Bool
isStdinReadCommand (T_Pipeline _ _ [T_Redirecting id :: Id
id redirs :: [Token]
redirs cmd :: Token
cmd]) =
let plaintext :: [String]
plaintext = Token -> [String]
oversimplify Token
cmd
in String -> [String] -> String
forall p. p -> [p] -> p
headOrDefault "" [String]
plaintext String -> String -> Bool
forall a. Eq a => a -> a -> Bool
== "read"
Bool -> Bool -> Bool
&& ("-u" String -> [String] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`notElem` [String]
plaintext)
Bool -> Bool -> Bool
&& Bool -> Bool
not ((Token -> Bool) -> [Token] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any Token -> Bool
stdinRedirect [Token]
redirs)
isStdinReadCommand _ = Bool
False
checkMuncher :: Token -> Writer [TokenComment] ()
checkMuncher :: Token -> WriterT [TokenComment] Identity ()
checkMuncher (T_Pipeline _ _ (T_Redirecting _ redirs :: [Token]
redirs cmd :: Token
cmd:_)) = do
case Token
cmd of
T_SimpleCommand _ vars :: [Token]
vars args :: [Token]
args ->
(Token -> WriterT [TokenComment] Identity ())
-> [Token] -> WriterT [TokenComment] Identity ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ Token -> WriterT [TokenComment] Identity ()
checkMuncher ([Token] -> WriterT [TokenComment] Identity ())
-> [Token] -> WriterT [TokenComment] Identity ()
forall a b. (a -> b) -> a -> b
$ [[Token]] -> [Token]
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat ([[Token]] -> [Token]) -> [[Token]] -> [Token]
forall a b. (a -> b) -> a -> b
$ (Token -> [[Token]]) -> [Token] -> [[Token]]
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap Token -> [[Token]]
getCommandSequences ([Token] -> [[Token]]) -> [Token] -> [[Token]]
forall a b. (a -> b) -> a -> b
$ (Token -> [Token]) -> [Token] -> [Token]
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap Token -> [Token]
getWords ([Token] -> [Token]) -> [Token] -> [Token]
forall a b. (a -> b) -> a -> b
$ [Token]
vars [Token] -> [Token] -> [Token]
forall a. [a] -> [a] -> [a]
++ [Token]
args
_ -> () -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
Bool
-> WriterT [TokenComment] Identity ()
-> WriterT [TokenComment] Identity ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
unless ((Token -> Bool) -> [Token] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any Token -> Bool
stdinRedirect [Token]
redirs) (WriterT [TokenComment] Identity ()
-> WriterT [TokenComment] Identity ())
-> WriterT [TokenComment] Identity ()
-> WriterT [TokenComment] Identity ()
forall a b. (a -> b) -> a -> b
$ do
(Token -> WriterT [TokenComment] Identity ())
-> [Token] -> WriterT [TokenComment] Identity ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ Token -> WriterT [TokenComment] Identity ()
checkMuncher ([Token] -> WriterT [TokenComment] Identity ())
-> [Token] -> WriterT [TokenComment] Identity ()
forall a b. (a -> b) -> a -> b
$ [[Token]] -> [Token]
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat ([[Token]] -> [Token]) -> [[Token]] -> [Token]
forall a b. (a -> b) -> a -> b
$ Token -> [[Token]]
getCommandSequences Token
cmd
Maybe (WriterT [TokenComment] Identity ())
-> WriterT [TokenComment] Identity ()
forall (t :: * -> *) (m :: * -> *) a.
(Foldable t, Monad m) =>
t (m a) -> m ()
sequence_ (Maybe (WriterT [TokenComment] Identity ())
-> WriterT [TokenComment] Identity ())
-> Maybe (WriterT [TokenComment] Identity ())
-> WriterT [TokenComment] Identity ()
forall a b. (a -> b) -> a -> b
$ do
String
name <- Token -> Maybe String
getCommandBasename Token
cmd
(check :: String -> Token -> Bool
check, fix :: String -> Token -> Fix
fix, flag :: String
flag) <- String
-> Map
String (String -> Token -> Bool, String -> Token -> Fix, String)
-> Maybe (String -> Token -> Bool, String -> Token -> Fix, String)
forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup String
name Map
String (String -> Token -> Bool, String -> Token -> Fix, String)
munchers
Bool -> Maybe ()
forall (f :: * -> *). Alternative f => Bool -> f ()
guard (Bool -> Maybe ()) -> Bool -> Maybe ()
forall a b. (a -> b) -> a -> b
$ Bool -> Bool
not (String -> Token -> Bool
check String
flag Token
cmd)
WriterT [TokenComment] Identity ()
-> Maybe (WriterT [TokenComment] Identity ())
forall (m :: * -> *) a. Monad m => a -> m a
return (WriterT [TokenComment] Identity ()
-> Maybe (WriterT [TokenComment] Identity ()))
-> WriterT [TokenComment] Identity ()
-> Maybe (WriterT [TokenComment] Identity ())
forall a b. (a -> b) -> a -> b
$ do
Id -> Code -> String -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
info Id
id 2095 (String -> WriterT [TokenComment] Identity ())
-> String -> WriterT [TokenComment] Identity ()
forall a b. (a -> b) -> a -> b
$
String
name String -> String -> String
forall a. [a] -> [a] -> [a]
++ " may swallow stdin, preventing this loop from working properly."
Id -> Code -> String -> Fix -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> Fix -> m ()
warnWithFix (Token -> Id
getId Token
cmd) 2095
("Use " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
name String -> String -> String
forall a. [a] -> [a] -> [a]
++ " " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
flag String -> String -> String
forall a. [a] -> [a] -> [a]
++ " to prevent " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
name String -> String -> String
forall a. [a] -> [a] -> [a]
++ " from swallowing stdin.")
(String -> Token -> Fix
fix String
flag Token
cmd)
checkMuncher (T_Backgrounded _ t :: Token
t) = Token -> WriterT [TokenComment] Identity ()
checkMuncher Token
t
checkMuncher _ = () -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
stdinRedirect :: Token -> Bool
stdinRedirect (T_FdRedirect _ fd :: String
fd op :: Token
op)
| String
fd String -> String -> Bool
forall a. Eq a => a -> a -> Bool
== "0" = Bool
True
| String
fd String -> String -> Bool
forall a. Eq a => a -> a -> Bool
== "" =
case Token
op of
T_IoFile _ (T_Less _) _ -> Bool
True
T_IoDuplicate _ (T_LESSAND _) _ -> Bool
True
T_HereString _ _ -> Bool
True
T_HereDoc {} -> Bool
True
_ -> Bool
False
stdinRedirect _ = Bool
False
getWords :: Token -> [Token]
getWords t :: Token
t =
case Token
t of
T_Assignment _ _ _ _ x :: Token
x -> Token -> [Token]
getWordParts Token
x
_ -> Token -> [Token]
getWordParts Token
t
checkWhileReadPitfalls _ _ = () -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
prop_checkPrefixAssign1 :: Bool
prop_checkPrefixAssign1 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkPrefixAssignmentReference "var=foo echo $var"
prop_checkPrefixAssign2 :: Bool
prop_checkPrefixAssign2 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkPrefixAssignmentReference "var=$(echo $var) cmd"
checkPrefixAssignmentReference :: Parameters -> Token -> m ()
checkPrefixAssignmentReference params :: Parameters
params t :: Token
t@(T_DollarBraced id :: Id
id _ value :: Token
value) =
[Token] -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
[Token] -> m ()
check [Token]
path
where
name :: String
name = String -> String
getBracedReference (String -> String) -> String -> String
forall a b. (a -> b) -> a -> b
$ [String] -> String
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat ([String] -> String) -> [String] -> String
forall a b. (a -> b) -> a -> b
$ Token -> [String]
oversimplify Token
value
path :: [Token]
path = Map Id Token -> Token -> [Token]
getPath (Parameters -> Map Id Token
parentMap Parameters
params) Token
t
idPath :: [Id]
idPath = (Token -> Id) -> [Token] -> [Id]
forall a b. (a -> b) -> [a] -> [b]
map Token -> Id
getId [Token]
path
check :: [Token] -> m ()
check [] = () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
check (t :: Token
t:rest :: [Token]
rest) =
case Token
t of
T_SimpleCommand _ vars :: [Token]
vars (_:_) -> (Token -> m ()) -> [Token] -> m ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ Token -> m ()
forall (m :: * -> *). MonadWriter [TokenComment] m => Token -> m ()
checkVar [Token]
vars
_ -> [Token] -> m ()
check [Token]
rest
checkVar :: Token -> m ()
checkVar (T_Assignment aId :: Id
aId mode :: AssignmentMode
mode aName :: String
aName [] value :: Token
value) |
String
aName String -> String -> Bool
forall a. Eq a => a -> a -> Bool
== String
name Bool -> Bool -> Bool
&& (Id
aId Id -> [Id] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`notElem` [Id]
idPath) = do
Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
warn Id
aId 2097 "This assignment is only seen by the forked process."
Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
warn Id
id 2098 "This expansion will not see the mentioned assignment."
checkVar _ = () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
checkPrefixAssignmentReference _ _ = () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
prop_checkCharRangeGlob1 :: Bool
prop_checkCharRangeGlob1 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkCharRangeGlob "ls *[:digit:].jpg"
prop_checkCharRangeGlob2 :: Bool
prop_checkCharRangeGlob2 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkCharRangeGlob "ls *[[:digit:]].jpg"
prop_checkCharRangeGlob3 :: Bool
prop_checkCharRangeGlob3 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkCharRangeGlob "ls [10-15]"
prop_checkCharRangeGlob4 :: Bool
prop_checkCharRangeGlob4 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkCharRangeGlob "ls [a-zA-Z]"
prop_checkCharRangeGlob5 :: Bool
prop_checkCharRangeGlob5 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkCharRangeGlob "tr -d [a-zA-Z]"
prop_checkCharRangeGlob6 :: Bool
prop_checkCharRangeGlob6 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkCharRangeGlob "[[ $x == [!!]* ]]"
checkCharRangeGlob :: Parameters -> Token -> m ()
checkCharRangeGlob p :: Parameters
p t :: Token
t@(T_Glob id :: Id
id str :: String
str) |
String -> Bool
isCharClass String
str Bool -> Bool -> Bool
&& Bool -> Bool
not (Map Id Token -> String -> Token -> Bool
isParamTo (Parameters -> Map Id Token
parentMap Parameters
p) "tr" Token
t) =
if ":" String -> String -> Bool
forall a. Eq a => [a] -> [a] -> Bool
`isPrefixOf` String
contents
Bool -> Bool -> Bool
&& ":" String -> String -> Bool
forall a. Eq a => [a] -> [a] -> Bool
`isSuffixOf` String
contents
Bool -> Bool -> Bool
&& String
contents String -> String -> Bool
forall a. Eq a => a -> a -> Bool
/= ":"
then Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
warn Id
id 2101 "Named class needs outer [], e.g. [[:digit:]]."
else
Bool -> m () -> m ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when ('[' Char -> String -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`notElem` String
contents Bool -> Bool -> Bool
&& Bool
hasDupes) (m () -> m ()) -> m () -> m ()
forall a b. (a -> b) -> a -> b
$
Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
info Id
id 2102 "Ranges can only match single chars (mentioned due to duplicates)."
where
isCharClass :: String -> Bool
isCharClass str :: String
str = "[" String -> String -> Bool
forall a. Eq a => [a] -> [a] -> Bool
`isPrefixOf` String
str Bool -> Bool -> Bool
&& "]" String -> String -> Bool
forall a. Eq a => [a] -> [a] -> Bool
`isSuffixOf` String
str
contents :: String
contents = String -> String
dropNegation (String -> String) -> (String -> String) -> String -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int -> String -> String
forall a. Int -> [a] -> [a]
drop 1 (String -> String) -> (String -> String) -> String -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int -> String -> String
forall a. Int -> [a] -> [a]
take (String -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length String
str Int -> Int -> Int
forall a. Num a => a -> a -> a
- 1) (String -> String) -> String -> String
forall a b. (a -> b) -> a -> b
$ String
str
hasDupes :: Bool
hasDupes = (String -> Bool) -> [String] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any ((Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>1) (Int -> Bool) -> (String -> Int) -> String -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length) ([String] -> Bool) -> (String -> [String]) -> String -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> [String]
forall a. Eq a => [a] -> [[a]]
group (String -> [String]) -> (String -> String) -> String -> [String]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> String
forall a. Ord a => [a] -> [a]
sort (String -> String) -> (String -> String) -> String -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Char -> Bool) -> String -> String
forall a. (a -> Bool) -> [a] -> [a]
filter (Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
/= '-') (String -> Bool) -> String -> Bool
forall a b. (a -> b) -> a -> b
$ String
contents
dropNegation :: String -> String
dropNegation s :: String
s =
case String
s of
'!':rest :: String
rest -> String
rest
'^':rest :: String
rest -> String
rest
x :: String
x -> String
x
checkCharRangeGlob _ _ = () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
prop_checkCdAndBack1 :: Bool
prop_checkCdAndBack1 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkCdAndBack "for f in *; do cd $f; git pull; cd ..; done"
prop_checkCdAndBack2 :: Bool
prop_checkCdAndBack2 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkCdAndBack "for f in *; do cd $f || continue; git pull; cd ..; done"
prop_checkCdAndBack3 :: Bool
prop_checkCdAndBack3 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkCdAndBack "while [[ $PWD != / ]]; do cd ..; done"
prop_checkCdAndBack4 :: Bool
prop_checkCdAndBack4 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkCdAndBack "cd $tmp; foo; cd -"
prop_checkCdAndBack5 :: Bool
prop_checkCdAndBack5 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkCdAndBack "cd ..; foo; cd .."
prop_checkCdAndBack6 :: Bool
prop_checkCdAndBack6 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkCdAndBack "for dir in */; do cd \"$dir\"; some_cmd; cd ..; done"
prop_checkCdAndBack7 :: Bool
prop_checkCdAndBack7 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkCdAndBack "set -e; for dir in */; do cd \"$dir\"; some_cmd; cd ..; done"
prop_checkCdAndBack8 :: Bool
prop_checkCdAndBack8 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkCdAndBack "cd tmp\nfoo\n# shellcheck disable=SC2103\ncd ..\n"
checkCdAndBack :: Parameters -> Token -> f ()
checkCdAndBack params :: Parameters
params t :: Token
t =
Bool -> f () -> f ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
unless (Parameters -> Bool
hasSetE Parameters
params) (f () -> f ()) -> f () -> f ()
forall a b. (a -> b) -> a -> b
$ ([Token] -> f ()) -> [[Token]] -> f ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ [Token] -> f ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
[Token] -> m ()
doList ([[Token]] -> f ()) -> [[Token]] -> f ()
forall a b. (a -> b) -> a -> b
$ Token -> [[Token]]
getCommandSequences Token
t
where
isCdRevert :: Token -> Bool
isCdRevert t :: Token
t =
case Token -> [String]
oversimplify Token
t of
[_, p :: String
p] -> String
p String -> [String] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` ["..", "-"]
_ -> Bool
False
getCandidate :: Token -> Maybe Token
getCandidate (T_Annotation _ _ x :: Token
x) = Token -> Maybe Token
getCandidate Token
x
getCandidate (T_Pipeline id :: Id
id _ [x :: Token
x]) | Token
x Token -> String -> Bool
`isCommand` "cd" = Token -> Maybe Token
forall (m :: * -> *) a. Monad m => a -> m a
return Token
x
getCandidate _ = Maybe Token
forall a. Maybe a
Nothing
findCdPair :: [Token] -> Maybe Id
findCdPair list :: [Token]
list =
case [Token]
list of
(a :: Token
a:b :: Token
b:rest :: [Token]
rest) ->
if Token -> Bool
isCdRevert Token
b Bool -> Bool -> Bool
&& Bool -> Bool
not (Token -> Bool
isCdRevert Token
a)
then Id -> Maybe Id
forall (m :: * -> *) a. Monad m => a -> m a
return (Id -> Maybe Id) -> Id -> Maybe Id
forall a b. (a -> b) -> a -> b
$ Token -> Id
getId Token
b
else [Token] -> Maybe Id
findCdPair (Token
bToken -> [Token] -> [Token]
forall a. a -> [a] -> [a]
:[Token]
rest)
_ -> Maybe Id
forall a. Maybe a
Nothing
doList :: [Token] -> m ()
doList list :: [Token]
list = Maybe (m ()) -> m ()
forall (t :: * -> *) (m :: * -> *) a.
(Foldable t, Monad m) =>
t (m a) -> m ()
sequence_ (Maybe (m ()) -> m ()) -> Maybe (m ()) -> m ()
forall a b. (a -> b) -> a -> b
$ do
Id
cd <- [Token] -> Maybe Id
findCdPair ([Token] -> Maybe Id) -> [Token] -> Maybe Id
forall a b. (a -> b) -> a -> b
$ (Token -> Maybe Token) -> [Token] -> [Token]
forall a b. (a -> Maybe b) -> [a] -> [b]
mapMaybe Token -> Maybe Token
getCandidate [Token]
list
m () -> Maybe (m ())
forall (m :: * -> *) a. Monad m => a -> m a
return (m () -> Maybe (m ())) -> m () -> Maybe (m ())
forall a b. (a -> b) -> a -> b
$ Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
info Id
cd 2103 "Use a ( subshell ) to avoid having to cd back."
prop_checkLoopKeywordScope1 :: Bool
prop_checkLoopKeywordScope1 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkLoopKeywordScope "continue 2"
prop_checkLoopKeywordScope2 :: Bool
prop_checkLoopKeywordScope2 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkLoopKeywordScope "for f; do ( break; ); done"
prop_checkLoopKeywordScope3 :: Bool
prop_checkLoopKeywordScope3 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkLoopKeywordScope "if true; then continue; fi"
prop_checkLoopKeywordScope4 :: Bool
prop_checkLoopKeywordScope4 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkLoopKeywordScope "while true; do break; done"
prop_checkLoopKeywordScope5 :: Bool
prop_checkLoopKeywordScope5 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkLoopKeywordScope "if true; then break; fi"
prop_checkLoopKeywordScope6 :: Bool
prop_checkLoopKeywordScope6 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkLoopKeywordScope "while true; do true | { break; }; done"
prop_checkLoopKeywordScope7 :: Bool
prop_checkLoopKeywordScope7 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkLoopKeywordScope "#!/bin/ksh\nwhile true; do true | { break; }; done"
checkLoopKeywordScope :: Parameters -> Token -> m ()
checkLoopKeywordScope params :: Parameters
params t :: Token
t |
Maybe String
name Maybe String -> [Maybe String] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` (String -> Maybe String) -> [String] -> [Maybe String]
forall a b. (a -> b) -> [a] -> [b]
map String -> Maybe String
forall a. a -> Maybe a
Just ["continue", "break"] =
if Bool -> Bool
not (Bool -> Bool) -> Bool -> Bool
forall a b. (a -> b) -> a -> b
$ (Token -> Bool) -> [Token] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any Token -> Bool
isLoop [Token]
path
then if (Token -> Bool) -> [Token] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any Token -> Bool
isFunction ([Token] -> Bool) -> [Token] -> Bool
forall a b. (a -> b) -> a -> b
$ Int -> [Token] -> [Token]
forall a. Int -> [a] -> [a]
take 1 [Token]
path
then Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
err (Token -> Id
getId Token
t) 2104 (String -> m ()) -> String -> m ()
forall a b. (a -> b) -> a -> b
$ "In functions, use return instead of " String -> String -> String
forall a. [a] -> [a] -> [a]
++ Maybe String -> String
forall a. HasCallStack => Maybe a -> a
fromJust Maybe String
name String -> String -> String
forall a. [a] -> [a] -> [a]
++ "."
else Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
err (Token -> Id
getId Token
t) 2105 (String -> m ()) -> String -> m ()
forall a b. (a -> b) -> a -> b
$ Maybe String -> String
forall a. HasCallStack => Maybe a -> a
fromJust Maybe String
name String -> String -> String
forall a. [a] -> [a] -> [a]
++ " is only valid in loops."
else case (Token -> Maybe String) -> [Token] -> [Maybe String]
forall a b. (a -> b) -> [a] -> [b]
map Token -> Maybe String
subshellType ([Token] -> [Maybe String]) -> [Token] -> [Maybe String]
forall a b. (a -> b) -> a -> b
$ (Token -> Bool) -> [Token] -> [Token]
forall a. (a -> Bool) -> [a] -> [a]
filter (Bool -> Bool
not (Bool -> Bool) -> (Token -> Bool) -> Token -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Token -> Bool
isFunction) [Token]
path of
Just str :: String
str:_ -> Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
warn (Token -> Id
getId Token
t) 2106 (String -> m ()) -> String -> m ()
forall a b. (a -> b) -> a -> b
$
"This only exits the subshell caused by the " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
str String -> String -> String
forall a. [a] -> [a] -> [a]
++ "."
_ -> () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
where
name :: Maybe String
name = Token -> Maybe String
getCommandName Token
t
path :: [Token]
path = let p :: [Token]
p = Map Id Token -> Token -> [Token]
getPath (Parameters -> Map Id Token
parentMap Parameters
params) Token
t in (Token -> Bool) -> [Token] -> [Token]
forall a. (a -> Bool) -> [a] -> [a]
filter Token -> Bool
relevant [Token]
p
subshellType :: Token -> Maybe String
subshellType t :: Token
t = case Parameters -> Token -> Scope
leadType Parameters
params Token
t of
NoneScope -> Maybe String
forall a. Maybe a
Nothing
SubshellScope str :: String
str -> String -> Maybe String
forall (m :: * -> *) a. Monad m => a -> m a
return String
str
relevant :: Token -> Bool
relevant t :: Token
t = Token -> Bool
isLoop Token
t Bool -> Bool -> Bool
|| Token -> Bool
isFunction Token
t Bool -> Bool -> Bool
|| Maybe String -> Bool
forall a. Maybe a -> Bool
isJust (Token -> Maybe String
subshellType Token
t)
checkLoopKeywordScope _ _ = () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
prop_checkFunctionDeclarations1 :: Bool
prop_checkFunctionDeclarations1 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkFunctionDeclarations "#!/bin/ksh\nfunction foo() { command foo --lol \"$@\"; }"
prop_checkFunctionDeclarations2 :: Bool
prop_checkFunctionDeclarations2 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkFunctionDeclarations "#!/bin/dash\nfunction foo { lol; }"
prop_checkFunctionDeclarations3 :: Bool
prop_checkFunctionDeclarations3 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkFunctionDeclarations "foo() { echo bar; }"
checkFunctionDeclarations :: Parameters -> Token -> m ()
checkFunctionDeclarations params :: Parameters
params
(T_Function id :: Id
id (FunctionKeyword hasKeyword :: Bool
hasKeyword) (FunctionParentheses hasParens :: Bool
hasParens) _ _) =
case Parameters -> Shell
shellType Parameters
params of
Bash -> () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
Ksh ->
Bool -> m () -> m ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (Bool
hasKeyword Bool -> Bool -> Bool
&& Bool
hasParens) (m () -> m ()) -> m () -> m ()
forall a b. (a -> b) -> a -> b
$
Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
err Id
id 2111 "ksh does not allow 'function' keyword and '()' at the same time."
Dash -> m ()
forSh
Sh -> m ()
forSh
where
forSh :: m ()
forSh = do
Bool -> m () -> m ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (Bool
hasKeyword Bool -> Bool -> Bool
&& Bool
hasParens) (m () -> m ()) -> m () -> m ()
forall a b. (a -> b) -> a -> b
$
Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
warn Id
id 2112 "'function' keyword is non-standard. Delete it."
Bool -> m () -> m ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (Bool
hasKeyword Bool -> Bool -> Bool
&& Bool -> Bool
not Bool
hasParens) (m () -> m ()) -> m () -> m ()
forall a b. (a -> b) -> a -> b
$
Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
warn Id
id 2113 "'function' keyword is non-standard. Use 'foo()' instead of 'function foo'."
checkFunctionDeclarations _ _ = () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
prop_checkStderrPipe1 :: Bool
prop_checkStderrPipe1 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkStderrPipe "#!/bin/ksh\nfoo |& bar"
prop_checkStderrPipe2 :: Bool
prop_checkStderrPipe2 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkStderrPipe "#!/bin/bash\nfoo |& bar"
checkStderrPipe :: Parameters -> Token -> m ()
checkStderrPipe params :: Parameters
params =
case Parameters -> Shell
shellType Parameters
params of
Ksh -> Token -> m ()
forall (m :: * -> *). MonadWriter [TokenComment] m => Token -> m ()
match
_ -> m () -> Token -> m ()
forall a b. a -> b -> a
const (m () -> Token -> m ()) -> m () -> Token -> m ()
forall a b. (a -> b) -> a -> b
$ () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
where
match :: Token -> m ()
match (T_Pipe id :: Id
id "|&") =
Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
err Id
id 2118 "Ksh does not support |&. Use 2>&1 |."
match _ = () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
prop_checkUnpassedInFunctions1 :: Bool
prop_checkUnpassedInFunctions1 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyTree Parameters -> Token -> [TokenComment]
checkUnpassedInFunctions "foo() { echo $1; }; foo"
prop_checkUnpassedInFunctions2 :: Bool
prop_checkUnpassedInFunctions2 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
checkUnpassedInFunctions "foo() { echo $1; };"
prop_checkUnpassedInFunctions3 :: Bool
prop_checkUnpassedInFunctions3 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
checkUnpassedInFunctions "foo() { echo $lol; }; foo"
prop_checkUnpassedInFunctions4 :: Bool
prop_checkUnpassedInFunctions4 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
checkUnpassedInFunctions "foo() { echo $0; }; foo"
prop_checkUnpassedInFunctions5 :: Bool
prop_checkUnpassedInFunctions5 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
checkUnpassedInFunctions "foo() { echo $1; }; foo 'lol'; foo"
prop_checkUnpassedInFunctions6 :: Bool
prop_checkUnpassedInFunctions6 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
checkUnpassedInFunctions "foo() { set -- *; echo $1; }; foo"
prop_checkUnpassedInFunctions7 :: Bool
prop_checkUnpassedInFunctions7 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyTree Parameters -> Token -> [TokenComment]
checkUnpassedInFunctions "foo() { echo $1; }; foo; foo;"
prop_checkUnpassedInFunctions8 :: Bool
prop_checkUnpassedInFunctions8 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
checkUnpassedInFunctions "foo() { echo $((1)); }; foo;"
prop_checkUnpassedInFunctions9 :: Bool
prop_checkUnpassedInFunctions9 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
checkUnpassedInFunctions "foo() { echo $(($b)); }; foo;"
prop_checkUnpassedInFunctions10 :: Bool
prop_checkUnpassedInFunctions10= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
checkUnpassedInFunctions "foo() { echo $!; }; foo;"
prop_checkUnpassedInFunctions11 :: Bool
prop_checkUnpassedInFunctions11= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
checkUnpassedInFunctions "foo() { bar() { echo $1; }; bar baz; }; foo;"
prop_checkUnpassedInFunctions12 :: Bool
prop_checkUnpassedInFunctions12= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
checkUnpassedInFunctions "foo() { echo ${!var*}; }; foo;"
prop_checkUnpassedInFunctions13 :: Bool
prop_checkUnpassedInFunctions13= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
checkUnpassedInFunctions "# shellcheck disable=SC2120\nfoo() { echo $1; }\nfoo\n"
prop_checkUnpassedInFunctions14 :: Bool
prop_checkUnpassedInFunctions14= (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyTree Parameters -> Token -> [TokenComment]
checkUnpassedInFunctions "foo() { echo $#; }; foo"
checkUnpassedInFunctions :: Parameters -> Token -> [TokenComment]
checkUnpassedInFunctions params :: Parameters
params root :: Token
root =
WriterT [TokenComment] Identity () -> [TokenComment]
forall w a. Writer w a -> w
execWriter (WriterT [TokenComment] Identity () -> [TokenComment])
-> WriterT [TokenComment] Identity () -> [TokenComment]
forall a b. (a -> b) -> a -> b
$ ([(String, Bool, Token)] -> WriterT [TokenComment] Identity ())
-> [[(String, Bool, Token)]] -> WriterT [TokenComment] Identity ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ [(String, Bool, Token)] -> WriterT [TokenComment] Identity ()
forall (f :: * -> *).
MonadWriter [TokenComment] f =>
[(String, Bool, Token)] -> f ()
warnForGroup [[(String, Bool, Token)]]
referenceGroups
where
functionMap :: Map.Map String Token
functionMap :: Map String Token
functionMap = [(String, Token)] -> Map String Token
forall k a. Ord k => [(k, a)] -> Map k a
Map.fromList ([(String, Token)] -> Map String Token)
-> [(String, Token)] -> Map String Token
forall a b. (a -> b) -> a -> b
$
(Token -> (String, Token)) -> [Token] -> [(String, Token)]
forall a b. (a -> b) -> [a] -> [b]
map (\t :: Token
t@(T_Function _ _ _ name :: String
name _) -> (String
name,Token
t)) [Token]
functions
functions :: [Token]
functions = Writer [Token] Token -> [Token]
forall w a. Writer w a -> w
execWriter (Writer [Token] Token -> [Token])
-> Writer [Token] Token -> [Token]
forall a b. (a -> b) -> a -> b
$ (Token -> WriterT [Token] Identity ())
-> Token -> Writer [Token] Token
forall (m :: * -> *).
Monad m =>
(Token -> m ()) -> Token -> m Token
doAnalysis ([Token] -> WriterT [Token] Identity ()
forall w (m :: * -> *). MonadWriter w m => w -> m ()
tell ([Token] -> WriterT [Token] Identity ())
-> (Token -> [Token]) -> Token -> WriterT [Token] Identity ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Maybe Token -> [Token]
forall a. Maybe a -> [a]
maybeToList (Maybe Token -> [Token])
-> (Token -> Maybe Token) -> Token -> [Token]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Token -> Maybe Token
findFunction) Token
root
findFunction :: Token -> Maybe Token
findFunction t :: Token
t@(T_Function id :: Id
id _ _ name :: String
name body :: Token
body)
| (StackData -> Bool) -> [StackData] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any (Token -> StackData -> Bool
isPositionalReference Token
t) [StackData]
flow Bool -> Bool -> Bool
&& Bool -> Bool
not ((StackData -> Bool) -> [StackData] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any StackData -> Bool
isPositionalAssignment [StackData]
flow)
= Token -> Maybe Token
forall (m :: * -> *) a. Monad m => a -> m a
return Token
t
where flow :: [StackData]
flow = Parameters -> Token -> [StackData]
getVariableFlow Parameters
params Token
body
findFunction _ = Maybe Token
forall a. Maybe a
Nothing
isPositionalAssignment :: StackData -> Bool
isPositionalAssignment x :: StackData
x =
case StackData
x of
Assignment (_, _, str :: String
str, _) -> String -> Bool
isPositional String
str
_ -> Bool
False
isPositionalReference :: Token -> StackData -> Bool
isPositionalReference function :: Token
function x :: StackData
x =
case StackData
x of
Reference (_, t :: Token
t, str :: String
str) -> String -> Bool
isPositional String
str Bool -> Bool -> Bool
&& Token
t Token -> Token -> Bool
`isDirectChildOf` Token
function
_ -> Bool
False
isDirectChildOf :: Token -> Token -> Bool
isDirectChildOf child :: Token
child parent :: Token
parent = Bool -> Maybe Bool -> Bool
forall a. a -> Maybe a -> a
fromMaybe Bool
False (Maybe Bool -> Bool) -> Maybe Bool -> Bool
forall a b. (a -> b) -> a -> b
$ do
Token
function <- (Token -> Bool) -> [Token] -> Maybe Token
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Maybe a
find (\x :: Token
x -> case Token
x of
T_Function {} -> Bool
True
T_Script {} -> Bool
True
_ -> Bool
False) ([Token] -> Maybe Token) -> [Token] -> Maybe Token
forall a b. (a -> b) -> a -> b
$
Map Id Token -> Token -> [Token]
getPath (Parameters -> Map Id Token
parentMap Parameters
params) Token
child
Bool -> Maybe Bool
forall (m :: * -> *) a. Monad m => a -> m a
return (Bool -> Maybe Bool) -> Bool -> Maybe Bool
forall a b. (a -> b) -> a -> b
$ Token -> Id
getId Token
parent Id -> Id -> Bool
forall a. Eq a => a -> a -> Bool
== Token -> Id
getId Token
function
referenceList :: [(String, Bool, Token)]
referenceList :: [(String, Bool, Token)]
referenceList = Writer [(String, Bool, Token)] Token -> [(String, Bool, Token)]
forall w a. Writer w a -> w
execWriter (Writer [(String, Bool, Token)] Token -> [(String, Bool, Token)])
-> Writer [(String, Bool, Token)] Token -> [(String, Bool, Token)]
forall a b. (a -> b) -> a -> b
$
(Token -> WriterT [(String, Bool, Token)] Identity ())
-> Token -> Writer [(String, Bool, Token)] Token
forall (m :: * -> *).
Monad m =>
(Token -> m ()) -> Token -> m Token
doAnalysis (Maybe (WriterT [(String, Bool, Token)] Identity ())
-> WriterT [(String, Bool, Token)] Identity ()
forall (t :: * -> *) (m :: * -> *) a.
(Foldable t, Monad m) =>
t (m a) -> m ()
sequence_ (Maybe (WriterT [(String, Bool, Token)] Identity ())
-> WriterT [(String, Bool, Token)] Identity ())
-> (Token -> Maybe (WriterT [(String, Bool, Token)] Identity ()))
-> Token
-> WriterT [(String, Bool, Token)] Identity ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Token -> Maybe (WriterT [(String, Bool, Token)] Identity ())
checkCommand) Token
root
checkCommand :: Token -> Maybe (Writer [(String, Bool, Token)] ())
checkCommand :: Token -> Maybe (WriterT [(String, Bool, Token)] Identity ())
checkCommand t :: Token
t@(T_SimpleCommand _ _ (cmd :: Token
cmd:args :: [Token]
args)) = do
String
str <- Token -> Maybe String
getLiteralString Token
cmd
Bool -> Maybe ()
forall (f :: * -> *). Alternative f => Bool -> f ()
guard (Bool -> Maybe ()) -> Bool -> Maybe ()
forall a b. (a -> b) -> a -> b
$ String -> Map String Token -> Bool
forall k a. Ord k => k -> Map k a -> Bool
Map.member String
str Map String Token
functionMap
WriterT [(String, Bool, Token)] Identity ()
-> Maybe (WriterT [(String, Bool, Token)] Identity ())
forall (m :: * -> *) a. Monad m => a -> m a
return (WriterT [(String, Bool, Token)] Identity ()
-> Maybe (WriterT [(String, Bool, Token)] Identity ()))
-> WriterT [(String, Bool, Token)] Identity ()
-> Maybe (WriterT [(String, Bool, Token)] Identity ())
forall a b. (a -> b) -> a -> b
$ [(String, Bool, Token)]
-> WriterT [(String, Bool, Token)] Identity ()
forall w (m :: * -> *). MonadWriter w m => w -> m ()
tell [(String
str, [Token] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null [Token]
args, Token
t)]
checkCommand _ = Maybe (WriterT [(String, Bool, Token)] Identity ())
forall a. Maybe a
Nothing
isPositional :: String -> Bool
isPositional str :: String
str = String
str String -> String -> Bool
forall a. Eq a => a -> a -> Bool
== "*" Bool -> Bool -> Bool
|| String
str String -> String -> Bool
forall a. Eq a => a -> a -> Bool
== "@" Bool -> Bool -> Bool
|| String
str String -> String -> Bool
forall a. Eq a => a -> a -> Bool
== "#"
Bool -> Bool -> Bool
|| ((Char -> Bool) -> String -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all Char -> Bool
isDigit String
str Bool -> Bool -> Bool
&& String
str String -> String -> Bool
forall a. Eq a => a -> a -> Bool
/= "0" Bool -> Bool -> Bool
&& String
str String -> String -> Bool
forall a. Eq a => a -> a -> Bool
/= "")
isArgumentless :: (a, b, c) -> b
isArgumentless (_, b :: b
b, _) = b
b
referenceGroups :: [[(String, Bool, Token)]]
referenceGroups = Map String [(String, Bool, Token)] -> [[(String, Bool, Token)]]
forall k a. Map k a -> [a]
Map.elems (Map String [(String, Bool, Token)] -> [[(String, Bool, Token)]])
-> Map String [(String, Bool, Token)] -> [[(String, Bool, Token)]]
forall a b. (a -> b) -> a -> b
$ ((String, Bool, Token)
-> Map String [(String, Bool, Token)]
-> Map String [(String, Bool, Token)])
-> Map String [(String, Bool, Token)]
-> [(String, Bool, Token)]
-> Map String [(String, Bool, Token)]
forall (t :: * -> *) a b.
Foldable t =>
(a -> b -> b) -> b -> t a -> b
foldr (String, Bool, Token)
-> Map String [(String, Bool, Token)]
-> Map String [(String, Bool, Token)]
forall k b c.
Ord k =>
(k, b, c) -> Map k [(k, b, c)] -> Map k [(k, b, c)]
updateWith Map String [(String, Bool, Token)]
forall k a. Map k a
Map.empty [(String, Bool, Token)]
referenceList
updateWith :: (k, b, c) -> Map k [(k, b, c)] -> Map k [(k, b, c)]
updateWith x :: (k, b, c)
x@(name :: k
name, _, _) = ([(k, b, c)] -> [(k, b, c)] -> [(k, b, c)])
-> k -> [(k, b, c)] -> Map k [(k, b, c)] -> Map k [(k, b, c)]
forall k a. Ord k => (a -> a -> a) -> k -> a -> Map k a -> Map k a
Map.insertWith [(k, b, c)] -> [(k, b, c)] -> [(k, b, c)]
forall a. [a] -> [a] -> [a]
(++) k
name [(k, b, c)
x]
warnForGroup :: [(String, Bool, Token)] -> f ()
warnForGroup group :: [(String, Bool, Token)]
group =
Bool -> f () -> f ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (((String, Bool, Token) -> Bool) -> [(String, Bool, Token)] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all (String, Bool, Token) -> Bool
forall a b c. (a, b, c) -> b
isArgumentless [(String, Bool, Token)]
group Bool -> Bool -> Bool
&& Bool -> Bool
not Bool
ignoring) (f () -> f ()) -> f () -> f ()
forall a b. (a -> b) -> a -> b
$ do
((String, Bool, Token) -> f ()) -> [(String, Bool, Token)] -> f ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ (String, Bool, Token) -> f ()
forall (m :: * -> *) b.
MonadWriter [TokenComment] m =>
(String, b, Token) -> m ()
suggestParams [(String, Bool, Token)]
group
Token -> String -> f ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Token -> String -> m ()
warnForDeclaration Token
func String
name
where (name :: String
name, func :: Token
func) = [(String, Bool, Token)] -> (String, Token)
forall b c. [(String, b, c)] -> (String, Token)
getFunction [(String, Bool, Token)]
group
ignoring :: Bool
ignoring = Parameters -> Code -> Token -> Bool
shouldIgnoreCode Parameters
params 2120 Token
func
suggestParams :: (String, b, Token) -> m ()
suggestParams (name :: String
name, _, thing :: Token
thing) =
Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
info (Token -> Id
getId Token
thing) 2119 (String -> m ()) -> String -> m ()
forall a b. (a -> b) -> a -> b
$
"Use " String -> String -> String
forall a. [a] -> [a] -> [a]
++ (String -> String
e4m String
name) String -> String -> String
forall a. [a] -> [a] -> [a]
++ " \"$@\" if function's $1 should mean script's $1."
warnForDeclaration :: Token -> String -> m ()
warnForDeclaration func :: Token
func name :: String
name =
Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
warn (Token -> Id
getId Token
func) 2120 (String -> m ()) -> String -> m ()
forall a b. (a -> b) -> a -> b
$
String
name String -> String -> String
forall a. [a] -> [a] -> [a]
++ " references arguments, but none are ever passed."
getFunction :: [(String, b, c)] -> (String, Token)
getFunction ((name :: String
name, _, _):_) =
(String
name, Map String Token
functionMap Map String Token -> String -> Token
forall k a. Ord k => Map k a -> k -> a
Map.! String
name)
prop_checkOverridingPath1 :: Bool
prop_checkOverridingPath1 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkOverridingPath "PATH=\"$var/$foo\""
prop_checkOverridingPath2 :: Bool
prop_checkOverridingPath2 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkOverridingPath "PATH=\"mydir\""
prop_checkOverridingPath3 :: Bool
prop_checkOverridingPath3 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkOverridingPath "PATH=/cow/foo"
prop_checkOverridingPath4 :: Bool
prop_checkOverridingPath4 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkOverridingPath "PATH=/cow/foo/bin"
prop_checkOverridingPath5 :: Bool
prop_checkOverridingPath5 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkOverridingPath "PATH='/bin:/sbin'"
prop_checkOverridingPath6 :: Bool
prop_checkOverridingPath6 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkOverridingPath "PATH=\"$var/$foo\" cmd"
prop_checkOverridingPath7 :: Bool
prop_checkOverridingPath7 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkOverridingPath "PATH=$OLDPATH"
prop_checkOverridingPath8 :: Bool
prop_checkOverridingPath8 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkOverridingPath "PATH=$PATH:/stuff"
checkOverridingPath :: p -> Token -> m ()
checkOverridingPath _ (T_SimpleCommand _ vars :: [Token]
vars []) =
(Token -> m ()) -> [Token] -> m ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ Token -> m ()
forall (m :: * -> *). MonadWriter [TokenComment] m => Token -> m ()
checkVar [Token]
vars
where
checkVar :: Token -> m ()
checkVar (T_Assignment id :: Id
id Assign "PATH" [] word :: Token
word)
| Bool -> Bool
not (Bool -> Bool) -> Bool -> Bool
forall a b. (a -> b) -> a -> b
$ (String -> Bool) -> [String] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any (String -> String -> Bool
forall a. Eq a => [a] -> [a] -> Bool
`isInfixOf` String
string) ["/bin", "/sbin" ] = do
Bool -> m () -> m ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when ('/' Char -> String -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` String
string Bool -> Bool -> Bool
&& ':' Char -> String -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`notElem` String
string) (m () -> m ()) -> m () -> m ()
forall a b. (a -> b) -> a -> b
$ Id -> m ()
forall (m :: * -> *). MonadWriter [TokenComment] m => Id -> m ()
notify Id
id
Bool -> m () -> m ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (Token -> Bool
isLiteral Token
word Bool -> Bool -> Bool
&& ':' Char -> String -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`notElem` String
string Bool -> Bool -> Bool
&& '/' Char -> String -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`notElem` String
string) (m () -> m ()) -> m () -> m ()
forall a b. (a -> b) -> a -> b
$ Id -> m ()
forall (m :: * -> *). MonadWriter [TokenComment] m => Id -> m ()
notify Id
id
where string :: String
string = [String] -> String
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat ([String] -> String) -> [String] -> String
forall a b. (a -> b) -> a -> b
$ Token -> [String]
oversimplify Token
word
checkVar _ = () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
notify :: Id -> m ()
notify id :: Id
id = Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
warn Id
id 2123 "PATH is the shell search path. Use another name."
checkOverridingPath _ _ = () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
prop_checkTildeInPath1 :: Bool
prop_checkTildeInPath1 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkTildeInPath "PATH=\"$PATH:~/bin\""
prop_checkTildeInPath2 :: Bool
prop_checkTildeInPath2 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkTildeInPath "PATH='~foo/bin'"
prop_checkTildeInPath3 :: Bool
prop_checkTildeInPath3 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkTildeInPath "PATH=~/bin"
checkTildeInPath :: p -> Token -> m ()
checkTildeInPath _ (T_SimpleCommand _ vars :: [Token]
vars _) =
(Token -> m ()) -> [Token] -> m ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ Token -> m ()
forall (m :: * -> *). MonadWriter [TokenComment] m => Token -> m ()
checkVar [Token]
vars
where
checkVar :: Token -> m ()
checkVar (T_Assignment id :: Id
id Assign "PATH" [] (T_NormalWord _ parts :: [Token]
parts))
| (Token -> Bool) -> [Token] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any (\x :: Token
x -> Token -> Bool
isQuoted Token
x Bool -> Bool -> Bool
&& Token -> Bool
hasTilde Token
x) [Token]
parts =
Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
warn Id
id 2147 "Literal tilde in PATH works poorly across programs."
checkVar _ = () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
hasTilde :: Token -> Bool
hasTilde t :: Token
t = '~' Char -> String -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` Token -> String
onlyLiteralString Token
t
isQuoted :: Token -> Bool
isQuoted T_DoubleQuoted {} = Bool
True
isQuoted T_SingleQuoted {} = Bool
True
isQuoted _ = Bool
False
checkTildeInPath _ _ = () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
prop_checkUnsupported3 :: Bool
prop_checkUnsupported3 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkUnsupported "#!/bin/sh\ncase foo in bar) baz ;& esac"
prop_checkUnsupported4 :: Bool
prop_checkUnsupported4 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkUnsupported "#!/bin/ksh\ncase foo in bar) baz ;;& esac"
prop_checkUnsupported5 :: Bool
prop_checkUnsupported5 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkUnsupported "#!/bin/bash\necho \"${ ls; }\""
checkUnsupported :: Parameters -> Token -> f ()
checkUnsupported params :: Parameters
params t :: Token
t =
Bool -> f () -> f ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
unless ([Shell] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null [Shell]
support Bool -> Bool -> Bool
|| (Parameters -> Shell
shellType Parameters
params Shell -> [Shell] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [Shell]
support)) (f () -> f ()) -> f () -> f ()
forall a b. (a -> b) -> a -> b
$
String -> f ()
forall (f :: * -> *).
MonadWriter [TokenComment] f =>
String -> f ()
report String
name
where
(name :: String
name, support :: [Shell]
support) = Token -> (String, [Shell])
shellSupport Token
t
report :: String -> m ()
report s :: String
s = Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
err (Token -> Id
getId Token
t) 2127 (String -> m ()) -> String -> m ()
forall a b. (a -> b) -> a -> b
$
"To use " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
s String -> String -> String
forall a. [a] -> [a] -> [a]
++ ", specify #!/usr/bin/env " String -> String -> String
forall a. [a] -> [a] -> [a]
++
(String -> [String] -> String
forall a. [a] -> [[a]] -> [a]
intercalate " or " ([String] -> String) -> ([Shell] -> [String]) -> [Shell] -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Shell -> String) -> [Shell] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map ((Char -> Char) -> String -> String
forall a b. (a -> b) -> [a] -> [b]
map Char -> Char
toLower (String -> String) -> (Shell -> String) -> Shell -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Shell -> String
forall a. Show a => a -> String
show) ([Shell] -> String) -> [Shell] -> String
forall a b. (a -> b) -> a -> b
$ [Shell]
support)
shellSupport :: Token -> (String, [Shell])
shellSupport t :: Token
t =
case Token
t of
T_CaseExpression _ _ list :: [(CaseType, [Token], [Token])]
list -> [CaseType] -> (String, [Shell])
forall (t :: * -> *). Foldable t => t CaseType -> (String, [Shell])
forCase (((CaseType, [Token], [Token]) -> CaseType)
-> [(CaseType, [Token], [Token])] -> [CaseType]
forall a b. (a -> b) -> [a] -> [b]
map (\(a :: CaseType
a,_,_) -> CaseType
a) [(CaseType, [Token], [Token])]
list)
T_DollarBraceCommandExpansion {} -> ("${ ..; } command expansion", [Shell
Ksh])
_ -> ("", [])
where
forCase :: t CaseType -> (String, [Shell])
forCase seps :: t CaseType
seps | CaseType
CaseContinue CaseType -> t CaseType -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` t CaseType
seps = ("cases with ;;&", [Shell
Bash])
forCase seps :: t CaseType
seps | CaseType
CaseFallThrough CaseType -> t CaseType -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` t CaseType
seps = ("cases with ;&", [Shell
Bash, Shell
Ksh])
forCase _ = ("", [])
groupWith :: (a -> a) -> [a] -> [[a]]
groupWith f :: a -> a
f = (a -> a -> Bool) -> [a] -> [[a]]
forall a. (a -> a -> Bool) -> [a] -> [[a]]
groupBy (a -> a -> Bool
forall a. Eq a => a -> a -> Bool
(==) (a -> a -> Bool) -> (a -> a) -> a -> a -> Bool
forall b c a. (b -> b -> c) -> (a -> b) -> a -> a -> c
`on` a -> a
f)
prop_checkMultipleAppends1 :: Bool
prop_checkMultipleAppends1 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkMultipleAppends "foo >> file; bar >> file; baz >> file;"
prop_checkMultipleAppends2 :: Bool
prop_checkMultipleAppends2 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkMultipleAppends "foo >> file; bar | grep f >> file; baz >> file;"
prop_checkMultipleAppends3 :: Bool
prop_checkMultipleAppends3 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkMultipleAppends "foo < file; bar < file; baz < file;"
checkMultipleAppends :: p -> Token -> m ()
checkMultipleAppends params :: p
params t :: Token
t =
([Token] -> m ()) -> [[Token]] -> m ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ [Token] -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
[Token] -> m ()
checkList ([[Token]] -> m ()) -> [[Token]] -> m ()
forall a b. (a -> b) -> a -> b
$ Token -> [[Token]]
getCommandSequences Token
t
where
checkList :: [Token] -> m ()
checkList list :: [Token]
list =
([Maybe (Token, Id)] -> m ()) -> [[Maybe (Token, Id)]] -> m ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ [Maybe (Token, Id)] -> m ()
forall (m :: * -> *) a.
MonadWriter [TokenComment] m =>
[Maybe (a, Id)] -> m ()
checkGroup ((Maybe (Token, Id) -> Maybe Token)
-> [Maybe (Token, Id)] -> [[Maybe (Token, Id)]]
forall a a. Eq a => (a -> a) -> [a] -> [[a]]
groupWith (((Token, Id) -> Token) -> Maybe (Token, Id) -> Maybe Token
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (Token, Id) -> Token
forall a b. (a, b) -> a
fst) ([Maybe (Token, Id)] -> [[Maybe (Token, Id)]])
-> [Maybe (Token, Id)] -> [[Maybe (Token, Id)]]
forall a b. (a -> b) -> a -> b
$ (Token -> Maybe (Token, Id)) -> [Token] -> [Maybe (Token, Id)]
forall a b. (a -> b) -> [a] -> [b]
map Token -> Maybe (Token, Id)
getTarget [Token]
list)
checkGroup :: [Maybe (a, Id)] -> m ()
checkGroup (Just (_,id :: Id
id):_:_:_) =
Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
style Id
id 2129
"Consider using { cmd1; cmd2; } >> file instead of individual redirects."
checkGroup _ = () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
getTarget :: Token -> Maybe (Token, Id)
getTarget (T_Annotation _ _ t :: Token
t) = Token -> Maybe (Token, Id)
getTarget Token
t
getTarget (T_Pipeline _ _ args :: [Token]
args@(_:_)) = Token -> Maybe (Token, Id)
getTarget ([Token] -> Token
forall a. [a] -> a
last [Token]
args)
getTarget (T_Redirecting id :: Id
id list :: [Token]
list _) = do
Token
file <- (Token -> Maybe Token) -> [Token] -> [Token]
forall a b. (a -> Maybe b) -> [a] -> [b]
mapMaybe Token -> Maybe Token
getAppend [Token]
list [Token] -> Int -> Maybe Token
forall a. [a] -> Int -> Maybe a
!!! 0
(Token, Id) -> Maybe (Token, Id)
forall (m :: * -> *) a. Monad m => a -> m a
return (Token
file, Id
id)
getTarget _ = Maybe (Token, Id)
forall a. Maybe a
Nothing
getAppend :: Token -> Maybe Token
getAppend (T_FdRedirect _ _ (T_IoFile _ T_DGREAT {} f :: Token
f)) = Token -> Maybe Token
forall (m :: * -> *) a. Monad m => a -> m a
return Token
f
getAppend _ = Maybe Token
forall a. Maybe a
Nothing
prop_checkSuspiciousIFS1 :: Bool
prop_checkSuspiciousIFS1 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkSuspiciousIFS "IFS=\"\\n\""
prop_checkSuspiciousIFS2 :: Bool
prop_checkSuspiciousIFS2 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkSuspiciousIFS "IFS=$'\\t'"
prop_checkSuspiciousIFS3 :: Bool
prop_checkSuspiciousIFS3 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkSuspiciousIFS "IFS=' \\t\\n'"
checkSuspiciousIFS :: Parameters -> Token -> m ()
checkSuspiciousIFS params :: Parameters
params (T_Assignment _ _ "IFS" [] value :: Token
value) =
(String -> m ()) -> Maybe String -> m ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ String -> m ()
forall (f :: * -> *).
MonadWriter [TokenComment] f =>
String -> f ()
check (Maybe String -> m ()) -> Maybe String -> m ()
forall a b. (a -> b) -> a -> b
$ Token -> Maybe String
getLiteralString Token
value
where
hasDollarSingle :: Bool
hasDollarSingle = Parameters -> Shell
shellType Parameters
params Shell -> Shell -> Bool
forall a. Eq a => a -> a -> Bool
== Shell
Bash Bool -> Bool -> Bool
|| Parameters -> Shell
shellType Parameters
params Shell -> Shell -> Bool
forall a. Eq a => a -> a -> Bool
== Shell
Ksh
n :: String
n = if Bool
hasDollarSingle then "$'\\n'" else "'<literal linefeed here>'"
t :: String
t = if Bool
hasDollarSingle then "$'\\t'" else "\"$(printf '\\t')\""
check :: String -> m ()
check value :: String
value =
case String
value of
"\\n" -> String -> m ()
forall (f :: * -> *).
MonadWriter [TokenComment] f =>
String -> f ()
suggest String
n
"\\t" -> String -> m ()
forall (f :: * -> *).
MonadWriter [TokenComment] f =>
String -> f ()
suggest String
t
x :: String
x | '\\' Char -> String -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` String
x -> String -> m ()
forall (f :: * -> *).
MonadWriter [TokenComment] f =>
String -> f ()
suggest2 "a literal backslash"
x :: String
x | 'n' Char -> String -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` String
x -> String -> m ()
forall (f :: * -> *).
MonadWriter [TokenComment] f =>
String -> f ()
suggest2 "the literal letter 'n'"
x :: String
x | 't' Char -> String -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` String
x -> String -> m ()
forall (f :: * -> *).
MonadWriter [TokenComment] f =>
String -> f ()
suggest2 "the literal letter 't'"
_ -> () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
suggest :: String -> m ()
suggest r :: String
r = Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
warn (Token -> Id
getId Token
value) 2141 (String -> m ()) -> String -> m ()
forall a b. (a -> b) -> a -> b
$ "This backslash is literal. Did you mean IFS=" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
r String -> String -> String
forall a. [a] -> [a] -> [a]
++ " ?"
suggest2 :: String -> m ()
suggest2 desc :: String
desc = Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
warn (Token -> Id
getId Token
value) 2141 (String -> m ()) -> String -> m ()
forall a b. (a -> b) -> a -> b
$ "This IFS value contains " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
desc String -> String -> String
forall a. [a] -> [a] -> [a]
++ ". For tabs/linefeeds/escapes, use $'..', literal, or printf."
checkSuspiciousIFS _ _ = () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
prop_checkGrepQ1 :: Bool
prop_checkGrepQ1= (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkShouldUseGrepQ "[[ $(foo | grep bar) ]]"
prop_checkGrepQ2 :: Bool
prop_checkGrepQ2= (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkShouldUseGrepQ "[ -z $(fgrep lol) ]"
prop_checkGrepQ3 :: Bool
prop_checkGrepQ3= (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkShouldUseGrepQ "[ -n \"$(foo | zgrep lol)\" ]"
prop_checkGrepQ4 :: Bool
prop_checkGrepQ4= (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkShouldUseGrepQ "[ -z $(grep bar | cmd) ]"
prop_checkGrepQ5 :: Bool
prop_checkGrepQ5= (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkShouldUseGrepQ "rm $(ls | grep file)"
prop_checkGrepQ6 :: Bool
prop_checkGrepQ6= (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkShouldUseGrepQ "[[ -n $(pgrep foo) ]]"
checkShouldUseGrepQ :: p -> Token -> m ()
checkShouldUseGrepQ params :: p
params t :: Token
t =
Maybe (m ()) -> m ()
forall (t :: * -> *) (m :: * -> *) a.
(Foldable t, Monad m) =>
t (m a) -> m ()
sequence_ (Maybe (m ()) -> m ()) -> Maybe (m ()) -> m ()
forall a b. (a -> b) -> a -> b
$ case Token
t of
TC_Nullary id :: Id
id _ token :: Token
token -> Id -> Bool -> Token -> Maybe (m ())
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Bool -> Token -> Maybe (m ())
check Id
id Bool
True Token
token
TC_Unary id :: Id
id _ "-n" token :: Token
token -> Id -> Bool -> Token -> Maybe (m ())
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Bool -> Token -> Maybe (m ())
check Id
id Bool
True Token
token
TC_Unary id :: Id
id _ "-z" token :: Token
token -> Id -> Bool -> Token -> Maybe (m ())
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Bool -> Token -> Maybe (m ())
check Id
id Bool
False Token
token
_ -> String -> Maybe (m ())
forall (m :: * -> *) a. MonadFail m => String -> m a
fail "not check"
where
check :: Id -> Bool -> Token -> Maybe (m ())
check id :: Id
id bool :: Bool
bool token :: Token
token = do
String
name <- Token -> Maybe String
getFinalGrep Token
token
let op :: String
op = if Bool
bool then "-n" else "-z"
let flip :: String
flip = if Bool
bool then "" else "! "
m () -> Maybe (m ())
forall (m :: * -> *) a. Monad m => a -> m a
return (m () -> Maybe (m ()))
-> (String -> m ()) -> String -> Maybe (m ())
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
style Id
id 2143 (String -> Maybe (m ())) -> String -> Maybe (m ())
forall a b. (a -> b) -> a -> b
$
"Use " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
flip String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
name String -> String -> String
forall a. [a] -> [a] -> [a]
++ " -q instead of " String -> String -> String
forall a. [a] -> [a] -> [a]
++
"comparing output with [ " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
op String -> String -> String
forall a. [a] -> [a] -> [a]
++ " .. ]."
getFinalGrep :: Token -> Maybe String
getFinalGrep t :: Token
t = do
[Token]
cmds <- Token -> Maybe [Token]
forall (m :: * -> *). MonadFail m => Token -> m [Token]
getPipeline Token
t
Bool -> Maybe ()
forall (f :: * -> *). Alternative f => Bool -> f ()
guard (Bool -> Maybe ()) -> ([Token] -> Bool) -> [Token] -> Maybe ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Bool -> Bool
not (Bool -> Bool) -> ([Token] -> Bool) -> [Token] -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Token] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null ([Token] -> Maybe ()) -> [Token] -> Maybe ()
forall a b. (a -> b) -> a -> b
$ [Token]
cmds
String
name <- Token -> Maybe String
getCommandBasename (Token -> Maybe String) -> Token -> Maybe String
forall a b. (a -> b) -> a -> b
$ [Token] -> Token
forall a. [a] -> a
last [Token]
cmds
Bool -> Maybe ()
forall (f :: * -> *). Alternative f => Bool -> f ()
guard (Bool -> Maybe ()) -> (String -> Bool) -> String -> Maybe ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> Bool
isGrep (String -> Maybe ()) -> String -> Maybe ()
forall a b. (a -> b) -> a -> b
$ String
name
String -> Maybe String
forall (m :: * -> *) a. Monad m => a -> m a
return String
name
getPipeline :: Token -> m [Token]
getPipeline t :: Token
t =
case Token
t of
T_NormalWord _ [x :: Token
x] -> Token -> m [Token]
getPipeline Token
x
T_DoubleQuoted _ [x :: Token
x] -> Token -> m [Token]
getPipeline Token
x
T_DollarExpansion _ [x :: Token
x] -> Token -> m [Token]
getPipeline Token
x
T_Pipeline _ _ cmds :: [Token]
cmds -> [Token] -> m [Token]
forall (m :: * -> *) a. Monad m => a -> m a
return [Token]
cmds
_ -> String -> m [Token]
forall (m :: * -> *) a. MonadFail m => String -> m a
fail "unknown"
isGrep :: String -> Bool
isGrep = (String -> [String] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` ["grep", "egrep", "fgrep", "zgrep"])
prop_checkTestArgumentSplitting1 :: Bool
prop_checkTestArgumentSplitting1 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
checkTestArgumentSplitting "[ -e *.mp3 ]"
prop_checkTestArgumentSplitting2 :: Bool
prop_checkTestArgumentSplitting2 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
checkTestArgumentSplitting "[[ $a == *b* ]]"
prop_checkTestArgumentSplitting3 :: Bool
prop_checkTestArgumentSplitting3 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
checkTestArgumentSplitting "[[ *.png == '' ]]"
prop_checkTestArgumentSplitting4 :: Bool
prop_checkTestArgumentSplitting4 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
checkTestArgumentSplitting "[[ foo == f{o,oo,ooo} ]]"
prop_checkTestArgumentSplitting5 :: Bool
prop_checkTestArgumentSplitting5 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
checkTestArgumentSplitting "[[ $@ ]]"
prop_checkTestArgumentSplitting6 :: Bool
prop_checkTestArgumentSplitting6 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
checkTestArgumentSplitting "[ -e $@ ]"
prop_checkTestArgumentSplitting7 :: Bool
prop_checkTestArgumentSplitting7 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
checkTestArgumentSplitting "[ $@ == $@ ]"
prop_checkTestArgumentSplitting8 :: Bool
prop_checkTestArgumentSplitting8 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
checkTestArgumentSplitting "[[ $@ = $@ ]]"
prop_checkTestArgumentSplitting9 :: Bool
prop_checkTestArgumentSplitting9 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
checkTestArgumentSplitting "[[ foo =~ bar{1,2} ]]"
prop_checkTestArgumentSplitting10 :: Bool
prop_checkTestArgumentSplitting10 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
checkTestArgumentSplitting "[ \"$@\" ]"
prop_checkTestArgumentSplitting11 :: Bool
prop_checkTestArgumentSplitting11 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
checkTestArgumentSplitting "[[ \"$@\" ]]"
prop_checkTestArgumentSplitting12 :: Bool
prop_checkTestArgumentSplitting12 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
checkTestArgumentSplitting "[ *.png ]"
prop_checkTestArgumentSplitting13 :: Bool
prop_checkTestArgumentSplitting13 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
checkTestArgumentSplitting "[ \"$@\" == \"\" ]"
prop_checkTestArgumentSplitting14 :: Bool
prop_checkTestArgumentSplitting14 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
checkTestArgumentSplitting "[[ \"$@\" == \"\" ]]"
prop_checkTestArgumentSplitting15 :: Bool
prop_checkTestArgumentSplitting15 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
checkTestArgumentSplitting "[[ \"$*\" == \"\" ]]"
prop_checkTestArgumentSplitting16 :: Bool
prop_checkTestArgumentSplitting16 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
checkTestArgumentSplitting "[[ -v foo[123] ]]"
prop_checkTestArgumentSplitting17 :: Bool
prop_checkTestArgumentSplitting17 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
checkTestArgumentSplitting "#!/bin/ksh\n[ -e foo* ]"
prop_checkTestArgumentSplitting18 :: Bool
prop_checkTestArgumentSplitting18 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
checkTestArgumentSplitting "#!/bin/ksh\n[ -d foo* ]"
prop_checkTestArgumentSplitting19 :: Bool
prop_checkTestArgumentSplitting19 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
checkTestArgumentSplitting "[[ var[x] -eq 2*3 ]]"
prop_checkTestArgumentSplitting20 :: Bool
prop_checkTestArgumentSplitting20 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
checkTestArgumentSplitting "[ var[x] -eq 2 ]"
prop_checkTestArgumentSplitting21 :: Bool
prop_checkTestArgumentSplitting21 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
checkTestArgumentSplitting "[ 6 -eq 2*3 ]"
checkTestArgumentSplitting :: Parameters -> Token -> Writer [TokenComment] ()
checkTestArgumentSplitting :: Parameters -> Token -> WriterT [TokenComment] Identity ()
checkTestArgumentSplitting params :: Parameters
params t :: Token
t =
case Token
t of
(TC_Unary _ typ :: ConditionType
typ op :: String
op token :: Token
token) | Token -> Bool
isGlob Token
token ->
if String
op String -> String -> Bool
forall a. Eq a => a -> a -> Bool
== "-v"
then
Bool
-> WriterT [TokenComment] Identity ()
-> WriterT [TokenComment] Identity ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (ConditionType
typ ConditionType -> ConditionType -> Bool
forall a. Eq a => a -> a -> Bool
== ConditionType
SingleBracket) (WriterT [TokenComment] Identity ()
-> WriterT [TokenComment] Identity ())
-> WriterT [TokenComment] Identity ()
-> WriterT [TokenComment] Identity ()
forall a b. (a -> b) -> a -> b
$
Id -> Code -> String -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
err (Token -> Id
getId Token
token) 2208 (String -> WriterT [TokenComment] Identity ())
-> String -> WriterT [TokenComment] Identity ()
forall a b. (a -> b) -> a -> b
$
"Use [[ ]] or quote arguments to -v to avoid glob expansion."
else
if (ConditionType
typ ConditionType -> ConditionType -> Bool
forall a. Eq a => a -> a -> Bool
== ConditionType
SingleBracket Bool -> Bool -> Bool
&& Parameters -> Shell
shellType Parameters
params Shell -> Shell -> Bool
forall a. Eq a => a -> a -> Bool
== Shell
Ksh)
then
Bool
-> WriterT [TokenComment] Identity ()
-> WriterT [TokenComment] Identity ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (String
op String -> [String] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [['-', Char
c] | Char
c <- "bcdfgkprsuwxLhNOGRS" ]) (WriterT [TokenComment] Identity ()
-> WriterT [TokenComment] Identity ())
-> WriterT [TokenComment] Identity ()
-> WriterT [TokenComment] Identity ()
forall a b. (a -> b) -> a -> b
$
Id -> Code -> String -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
warn (Token -> Id
getId Token
token) 2245 (String -> WriterT [TokenComment] Identity ())
-> String -> WriterT [TokenComment] Identity ()
forall a b. (a -> b) -> a -> b
$
String
op String -> String -> String
forall a. [a] -> [a] -> [a]
++ " only applies to the first expansion of this glob. Use a loop to check any/all."
else
Id -> Code -> String -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
err (Token -> Id
getId Token
token) 2144 (String -> WriterT [TokenComment] Identity ())
-> String -> WriterT [TokenComment] Identity ()
forall a b. (a -> b) -> a -> b
$
String
op String -> String -> String
forall a. [a] -> [a] -> [a]
++ " doesn't work with globs. Use a for loop."
(TC_Nullary _ typ :: ConditionType
typ token :: Token
token) -> do
ConditionType -> Token -> WriterT [TokenComment] Identity ()
forall (f :: * -> *).
MonadWriter [TokenComment] f =>
ConditionType -> Token -> f ()
checkBraces ConditionType
typ Token
token
ConditionType -> Token -> WriterT [TokenComment] Identity ()
forall (f :: * -> *).
MonadWriter [TokenComment] f =>
ConditionType -> Token -> f ()
checkGlobs ConditionType
typ Token
token
Bool
-> WriterT [TokenComment] Identity ()
-> WriterT [TokenComment] Identity ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (ConditionType
typ ConditionType -> ConditionType -> Bool
forall a. Eq a => a -> a -> Bool
== ConditionType
DoubleBracket) (WriterT [TokenComment] Identity ()
-> WriterT [TokenComment] Identity ())
-> WriterT [TokenComment] Identity ()
-> WriterT [TokenComment] Identity ()
forall a b. (a -> b) -> a -> b
$
ConditionType -> Token -> WriterT [TokenComment] Identity ()
forall (f :: * -> *).
MonadWriter [TokenComment] f =>
ConditionType -> Token -> f ()
checkArrays ConditionType
typ Token
token
(TC_Unary _ typ :: ConditionType
typ op :: String
op token :: Token
token) -> ConditionType -> Token -> WriterT [TokenComment] Identity ()
forall (f :: * -> *).
MonadWriter [TokenComment] f =>
ConditionType -> Token -> f ()
checkAll ConditionType
typ Token
token
(TC_Binary _ typ :: ConditionType
typ op :: String
op lhs :: Token
lhs rhs :: Token
rhs) | String
op String -> [String] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [String]
arithmeticBinaryTestOps ->
if ConditionType
typ ConditionType -> ConditionType -> Bool
forall a. Eq a => a -> a -> Bool
== ConditionType
DoubleBracket
then
(Token -> WriterT [TokenComment] Identity ())
-> [Token] -> WriterT [TokenComment] Identity ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ (\c :: Token
c -> do
ConditionType -> Token -> WriterT [TokenComment] Identity ()
forall (f :: * -> *).
MonadWriter [TokenComment] f =>
ConditionType -> Token -> f ()
checkArrays ConditionType
typ Token
c
ConditionType -> Token -> WriterT [TokenComment] Identity ()
forall (f :: * -> *).
MonadWriter [TokenComment] f =>
ConditionType -> Token -> f ()
checkBraces ConditionType
typ Token
c) [Token
lhs, Token
rhs]
else
(Token -> WriterT [TokenComment] Identity ())
-> [Token] -> WriterT [TokenComment] Identity ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ (\c :: Token
c -> do
ConditionType -> Token -> WriterT [TokenComment] Identity ()
forall (f :: * -> *).
MonadWriter [TokenComment] f =>
ConditionType -> Token -> f ()
checkNumericalGlob ConditionType
typ Token
c
ConditionType -> Token -> WriterT [TokenComment] Identity ()
forall (f :: * -> *).
MonadWriter [TokenComment] f =>
ConditionType -> Token -> f ()
checkArrays ConditionType
typ Token
c
ConditionType -> Token -> WriterT [TokenComment] Identity ()
forall (f :: * -> *).
MonadWriter [TokenComment] f =>
ConditionType -> Token -> f ()
checkBraces ConditionType
typ Token
c) [Token
lhs, Token
rhs]
(TC_Binary _ typ :: ConditionType
typ op :: String
op lhs :: Token
lhs rhs :: Token
rhs) ->
if String
op String -> [String] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` ["=", "==", "!=", "=~"]
then do
ConditionType -> Token -> WriterT [TokenComment] Identity ()
forall (f :: * -> *).
MonadWriter [TokenComment] f =>
ConditionType -> Token -> f ()
checkAll ConditionType
typ Token
lhs
ConditionType -> Token -> WriterT [TokenComment] Identity ()
forall (f :: * -> *).
MonadWriter [TokenComment] f =>
ConditionType -> Token -> f ()
checkArrays ConditionType
typ Token
rhs
ConditionType -> Token -> WriterT [TokenComment] Identity ()
forall (f :: * -> *).
MonadWriter [TokenComment] f =>
ConditionType -> Token -> f ()
checkBraces ConditionType
typ Token
rhs
else (Token -> WriterT [TokenComment] Identity ())
-> [Token] -> WriterT [TokenComment] Identity ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ (ConditionType -> Token -> WriterT [TokenComment] Identity ()
forall (f :: * -> *).
MonadWriter [TokenComment] f =>
ConditionType -> Token -> f ()
checkAll ConditionType
typ) [Token
lhs, Token
rhs]
_ -> () -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
where
checkAll :: ConditionType -> Token -> m ()
checkAll typ :: ConditionType
typ token :: Token
token = do
ConditionType -> Token -> m ()
forall (f :: * -> *).
MonadWriter [TokenComment] f =>
ConditionType -> Token -> f ()
checkArrays ConditionType
typ Token
token
ConditionType -> Token -> m ()
forall (f :: * -> *).
MonadWriter [TokenComment] f =>
ConditionType -> Token -> f ()
checkBraces ConditionType
typ Token
token
ConditionType -> Token -> m ()
forall (f :: * -> *).
MonadWriter [TokenComment] f =>
ConditionType -> Token -> f ()
checkGlobs ConditionType
typ Token
token
checkArrays :: ConditionType -> Token -> f ()
checkArrays typ :: ConditionType
typ token :: Token
token =
Bool -> f () -> f ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when ((Token -> Bool) -> [Token] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any Token -> Bool
isArrayExpansion ([Token] -> Bool) -> [Token] -> Bool
forall a b. (a -> b) -> a -> b
$ Token -> [Token]
getWordParts Token
token) (f () -> f ()) -> f () -> f ()
forall a b. (a -> b) -> a -> b
$
if ConditionType
typ ConditionType -> ConditionType -> Bool
forall a. Eq a => a -> a -> Bool
== ConditionType
SingleBracket
then Id -> Code -> String -> f ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
warn (Token -> Id
getId Token
token) 2198 "Arrays don't work as operands in [ ]. Use a loop (or concatenate with * instead of @)."
else Id -> Code -> String -> f ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
err (Token -> Id
getId Token
token) 2199 "Arrays implicitly concatenate in [[ ]]. Use a loop (or explicit * instead of @)."
checkBraces :: ConditionType -> Token -> f ()
checkBraces typ :: ConditionType
typ token :: Token
token =
Bool -> f () -> f ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when ((Token -> Bool) -> [Token] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any Token -> Bool
isBraceExpansion ([Token] -> Bool) -> [Token] -> Bool
forall a b. (a -> b) -> a -> b
$ Token -> [Token]
getWordParts Token
token) (f () -> f ()) -> f () -> f ()
forall a b. (a -> b) -> a -> b
$
if ConditionType
typ ConditionType -> ConditionType -> Bool
forall a. Eq a => a -> a -> Bool
== ConditionType
SingleBracket
then Id -> Code -> String -> f ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
warn (Token -> Id
getId Token
token) 2200 "Brace expansions don't work as operands in [ ]. Use a loop."
else Id -> Code -> String -> f ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
err (Token -> Id
getId Token
token) 2201 "Brace expansion doesn't happen in [[ ]]. Use a loop."
checkGlobs :: ConditionType -> Token -> f ()
checkGlobs typ :: ConditionType
typ token :: Token
token =
Bool -> f () -> f ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (Token -> Bool
isGlob Token
token) (f () -> f ()) -> f () -> f ()
forall a b. (a -> b) -> a -> b
$
if ConditionType
typ ConditionType -> ConditionType -> Bool
forall a. Eq a => a -> a -> Bool
== ConditionType
SingleBracket
then Id -> Code -> String -> f ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
warn (Token -> Id
getId Token
token) 2202 "Globs don't work as operands in [ ]. Use a loop."
else Id -> Code -> String -> f ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
err (Token -> Id
getId Token
token) 2203 "Globs are ignored in [[ ]] except right of =/!=. Use a loop."
checkNumericalGlob :: ConditionType -> Token -> f ()
checkNumericalGlob SingleBracket token :: Token
token =
Bool -> f () -> f ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (Parameters -> Shell
shellType Parameters
params Shell -> Shell -> Bool
forall a. Eq a => a -> a -> Bool
/= Shell
Ksh Bool -> Bool -> Bool
&& Token -> Bool
isGlob Token
token) (f () -> f ()) -> f () -> f ()
forall a b. (a -> b) -> a -> b
$
Id -> Code -> String -> f ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
err (Token -> Id
getId Token
token) 2255 "[ ] does not apply arithmetic evaluation. Evaluate with $((..)) for numbers, or use string comparator for strings."
prop_checkMaskedReturns1 :: Bool
prop_checkMaskedReturns1 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkMaskedReturns "f() { local a=$(false); }"
prop_checkMaskedReturns2 :: Bool
prop_checkMaskedReturns2 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkMaskedReturns "declare a=$(false)"
prop_checkMaskedReturns3 :: Bool
prop_checkMaskedReturns3 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkMaskedReturns "declare a=\"`false`\""
prop_checkMaskedReturns4 :: Bool
prop_checkMaskedReturns4 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkMaskedReturns "readonly a=$(false)"
prop_checkMaskedReturns5 :: Bool
prop_checkMaskedReturns5 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkMaskedReturns "readonly a=\"`false`\""
prop_checkMaskedReturns6 :: Bool
prop_checkMaskedReturns6 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkMaskedReturns "declare a; a=$(false)"
prop_checkMaskedReturns7 :: Bool
prop_checkMaskedReturns7 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkMaskedReturns "f() { local -r a=$(false); }"
prop_checkMaskedReturns8 :: Bool
prop_checkMaskedReturns8 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkMaskedReturns "a=$(false); readonly a"
checkMaskedReturns :: p -> Token -> m ()
checkMaskedReturns _ t :: Token
t@(T_SimpleCommand id :: Id
id _ (cmd :: Token
cmd:rest :: [Token]
rest)) = Maybe (m ()) -> m ()
forall (t :: * -> *) (m :: * -> *) a.
(Foldable t, Monad m) =>
t (m a) -> m ()
sequence_ (Maybe (m ()) -> m ()) -> Maybe (m ()) -> m ()
forall a b. (a -> b) -> a -> b
$ do
String
name <- Token -> Maybe String
getCommandName Token
t
Bool -> Maybe ()
forall (f :: * -> *). Alternative f => Bool -> f ()
guard (Bool -> Maybe ()) -> Bool -> Maybe ()
forall a b. (a -> b) -> a -> b
$ String
name String -> [String] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` ["declare", "export", "readonly"]
Bool -> Bool -> Bool
|| String
name String -> String -> Bool
forall a. Eq a => a -> a -> Bool
== "local" Bool -> Bool -> Bool
&& "r" String -> [String] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`notElem` ((Token, String) -> String) -> [(Token, String)] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map (Token, String) -> String
forall a b. (a, b) -> b
snd (Token -> [(Token, String)]
getAllFlags Token
t)
m () -> Maybe (m ())
forall (m :: * -> *) a. Monad m => a -> m a
return (m () -> Maybe (m ())) -> m () -> Maybe (m ())
forall a b. (a -> b) -> a -> b
$ (Token -> m ()) -> [Token] -> m ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ Token -> m ()
forall (m :: * -> *). MonadWriter [TokenComment] m => Token -> m ()
checkArgs [Token]
rest
where
checkArgs :: Token -> m ()
checkArgs (T_Assignment id :: Id
id _ _ _ word :: Token
word) | (Token -> Bool) -> [Token] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any Token -> Bool
hasReturn ([Token] -> Bool) -> [Token] -> Bool
forall a b. (a -> b) -> a -> b
$ Token -> [Token]
getWordParts Token
word =
Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
warn Id
id 2155 "Declare and assign separately to avoid masking return values."
checkArgs _ = () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
hasReturn :: Token -> Bool
hasReturn t :: Token
t = case Token
t of
T_Backticked {} -> Bool
True
T_DollarExpansion {} -> Bool
True
_ -> Bool
False
checkMaskedReturns _ _ = () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
prop_checkReadWithoutR1 :: Bool
prop_checkReadWithoutR1 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkReadWithoutR "read -a foo"
prop_checkReadWithoutR2 :: Bool
prop_checkReadWithoutR2 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkReadWithoutR "read -ar foo"
prop_checkReadWithoutR3 :: Bool
prop_checkReadWithoutR3 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkReadWithoutR "read -t 0"
prop_checkReadWithoutR4 :: Bool
prop_checkReadWithoutR4 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkReadWithoutR "read -t 0 && read --d '' -r bar"
prop_checkReadWithoutR5 :: Bool
prop_checkReadWithoutR5 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkReadWithoutR "read -t 0 foo < file.txt"
prop_checkReadWithoutR6 :: Bool
prop_checkReadWithoutR6 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkReadWithoutR "read -u 3 -t 0"
checkReadWithoutR :: p -> Token -> m ()
checkReadWithoutR _ t :: Token
t@T_SimpleCommand {} | Token
t Token -> String -> Bool
`isUnqualifiedCommand` "read"
Bool -> Bool -> Bool
&& "r" String -> [String] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`notElem` ((Token, String) -> String) -> [(Token, String)] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map (Token, String) -> String
forall a b. (a, b) -> b
snd [(Token, String)]
flags Bool -> Bool -> Bool
&& Bool -> Bool
not Bool
has_t0 =
Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
info (Token -> Id
getId (Token -> Id) -> Token -> Id
forall a b. (a -> b) -> a -> b
$ Token -> Token
getCommandTokenOrThis Token
t) 2162 "read without -r will mangle backslashes."
where
flags :: [(Token, String)]
flags = Token -> [(Token, String)]
getAllFlags Token
t
has_t0 :: Bool
has_t0 = String -> Maybe String
forall a. a -> Maybe a
Just "0" Maybe String -> Maybe String -> Bool
forall a. Eq a => a -> a -> Bool
== do
[(String, (Token, Token))]
parsed <- String -> [Token] -> Maybe [(String, (Token, Token))]
getGnuOpts String
flagsForRead ([Token] -> Maybe [(String, (Token, Token))])
-> [Token] -> Maybe [(String, (Token, Token))]
forall a b. (a -> b) -> a -> b
$ Token -> [Token]
arguments Token
t
(_, t :: Token
t) <- String -> [(String, (Token, Token))] -> Maybe (Token, Token)
forall a b. Eq a => a -> [(a, b)] -> Maybe b
lookup "t" [(String, (Token, Token))]
parsed
Token -> Maybe String
getLiteralString Token
t
checkReadWithoutR _ _ = () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
prop_checkUncheckedCd1 :: Bool
prop_checkUncheckedCd1 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyTree Parameters -> Token -> [TokenComment]
checkUncheckedCdPushdPopd "cd ~/src; rm -r foo"
prop_checkUncheckedCd2 :: Bool
prop_checkUncheckedCd2 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
checkUncheckedCdPushdPopd "cd ~/src || exit; rm -r foo"
prop_checkUncheckedCd3 :: Bool
prop_checkUncheckedCd3 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
checkUncheckedCdPushdPopd "set -e; cd ~/src; rm -r foo"
prop_checkUncheckedCd4 :: Bool
prop_checkUncheckedCd4 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
checkUncheckedCdPushdPopd "if cd foo; then rm foo; fi"
prop_checkUncheckedCd5 :: Bool
prop_checkUncheckedCd5 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyTree Parameters -> Token -> [TokenComment]
checkUncheckedCdPushdPopd "if true; then cd foo; fi"
prop_checkUncheckedCd6 :: Bool
prop_checkUncheckedCd6 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
checkUncheckedCdPushdPopd "cd .."
prop_checkUncheckedCd7 :: Bool
prop_checkUncheckedCd7 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
checkUncheckedCdPushdPopd "#!/bin/bash -e\ncd foo\nrm bar"
prop_checkUncheckedCd8 :: Bool
prop_checkUncheckedCd8 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
checkUncheckedCdPushdPopd "set -o errexit; cd foo; rm bar"
prop_checkUncheckedCd9 :: Bool
prop_checkUncheckedCd9 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyTree Parameters -> Token -> [TokenComment]
checkUncheckedCdPushdPopd "builtin cd ~/src; rm -r foo"
prop_checkUncheckedPushd1 :: Bool
prop_checkUncheckedPushd1 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyTree Parameters -> Token -> [TokenComment]
checkUncheckedCdPushdPopd "pushd ~/src; rm -r foo"
prop_checkUncheckedPushd2 :: Bool
prop_checkUncheckedPushd2 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
checkUncheckedCdPushdPopd "pushd ~/src || exit; rm -r foo"
prop_checkUncheckedPushd3 :: Bool
prop_checkUncheckedPushd3 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
checkUncheckedCdPushdPopd "set -e; pushd ~/src; rm -r foo"
prop_checkUncheckedPushd4 :: Bool
prop_checkUncheckedPushd4 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
checkUncheckedCdPushdPopd "if pushd foo; then rm foo; fi"
prop_checkUncheckedPushd5 :: Bool
prop_checkUncheckedPushd5 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyTree Parameters -> Token -> [TokenComment]
checkUncheckedCdPushdPopd "if true; then pushd foo; fi"
prop_checkUncheckedPushd6 :: Bool
prop_checkUncheckedPushd6 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
checkUncheckedCdPushdPopd "pushd .."
prop_checkUncheckedPushd7 :: Bool
prop_checkUncheckedPushd7 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
checkUncheckedCdPushdPopd "#!/bin/bash -e\npushd foo\nrm bar"
prop_checkUncheckedPushd8 :: Bool
prop_checkUncheckedPushd8 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
checkUncheckedCdPushdPopd "set -o errexit; pushd foo; rm bar"
prop_checkUncheckedPushd9 :: Bool
prop_checkUncheckedPushd9 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
checkUncheckedCdPushdPopd "pushd -n foo"
prop_checkUncheckedPopd1 :: Bool
prop_checkUncheckedPopd1 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyTree Parameters -> Token -> [TokenComment]
checkUncheckedCdPushdPopd "popd; rm -r foo"
prop_checkUncheckedPopd2 :: Bool
prop_checkUncheckedPopd2 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
checkUncheckedCdPushdPopd "popd || exit; rm -r foo"
prop_checkUncheckedPopd3 :: Bool
prop_checkUncheckedPopd3 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
checkUncheckedCdPushdPopd "set -e; popd; rm -r foo"
prop_checkUncheckedPopd4 :: Bool
prop_checkUncheckedPopd4 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
checkUncheckedCdPushdPopd "if popd; then rm foo; fi"
prop_checkUncheckedPopd5 :: Bool
prop_checkUncheckedPopd5 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyTree Parameters -> Token -> [TokenComment]
checkUncheckedCdPushdPopd "if true; then popd; fi"
prop_checkUncheckedPopd6 :: Bool
prop_checkUncheckedPopd6 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyTree Parameters -> Token -> [TokenComment]
checkUncheckedCdPushdPopd "popd"
prop_checkUncheckedPopd7 :: Bool
prop_checkUncheckedPopd7 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
checkUncheckedCdPushdPopd "#!/bin/bash -e\npopd\nrm bar"
prop_checkUncheckedPopd8 :: Bool
prop_checkUncheckedPopd8 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
checkUncheckedCdPushdPopd "set -o errexit; popd; rm bar"
prop_checkUncheckedPopd9 :: Bool
prop_checkUncheckedPopd9 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
checkUncheckedCdPushdPopd "popd -n foo"
prop_checkUncheckedPopd10 :: Bool
prop_checkUncheckedPopd10 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
checkUncheckedCdPushdPopd "cd ../.."
prop_checkUncheckedPopd11 :: Bool
prop_checkUncheckedPopd11 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
checkUncheckedCdPushdPopd "cd ../.././.."
prop_checkUncheckedPopd12 :: Bool
prop_checkUncheckedPopd12 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
checkUncheckedCdPushdPopd "cd /"
prop_checkUncheckedPopd13 :: Bool
prop_checkUncheckedPopd13 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyTree Parameters -> Token -> [TokenComment]
checkUncheckedCdPushdPopd "cd ../../.../.."
checkUncheckedCdPushdPopd :: Parameters -> Token -> [TokenComment]
checkUncheckedCdPushdPopd params :: Parameters
params root :: Token
root =
if Parameters -> Bool
hasSetE Parameters
params then
[]
else Writer [TokenComment] Token -> [TokenComment]
forall w a. Writer w a -> w
execWriter (Writer [TokenComment] Token -> [TokenComment])
-> Writer [TokenComment] Token -> [TokenComment]
forall a b. (a -> b) -> a -> b
$ (Token -> WriterT [TokenComment] Identity ())
-> Token -> Writer [TokenComment] Token
forall (m :: * -> *).
Monad m =>
(Token -> m ()) -> Token -> m Token
doAnalysis Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *). MonadWriter [TokenComment] m => Token -> m ()
checkElement Token
root
where
checkElement :: Token -> m ()
checkElement t :: Token
t@T_SimpleCommand {}
| String
name String -> [String] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` ["cd", "pushd", "popd"]
Bool -> Bool -> Bool
&& Bool -> Bool
not (Token -> Bool
isSafeDir Token
t)
Bool -> Bool -> Bool
&& Bool -> Bool
not (String
name String -> [String] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` ["pushd", "popd"] Bool -> Bool -> Bool
&& ("n" String -> [String] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` ((Token, String) -> String) -> [(Token, String)] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map (Token, String) -> String
forall a b. (a, b) -> b
snd (Token -> [(Token, String)]
getAllFlags Token
t)))
Bool -> Bool -> Bool
&& Bool -> Bool
not ([Token] -> Bool
isCondition ([Token] -> Bool) -> [Token] -> Bool
forall a b. (a -> b) -> a -> b
$ Map Id Token -> Token -> [Token]
getPath (Parameters -> Map Id Token
parentMap Parameters
params) Token
t) =
Id -> Code -> String -> Fix -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> Fix -> m ()
warnWithFix (Token -> Id
getId Token
t) 2164
("Use '" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
name String -> String -> String
forall a. [a] -> [a] -> [a]
++ " ... || exit' or '" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
name String -> String -> String
forall a. [a] -> [a] -> [a]
++ " ... || return' in case " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
name String -> String -> String
forall a. [a] -> [a] -> [a]
++ " fails.")
([Replacement] -> Fix
fixWith [Id -> Parameters -> Code -> String -> Replacement
replaceEnd (Token -> Id
getId Token
t) Parameters
params 0 " || exit"])
where name :: String
name = Token -> String
getName Token
t
checkElement _ = () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
getName :: Token -> String
getName t :: Token
t = String -> Maybe String -> String
forall a. a -> Maybe a -> a
fromMaybe "" (Maybe String -> String) -> Maybe String -> String
forall a b. (a -> b) -> a -> b
$ Token -> Maybe String
getCommandName Token
t
isSafeDir :: Token -> Bool
isSafeDir t :: Token
t = case Token -> [String]
oversimplify Token
t of
[_, str :: String
str] -> String
str String -> Regex -> Bool
`matches` Regex
regex
_ -> Bool
False
regex :: Regex
regex = String -> Regex
mkRegex "^/*((\\.|\\.\\.)/+)*(\\.|\\.\\.)?$"
prop_checkLoopVariableReassignment1 :: Bool
prop_checkLoopVariableReassignment1 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkLoopVariableReassignment "for i in *; do for i in *.bar; do true; done; done"
prop_checkLoopVariableReassignment2 :: Bool
prop_checkLoopVariableReassignment2 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkLoopVariableReassignment "for i in *; do for((i=0; i<3; i++)); do true; done; done"
prop_checkLoopVariableReassignment3 :: Bool
prop_checkLoopVariableReassignment3 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkLoopVariableReassignment "for i in *; do for j in *.bar; do true; done; done"
checkLoopVariableReassignment :: Parameters -> Token -> m ()
checkLoopVariableReassignment params :: Parameters
params token :: Token
token =
Maybe (m ()) -> m ()
forall (t :: * -> *) (m :: * -> *) a.
(Foldable t, Monad m) =>
t (m a) -> m ()
sequence_ (Maybe (m ()) -> m ()) -> Maybe (m ()) -> m ()
forall a b. (a -> b) -> a -> b
$ case Token
token of
T_ForIn {} -> Maybe (m ())
check
T_ForArithmetic {} -> Maybe (m ())
check
_ -> Maybe (m ())
forall a. Maybe a
Nothing
where
check :: Maybe (m ())
check = do
String
str <- Token -> Maybe String
loopVariable Token
token
Token
next <- (Token -> Bool) -> [Token] -> Maybe Token
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Maybe a
find (\x :: Token
x -> Token -> Maybe String
loopVariable Token
x Maybe String -> Maybe String -> Bool
forall a. Eq a => a -> a -> Bool
== String -> Maybe String
forall a. a -> Maybe a
Just String
str) [Token]
path
m () -> Maybe (m ())
forall (m :: * -> *) a. Monad m => a -> m a
return (m () -> Maybe (m ())) -> m () -> Maybe (m ())
forall a b. (a -> b) -> a -> b
$ do
Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
warn (Token -> Id
getId Token
token) 2165 "This nested loop overrides the index variable of its parent."
Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
warn (Token -> Id
getId Token
next) 2167 "This parent loop has its index variable overridden."
path :: [Token]
path = Int -> [Token] -> [Token]
forall a. Int -> [a] -> [a]
drop 1 ([Token] -> [Token]) -> [Token] -> [Token]
forall a b. (a -> b) -> a -> b
$ Map Id Token -> Token -> [Token]
getPath (Parameters -> Map Id Token
parentMap Parameters
params) Token
token
loopVariable :: Token -> Maybe String
loopVariable :: Token -> Maybe String
loopVariable t :: Token
t =
case Token
t of
T_ForIn _ s :: String
s _ _ -> String -> Maybe String
forall (m :: * -> *) a. Monad m => a -> m a
return String
s
T_ForArithmetic _
(TA_Sequence _
[TA_Assignment _ "="
(TA_Variable _ var :: String
var _ ) _])
_ _ _ -> String -> Maybe String
forall (m :: * -> *) a. Monad m => a -> m a
return String
var
_ -> String -> Maybe String
forall (m :: * -> *) a. MonadFail m => String -> m a
fail "not loop"
prop_checkTrailingBracket1 :: Bool
prop_checkTrailingBracket1 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkTrailingBracket "if -z n ]]; then true; fi "
prop_checkTrailingBracket2 :: Bool
prop_checkTrailingBracket2 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkTrailingBracket "if [[ -z n ]]; then true; fi "
prop_checkTrailingBracket3 :: Bool
prop_checkTrailingBracket3 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkTrailingBracket "a || b ] && thing"
prop_checkTrailingBracket4 :: Bool
prop_checkTrailingBracket4 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkTrailingBracket "run [ foo ]"
prop_checkTrailingBracket5 :: Bool
prop_checkTrailingBracket5 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkTrailingBracket "run bar ']'"
checkTrailingBracket :: p -> Token -> m ()
checkTrailingBracket _ token :: Token
token =
case Token
token of
T_SimpleCommand _ _ tokens :: [Token]
tokens@(_:_) -> Token -> Token -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Token -> Token -> m ()
check ([Token] -> Token
forall a. [a] -> a
last [Token]
tokens) Token
token
_ -> () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
where
check :: Token -> Token -> m ()
check (T_NormalWord id :: Id
id [T_Literal _ str :: String
str]) command :: Token
command
| String
str String -> [String] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [ "]]", "]" ]
Bool -> Bool -> Bool
&& String
opposite String -> [String] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`notElem` [String]
parameters
= Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
warn Id
id 2171 (String -> m ()) -> String -> m ()
forall a b. (a -> b) -> a -> b
$
"Found trailing " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
str String -> String -> String
forall a. [a] -> [a] -> [a]
++ " outside test. Add missing " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
opposite String -> String -> String
forall a. [a] -> [a] -> [a]
++ " or quote if intentional."
where
opposite :: String
opposite = String -> String
invert String
str
parameters :: [String]
parameters = Token -> [String]
oversimplify Token
command
check _ _ = () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
invert :: String -> String
invert s :: String
s =
case String
s of
"]]" -> "[["
"]" -> "["
x :: String
x -> String
x
prop_checkReturnAgainstZero1 :: Bool
prop_checkReturnAgainstZero1 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkReturnAgainstZero "[ $? -eq 0 ]"
prop_checkReturnAgainstZero2 :: Bool
prop_checkReturnAgainstZero2 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkReturnAgainstZero "[[ \"$?\" -gt 0 ]]"
prop_checkReturnAgainstZero3 :: Bool
prop_checkReturnAgainstZero3 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkReturnAgainstZero "[[ 0 -ne $? ]]"
prop_checkReturnAgainstZero4 :: Bool
prop_checkReturnAgainstZero4 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkReturnAgainstZero "[[ $? -eq 4 ]]"
prop_checkReturnAgainstZero5 :: Bool
prop_checkReturnAgainstZero5 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkReturnAgainstZero "[[ 0 -eq $? ]]"
prop_checkReturnAgainstZero6 :: Bool
prop_checkReturnAgainstZero6 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkReturnAgainstZero "[[ $R -eq 0 ]]"
prop_checkReturnAgainstZero7 :: Bool
prop_checkReturnAgainstZero7 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkReturnAgainstZero "(( $? == 0 ))"
prop_checkReturnAgainstZero8 :: Bool
prop_checkReturnAgainstZero8 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkReturnAgainstZero "(( $? ))"
prop_checkReturnAgainstZero9 :: Bool
prop_checkReturnAgainstZero9 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkReturnAgainstZero "(( ! $? ))"
checkReturnAgainstZero :: p -> Token -> m ()
checkReturnAgainstZero _ token :: Token
token =
case Token
token of
TC_Binary id :: Id
id _ _ lhs :: Token
lhs rhs :: Token
rhs -> Token -> Token -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Token -> Token -> m ()
check Token
lhs Token
rhs
TA_Binary id :: Id
id _ lhs :: Token
lhs rhs :: Token
rhs -> Token -> Token -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Token -> Token -> m ()
check Token
lhs Token
rhs
TA_Unary id :: Id
id _ exp :: Token
exp
| Token -> Bool
isExitCode Token
exp -> Id -> m ()
forall (m :: * -> *). MonadWriter [TokenComment] m => Id -> m ()
message (Token -> Id
getId Token
exp)
TA_Sequence _ [exp :: Token
exp]
| Token -> Bool
isExitCode Token
exp -> Id -> m ()
forall (m :: * -> *). MonadWriter [TokenComment] m => Id -> m ()
message (Token -> Id
getId Token
exp)
_ -> () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
where
check :: Token -> Token -> m ()
check lhs :: Token
lhs rhs :: Token
rhs =
if Token -> Bool
isZero Token
rhs Bool -> Bool -> Bool
&& Token -> Bool
isExitCode Token
lhs
then Id -> m ()
forall (m :: * -> *). MonadWriter [TokenComment] m => Id -> m ()
message (Token -> Id
getId Token
lhs)
else Bool -> m () -> m ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (Token -> Bool
isZero Token
lhs Bool -> Bool -> Bool
&& Token -> Bool
isExitCode Token
rhs) (m () -> m ()) -> m () -> m ()
forall a b. (a -> b) -> a -> b
$ Id -> m ()
forall (m :: * -> *). MonadWriter [TokenComment] m => Id -> m ()
message (Token -> Id
getId Token
rhs)
isZero :: Token -> Bool
isZero t :: Token
t = Token -> Maybe String
getLiteralString Token
t Maybe String -> Maybe String -> Bool
forall a. Eq a => a -> a -> Bool
== String -> Maybe String
forall a. a -> Maybe a
Just "0"
isExitCode :: Token -> Bool
isExitCode t :: Token
t =
case Token -> [Token]
getWordParts Token
t of
[T_DollarBraced _ _ l :: Token
l] -> [String] -> String
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat (Token -> [String]
oversimplify Token
l) String -> String -> Bool
forall a. Eq a => a -> a -> Bool
== "?"
_ -> Bool
False
message :: Id -> m ()
message id :: Id
id = Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
style Id
id 2181 "Check exit code directly with e.g. 'if mycmd;', not indirectly with $?."
prop_checkRedirectedNowhere1 :: Bool
prop_checkRedirectedNowhere1 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkRedirectedNowhere "> file"
prop_checkRedirectedNowhere2 :: Bool
prop_checkRedirectedNowhere2 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkRedirectedNowhere "> file | grep foo"
prop_checkRedirectedNowhere3 :: Bool
prop_checkRedirectedNowhere3 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkRedirectedNowhere "grep foo | > bar"
prop_checkRedirectedNowhere4 :: Bool
prop_checkRedirectedNowhere4 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkRedirectedNowhere "grep foo > bar"
prop_checkRedirectedNowhere5 :: Bool
prop_checkRedirectedNowhere5 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkRedirectedNowhere "foo | grep bar > baz"
prop_checkRedirectedNowhere6 :: Bool
prop_checkRedirectedNowhere6 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkRedirectedNowhere "var=$(value) 2> /dev/null"
prop_checkRedirectedNowhere7 :: Bool
prop_checkRedirectedNowhere7 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkRedirectedNowhere "var=$(< file)"
prop_checkRedirectedNowhere8 :: Bool
prop_checkRedirectedNowhere8 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkRedirectedNowhere "var=`< file`"
checkRedirectedNowhere :: Parameters -> Token -> m ()
checkRedirectedNowhere params :: Parameters
params token :: Token
token =
case Token
token of
T_Pipeline _ _ [single :: Token
single] -> Maybe (m ()) -> m ()
forall (t :: * -> *) (m :: * -> *) a.
(Foldable t, Monad m) =>
t (m a) -> m ()
sequence_ (Maybe (m ()) -> m ()) -> Maybe (m ()) -> m ()
forall a b. (a -> b) -> a -> b
$ do
Token
redir <- Token -> Maybe Token
getDanglingRedirect Token
single
Bool -> Maybe ()
forall (f :: * -> *). Alternative f => Bool -> f ()
guard (Bool -> Maybe ()) -> (Bool -> Bool) -> Bool -> Maybe ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Bool -> Bool
not (Bool -> Maybe ()) -> Bool -> Maybe ()
forall a b. (a -> b) -> a -> b
$ Token -> Bool
isInExpansion Token
token
m () -> Maybe (m ())
forall (m :: * -> *) a. Monad m => a -> m a
return (m () -> Maybe (m ())) -> m () -> Maybe (m ())
forall a b. (a -> b) -> a -> b
$ Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
warn (Token -> Id
getId Token
redir) 2188 "This redirection doesn't have a command. Move to its command (or use 'true' as no-op)."
T_Pipeline _ _ list :: [Token]
list -> [Token] -> (Token -> m ()) -> m ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
t a -> (a -> m b) -> m ()
forM_ [Token]
list ((Token -> m ()) -> m ()) -> (Token -> m ()) -> m ()
forall a b. (a -> b) -> a -> b
$ \x :: Token
x -> Maybe (m ()) -> m ()
forall (t :: * -> *) (m :: * -> *) a.
(Foldable t, Monad m) =>
t (m a) -> m ()
sequence_ (Maybe (m ()) -> m ()) -> Maybe (m ()) -> m ()
forall a b. (a -> b) -> a -> b
$ do
Token
redir <- Token -> Maybe Token
getDanglingRedirect Token
x
m () -> Maybe (m ())
forall (m :: * -> *) a. Monad m => a -> m a
return (m () -> Maybe (m ())) -> m () -> Maybe (m ())
forall a b. (a -> b) -> a -> b
$ Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
err (Token -> Id
getId Token
redir) 2189 "You can't have | between this redirection and the command it should apply to."
_ -> () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
where
isInExpansion :: Token -> Bool
isInExpansion t :: Token
t =
case Int -> [Token] -> [Token]
forall a. Int -> [a] -> [a]
drop 1 ([Token] -> [Token]) -> [Token] -> [Token]
forall a b. (a -> b) -> a -> b
$ Map Id Token -> Token -> [Token]
getPath (Parameters -> Map Id Token
parentMap Parameters
params) Token
t of
T_DollarExpansion _ [_] : _ -> Bool
True
T_Backticked _ [_] : _ -> Bool
True
t :: Token
t@T_Annotation {} : _ -> Token -> Bool
isInExpansion Token
t
_ -> Bool
False
getDanglingRedirect :: Token -> Maybe Token
getDanglingRedirect token :: Token
token =
case Token
token of
T_Redirecting _ (first :: Token
first:_) (T_SimpleCommand _ [] []) -> Token -> Maybe Token
forall (m :: * -> *) a. Monad m => a -> m a
return Token
first
_ -> Maybe Token
forall a. Maybe a
Nothing
prop_checkArrayAssignmentIndices1 :: Bool
prop_checkArrayAssignmentIndices1 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyTree Parameters -> Token -> [TokenComment]
checkArrayAssignmentIndices "declare -A foo; foo=(bar)"
prop_checkArrayAssignmentIndices2 :: Bool
prop_checkArrayAssignmentIndices2 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
checkArrayAssignmentIndices "declare -a foo; foo=(bar)"
prop_checkArrayAssignmentIndices3 :: Bool
prop_checkArrayAssignmentIndices3 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
checkArrayAssignmentIndices "declare -A foo; foo=([i]=bar)"
prop_checkArrayAssignmentIndices4 :: Bool
prop_checkArrayAssignmentIndices4 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyTree Parameters -> Token -> [TokenComment]
checkArrayAssignmentIndices "typeset -A foo; foo+=(bar)"
prop_checkArrayAssignmentIndices5 :: Bool
prop_checkArrayAssignmentIndices5 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyTree Parameters -> Token -> [TokenComment]
checkArrayAssignmentIndices "arr=( [foo]= bar )"
prop_checkArrayAssignmentIndices6 :: Bool
prop_checkArrayAssignmentIndices6 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyTree Parameters -> Token -> [TokenComment]
checkArrayAssignmentIndices "arr=( [foo] = bar )"
prop_checkArrayAssignmentIndices7 :: Bool
prop_checkArrayAssignmentIndices7 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
checkArrayAssignmentIndices "arr=( var=value )"
prop_checkArrayAssignmentIndices8 :: Bool
prop_checkArrayAssignmentIndices8 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
checkArrayAssignmentIndices "arr=( [foo]=bar )"
prop_checkArrayAssignmentIndices9 :: Bool
prop_checkArrayAssignmentIndices9 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
checkArrayAssignmentIndices "arr=( [foo]=\"\" )"
prop_checkArrayAssignmentIndices10 :: Bool
prop_checkArrayAssignmentIndices10 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyTree Parameters -> Token -> [TokenComment]
checkArrayAssignmentIndices "declare -A arr; arr=( var=value )"
prop_checkArrayAssignmentIndices11 :: Bool
prop_checkArrayAssignmentIndices11 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyTree Parameters -> Token -> [TokenComment]
checkArrayAssignmentIndices "arr=( 1=value )"
prop_checkArrayAssignmentIndices12 :: Bool
prop_checkArrayAssignmentIndices12 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyTree Parameters -> Token -> [TokenComment]
checkArrayAssignmentIndices "arr=( $a=value )"
prop_checkArrayAssignmentIndices13 :: Bool
prop_checkArrayAssignmentIndices13 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyTree Parameters -> Token -> [TokenComment]
checkArrayAssignmentIndices "arr=( $((1+1))=value )"
checkArrayAssignmentIndices :: Parameters -> Token -> [TokenComment]
checkArrayAssignmentIndices params :: Parameters
params root :: Token
root =
(Parameters -> Token -> WriterT [TokenComment] Identity ())
-> Parameters -> Token -> [TokenComment]
forall w t.
Monoid w =>
(t -> Token -> WriterT w Identity ()) -> t -> Token -> w
runNodeAnalysis Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
check Parameters
params Token
root
where
assocs :: [String]
assocs = Token -> [String]
getAssociativeArrays Token
root
check :: p -> Token -> m ()
check _ t :: Token
t =
case Token
t of
T_Assignment _ _ name :: String
name [] (T_Array _ list :: [Token]
list) ->
let isAssoc :: Bool
isAssoc = String
name String -> [String] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [String]
assocs in
(Token -> m ()) -> [Token] -> m ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ (Bool -> Token -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Bool -> Token -> m ()
checkElement Bool
isAssoc) [Token]
list
_ -> () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
checkElement :: Bool -> Token -> m ()
checkElement isAssociative :: Bool
isAssociative t :: Token
t =
case Token
t of
T_IndexedElement _ _ (T_Literal id :: Id
id "") ->
Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
warn Id
id 2192 "This array element has no value. Remove spaces after = or use \"\" for empty string."
T_IndexedElement {} ->
() -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
T_NormalWord _ parts :: [Token]
parts ->
let literalEquals :: [m ()]
literalEquals = do
T_Literal id :: Id
id str :: String
str <- [Token]
parts
let (before :: String
before, after :: String
after) = (Char -> Bool) -> String -> (String, String)
forall a. (a -> Bool) -> [a] -> ([a], [a])
break ('=' Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
==) String
str
Bool -> [()]
forall (f :: * -> *). Alternative f => Bool -> f ()
guard (Bool -> [()]) -> Bool -> [()]
forall a b. (a -> b) -> a -> b
$ (Char -> Bool) -> String -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all Char -> Bool
isDigit String
before Bool -> Bool -> Bool
&& Bool -> Bool
not (String -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null String
after)
m () -> [m ()]
forall (m :: * -> *) a. Monad m => a -> m a
return (m () -> [m ()]) -> m () -> [m ()]
forall a b. (a -> b) -> a -> b
$ Id -> Code -> String -> Fix -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> Fix -> m ()
warnWithFix Id
id 2191 "The = here is literal. To assign by index, use ( [index]=value ) with no spaces. To keep as literal, quote it." (Id -> Parameters -> String -> Fix
surroundWidth Id
id Parameters
params "\"")
in
if [m ()] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null [m ()]
literalEquals Bool -> Bool -> Bool
&& Bool
isAssociative
then Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
warn (Token -> Id
getId Token
t) 2190 "Elements in associative arrays need index, e.g. array=( [index]=value ) ."
else [m ()] -> m ()
forall (t :: * -> *) (m :: * -> *) a.
(Foldable t, Monad m) =>
t (m a) -> m ()
sequence_ [m ()]
literalEquals
_ -> () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
prop_checkUnmatchableCases1 :: Bool
prop_checkUnmatchableCases1 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkUnmatchableCases "case foo in bar) true; esac"
prop_checkUnmatchableCases2 :: Bool
prop_checkUnmatchableCases2 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkUnmatchableCases "case foo-$bar in ??|*) true; esac"
prop_checkUnmatchableCases3 :: Bool
prop_checkUnmatchableCases3 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkUnmatchableCases "case foo in foo) true; esac"
prop_checkUnmatchableCases4 :: Bool
prop_checkUnmatchableCases4 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkUnmatchableCases "case foo-$bar in foo*|*bar|*baz*) true; esac"
prop_checkUnmatchableCases5 :: Bool
prop_checkUnmatchableCases5 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkUnmatchableCases "case $f in *.txt) true;; f??.txt) false;; esac"
prop_checkUnmatchableCases6 :: Bool
prop_checkUnmatchableCases6 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkUnmatchableCases "case $f in ?*) true;; *) false;; esac"
prop_checkUnmatchableCases7 :: Bool
prop_checkUnmatchableCases7 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkUnmatchableCases "case $f in $(x)) true;; asdf) false;; esac"
prop_checkUnmatchableCases8 :: Bool
prop_checkUnmatchableCases8 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkUnmatchableCases "case $f in cow) true;; bar|cow) false;; esac"
prop_checkUnmatchableCases9 :: Bool
prop_checkUnmatchableCases9 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkUnmatchableCases "case $f in x) true;;& x) false;; esac"
checkUnmatchableCases :: Parameters -> Token -> m ()
checkUnmatchableCases params :: Parameters
params t :: Token
t =
case Token
t of
T_CaseExpression _ word :: Token
word list :: [(CaseType, [Token], [Token])]
list -> do
let allpatterns :: [Token]
allpatterns = ((CaseType, [Token], [Token]) -> [Token])
-> [(CaseType, [Token], [Token])] -> [Token]
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap (CaseType, [Token], [Token]) -> [Token]
forall a b c. (a, b, c) -> b
snd3 [(CaseType, [Token], [Token])]
list
let breakpatterns :: [Token]
breakpatterns = ((CaseType, [Token], [Token]) -> [Token])
-> [(CaseType, [Token], [Token])] -> [Token]
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap (CaseType, [Token], [Token]) -> [Token]
forall a b c. (a, b, c) -> b
snd3 ([(CaseType, [Token], [Token])] -> [Token])
-> [(CaseType, [Token], [Token])] -> [Token]
forall a b. (a -> b) -> a -> b
$ ((CaseType, [Token], [Token]) -> Bool)
-> [(CaseType, [Token], [Token])] -> [(CaseType, [Token], [Token])]
forall a. (a -> Bool) -> [a] -> [a]
filter (\x :: (CaseType, [Token], [Token])
x -> (CaseType, [Token], [Token]) -> CaseType
forall a b c. (a, b, c) -> a
fst3 (CaseType, [Token], [Token])
x CaseType -> CaseType -> Bool
forall a. Eq a => a -> a -> Bool
== CaseType
CaseBreak) [(CaseType, [Token], [Token])]
list
if Token -> Bool
isConstant Token
word
then Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
warn (Token -> Id
getId Token
word) 2194
"This word is constant. Did you forget the $ on a variable?"
else (Token -> m ()) -> [Token] -> m ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ ([PseudoGlob] -> Token -> m ()
forall (f :: * -> *).
MonadWriter [TokenComment] f =>
[PseudoGlob] -> Token -> f ()
check ([PseudoGlob] -> Token -> m ()) -> [PseudoGlob] -> Token -> m ()
forall a b. (a -> b) -> a -> b
$ Token -> [PseudoGlob]
wordToPseudoGlob Token
word) [Token]
allpatterns
let exactGlobs :: [(Token, Maybe [PseudoGlob])]
exactGlobs = (Token -> Maybe [PseudoGlob])
-> [Token] -> [(Token, Maybe [PseudoGlob])]
forall t b. (t -> b) -> [t] -> [(t, b)]
tupMap Token -> Maybe [PseudoGlob]
wordToExactPseudoGlob [Token]
breakpatterns
let fuzzyGlobs :: [(Token, [PseudoGlob])]
fuzzyGlobs = (Token -> [PseudoGlob]) -> [Token] -> [(Token, [PseudoGlob])]
forall t b. (t -> b) -> [t] -> [(t, b)]
tupMap Token -> [PseudoGlob]
wordToPseudoGlob [Token]
breakpatterns
let dominators :: [((Token, Maybe [PseudoGlob]), [(Token, [PseudoGlob])])]
dominators = [(Token, Maybe [PseudoGlob])]
-> [[(Token, [PseudoGlob])]]
-> [((Token, Maybe [PseudoGlob]), [(Token, [PseudoGlob])])]
forall a b. [a] -> [b] -> [(a, b)]
zip [(Token, Maybe [PseudoGlob])]
exactGlobs ([(Token, [PseudoGlob])] -> [[(Token, [PseudoGlob])]]
forall a. [a] -> [[a]]
tails ([(Token, [PseudoGlob])] -> [[(Token, [PseudoGlob])]])
-> [(Token, [PseudoGlob])] -> [[(Token, [PseudoGlob])]]
forall a b. (a -> b) -> a -> b
$ Int -> [(Token, [PseudoGlob])] -> [(Token, [PseudoGlob])]
forall a. Int -> [a] -> [a]
drop 1 [(Token, [PseudoGlob])]
fuzzyGlobs)
(((Token, Maybe [PseudoGlob]), [(Token, [PseudoGlob])]) -> m ())
-> [((Token, Maybe [PseudoGlob]), [(Token, [PseudoGlob])])] -> m ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ ((Token, Maybe [PseudoGlob]), [(Token, [PseudoGlob])]) -> m ()
forall (m :: * -> *) (t :: * -> *).
(Foldable t, MonadWriter [TokenComment] m) =>
((Token, Maybe [PseudoGlob]), t (Token, [PseudoGlob])) -> m ()
checkDoms [((Token, Maybe [PseudoGlob]), [(Token, [PseudoGlob])])]
dominators
_ -> () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
where
fst3 :: (a, b, c) -> a
fst3 (x :: a
x,_,_) = a
x
snd3 :: (a, b, c) -> b
snd3 (_,x :: b
x,_) = b
x
tp :: Map Id (Position, Position)
tp = Parameters -> Map Id (Position, Position)
tokenPositions Parameters
params
check :: [PseudoGlob] -> Token -> f ()
check target :: [PseudoGlob]
target candidate :: Token
candidate = Bool -> f () -> f ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
unless ([PseudoGlob] -> [PseudoGlob] -> Bool
pseudoGlobsCanOverlap [PseudoGlob]
target ([PseudoGlob] -> Bool) -> [PseudoGlob] -> Bool
forall a b. (a -> b) -> a -> b
$ Token -> [PseudoGlob]
wordToPseudoGlob Token
candidate) (f () -> f ()) -> f () -> f ()
forall a b. (a -> b) -> a -> b
$
Id -> Code -> String -> f ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
warn (Token -> Id
getId Token
candidate) 2195
"This pattern will never match the case statement's word. Double check them."
tupMap :: (t -> b) -> [t] -> [(t, b)]
tupMap f :: t -> b
f l :: [t]
l = (t -> (t, b)) -> [t] -> [(t, b)]
forall a b. (a -> b) -> [a] -> [b]
map (\x :: t
x -> (t
x, t -> b
f t
x)) [t]
l
checkDoms :: ((Token, Maybe [PseudoGlob]), t (Token, [PseudoGlob])) -> m ()
checkDoms ((glob :: Token
glob, Just x :: [PseudoGlob]
x), rest :: t (Token, [PseudoGlob])
rest) =
Maybe (Token, [PseudoGlob])
-> ((Token, [PseudoGlob]) -> m ()) -> m ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
t a -> (a -> m b) -> m ()
forM_ (((Token, [PseudoGlob]) -> Bool)
-> t (Token, [PseudoGlob]) -> Maybe (Token, [PseudoGlob])
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Maybe a
find (\(_, p :: [PseudoGlob]
p) -> [PseudoGlob]
x [PseudoGlob] -> [PseudoGlob] -> Bool
`pseudoGlobIsSuperSetof` [PseudoGlob]
p) t (Token, [PseudoGlob])
rest) (((Token, [PseudoGlob]) -> m ()) -> m ())
-> ((Token, [PseudoGlob]) -> m ()) -> m ()
forall a b. (a -> b) -> a -> b
$
\(first :: Token
first,_) -> do
Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
warn (Token -> Id
getId Token
glob) 2221 (String -> m ()) -> String -> m ()
forall a b. (a -> b) -> a -> b
$ "This pattern always overrides a later one" String -> String -> String
forall a. Semigroup a => a -> a -> a
<> Id -> String
patternContext (Token -> Id
getId Token
first)
Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
warn (Token -> Id
getId Token
first) 2222 (String -> m ()) -> String -> m ()
forall a b. (a -> b) -> a -> b
$ "This pattern never matches because of a previous pattern" String -> String -> String
forall a. Semigroup a => a -> a -> a
<> Id -> String
patternContext (Token -> Id
getId Token
glob)
where
patternContext :: Id -> String
patternContext :: Id -> String
patternContext id :: Id
id =
case Position -> Code
posLine (Position -> Code)
-> ((Position, Position) -> Position)
-> (Position, Position)
-> Code
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Position, Position) -> Position
forall a b. (a, b) -> a
fst ((Position, Position) -> Code)
-> Maybe (Position, Position) -> Maybe Code
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Id -> Map Id (Position, Position) -> Maybe (Position, Position)
forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup Id
id Map Id (Position, Position)
tp of
Just l :: Code
l -> " on line " String -> String -> String
forall a. Semigroup a => a -> a -> a
<> Code -> String
forall a. Show a => a -> String
show Code
l String -> String -> String
forall a. Semigroup a => a -> a -> a
<> "."
_ -> "."
checkDoms _ = () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
prop_checkSubshellAsTest1 :: Bool
prop_checkSubshellAsTest1 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkSubshellAsTest "( -e file )"
prop_checkSubshellAsTest2 :: Bool
prop_checkSubshellAsTest2 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkSubshellAsTest "( 1 -gt 2 )"
prop_checkSubshellAsTest3 :: Bool
prop_checkSubshellAsTest3 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkSubshellAsTest "( grep -c foo bar )"
prop_checkSubshellAsTest4 :: Bool
prop_checkSubshellAsTest4 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkSubshellAsTest "[ 1 -gt 2 ]"
prop_checkSubshellAsTest5 :: Bool
prop_checkSubshellAsTest5 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkSubshellAsTest "( -e file && -x file )"
prop_checkSubshellAsTest6 :: Bool
prop_checkSubshellAsTest6 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkSubshellAsTest "( -e file || -x file && -t 1 )"
prop_checkSubshellAsTest7 :: Bool
prop_checkSubshellAsTest7 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkSubshellAsTest "( ! -d file )"
checkSubshellAsTest :: p -> Token -> m ()
checkSubshellAsTest _ t :: Token
t =
case Token
t of
T_Subshell id :: Id
id [w :: Token
w] -> Id -> Token -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Token -> m ()
check Id
id Token
w
_ -> () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
where
check :: Id -> Token -> m ()
check id :: Id
id t :: Token
t = case Token
t of
(T_Banged _ w :: Token
w) -> Id -> Token -> m ()
check Id
id Token
w
(T_AndIf _ w :: Token
w _) -> Id -> Token -> m ()
check Id
id Token
w
(T_OrIf _ w :: Token
w _) -> Id -> Token -> m ()
check Id
id Token
w
(T_Pipeline _ _ [T_Redirecting _ _ (T_SimpleCommand _ [] (first :: Token
first:second :: Token
second:_))]) ->
Id -> Token -> Token -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Token -> Token -> m ()
checkParams Id
id Token
first Token
second
_ -> () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
checkParams :: Id -> Token -> Token -> m ()
checkParams id :: Id
id first :: Token
first second :: Token
second = do
Bool -> m () -> m ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (Bool -> (String -> Bool) -> Maybe String -> Bool
forall b a. b -> (a -> b) -> Maybe a -> b
maybe Bool
False (String -> [String] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [String]
unaryTestOps) (Maybe String -> Bool) -> Maybe String -> Bool
forall a b. (a -> b) -> a -> b
$ Token -> Maybe String
getLiteralString Token
first) (m () -> m ()) -> m () -> m ()
forall a b. (a -> b) -> a -> b
$
Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
err Id
id 2204 "(..) is a subshell. Did you mean [ .. ], a test expression?"
Bool -> m () -> m ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (Bool -> (String -> Bool) -> Maybe String -> Bool
forall b a. b -> (a -> b) -> Maybe a -> b
maybe Bool
False (String -> [String] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [String]
binaryTestOps) (Maybe String -> Bool) -> Maybe String -> Bool
forall a b. (a -> b) -> a -> b
$ Token -> Maybe String
getLiteralString Token
second) (m () -> m ()) -> m () -> m ()
forall a b. (a -> b) -> a -> b
$
Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
warn Id
id 2205 "(..) is a subshell. Did you mean [ .. ], a test expression?"
prop_checkSplittingInArrays1 :: Bool
prop_checkSplittingInArrays1 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkSplittingInArrays "a=( $var )"
prop_checkSplittingInArrays2 :: Bool
prop_checkSplittingInArrays2 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkSplittingInArrays "a=( $(cmd) )"
prop_checkSplittingInArrays3 :: Bool
prop_checkSplittingInArrays3 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkSplittingInArrays "a=( \"$var\" )"
prop_checkSplittingInArrays4 :: Bool
prop_checkSplittingInArrays4 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkSplittingInArrays "a=( \"$(cmd)\" )"
prop_checkSplittingInArrays5 :: Bool
prop_checkSplittingInArrays5 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkSplittingInArrays "a=( $! $$ $# )"
prop_checkSplittingInArrays6 :: Bool
prop_checkSplittingInArrays6 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkSplittingInArrays "a=( ${#arr[@]} )"
prop_checkSplittingInArrays7 :: Bool
prop_checkSplittingInArrays7 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkSplittingInArrays "a=( foo{1,2} )"
prop_checkSplittingInArrays8 :: Bool
prop_checkSplittingInArrays8 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkSplittingInArrays "a=( * )"
checkSplittingInArrays :: Parameters -> Token -> m ()
checkSplittingInArrays params :: Parameters
params t :: Token
t =
case Token
t of
T_Array _ elements :: [Token]
elements -> (Token -> m ()) -> [Token] -> m ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ Token -> m ()
forall (m :: * -> *). MonadWriter [TokenComment] m => Token -> m ()
check [Token]
elements
_ -> () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
where
check :: Token -> m ()
check word :: Token
word = case Token
word of
T_NormalWord _ parts :: [Token]
parts -> (Token -> m ()) -> [Token] -> m ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ Token -> m ()
forall (m :: * -> *). MonadWriter [TokenComment] m => Token -> m ()
checkPart [Token]
parts
_ -> () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
checkPart :: Token -> m ()
checkPart part :: Token
part = case Token
part of
T_DollarExpansion id :: Id
id _ -> Id -> m ()
forall (m :: * -> *). MonadWriter [TokenComment] m => Id -> m ()
forCommand Id
id
T_DollarBraceCommandExpansion id :: Id
id _ -> Id -> m ()
forall (m :: * -> *). MonadWriter [TokenComment] m => Id -> m ()
forCommand Id
id
T_Backticked id :: Id
id _ -> Id -> m ()
forall (m :: * -> *). MonadWriter [TokenComment] m => Id -> m ()
forCommand Id
id
T_DollarBraced id :: Id
id _ str :: Token
str |
Bool -> Bool
not (Token -> Bool
isCountingReference Token
part)
Bool -> Bool -> Bool
&& Bool -> Bool
not (Token -> Bool
isQuotedAlternativeReference Token
part)
Bool -> Bool -> Bool
&& String -> String
getBracedReference ([String] -> String
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat ([String] -> String) -> [String] -> String
forall a b. (a -> b) -> a -> b
$ Token -> [String]
oversimplify Token
str) String -> [String] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`notElem` [String]
variablesWithoutSpaces
-> Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
warn Id
id 2206 (String -> m ()) -> String -> m ()
forall a b. (a -> b) -> a -> b
$
if Parameters -> Shell
shellType Parameters
params Shell -> Shell -> Bool
forall a. Eq a => a -> a -> Bool
== Shell
Ksh
then "Quote to prevent word splitting/globbing, or split robustly with read -A or while read."
else "Quote to prevent word splitting/globbing, or split robustly with mapfile or read -a."
_ -> () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
forCommand :: Id -> m ()
forCommand id :: Id
id =
Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
warn Id
id 2207 (String -> m ()) -> String -> m ()
forall a b. (a -> b) -> a -> b
$
if Parameters -> Shell
shellType Parameters
params Shell -> Shell -> Bool
forall a. Eq a => a -> a -> Bool
== Shell
Ksh
then "Prefer read -A or while read to split command output (or quote to avoid splitting)."
else "Prefer mapfile or read -a to split command output (or quote to avoid splitting)."
prop_checkRedirectionToNumber1 :: Bool
prop_checkRedirectionToNumber1 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkRedirectionToNumber "( 1 > 2 )"
prop_checkRedirectionToNumber2 :: Bool
prop_checkRedirectionToNumber2 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkRedirectionToNumber "foo 1>2"
prop_checkRedirectionToNumber3 :: Bool
prop_checkRedirectionToNumber3 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkRedirectionToNumber "echo foo > '2'"
prop_checkRedirectionToNumber4 :: Bool
prop_checkRedirectionToNumber4 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkRedirectionToNumber "foo 1>&2"
checkRedirectionToNumber :: p -> Token -> m ()
checkRedirectionToNumber _ t :: Token
t = case Token
t of
T_IoFile id :: Id
id _ word :: Token
word -> Maybe (m ()) -> m ()
forall (t :: * -> *) (m :: * -> *) a.
(Foldable t, Monad m) =>
t (m a) -> m ()
sequence_ (Maybe (m ()) -> m ()) -> Maybe (m ()) -> m ()
forall a b. (a -> b) -> a -> b
$ do
String
file <- Token -> Maybe String
getUnquotedLiteral Token
word
Bool -> Maybe ()
forall (f :: * -> *). Alternative f => Bool -> f ()
guard (Bool -> Maybe ()) -> Bool -> Maybe ()
forall a b. (a -> b) -> a -> b
$ (Char -> Bool) -> String -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all Char -> Bool
isDigit String
file
m () -> Maybe (m ())
forall (m :: * -> *) a. Monad m => a -> m a
return (m () -> Maybe (m ())) -> m () -> Maybe (m ())
forall a b. (a -> b) -> a -> b
$ Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
warn Id
id 2210 "This is a file redirection. Was it supposed to be a comparison or fd operation?"
_ -> () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
prop_checkGlobAsCommand1 :: Bool
prop_checkGlobAsCommand1 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkGlobAsCommand "foo*"
prop_checkGlobAsCommand2 :: Bool
prop_checkGlobAsCommand2 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkGlobAsCommand "$(var[i])"
prop_checkGlobAsCommand3 :: Bool
prop_checkGlobAsCommand3 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkGlobAsCommand "echo foo*"
checkGlobAsCommand :: p -> Token -> m ()
checkGlobAsCommand _ t :: Token
t = case Token
t of
T_SimpleCommand _ _ (first :: Token
first:_)
| Token -> Bool
isGlob Token
first ->
Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
warn (Token -> Id
getId Token
first) 2211 "This is a glob used as a command name. Was it supposed to be in ${..}, array, or is it missing quoting?"
_ -> () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
prop_checkFlagAsCommand1 :: Bool
prop_checkFlagAsCommand1 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkFlagAsCommand "-e file"
prop_checkFlagAsCommand2 :: Bool
prop_checkFlagAsCommand2 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkFlagAsCommand "foo\n --bar=baz"
prop_checkFlagAsCommand3 :: Bool
prop_checkFlagAsCommand3 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkFlagAsCommand "'--myexec--' args"
prop_checkFlagAsCommand4 :: Bool
prop_checkFlagAsCommand4 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkFlagAsCommand "var=cmd --arg"
checkFlagAsCommand :: p -> Token -> m ()
checkFlagAsCommand _ t :: Token
t = case Token
t of
T_SimpleCommand _ [] (first :: Token
first:_)
| Token -> Bool
isUnquotedFlag Token
first ->
Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
warn (Token -> Id
getId Token
first) 2215 "This flag is used as a command name. Bad line break or missing [ .. ]?"
_ -> () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
prop_checkEmptyCondition1 :: Bool
prop_checkEmptyCondition1 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkEmptyCondition "if [ ]; then ..; fi"
prop_checkEmptyCondition2 :: Bool
prop_checkEmptyCondition2 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkEmptyCondition "[ foo -o bar ]"
checkEmptyCondition :: p -> Token -> m ()
checkEmptyCondition _ t :: Token
t = case Token
t of
TC_Empty id :: Id
id _ -> Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
style Id
id 2212 "Use 'false' instead of empty [/[[ conditionals."
_ -> () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
prop_checkPipeToNowhere1 :: Bool
prop_checkPipeToNowhere1 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
checkPipeToNowhere "foo | echo bar"
prop_checkPipeToNowhere2 :: Bool
prop_checkPipeToNowhere2 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
checkPipeToNowhere "basename < file.txt"
prop_checkPipeToNowhere3 :: Bool
prop_checkPipeToNowhere3 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
checkPipeToNowhere "printf 'Lol' <<< str"
prop_checkPipeToNowhere4 :: Bool
prop_checkPipeToNowhere4 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
checkPipeToNowhere "printf 'Lol' << eof\nlol\neof\n"
prop_checkPipeToNowhere5 :: Bool
prop_checkPipeToNowhere5 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
checkPipeToNowhere "echo foo | xargs du"
prop_checkPipeToNowhere6 :: Bool
prop_checkPipeToNowhere6 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
checkPipeToNowhere "ls | echo $(cat)"
prop_checkPipeToNowhere7 :: Bool
prop_checkPipeToNowhere7 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
checkPipeToNowhere "echo foo | var=$(cat) ls"
prop_checkPipeToNowhere8 :: Bool
prop_checkPipeToNowhere8 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
checkPipeToNowhere "foo | true"
prop_checkPipeToNowhere9 :: Bool
prop_checkPipeToNowhere9 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
checkPipeToNowhere "mv -i f . < /dev/stdin"
prop_checkPipeToNowhere10 :: Bool
prop_checkPipeToNowhere10 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
checkPipeToNowhere "ls > file | grep foo"
prop_checkPipeToNowhere11 :: Bool
prop_checkPipeToNowhere11 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
checkPipeToNowhere "ls | grep foo < file"
prop_checkPipeToNowhere12 :: Bool
prop_checkPipeToNowhere12 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
checkPipeToNowhere "ls > foo > bar"
prop_checkPipeToNowhere13 :: Bool
prop_checkPipeToNowhere13 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
checkPipeToNowhere "ls > foo 2> bar > baz"
prop_checkPipeToNowhere14 :: Bool
prop_checkPipeToNowhere14 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
checkPipeToNowhere "ls > foo &> bar"
prop_checkPipeToNowhere15 :: Bool
prop_checkPipeToNowhere15 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
checkPipeToNowhere "ls > foo 2> bar |& grep 'No space left'"
prop_checkPipeToNowhere16 :: Bool
prop_checkPipeToNowhere16 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
checkPipeToNowhere "echo World | cat << EOF\nhello $(cat)\nEOF\n"
prop_checkPipeToNowhere17 :: Bool
prop_checkPipeToNowhere17 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
checkPipeToNowhere "echo World | cat << 'EOF'\nhello $(cat)\nEOF\n"
prop_checkPipeToNowhere18 :: Bool
prop_checkPipeToNowhere18 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
checkPipeToNowhere "ls 1>&3 3>&1 3>&- | wc -l"
prop_checkPipeToNowhere19 :: Bool
prop_checkPipeToNowhere19 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
checkPipeToNowhere "find . -print0 | du --files0-from=/dev/stdin"
prop_checkPipeToNowhere20 :: Bool
prop_checkPipeToNowhere20 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
checkPipeToNowhere "find . | du --exclude-from=/dev/fd/0"
data PipeType = StdoutPipe | StdoutStderrPipe | NoPipe deriving (PipeType -> PipeType -> Bool
(PipeType -> PipeType -> Bool)
-> (PipeType -> PipeType -> Bool) -> Eq PipeType
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: PipeType -> PipeType -> Bool
$c/= :: PipeType -> PipeType -> Bool
== :: PipeType -> PipeType -> Bool
$c== :: PipeType -> PipeType -> Bool
Eq)
checkPipeToNowhere :: Parameters -> Token -> WriterT [TokenComment] Identity ()
checkPipeToNowhere :: Parameters -> Token -> WriterT [TokenComment] Identity ()
checkPipeToNowhere params :: Parameters
params t :: Token
t =
case Token
t of
T_Pipeline _ pipes :: [Token]
pipes cmds :: [Token]
cmds ->
((PipeType, Token, PipeType) -> WriterT [TokenComment] Identity ())
-> [(PipeType, Token, PipeType)]
-> WriterT [TokenComment] Identity ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ (PipeType, Token, PipeType) -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
(PipeType, Token, PipeType) -> m ()
checkPipe ([(PipeType, Token, PipeType)]
-> WriterT [TokenComment] Identity ())
-> [(PipeType, Token, PipeType)]
-> WriterT [TokenComment] Identity ()
forall a b. (a -> b) -> a -> b
$ [Token] -> [Token] -> [(PipeType, Token, PipeType)]
forall b. [Token] -> [b] -> [(PipeType, b, PipeType)]
commandsWithContext [Token]
pipes [Token]
cmds
T_Redirecting _ redirects :: [Token]
redirects cmd :: Token
cmd | (Token -> Bool) -> [Token] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any Token -> Bool
redirectsStdin [Token]
redirects -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *). MonadWriter [TokenComment] m => Token -> m ()
checkRedir Token
cmd
_ -> () -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
where
checkPipe :: (PipeType, Token, PipeType) -> m ()
checkPipe (input :: PipeType
input, stage :: Token
stage, output :: PipeType
output) = do
let hasConsumers :: Bool
hasConsumers = Token -> Bool
hasAdditionalConsumers Token
stage
let hasProducers :: Bool
hasProducers = Token -> Bool
hasAdditionalProducers Token
stage
Maybe (m ()) -> m ()
forall (t :: * -> *) (m :: * -> *) a.
(Foldable t, Monad m) =>
t (m a) -> m ()
sequence_ (Maybe (m ()) -> m ()) -> Maybe (m ()) -> m ()
forall a b. (a -> b) -> a -> b
$ do
Token
cmd <- Token -> Maybe Token
getCommand Token
stage
String
name <- Token -> Maybe String
getCommandBasename Token
cmd
Bool -> Maybe ()
forall (f :: * -> *). Alternative f => Bool -> f ()
guard (Bool -> Maybe ()) -> Bool -> Maybe ()
forall a b. (a -> b) -> a -> b
$ String
name String -> [String] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [String]
nonReadingCommands
Bool -> Maybe ()
forall (f :: * -> *). Alternative f => Bool -> f ()
guard (Bool -> Maybe ()) -> Bool -> Maybe ()
forall a b. (a -> b) -> a -> b
$ Bool -> Bool
not Bool
hasConsumers Bool -> Bool -> Bool
&& PipeType
input PipeType -> PipeType -> Bool
forall a. Eq a => a -> a -> Bool
/= PipeType
NoPipe
Bool -> Maybe ()
forall (f :: * -> *). Alternative f => Bool -> f ()
guard (Bool -> Maybe ()) -> (Bool -> Bool) -> Bool -> Maybe ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Bool -> Bool
not (Bool -> Maybe ()) -> Bool -> Maybe ()
forall a b. (a -> b) -> a -> b
$ String -> Token -> Bool
commandSpecificException String
name Token
cmd
let suggestion :: String
suggestion =
if String
name String -> String -> Bool
forall a. Eq a => a -> a -> Bool
== "echo"
then "Did you want 'cat' instead?"
else "Wrong command or missing xargs?"
m () -> Maybe (m ())
forall (m :: * -> *) a. Monad m => a -> m a
return (m () -> Maybe (m ())) -> m () -> Maybe (m ())
forall a b. (a -> b) -> a -> b
$ Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
warn (Token -> Id
getId Token
cmd) 2216 (String -> m ()) -> String -> m ()
forall a b. (a -> b) -> a -> b
$
"Piping to '" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
name String -> String -> String
forall a. [a] -> [a] -> [a]
++ "', a command that doesn't read stdin. " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
suggestion
Maybe (m ()) -> m ()
forall (t :: * -> *) (m :: * -> *) a.
(Foldable t, Monad m) =>
t (m a) -> m ()
sequence_ (Maybe (m ()) -> m ()) -> Maybe (m ()) -> m ()
forall a b. (a -> b) -> a -> b
$ do
T_Redirecting _ redirs :: [Token]
redirs cmd :: Token
cmd <- Token -> Maybe Token
forall (m :: * -> *) a. Monad m => a -> m a
return Token
stage
[[Code]]
fds <- (Token -> Maybe [Code]) -> [Token] -> Maybe [[Code]]
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
mapM Token -> Maybe [Code]
forall a. (Num a, Read a) => Token -> Maybe [a]
getRedirectionFds [Token]
redirs
let fdAndToken :: [(Integer, Token)]
fdAndToken :: [(Code, Token)]
fdAndToken =
(([Code], Token) -> [(Code, Token)])
-> [([Code], Token)] -> [(Code, Token)]
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap (\(list :: [Code]
list, redir :: Token
redir) -> (Code -> (Code, Token)) -> [Code] -> [(Code, Token)]
forall a b. (a -> b) -> [a] -> [b]
map (\n :: Code
n -> (Code
n, Token
redir)) [Code]
list) ([([Code], Token)] -> [(Code, Token)])
-> [([Code], Token)] -> [(Code, Token)]
forall a b. (a -> b) -> a -> b
$
[[Code]] -> [Token] -> [([Code], Token)]
forall a b. [a] -> [b] -> [(a, b)]
zip [[Code]]
fds [Token]
redirs
let fdMap :: Map Code [Token]
fdMap =
([Token] -> [Token] -> [Token])
-> [(Code, [Token])] -> Map Code [Token]
forall k a. Ord k => (a -> a -> a) -> [(k, a)] -> Map k a
Map.fromListWith [Token] -> [Token] -> [Token]
forall a. [a] -> [a] -> [a]
(++) ([(Code, [Token])] -> Map Code [Token])
-> [(Code, [Token])] -> Map Code [Token]
forall a b. (a -> b) -> a -> b
$
((Code, Token) -> (Code, [Token]))
-> [(Code, Token)] -> [(Code, [Token])]
forall a b. (a -> b) -> [a] -> [b]
map (\(a :: Code
a,b :: Token
b) -> (Code
a,[Token
b])) [(Code, Token)]
fdAndToken
let inputWarning :: m ()
inputWarning = Maybe (m ()) -> m ()
forall (t :: * -> *) (m :: * -> *) a.
(Foldable t, Monad m) =>
t (m a) -> m ()
sequence_ (Maybe (m ()) -> m ()) -> Maybe (m ()) -> m ()
forall a b. (a -> b) -> a -> b
$ do
Bool -> Maybe ()
forall (f :: * -> *). Alternative f => Bool -> f ()
guard (Bool -> Maybe ()) -> Bool -> Maybe ()
forall a b. (a -> b) -> a -> b
$ PipeType
input PipeType -> PipeType -> Bool
forall a. Eq a => a -> a -> Bool
/= PipeType
NoPipe Bool -> Bool -> Bool
&& Bool -> Bool
not Bool
hasConsumers
(override :: Token
override:_) <- Code -> Map Code [Token] -> Maybe [Token]
forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup 0 Map Code [Token]
fdMap
m () -> Maybe (m ())
forall (m :: * -> *) a. Monad m => a -> m a
return (m () -> Maybe (m ())) -> m () -> Maybe (m ())
forall a b. (a -> b) -> a -> b
$ Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
err (Token -> Id
getOpId Token
override) 2259 (String -> m ()) -> String -> m ()
forall a b. (a -> b) -> a -> b
$
"This redirection overrides piped input. To use both, merge or pass filenames."
let outputWarning :: m ()
outputWarning = Maybe (m ()) -> m ()
forall (t :: * -> *) (m :: * -> *) a.
(Foldable t, Monad m) =>
t (m a) -> m ()
sequence_ (Maybe (m ()) -> m ()) -> Maybe (m ()) -> m ()
forall a b. (a -> b) -> a -> b
$ do
Bool -> Maybe ()
forall (f :: * -> *). Alternative f => Bool -> f ()
guard (Bool -> Maybe ()) -> Bool -> Maybe ()
forall a b. (a -> b) -> a -> b
$ PipeType
output PipeType -> PipeType -> Bool
forall a. Eq a => a -> a -> Bool
== PipeType
StdoutPipe Bool -> Bool -> Bool
&& Bool -> Bool
not Bool
hasProducers
(override :: Token
override:_) <- Code -> Map Code [Token] -> Maybe [Token]
forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup 1 Map Code [Token]
fdMap
m () -> Maybe (m ())
forall (m :: * -> *) a. Monad m => a -> m a
return (m () -> Maybe (m ())) -> m () -> Maybe (m ())
forall a b. (a -> b) -> a -> b
$ Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
err (Token -> Id
getOpId Token
override) 2260 (String -> m ()) -> String -> m ()
forall a b. (a -> b) -> a -> b
$
"This redirection overrides the output pipe. Use 'tee' to output to both."
m () -> Maybe (m ())
forall (m :: * -> *) a. Monad m => a -> m a
return (m () -> Maybe (m ())) -> m () -> Maybe (m ())
forall a b. (a -> b) -> a -> b
$ do
m ()
inputWarning
m ()
outputWarning
((Code, [Token]) -> m ()) -> [(Code, [Token])] -> m ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ (Code, [Token]) -> m ()
forall (m :: * -> *) a.
(MonadWriter [TokenComment] m, Eq a, Num a, Show a) =>
(a, [Token]) -> m ()
warnAboutDupes ([(Code, [Token])] -> m ()) -> [(Code, [Token])] -> m ()
forall a b. (a -> b) -> a -> b
$ Map Code [Token] -> [(Code, [Token])]
forall k a. Map k a -> [(k, a)]
Map.assocs Map Code [Token]
fdMap
commandSpecificException :: String -> Token -> Bool
commandSpecificException name :: String
name cmd :: Token
cmd =
case String
name of
"du" -> ((Token, String) -> Bool) -> [(Token, String)] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any ((String -> [String] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` ["exclude-from", "files0-from"]) (String -> Bool)
-> ((Token, String) -> String) -> (Token, String) -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Token, String) -> String
forall a b. (a, b) -> b
snd) ([(Token, String)] -> Bool) -> [(Token, String)] -> Bool
forall a b. (a -> b) -> a -> b
$ Token -> [(Token, String)]
getAllFlags Token
cmd
_ -> Bool
False
warnAboutDupes :: (a, [Token]) -> m ()
warnAboutDupes (n :: a
n, list :: [Token]
list@(_:_:_)) =
[Token] -> (Token -> m ()) -> m ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
t a -> (a -> m b) -> m ()
forM_ [Token]
list ((Token -> m ()) -> m ()) -> (Token -> m ()) -> m ()
forall a b. (a -> b) -> a -> b
$ \c :: Token
c -> Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
err (Token -> Id
getOpId Token
c) 2261 (String -> m ()) -> String -> m ()
forall a b. (a -> b) -> a -> b
$
"Multiple redirections compete for " String -> String -> String
forall a. [a] -> [a] -> [a]
++ a -> String
forall a. (Eq a, Num a, Show a) => a -> String
str a
n String -> String -> String
forall a. [a] -> [a] -> [a]
++ ". Use cat, tee, or pass filenames instead."
warnAboutDupes _ = () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
alternative :: String
alternative =
if Parameters -> Shell
shellType Parameters
params Shell -> [Shell] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [Shell
Bash, Shell
Ksh]
then "process substitutions or temp files"
else "temporary files"
str :: a -> String
str n :: a
n =
case a
n of
0 -> "stdin"
1 -> "stdout"
2 -> "stderr"
_ -> "FD " String -> String -> String
forall a. [a] -> [a] -> [a]
++ a -> String
forall a. Show a => a -> String
show a
n
checkRedir :: Token -> m ()
checkRedir cmd :: Token
cmd = Maybe (m ()) -> m ()
forall (t :: * -> *) (m :: * -> *) a.
(Foldable t, Monad m) =>
t (m a) -> m ()
sequence_ (Maybe (m ()) -> m ()) -> Maybe (m ()) -> m ()
forall a b. (a -> b) -> a -> b
$ do
String
name <- Token -> Maybe String
getCommandBasename Token
cmd
Bool -> Maybe ()
forall (f :: * -> *). Alternative f => Bool -> f ()
guard (Bool -> Maybe ()) -> Bool -> Maybe ()
forall a b. (a -> b) -> a -> b
$ String
name String -> [String] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [String]
nonReadingCommands
Bool -> Maybe ()
forall (f :: * -> *). Alternative f => Bool -> f ()
guard (Bool -> Maybe ()) -> (Bool -> Bool) -> Bool -> Maybe ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Bool -> Bool
not (Bool -> Maybe ()) -> Bool -> Maybe ()
forall a b. (a -> b) -> a -> b
$ Token -> Bool
hasAdditionalConsumers Token
cmd
Bool -> Maybe ()
forall (f :: * -> *). Alternative f => Bool -> f ()
guard (Bool -> Maybe ()) -> (Bool -> Bool) -> Bool -> Maybe ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Bool -> Bool
not (Bool -> Maybe ()) -> Bool -> Maybe ()
forall a b. (a -> b) -> a -> b
$ String
name String -> [String] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` ["cp", "mv", "rm"] Bool -> Bool -> Bool
&& Token
cmd Token -> String -> Bool
`hasFlag` "i"
let suggestion :: String
suggestion =
if String
name String -> String -> Bool
forall a. Eq a => a -> a -> Bool
== "echo"
then "Did you want 'cat' instead?"
else "Bad quoting, wrong command or missing xargs?"
m () -> Maybe (m ())
forall (m :: * -> *) a. Monad m => a -> m a
return (m () -> Maybe (m ())) -> m () -> Maybe (m ())
forall a b. (a -> b) -> a -> b
$ Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
warn (Token -> Id
getId Token
cmd) 2217 (String -> m ()) -> String -> m ()
forall a b. (a -> b) -> a -> b
$
"Redirecting to '" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
name String -> String -> String
forall a. [a] -> [a] -> [a]
++ "', a command that doesn't read stdin. " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
suggestion
hasAdditionalConsumers :: Token -> Bool
hasAdditionalConsumers = (Token -> Bool) -> Token -> Bool
treeContains Token -> Bool
mayConsume
hasAdditionalProducers :: Token -> Bool
hasAdditionalProducers = (Token -> Bool) -> Token -> Bool
treeContains Token -> Bool
mayProduce
treeContains :: (Token -> Bool) -> Token -> Bool
treeContains pred :: Token -> Bool
pred t :: Token
t = Maybe Token -> Bool
forall a. Maybe a -> Bool
isNothing (Maybe Token -> Bool) -> Maybe Token -> Bool
forall a b. (a -> b) -> a -> b
$
(Token -> Maybe ()) -> Token -> Maybe Token
forall (m :: * -> *).
Monad m =>
(Token -> m ()) -> Token -> m Token
doAnalysis (Bool -> Maybe ()
forall (f :: * -> *). Alternative f => Bool -> f ()
guard (Bool -> Maybe ()) -> (Token -> Bool) -> Token -> Maybe ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Bool -> Bool
not (Bool -> Bool) -> (Token -> Bool) -> Token -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Token -> Bool
pred) Token
t
mayConsume :: Token -> Bool
mayConsume t :: Token
t =
case Token
t of
T_ProcSub _ "<" _ -> Bool
True
T_Backticked {} -> Bool
True
T_DollarExpansion {} -> Bool
True
_ -> Bool
False
mayProduce :: Token -> Bool
mayProduce t :: Token
t =
case Token
t of
T_ProcSub _ ">" _ -> Bool
True
_ -> Bool
False
getOpId :: Token -> Id
getOpId t :: Token
t =
case Token
t of
T_FdRedirect _ _ x :: Token
x -> Token -> Id
getOpId Token
x
T_IoFile _ op :: Token
op _ -> Token -> Id
getId Token
op
_ -> Token -> Id
getId Token
t
getRedirectionFds :: Token -> Maybe [a]
getRedirectionFds t :: Token
t =
case Token
t of
T_FdRedirect _ "" x :: Token
x -> Token -> Maybe [a]
forall a. Num a => Token -> Maybe [a]
getDefaultFds Token
x
T_FdRedirect _ "&" _ -> [a] -> Maybe [a]
forall (m :: * -> *) a. Monad m => a -> m a
return [1, 2]
T_FdRedirect _ num :: String
num x :: Token
x | (Char -> Bool) -> String -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all Char -> Bool
isDigit String
num ->
Token -> Maybe [Code]
forall a. Num a => Token -> Maybe [a]
getDefaultFds Token
x Maybe [Code] -> Maybe [a] -> Maybe [a]
forall (f :: * -> *) a b. Applicative f => f a -> f b -> f b
*> [a] -> Maybe [a]
forall (m :: * -> *) a. Monad m => a -> m a
return [String -> a
forall a. Read a => String -> a
read String
num]
_ -> Maybe [a]
forall a. Maybe a
Nothing
getDefaultFds :: Token -> Maybe [a]
getDefaultFds redir :: Token
redir =
case Token
redir of
T_HereDoc {} -> [a] -> Maybe [a]
forall (m :: * -> *) a. Monad m => a -> m a
return [0]
T_HereString {} -> [a] -> Maybe [a]
forall (m :: * -> *) a. Monad m => a -> m a
return [0]
T_IoFile _ op :: Token
op _ ->
case Token
op of
T_Less {} -> [a] -> Maybe [a]
forall (m :: * -> *) a. Monad m => a -> m a
return [0]
T_Greater {} -> [a] -> Maybe [a]
forall (m :: * -> *) a. Monad m => a -> m a
return [1]
T_DGREAT {} -> [a] -> Maybe [a]
forall (m :: * -> *) a. Monad m => a -> m a
return [1]
T_GREATAND {} -> [a] -> Maybe [a]
forall (m :: * -> *) a. Monad m => a -> m a
return [1, 2]
T_CLOBBER {} -> [a] -> Maybe [a]
forall (m :: * -> *) a. Monad m => a -> m a
return [1]
T_IoDuplicate _ op :: Token
op "-" -> Token -> Maybe [a]
getDefaultFds Token
op
_ -> Maybe [a]
forall a. Maybe a
Nothing
_ -> Maybe [a]
forall a. Maybe a
Nothing
redirectsStdin :: Token -> Bool
redirectsStdin t :: Token
t =
Bool -> Maybe Bool -> Bool
forall a. a -> Maybe a -> a
fromMaybe Bool
False (Maybe Bool -> Bool) -> Maybe Bool -> Bool
forall a b. (a -> b) -> a -> b
$ do
[Code]
fds <- Token -> Maybe [Code]
forall a. (Num a, Read a) => Token -> Maybe [a]
getRedirectionFds Token
t
Bool -> Maybe Bool
forall (m :: * -> *) a. Monad m => a -> m a
return (Bool -> Maybe Bool) -> Bool -> Maybe Bool
forall a b. (a -> b) -> a -> b
$ 0 Code -> [Code] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [Code]
fds
pipeType :: Token -> PipeType
pipeType t :: Token
t =
case Token
t of
T_Pipe _ "|" -> PipeType
StdoutPipe
T_Pipe _ "|&" -> PipeType
StdoutStderrPipe
_ -> PipeType
NoPipe
commandsWithContext :: [Token] -> [b] -> [(PipeType, b, PipeType)]
commandsWithContext pipes :: [Token]
pipes cmds :: [b]
cmds =
let pipeTypes :: [PipeType]
pipeTypes = (Token -> PipeType) -> [Token] -> [PipeType]
forall a b. (a -> b) -> [a] -> [b]
map Token -> PipeType
pipeType [Token]
pipes
inputs :: [PipeType]
inputs = PipeType
NoPipe PipeType -> [PipeType] -> [PipeType]
forall a. a -> [a] -> [a]
: [PipeType]
pipeTypes
outputs :: [PipeType]
outputs = [PipeType]
pipeTypes [PipeType] -> [PipeType] -> [PipeType]
forall a. [a] -> [a] -> [a]
++ [PipeType
NoPipe]
in
[PipeType] -> [b] -> [PipeType] -> [(PipeType, b, PipeType)]
forall a b c. [a] -> [b] -> [c] -> [(a, b, c)]
zip3 [PipeType]
inputs [b]
cmds [PipeType]
outputs
prop_checkUseBeforeDefinition1 :: Bool
prop_checkUseBeforeDefinition1 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyTree Parameters -> Token -> [TokenComment]
forall t. t -> Token -> [TokenComment]
checkUseBeforeDefinition "f; f() { true; }"
prop_checkUseBeforeDefinition2 :: Bool
prop_checkUseBeforeDefinition2 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
forall t. t -> Token -> [TokenComment]
checkUseBeforeDefinition "f() { true; }; f"
prop_checkUseBeforeDefinition3 :: Bool
prop_checkUseBeforeDefinition3 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
forall t. t -> Token -> [TokenComment]
checkUseBeforeDefinition "if ! mycmd --version; then mycmd() { true; }; fi"
prop_checkUseBeforeDefinition4 :: Bool
prop_checkUseBeforeDefinition4 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
forall t. t -> Token -> [TokenComment]
checkUseBeforeDefinition "mycmd || mycmd() { f; }"
checkUseBeforeDefinition :: p -> Token -> [TokenComment]
checkUseBeforeDefinition _ t :: Token
t =
WriterT [TokenComment] Identity () -> [TokenComment]
forall w a. Writer w a -> w
execWriter (WriterT [TokenComment] Identity () -> [TokenComment])
-> WriterT [TokenComment] Identity () -> [TokenComment]
forall a b. (a -> b) -> a -> b
$ StateT (Map String Token) (WriterT [TokenComment] Identity) ()
-> Map String Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) s a. Monad m => StateT s m a -> s -> m a
evalStateT ((Token
-> StateT (Map String Token) (WriterT [TokenComment] Identity) ())
-> [Token]
-> StateT (Map String Token) (WriterT [TokenComment] Identity) ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ Token
-> StateT (Map String Token) (WriterT [TokenComment] Identity) ()
forall (m :: * -> *).
(MonadState (Map String Token) m, MonadWriter [TokenComment] m) =>
Token -> m ()
examine ([Token]
-> StateT (Map String Token) (WriterT [TokenComment] Identity) ())
-> [Token]
-> StateT (Map String Token) (WriterT [TokenComment] Identity) ()
forall a b. (a -> b) -> a -> b
$ [Token]
revCommands) Map String Token
forall k a. Map k a
Map.empty
where
examine :: Token -> m ()
examine t :: Token
t = case Token
t of
T_Pipeline _ _ [T_Redirecting _ _ (T_Function _ _ _ name :: String
name _)] ->
(Map String Token -> Map String Token) -> m ()
forall s (m :: * -> *). MonadState s m => (s -> s) -> m ()
modify ((Map String Token -> Map String Token) -> m ())
-> (Map String Token -> Map String Token) -> m ()
forall a b. (a -> b) -> a -> b
$ String -> Token -> Map String Token -> Map String Token
forall k a. Ord k => k -> a -> Map k a -> Map k a
Map.insert String
name Token
t
T_Annotation _ _ w :: Token
w -> Token -> m ()
examine Token
w
T_Pipeline _ _ cmds :: [Token]
cmds -> do
Map String Token
m <- m (Map String Token)
forall s (m :: * -> *). MonadState s m => m s
get
Bool -> m () -> m ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
unless (Map String Token -> Bool
forall k a. Map k a -> Bool
Map.null Map String Token
m) (m () -> m ()) -> m () -> m ()
forall a b. (a -> b) -> a -> b
$
(Token -> m ()) -> [Token] -> m ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ (Map String Token -> Token -> m ()
forall (m :: * -> *) a.
MonadWriter [TokenComment] m =>
Map String a -> Token -> m ()
checkUsage Map String Token
m) ([Token] -> m ()) -> [Token] -> m ()
forall a b. (a -> b) -> a -> b
$ (Token -> [Token]) -> [Token] -> [Token]
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap Token -> [Token]
recursiveSequences [Token]
cmds
_ -> () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
checkUsage :: Map String a -> Token -> m ()
checkUsage map :: Map String a
map cmd :: Token
cmd = Maybe (m ()) -> m ()
forall (t :: * -> *) (m :: * -> *) a.
(Foldable t, Monad m) =>
t (m a) -> m ()
sequence_ (Maybe (m ()) -> m ()) -> Maybe (m ()) -> m ()
forall a b. (a -> b) -> a -> b
$ do
String
name <- Token -> Maybe String
getCommandName Token
cmd
a
def <- String -> Map String a -> Maybe a
forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup String
name Map String a
map
m () -> Maybe (m ())
forall (m :: * -> *) a. Monad m => a -> m a
return (m () -> Maybe (m ())) -> m () -> Maybe (m ())
forall a b. (a -> b) -> a -> b
$
Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
err (Token -> Id
getId Token
cmd) 2218
"This function is only defined later. Move the definition up."
revCommands :: [Token]
revCommands = [Token] -> [Token]
forall a. [a] -> [a]
reverse ([Token] -> [Token]) -> [Token] -> [Token]
forall a b. (a -> b) -> a -> b
$ [[Token]] -> [Token]
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat ([[Token]] -> [Token]) -> [[Token]] -> [Token]
forall a b. (a -> b) -> a -> b
$ Token -> [[Token]]
getCommandSequences Token
t
recursiveSequences :: Token -> [Token]
recursiveSequences x :: Token
x =
let list :: [Token]
list = [[Token]] -> [Token]
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat ([[Token]] -> [Token]) -> [[Token]] -> [Token]
forall a b. (a -> b) -> a -> b
$ Token -> [[Token]]
getCommandSequences Token
x in
if [Token] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null [Token]
list
then [Token
x]
else (Token -> [Token]) -> [Token] -> [Token]
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap Token -> [Token]
recursiveSequences [Token]
list
prop_checkForLoopGlobVariables1 :: Bool
prop_checkForLoopGlobVariables1 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkForLoopGlobVariables "for i in $var/*.txt; do true; done"
prop_checkForLoopGlobVariables2 :: Bool
prop_checkForLoopGlobVariables2 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkForLoopGlobVariables "for i in \"$var\"/*.txt; do true; done"
prop_checkForLoopGlobVariables3 :: Bool
prop_checkForLoopGlobVariables3 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkForLoopGlobVariables "for i in $var; do true; done"
checkForLoopGlobVariables :: p -> Token -> m ()
checkForLoopGlobVariables _ t :: Token
t =
case Token
t of
T_ForIn _ _ words :: [Token]
words _ -> (Token -> m ()) -> [Token] -> m ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ Token -> m ()
forall (m :: * -> *). MonadWriter [TokenComment] m => Token -> m ()
check [Token]
words
_ -> () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
where
check :: Token -> f ()
check (T_NormalWord _ parts :: [Token]
parts) =
Bool -> f () -> f ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when ((Token -> Bool) -> [Token] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any Token -> Bool
isGlob [Token]
parts) (f () -> f ()) -> f () -> f ()
forall a b. (a -> b) -> a -> b
$
(Token -> f ()) -> [Token] -> f ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ Token -> f ()
forall (m :: * -> *). MonadWriter [TokenComment] m => Token -> m ()
suggest ([Token] -> f ()) -> [Token] -> f ()
forall a b. (a -> b) -> a -> b
$ (Token -> Bool) -> [Token] -> [Token]
forall a. (a -> Bool) -> [a] -> [a]
filter Token -> Bool
isQuoteableExpansion [Token]
parts
suggest :: Token -> m ()
suggest t :: Token
t = Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
info (Token -> Id
getId Token
t) 2231
"Quote expansions in this for loop glob to prevent wordsplitting, e.g. \"$dir\"/*.txt ."
prop_checkSubshelledTests1 :: Bool
prop_checkSubshelledTests1 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkSubshelledTests "a && ( [ b ] || ! [ c ] )"
prop_checkSubshelledTests2 :: Bool
prop_checkSubshelledTests2 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkSubshelledTests "( [ a ] )"
prop_checkSubshelledTests3 :: Bool
prop_checkSubshelledTests3 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkSubshelledTests "( [ a ] && [ b ] || test c )"
prop_checkSubshelledTests4 :: Bool
prop_checkSubshelledTests4 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkSubshelledTests "( [ a ] && { [ b ] && [ c ]; } )"
checkSubshelledTests :: Parameters -> Token -> m ()
checkSubshelledTests params :: Parameters
params t :: Token
t =
case Token
t of
T_Subshell id :: Id
id list :: [Token]
list | (Token -> Bool) -> [Token] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all Token -> Bool
isTestStructure [Token]
list ->
case () of
_ | [Token] -> Bool
isCompoundCondition (Map Id Token -> Token -> [Token]
getPath (Parameters -> Map Id Token
parentMap Parameters
params) Token
t) ->
Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
style Id
id 2233 "Remove superfluous (..) around condition."
_ | [Token] -> Bool
isSingleTest [Token]
list ->
Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
style Id
id 2234 "Remove superfluous (..) around test command."
_ -> Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
style Id
id 2235 "Use { ..; } instead of (..) to avoid subshell overhead."
_ -> () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
where
isSingleTest :: [Token] -> Bool
isSingleTest cmds :: [Token]
cmds =
case [Token]
cmds of
[c :: Token
c] | Token -> Bool
isTestCommand Token
c -> Bool
True
_ -> Bool
False
isTestStructure :: Token -> Bool
isTestStructure t :: Token
t =
case Token
t of
T_Banged _ t :: Token
t -> Token -> Bool
isTestStructure Token
t
T_AndIf _ a :: Token
a b :: Token
b -> Token -> Bool
isTestStructure Token
a Bool -> Bool -> Bool
&& Token -> Bool
isTestStructure Token
b
T_OrIf _ a :: Token
a b :: Token
b -> Token -> Bool
isTestStructure Token
a Bool -> Bool -> Bool
&& Token -> Bool
isTestStructure Token
b
T_Pipeline _ [] [T_Redirecting _ _ cmd :: Token
cmd] ->
case Token
cmd of
T_BraceGroup _ ts :: [Token]
ts -> (Token -> Bool) -> [Token] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all Token -> Bool
isTestStructure [Token]
ts
T_Subshell _ ts :: [Token]
ts -> (Token -> Bool) -> [Token] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all Token -> Bool
isTestStructure [Token]
ts
_ -> Token -> Bool
isTestCommand Token
t
_ -> Token -> Bool
isTestCommand Token
t
isTestCommand :: Token -> Bool
isTestCommand t :: Token
t =
case Token
t of
T_Pipeline _ [] [T_Redirecting _ _ cmd :: Token
cmd] ->
case Token
cmd of
T_Condition {} -> Bool
True
_ -> Token
cmd Token -> String -> Bool
`isCommand` "test"
_ -> Bool
False
isCompoundCondition :: [Token] -> Bool
isCompoundCondition chain :: [Token]
chain =
case (Token -> Bool) -> [Token] -> [Token]
forall a. (a -> Bool) -> [a] -> [a]
dropWhile Token -> Bool
skippable (Int -> [Token] -> [Token]
forall a. Int -> [a] -> [a]
drop 1 [Token]
chain) of
T_IfExpression {} : _ -> Bool
True
T_WhileExpression {} : _ -> Bool
True
T_UntilExpression {} : _ -> Bool
True
_ -> Bool
False
skippable :: Token -> Bool
skippable t :: Token
t =
case Token
t of
T_Redirecting _ [] _ -> Bool
True
T_Pipeline _ [] _ -> Bool
True
T_Annotation {} -> Bool
True
_ -> Bool
False
prop_checkInvertedStringTest1 :: Bool
prop_checkInvertedStringTest1 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkInvertedStringTest "[ ! -z $var ]"
prop_checkInvertedStringTest2 :: Bool
prop_checkInvertedStringTest2 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkInvertedStringTest "! [[ -n $var ]]"
prop_checkInvertedStringTest3 :: Bool
prop_checkInvertedStringTest3 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkInvertedStringTest "! [ -x $var ]"
prop_checkInvertedStringTest4 :: Bool
prop_checkInvertedStringTest4 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkInvertedStringTest "[[ ! -w $var ]]"
prop_checkInvertedStringTest5 :: Bool
prop_checkInvertedStringTest5 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkInvertedStringTest "[ -z $var ]"
checkInvertedStringTest :: p -> Token -> m ()
checkInvertedStringTest _ t :: Token
t =
case Token
t of
TC_Unary _ _ "!" (TC_Unary _ _ op :: String
op _) ->
case String
op of
"-n" -> Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
style (Token -> Id
getId Token
t) 2236 "Use -z instead of ! -n."
"-z" -> Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
style (Token -> Id
getId Token
t) 2236 "Use -n instead of ! -z."
_ -> () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
T_Banged _ (T_Pipeline _ _
[T_Redirecting _ _ (T_Condition _ _ (TC_Unary _ _ op :: String
op _))]) ->
case String
op of
"-n" -> Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
style (Token -> Id
getId Token
t) 2237 "Use [ -z .. ] instead of ! [ -n .. ]."
"-z" -> Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
style (Token -> Id
getId Token
t) 2237 "Use [ -n .. ] instead of ! [ -z .. ]."
_ -> () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
_ -> () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
prop_checkRedirectionToCommand1 :: Bool
prop_checkRedirectionToCommand1 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkRedirectionToCommand "ls > rm"
prop_checkRedirectionToCommand2 :: Bool
prop_checkRedirectionToCommand2 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkRedirectionToCommand "ls > 'rm'"
prop_checkRedirectionToCommand3 :: Bool
prop_checkRedirectionToCommand3 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkRedirectionToCommand "ls > myfile"
checkRedirectionToCommand :: p -> Token -> m ()
checkRedirectionToCommand _ t :: Token
t =
case Token
t of
T_IoFile _ _ (T_NormalWord id :: Id
id [T_Literal _ str :: String
str]) | String
str String -> [String] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [String]
commonCommands
Bool -> Bool -> Bool
&& String
str String -> String -> Bool
forall a. Eq a => a -> a -> Bool
/= "file" ->
Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
warn Id
id 2238 "Redirecting to/from command name instead of file. Did you want pipes/xargs (or quote to ignore)?"
_ -> () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
prop_checkNullaryExpansionTest1 :: Bool
prop_checkNullaryExpansionTest1 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkNullaryExpansionTest "[[ $(a) ]]"
prop_checkNullaryExpansionTest2 :: Bool
prop_checkNullaryExpansionTest2 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkNullaryExpansionTest "[[ $a ]]"
prop_checkNullaryExpansionTest3 :: Bool
prop_checkNullaryExpansionTest3 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkNullaryExpansionTest "[[ $a=1 ]]"
prop_checkNullaryExpansionTest4 :: Bool
prop_checkNullaryExpansionTest4 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkNullaryExpansionTest "[[ -n $(a) ]]"
prop_checkNullaryExpansionTest5 :: Bool
prop_checkNullaryExpansionTest5 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkNullaryExpansionTest "[[ \"$a$b\" ]]"
prop_checkNullaryExpansionTest6 :: Bool
prop_checkNullaryExpansionTest6 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkNullaryExpansionTest "[[ `x` ]]"
checkNullaryExpansionTest :: Parameters -> Token -> m ()
checkNullaryExpansionTest params :: Parameters
params t :: Token
t =
case Token
t of
TC_Nullary _ _ word :: Token
word ->
case Token -> [Token]
getWordParts Token
word of
[t :: Token
t] | Token -> Bool
isCommandSubstitution Token
t ->
Id -> Code -> String -> Fix -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> Fix -> m ()
styleWithFix Id
id 2243 "Prefer explicit -n to check for output (or run command without [/[[ to check for success)." Fix
fix
x :: [Token]
x | (Token -> Bool) -> [Token] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all (Bool -> Bool
not (Bool -> Bool) -> (Token -> Bool) -> Token -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Token -> Bool
isConstant) [Token]
x ->
Id -> Code -> String -> Fix -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> Fix -> m ()
styleWithFix Id
id 2244 "Prefer explicit -n to check non-empty string (or use =/-ne to check boolean/integer)." Fix
fix
_ -> () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
where
id :: Id
id = Token -> Id
getId Token
word
fix :: Fix
fix = [Replacement] -> Fix
fixWith [Id -> Parameters -> Code -> String -> Replacement
replaceStart Id
id Parameters
params 0 "-n "]
_ -> () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
prop_checkDollarQuoteParen1 :: Bool
prop_checkDollarQuoteParen1 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkDollarQuoteParen "$\"(foo)\""
prop_checkDollarQuoteParen2 :: Bool
prop_checkDollarQuoteParen2 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkDollarQuoteParen "$\"{foo}\""
prop_checkDollarQuoteParen3 :: Bool
prop_checkDollarQuoteParen3 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkDollarQuoteParen "\"$(foo)\""
prop_checkDollarQuoteParen4 :: Bool
prop_checkDollarQuoteParen4 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkDollarQuoteParen "$\"..\""
checkDollarQuoteParen :: Parameters -> Token -> m ()
checkDollarQuoteParen params :: Parameters
params t :: Token
t =
case Token
t of
T_DollarDoubleQuoted id :: Id
id ((T_Literal _ (c :: Char
c:_)):_) | Char
c Char -> String -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` "({" ->
Id -> Code -> String -> Fix -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> Fix -> m ()
warnWithFix Id
id 2247 "Flip leading $ and \" if this should be a quoted substitution." (Id -> Fix
fix Id
id)
_ -> () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
where
fix :: Id -> Fix
fix id :: Id
id = [Replacement] -> Fix
fixWith [Id -> Parameters -> Code -> String -> Replacement
replaceStart Id
id Parameters
params 2 "\"$"]
prop_checkTranslatedStringVariable1 :: Bool
prop_checkTranslatedStringVariable1 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkTranslatedStringVariable "foo_bar2=val; $\"foo_bar2\""
prop_checkTranslatedStringVariable2 :: Bool
prop_checkTranslatedStringVariable2 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkTranslatedStringVariable "$\"foo_bar2\""
prop_checkTranslatedStringVariable3 :: Bool
prop_checkTranslatedStringVariable3 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkTranslatedStringVariable "$\"..\""
prop_checkTranslatedStringVariable4 :: Bool
prop_checkTranslatedStringVariable4 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkTranslatedStringVariable "var=val; $\"$var\""
prop_checkTranslatedStringVariable5 :: Bool
prop_checkTranslatedStringVariable5 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkTranslatedStringVariable "foo=var; bar=val2; $\"foo bar\""
checkTranslatedStringVariable :: Parameters -> Token -> m ()
checkTranslatedStringVariable params :: Parameters
params (T_DollarDoubleQuoted id :: Id
id [T_Literal _ s :: String
s])
| (Char -> Bool) -> String -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all Char -> Bool
isVariableChar String
s
Bool -> Bool -> Bool
&& String -> Map String Token -> Bool
forall k a. Ord k => k -> Map k a -> Bool
Map.member String
s Map String Token
assignments
= Id -> Code -> String -> Fix -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> Fix -> m ()
warnWithFix Id
id 2256 "This translated string is the name of a variable. Flip leading $ and \" if this should be a quoted substitution." (Id -> Fix
fix Id
id)
where
assignments :: Map String Token
assignments = (Map String Token
-> (Map String Token -> Map String Token) -> Map String Token)
-> Map String Token
-> [Map String Token -> Map String Token]
-> Map String Token
forall (t :: * -> *) b a.
Foldable t =>
(b -> a -> b) -> b -> t a -> b
foldl (((Map String Token -> Map String Token)
-> Map String Token -> Map String Token)
-> Map String Token
-> (Map String Token -> Map String Token)
-> Map String Token
forall a b c. (a -> b -> c) -> b -> a -> c
flip (Map String Token -> Map String Token)
-> Map String Token -> Map String Token
forall a b. (a -> b) -> a -> b
($)) Map String Token
forall k a. Map k a
Map.empty ((StackData -> Map String Token -> Map String Token)
-> [StackData] -> [Map String Token -> Map String Token]
forall a b. (a -> b) -> [a] -> [b]
map StackData -> Map String Token -> Map String Token
insertAssignment ([StackData] -> [Map String Token -> Map String Token])
-> [StackData] -> [Map String Token -> Map String Token]
forall a b. (a -> b) -> a -> b
$ Parameters -> [StackData]
variableFlow Parameters
params)
insertAssignment :: StackData -> Map String Token -> Map String Token
insertAssignment (Assignment (_, token :: Token
token, name :: String
name, _)) | String -> Bool
isVariableName String
name =
String -> Token -> Map String Token -> Map String Token
forall k a. Ord k => k -> a -> Map k a -> Map k a
Map.insert String
name Token
token
insertAssignment _ = Map String Token -> Map String Token
forall a. a -> a
Prelude.id
fix :: Id -> Fix
fix id :: Id
id = [Replacement] -> Fix
fixWith [Id -> Parameters -> Code -> String -> Replacement
replaceStart Id
id Parameters
params 2 "\"$"]
checkTranslatedStringVariable _ _ = () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
prop_checkDefaultCase1 :: Bool
prop_checkDefaultCase1 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkDefaultCase "case $1 in a) true ;; esac"
prop_checkDefaultCase2 :: Bool
prop_checkDefaultCase2 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkDefaultCase "case $1 in ?*?) true ;; *? ) true ;; esac"
prop_checkDefaultCase3 :: Bool
prop_checkDefaultCase3 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkDefaultCase "case $1 in x|*) true ;; esac"
prop_checkDefaultCase4 :: Bool
prop_checkDefaultCase4 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkDefaultCase "case $1 in **) true ;; esac"
checkDefaultCase :: p -> Token -> f ()
checkDefaultCase _ t :: Token
t =
case Token
t of
T_CaseExpression id :: Id
id _ list :: [(CaseType, [Token], [Token])]
list ->
Bool -> f () -> f ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
unless (((CaseType, [Token], [Token]) -> Bool)
-> [(CaseType, [Token], [Token])] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any (CaseType, [Token], [Token]) -> Bool
forall (t :: * -> *) a c. Foldable t => (a, t Token, c) -> Bool
canMatchAny [(CaseType, [Token], [Token])]
list) (f () -> f ()) -> f () -> f ()
forall a b. (a -> b) -> a -> b
$
Id -> Code -> String -> f ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
info Id
id 2249 "Consider adding a default *) case, even if it just exits with error."
_ -> () -> f ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
where
canMatchAny :: (a, t Token, c) -> Bool
canMatchAny (_, list :: t Token
list, _) = (Token -> Bool) -> t Token -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any Token -> Bool
canMatchAny' t Token
list
canMatchAny' :: Token -> Bool
canMatchAny' pat :: Token
pat = Bool -> Maybe Bool -> Bool
forall a. a -> Maybe a -> a
fromMaybe Bool
False (Maybe Bool -> Bool) -> Maybe Bool -> Bool
forall a b. (a -> b) -> a -> b
$ do
[PseudoGlob]
pg <- Token -> Maybe [PseudoGlob]
wordToExactPseudoGlob Token
pat
Bool -> Maybe Bool
forall (m :: * -> *) a. Monad m => a -> m a
return (Bool -> Maybe Bool) -> Bool -> Maybe Bool
forall a b. (a -> b) -> a -> b
$ [PseudoGlob] -> [PseudoGlob] -> Bool
pseudoGlobIsSuperSetof [PseudoGlob]
pg [PseudoGlob
PGMany]
prop_checkUselessBang1 :: Bool
prop_checkUselessBang1 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkUselessBang "set -e; ! true; rest"
prop_checkUselessBang2 :: Bool
prop_checkUselessBang2 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkUselessBang "! true; rest"
prop_checkUselessBang3 :: Bool
prop_checkUselessBang3 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkUselessBang "set -e; while true; do ! true; done"
prop_checkUselessBang4 :: Bool
prop_checkUselessBang4 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkUselessBang "set -e; if ! true; then true; fi"
prop_checkUselessBang5 :: Bool
prop_checkUselessBang5 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkUselessBang "set -e; ( ! true )"
prop_checkUselessBang6 :: Bool
prop_checkUselessBang6 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkUselessBang "set -e; { ! true; }"
prop_checkUselessBang7 :: Bool
prop_checkUselessBang7 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkUselessBang "set -e; x() { ! [ x ]; }"
prop_checkUselessBang8 :: Bool
prop_checkUselessBang8 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkUselessBang "set -e; if { ! true; }; then true; fi"
prop_checkUselessBang9 :: Bool
prop_checkUselessBang9 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkUselessBang "set -e; while ! true; do true; done"
checkUselessBang :: Parameters -> Token -> f ()
checkUselessBang params :: Parameters
params t :: Token
t = Bool -> f () -> f ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (Parameters -> Bool
hasSetE Parameters
params) (f () -> f ()) -> f () -> f ()
forall a b. (a -> b) -> a -> b
$ (Token -> f ()) -> [Token] -> f ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ Token -> f ()
forall (m :: * -> *). MonadWriter [TokenComment] m => Token -> m ()
check (Token -> [Token]
getNonReturningCommands Token
t)
where
check :: Token -> m ()
check t :: Token
t =
case Token
t of
T_Banged id :: Id
id cmd :: Token
cmd | Bool -> Bool
not (Bool -> Bool) -> Bool -> Bool
forall a b. (a -> b) -> a -> b
$ [Token] -> Bool
isCondition (Map Id Token -> Token -> [Token]
getPath (Parameters -> Map Id Token
parentMap Parameters
params) Token
t) ->
TokenComment -> m ()
forall a (m :: * -> *). (NFData a, MonadWriter [a] m) => a -> m ()
addComment (TokenComment -> m ()) -> TokenComment -> m ()
forall a b. (a -> b) -> a -> b
$ Severity -> Id -> Code -> String -> Fix -> TokenComment
makeCommentWithFix Severity
InfoC Id
id 2251
"This ! is not on a condition and skips errexit. Use `&& exit 1` instead, or make sure $? is checked."
([Replacement] -> Fix
fixWith [Id -> Parameters -> Code -> String -> Replacement
replaceStart Id
id Parameters
params 1 "", Id -> Parameters -> Code -> String -> Replacement
replaceEnd (Token -> Id
getId Token
cmd) Parameters
params 0 " && exit 1"])
_ -> () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
getNonReturningCommands :: Token -> [Token]
getNonReturningCommands :: Token -> [Token]
getNonReturningCommands t :: Token
t =
case Token
t of
T_Script _ _ list :: [Token]
list -> [Token] -> [Token]
forall a. [a] -> [a]
dropLast [Token]
list
T_BraceGroup _ list :: [Token]
list -> if Token -> Bool
isFunctionBody Token
t then [Token] -> [Token]
forall a. [a] -> [a]
dropLast [Token]
list else [Token]
list
T_Subshell _ list :: [Token]
list -> [Token] -> [Token]
forall a. [a] -> [a]
dropLast [Token]
list
T_WhileExpression _ conds :: [Token]
conds cmds :: [Token]
cmds -> [Token] -> [Token]
forall a. [a] -> [a]
dropLast [Token]
conds [Token] -> [Token] -> [Token]
forall a. [a] -> [a] -> [a]
++ [Token]
cmds
T_UntilExpression _ conds :: [Token]
conds cmds :: [Token]
cmds -> [Token] -> [Token]
forall a. [a] -> [a]
dropLast [Token]
conds [Token] -> [Token] -> [Token]
forall a. [a] -> [a] -> [a]
++ [Token]
cmds
T_ForIn _ _ _ list :: [Token]
list -> [Token]
list
T_ForArithmetic _ _ _ _ list :: [Token]
list -> [Token]
list
T_Annotation _ _ t :: Token
t -> Token -> [Token]
getNonReturningCommands Token
t
T_IfExpression _ conds :: [([Token], [Token])]
conds elses :: [Token]
elses ->
(([Token], [Token]) -> [Token]) -> [([Token], [Token])] -> [Token]
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap ([Token] -> [Token]
forall a. [a] -> [a]
dropLast ([Token] -> [Token])
-> (([Token], [Token]) -> [Token]) -> ([Token], [Token]) -> [Token]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ([Token], [Token]) -> [Token]
forall a b. (a, b) -> a
fst) [([Token], [Token])]
conds [Token] -> [Token] -> [Token]
forall a. [a] -> [a] -> [a]
++ (([Token], [Token]) -> [Token]) -> [([Token], [Token])] -> [Token]
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap ([Token], [Token]) -> [Token]
forall a b. (a, b) -> b
snd [([Token], [Token])]
conds [Token] -> [Token] -> [Token]
forall a. [a] -> [a] -> [a]
++ [Token]
elses
_ -> []
isFunctionBody :: Token -> Bool
isFunctionBody t :: Token
t =
case Map Id Token -> Token -> [Token]
getPath (Parameters -> Map Id Token
parentMap Parameters
params) Token
t of
_:T_Function {}:_-> Bool
True
_ -> Bool
False
dropLast :: [a] -> [a]
dropLast t :: [a]
t =
case [a]
t of
[_] -> []
x :: a
x:rest :: [a]
rest -> a
x a -> [a] -> [a]
forall a. a -> [a] -> [a]
: [a] -> [a]
dropLast [a]
rest
_ -> []
prop_checkModifiedArithmeticInRedirection1 :: Bool
prop_checkModifiedArithmeticInRedirection1 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkModifiedArithmeticInRedirection "ls > $((i++))"
prop_checkModifiedArithmeticInRedirection2 :: Bool
prop_checkModifiedArithmeticInRedirection2 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkModifiedArithmeticInRedirection "cat < \"foo$((i++)).txt\""
prop_checkModifiedArithmeticInRedirection3 :: Bool
prop_checkModifiedArithmeticInRedirection3 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkModifiedArithmeticInRedirection "while true; do true; done > $((i++))"
prop_checkModifiedArithmeticInRedirection4 :: Bool
prop_checkModifiedArithmeticInRedirection4 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkModifiedArithmeticInRedirection "cat <<< $((i++))"
prop_checkModifiedArithmeticInRedirection5 :: Bool
prop_checkModifiedArithmeticInRedirection5 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkModifiedArithmeticInRedirection "cat << foo\n$((i++))\nfoo\n"
prop_checkModifiedArithmeticInRedirection6 :: Bool
prop_checkModifiedArithmeticInRedirection6 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkModifiedArithmeticInRedirection "#!/bin/dash\nls > $((i=i+1))"
checkModifiedArithmeticInRedirection :: Parameters -> Token -> f ()
checkModifiedArithmeticInRedirection params :: Parameters
params t :: Token
t = Bool -> f () -> f ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
unless (Parameters -> Shell
shellType Parameters
params Shell -> Shell -> Bool
forall a. Eq a => a -> a -> Bool
== Shell
Dash) (f () -> f ()) -> f () -> f ()
forall a b. (a -> b) -> a -> b
$
case Token
t of
T_Redirecting _ redirs :: [Token]
redirs (T_SimpleCommand _ _ (_:_)) -> (Token -> f ()) -> [Token] -> f ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ Token -> f ()
forall (m :: * -> *). MonadWriter [TokenComment] m => Token -> m ()
checkRedirs [Token]
redirs
_ -> () -> f ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
where
checkRedirs :: Token -> m ()
checkRedirs t :: Token
t =
case Token
t of
T_FdRedirect _ _ (T_IoFile _ _ word :: Token
word) ->
(Token -> m ()) -> [Token] -> m ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ Token -> m ()
forall (m :: * -> *). MonadWriter [TokenComment] m => Token -> m ()
checkArithmetic ([Token] -> m ()) -> [Token] -> m ()
forall a b. (a -> b) -> a -> b
$ Token -> [Token]
getWordParts Token
word
T_FdRedirect _ _ (T_HereString _ word :: Token
word) ->
(Token -> m ()) -> [Token] -> m ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ Token -> m ()
forall (m :: * -> *). MonadWriter [TokenComment] m => Token -> m ()
checkArithmetic ([Token] -> m ()) -> [Token] -> m ()
forall a b. (a -> b) -> a -> b
$ Token -> [Token]
getWordParts Token
word
T_FdRedirect _ _ (T_HereDoc _ _ _ _ list :: [Token]
list) ->
(Token -> m ()) -> [Token] -> m ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ Token -> m ()
forall (m :: * -> *). MonadWriter [TokenComment] m => Token -> m ()
checkArithmetic [Token]
list
_ -> () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
checkArithmetic :: Token -> m ()
checkArithmetic t :: Token
t =
case Token
t of
T_DollarArithmetic _ x :: Token
x -> Token -> m ()
forall (m :: * -> *). MonadWriter [TokenComment] m => Token -> m ()
checkModifying Token
x
_ -> () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
checkModifying :: Token -> m ()
checkModifying t :: Token
t =
case Token
t of
TA_Sequence _ list :: [Token]
list -> (Token -> m ()) -> [Token] -> m ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ Token -> m ()
checkModifying [Token]
list
TA_Unary id :: Id
id s :: String
s _ | String
s String -> [String] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` ["|++", "++|", "|--", "--|"] -> Id -> m ()
forall (m :: * -> *). MonadWriter [TokenComment] m => Id -> m ()
warnFor Id
id
TA_Assignment id :: Id
id _ _ _ -> Id -> m ()
forall (m :: * -> *). MonadWriter [TokenComment] m => Id -> m ()
warnFor Id
id
TA_Binary _ _ x :: Token
x y :: Token
y -> (Token -> m ()) -> [Token] -> m ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ Token -> m ()
checkModifying [Token
x ,Token
y]
TA_Trinary _ x :: Token
x y :: Token
y z :: Token
z -> (Token -> m ()) -> [Token] -> m ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ Token -> m ()
checkModifying [Token
x, Token
y, Token
z]
_ -> () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
warnFor :: Id -> m ()
warnFor id :: Id
id =
Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
warn Id
id 2257 "Arithmetic modifications in command redirections may be discarded. Do them separately."
prop_checkAliasUsedInSameParsingUnit1 :: Bool
prop_checkAliasUsedInSameParsingUnit1 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyTree Parameters -> Token -> [TokenComment]
checkAliasUsedInSameParsingUnit "alias x=y; x"
prop_checkAliasUsedInSameParsingUnit2 :: Bool
prop_checkAliasUsedInSameParsingUnit2 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
checkAliasUsedInSameParsingUnit "alias x=y\nx"
prop_checkAliasUsedInSameParsingUnit3 :: Bool
prop_checkAliasUsedInSameParsingUnit3 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyTree Parameters -> Token -> [TokenComment]
checkAliasUsedInSameParsingUnit "{ alias x=y\nx\n}"
prop_checkAliasUsedInSameParsingUnit4 :: Bool
prop_checkAliasUsedInSameParsingUnit4 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
checkAliasUsedInSameParsingUnit "alias x=y; 'x';"
prop_checkAliasUsedInSameParsingUnit5 :: Bool
prop_checkAliasUsedInSameParsingUnit5 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
checkAliasUsedInSameParsingUnit ":\n{\n#shellcheck disable=SC2262\nalias x=y\nx\n}"
prop_checkAliasUsedInSameParsingUnit6 :: Bool
prop_checkAliasUsedInSameParsingUnit6 = (Parameters -> Token -> [TokenComment]) -> String -> Bool
verifyNotTree Parameters -> Token -> [TokenComment]
checkAliasUsedInSameParsingUnit ":\n{\n#shellcheck disable=SC2262\nalias x=y\nalias x=z\nx\n}"
checkAliasUsedInSameParsingUnit :: Parameters -> Token -> [TokenComment]
checkAliasUsedInSameParsingUnit :: Parameters -> Token -> [TokenComment]
checkAliasUsedInSameParsingUnit params :: Parameters
params root :: Token
root =
let
commands :: [Token]
commands = [[Token]] -> [Token]
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat ([[Token]] -> [Token]) -> [[Token]] -> [Token]
forall a b. (a -> b) -> a -> b
$ Token -> [[Token]]
getCommandSequences Token
root
units :: [[Token]]
units = (Token -> Token -> Bool) -> [Token] -> [[Token]]
forall a. (a -> a -> Bool) -> [a] -> [[a]]
groupByLink Token -> Token -> Bool
followsOnLine [Token]
commands
in
WriterT [TokenComment] Identity () -> [TokenComment]
forall w a. Writer w a -> w
execWriter (WriterT [TokenComment] Identity () -> [TokenComment])
-> WriterT [TokenComment] Identity () -> [TokenComment]
forall a b. (a -> b) -> a -> b
$ ([Token] -> WriterT [TokenComment] Identity ())
-> [[Token]] -> WriterT [TokenComment] Identity ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ [Token] -> WriterT [TokenComment] Identity ()
checkUnit [[Token]]
units
where
lineSpan :: Id -> Maybe (Code, Code)
lineSpan t :: Id
t =
let m :: Map Id (Position, Position)
m = Parameters -> Map Id (Position, Position)
tokenPositions Parameters
params in do
(start :: Position
start, end :: Position
end) <- Id -> Map Id (Position, Position) -> Maybe (Position, Position)
forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup Id
t Map Id (Position, Position)
m
(Code, Code) -> Maybe (Code, Code)
forall (m :: * -> *) a. Monad m => a -> m a
return ((Code, Code) -> Maybe (Code, Code))
-> (Code, Code) -> Maybe (Code, Code)
forall a b. (a -> b) -> a -> b
$ (Position -> Code
posLine Position
start, Position -> Code
posLine Position
end)
followsOnLine :: Token -> Token -> Bool
followsOnLine a :: Token
a b :: Token
b = Bool -> Maybe Bool -> Bool
forall a. a -> Maybe a -> a
fromMaybe Bool
False (Maybe Bool -> Bool) -> Maybe Bool -> Bool
forall a b. (a -> b) -> a -> b
$ do
(_, end :: Code
end) <- Id -> Maybe (Code, Code)
lineSpan (Token -> Id
getId Token
a)
(start :: Code
start, _) <- Id -> Maybe (Code, Code)
lineSpan (Token -> Id
getId Token
b)
Bool -> Maybe Bool
forall (m :: * -> *) a. Monad m => a -> m a
return (Bool -> Maybe Bool) -> Bool -> Maybe Bool
forall a b. (a -> b) -> a -> b
$ Code
end Code -> Code -> Bool
forall a. Eq a => a -> a -> Bool
== Code
start
checkUnit :: [Token] -> Writer [TokenComment] ()
checkUnit :: [Token] -> WriterT [TokenComment] Identity ()
checkUnit unit :: [Token]
unit = StateT (Map String Token) (WriterT [TokenComment] Identity) ()
-> Map String Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) s a. Monad m => StateT s m a -> s -> m a
evalStateT ((Token
-> StateT
(Map String Token) (WriterT [TokenComment] Identity) Token)
-> [Token]
-> StateT (Map String Token) (WriterT [TokenComment] Identity) ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ ((Token
-> StateT (Map String Token) (WriterT [TokenComment] Identity) ())
-> Token
-> StateT
(Map String Token) (WriterT [TokenComment] Identity) Token
forall (m :: * -> *).
Monad m =>
(Token -> m ()) -> Token -> m Token
doAnalysis Token
-> StateT (Map String Token) (WriterT [TokenComment] Identity) ()
findCommands) [Token]
unit) (Map String Token
forall k a. Map k a
Map.empty)
isSourced :: Token -> Bool
isSourced t :: Token
t =
let
f :: Token -> Bool
f (T_SourceCommand {}) = Bool
True
f _ = Bool
False
in
(Token -> Bool) -> [Token] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any Token -> Bool
f ([Token] -> Bool) -> [Token] -> Bool
forall a b. (a -> b) -> a -> b
$ Map Id Token -> Token -> [Token]
getPath (Parameters -> Map Id Token
parentMap Parameters
params) Token
t
findCommands :: Token -> StateT (Map.Map String Token) (Writer [TokenComment]) ()
findCommands :: Token
-> StateT (Map String Token) (WriterT [TokenComment] Identity) ()
findCommands t :: Token
t = case Token
t of
T_SimpleCommand _ _ (cmd :: Token
cmd:args :: [Token]
args) ->
case Token -> Maybe String
getUnquotedLiteral Token
cmd of
Just "alias" ->
(Token
-> StateT (Map String Token) (WriterT [TokenComment] Identity) ())
-> [Token]
-> StateT (Map String Token) (WriterT [TokenComment] Identity) ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ Token
-> StateT (Map String Token) (WriterT [TokenComment] Identity) ()
forall (f :: * -> *).
MonadState (Map String Token) f =>
Token -> f ()
addAlias [Token]
args
Just name :: String
name | '/' Char -> String -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`notElem` String
name -> do
Maybe Token
cmd <- (Map String Token -> Maybe Token)
-> StateT
(Map String Token) (WriterT [TokenComment] Identity) (Maybe Token)
forall s (m :: * -> *) a. MonadState s m => (s -> a) -> m a
gets (String -> Map String Token -> Maybe Token
forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup String
name)
case Maybe Token
cmd of
Just alias :: Token
alias ->
Bool
-> StateT (Map String Token) (WriterT [TokenComment] Identity) ()
-> StateT (Map String Token) (WriterT [TokenComment] Identity) ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
unless (Token -> Bool
isSourced Token
t Bool -> Bool -> Bool
|| Parameters -> Code -> Token -> Bool
shouldIgnoreCode Parameters
params 2262 Token
alias) (StateT (Map String Token) (WriterT [TokenComment] Identity) ()
-> StateT (Map String Token) (WriterT [TokenComment] Identity) ())
-> StateT (Map String Token) (WriterT [TokenComment] Identity) ()
-> StateT (Map String Token) (WriterT [TokenComment] Identity) ()
forall a b. (a -> b) -> a -> b
$ do
Id
-> Code
-> String
-> StateT (Map String Token) (WriterT [TokenComment] Identity) ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
warn (Token -> Id
getId Token
alias) 2262 "This alias can't be defined and used in the same parsing unit. Use a function instead."
Id
-> Code
-> String
-> StateT (Map String Token) (WriterT [TokenComment] Identity) ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
info (Token -> Id
getId Token
t) 2263 "Since they're in the same parsing unit, this command will not refer to the previously mentioned alias."
_ -> ()
-> StateT (Map String Token) (WriterT [TokenComment] Identity) ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
_ -> ()
-> StateT (Map String Token) (WriterT [TokenComment] Identity) ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
_ -> ()
-> StateT (Map String Token) (WriterT [TokenComment] Identity) ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
addAlias :: Token -> f ()
addAlias arg :: Token
arg = do
let (name :: String
name, value :: String
value) = (Char -> Bool) -> String -> (String, String)
forall a. (a -> Bool) -> [a] -> ([a], [a])
break (Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
== '=') (String -> (String, String)) -> String -> (String, String)
forall a b. (a -> b) -> a -> b
$ String -> Token -> String
getLiteralStringDef "-" Token
arg
Bool -> f () -> f ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (String -> Bool
isVariableName String
name Bool -> Bool -> Bool
&& Bool -> Bool
not (String -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null String
value)) (f () -> f ()) -> f () -> f ()
forall a b. (a -> b) -> a -> b
$
(Map String Token -> Map String Token) -> f ()
forall s (m :: * -> *). MonadState s m => (s -> s) -> m ()
modify ((Token -> Token -> Token)
-> String -> Token -> Map String Token -> Map String Token
forall k a. Ord k => (a -> a -> a) -> k -> a -> Map k a -> Map k a
Map.insertWith (\new :: Token
new old :: Token
old -> Token
old) String
name Token
arg)
prop_groupByLink1 :: Bool
prop_groupByLink1 = (Code -> Code -> Bool) -> [Code] -> [[Code]]
forall a. (a -> a -> Bool) -> [a] -> [[a]]
groupByLink (\a :: Code
a b :: Code
b -> Code
aCode -> Code -> Code
forall a. Num a => a -> a -> a
+1 Code -> Code -> Bool
forall a. Eq a => a -> a -> Bool
== Code
b) [1,2,3,2,3,7,8,9] [[Code]] -> [[Code]] -> Bool
forall a. Eq a => a -> a -> Bool
== [[1,2,3], [2,3], [7,8,9]]
prop_groupByLink2 :: Bool
prop_groupByLink2 = (() -> () -> Bool) -> [()] -> [[()]]
forall a. (a -> a -> Bool) -> [a] -> [[a]]
groupByLink () -> () -> Bool
forall a. Eq a => a -> a -> Bool
(==) ([] :: [()]) [[()]] -> [[()]] -> Bool
forall a. Eq a => a -> a -> Bool
== []
groupByLink :: (a -> a -> Bool) -> [a] -> [[a]]
groupByLink :: (a -> a -> Bool) -> [a] -> [[a]]
groupByLink f :: a -> a -> Bool
f list :: [a]
list =
case [a]
list of
[] -> []
(x :: a
x:xs :: [a]
xs) -> (a -> (a -> [a] -> [[a]]) -> a -> [a] -> [[a]])
-> (a -> [a] -> [[a]]) -> [a] -> a -> [a] -> [[a]]
forall (t :: * -> *) a b.
Foldable t =>
(a -> b -> b) -> b -> t a -> b
foldr a -> (a -> [a] -> [[a]]) -> a -> [a] -> [[a]]
c a -> [a] -> [[a]]
forall a. a -> [a] -> [[a]]
n [a]
xs a
x []
where
c :: a -> (a -> [a] -> [[a]]) -> a -> [a] -> [[a]]
c next :: a
next rest :: a -> [a] -> [[a]]
rest current :: a
current span :: [a]
span =
if a -> a -> Bool
f a
current a
next
then a -> [a] -> [[a]]
rest a
next (a
currenta -> [a] -> [a]
forall a. a -> [a] -> [a]
:[a]
span)
else ([a] -> [a]
forall a. [a] -> [a]
reverse ([a] -> [a]) -> [a] -> [a]
forall a b. (a -> b) -> a -> b
$ a
currenta -> [a] -> [a]
forall a. a -> [a] -> [a]
:[a]
span) [a] -> [[a]] -> [[a]]
forall a. a -> [a] -> [a]
: a -> [a] -> [[a]]
rest a
next []
n :: a -> [a] -> [[a]]
n current :: a
current span :: [a]
span = [[a] -> [a]
forall a. [a] -> [a]
reverse (a
currenta -> [a] -> [a]
forall a. a -> [a] -> [a]
:[a]
span)]
prop_checkBlatantRecursion1 :: Bool
prop_checkBlatantRecursion1 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
checkBlatantRecursion ":(){ :|:& };:"
prop_checkBlatantRecursion2 :: Bool
prop_checkBlatantRecursion2 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
checkBlatantRecursion "f() { f; }"
prop_checkBlatantRecursion3 :: Bool
prop_checkBlatantRecursion3 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
checkBlatantRecursion "f() { command f; }"
prop_checkBlatantRecursion4 :: Bool
prop_checkBlatantRecursion4 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
checkBlatantRecursion "cd() { cd \"$lol/$1\" || exit; }"
prop_checkBlatantRecursion5 :: Bool
prop_checkBlatantRecursion5 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
checkBlatantRecursion "cd() { [ -z \"$1\" ] || cd \"$1\"; }"
prop_checkBlatantRecursion6 :: Bool
prop_checkBlatantRecursion6 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
checkBlatantRecursion "cd() { something; cd $1; }"
prop_checkBlatantRecursion7 :: Bool
prop_checkBlatantRecursion7 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
checkBlatantRecursion "cd() { builtin cd $1; }"
checkBlatantRecursion :: Parameters -> Token -> Writer [TokenComment] ()
checkBlatantRecursion :: Parameters -> Token -> WriterT [TokenComment] Identity ()
checkBlatantRecursion params :: Parameters
params t :: Token
t =
case Token
t of
T_Function _ _ _ name :: String
name body :: Token
body ->
case Token -> [[Token]]
getCommandSequences Token
body of
[first :: Token
first : _] -> String -> Token -> WriterT [TokenComment] Identity ()
checkList String
name Token
first
_ -> () -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
_ -> () -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
where
checkList :: String -> Token -> Writer [TokenComment] ()
checkList :: String -> Token -> WriterT [TokenComment] Identity ()
checkList name :: String
name t :: Token
t =
case Token
t of
T_Backgrounded _ t :: Token
t -> String -> Token -> WriterT [TokenComment] Identity ()
checkList String
name Token
t
T_AndIf _ lhs :: Token
lhs _ -> String -> Token -> WriterT [TokenComment] Identity ()
checkList String
name Token
lhs
T_OrIf _ lhs :: Token
lhs _ -> String -> Token -> WriterT [TokenComment] Identity ()
checkList String
name Token
lhs
T_Pipeline _ _ cmds :: [Token]
cmds -> (Token -> WriterT [TokenComment] Identity ())
-> [Token] -> WriterT [TokenComment] Identity ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ (String -> Token -> WriterT [TokenComment] Identity ()
checkCommand String
name) [Token]
cmds
_ -> () -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
checkCommand :: String -> Token -> Writer [TokenComment] ()
checkCommand :: String -> Token -> WriterT [TokenComment] Identity ()
checkCommand name :: String
name cmd :: Token
cmd = Maybe (WriterT [TokenComment] Identity ())
-> WriterT [TokenComment] Identity ()
forall (t :: * -> *) (m :: * -> *) a.
(Foldable t, Monad m) =>
t (m a) -> m ()
sequence_ (Maybe (WriterT [TokenComment] Identity ())
-> WriterT [TokenComment] Identity ())
-> Maybe (WriterT [TokenComment] Identity ())
-> WriterT [TokenComment] Identity ()
forall a b. (a -> b) -> a -> b
$ do
let (invokedM :: Maybe String
invokedM, t :: Token
t) = Bool -> Token -> (Maybe String, Token)
getCommandNameAndToken Bool
True Token
cmd
String
invoked <- Maybe String
invokedM
Bool -> Maybe ()
forall (f :: * -> *). Alternative f => Bool -> f ()
guard (Bool -> Maybe ()) -> Bool -> Maybe ()
forall a b. (a -> b) -> a -> b
$ String
name String -> String -> Bool
forall a. Eq a => a -> a -> Bool
== String
invoked
WriterT [TokenComment] Identity ()
-> Maybe (WriterT [TokenComment] Identity ())
forall (m :: * -> *) a. Monad m => a -> m a
return (WriterT [TokenComment] Identity ()
-> Maybe (WriterT [TokenComment] Identity ()))
-> WriterT [TokenComment] Identity ()
-> Maybe (WriterT [TokenComment] Identity ())
forall a b. (a -> b) -> a -> b
$
Id -> Code -> String -> Fix -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> Fix -> m ()
errWithFix (Token -> Id
getId Token
t) 2264
("This function unconditionally re-invokes itself. Missing 'command'?")
([Replacement] -> Fix
fixWith [Id -> Parameters -> Code -> String -> Replacement
replaceStart (Token -> Id
getId Token
t) Parameters
params 0 (String -> Replacement) -> String -> Replacement
forall a b. (a -> b) -> a -> b
$ "command "])
prop_checkBadTestAndOr1 :: Bool
prop_checkBadTestAndOr1 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkBadTestAndOr "[ x ] & [ y ]"
prop_checkBadTestAndOr2 :: Bool
prop_checkBadTestAndOr2 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkBadTestAndOr "test -e foo & [ y ]"
prop_checkBadTestAndOr3 :: Bool
prop_checkBadTestAndOr3 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkBadTestAndOr "[ x ] | [ y ]"
checkBadTestAndOr :: Parameters -> Token -> m ()
checkBadTestAndOr params :: Parameters
params t :: Token
t =
case Token
t of
T_Pipeline _ seps :: [Token]
seps cmds :: [Token]
cmds@(_:_:_) -> [Token] -> [Token] -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
[Token] -> [Token] -> m ()
checkOrs [Token]
seps [Token]
cmds
T_Backgrounded id :: Id
id cmd :: Token
cmd -> Id -> Token -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Token -> m ()
checkAnds Id
id Token
cmd
_ -> () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
where
checkOrs :: [Token] -> [Token] -> m ()
checkOrs seps :: [Token]
seps cmds :: [Token]
cmds =
let maybeSeps :: [Maybe Token]
maybeSeps = (Token -> Maybe Token) -> [Token] -> [Maybe Token]
forall a b. (a -> b) -> [a] -> [b]
map Token -> Maybe Token
forall a. a -> Maybe a
Just [Token]
seps
commandWithSeps :: [(Maybe Token, Token, Maybe Token)]
commandWithSeps = [Maybe Token]
-> [Token] -> [Maybe Token] -> [(Maybe Token, Token, Maybe Token)]
forall a b c. [a] -> [b] -> [c] -> [(a, b, c)]
zip3 (Maybe Token
forall a. Maybe a
NothingMaybe Token -> [Maybe Token] -> [Maybe Token]
forall a. a -> [a] -> [a]
:[Maybe Token]
maybeSeps) [Token]
cmds ([Maybe Token]
maybeSeps [Maybe Token] -> [Maybe Token] -> [Maybe Token]
forall a. [a] -> [a] -> [a]
++ [Maybe Token
forall a. Maybe a
Nothing])
in
((Maybe Token, Token, Maybe Token) -> m ())
-> [(Maybe Token, Token, Maybe Token)] -> m ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ (Maybe Token, Token, Maybe Token) -> m ()
forall (f :: * -> *).
MonadWriter [TokenComment] f =>
(Maybe Token, Token, Maybe Token) -> f ()
checkTest [(Maybe Token, Token, Maybe Token)]
commandWithSeps
checkTest :: (Maybe Token, Token, Maybe Token) -> f ()
checkTest (before :: Maybe Token
before, cmd :: Token
cmd, after :: Maybe Token
after) =
Bool -> f () -> f ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (Token -> Bool
isTest Token
cmd) (f () -> f ()) -> f () -> f ()
forall a b. (a -> b) -> a -> b
$ do
Maybe Token -> f ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Maybe Token -> m ()
checkPipe Maybe Token
before
Maybe Token -> f ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Maybe Token -> m ()
checkPipe Maybe Token
after
checkPipe :: Maybe Token -> m ()
checkPipe t :: Maybe Token
t =
case Maybe Token
t of
Just (T_Pipe id :: Id
id "|") ->
Id -> Code -> String -> Fix -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> Fix -> m ()
warnWithFix Id
id 2266 "Use || for logical OR. Single | will pipe." (Fix -> m ()) -> Fix -> m ()
forall a b. (a -> b) -> a -> b
$
[Replacement] -> Fix
fixWith [Id -> Parameters -> Code -> String -> Replacement
replaceEnd Id
id Parameters
params 0 "|"]
_ -> () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
checkAnds :: Id -> Token -> f ()
checkAnds id :: Id
id t :: Token
t =
case Token
t of
T_AndIf _ _ rhs :: Token
rhs -> Id -> Token -> f ()
checkAnds Id
id Token
rhs
T_OrIf _ _ rhs :: Token
rhs -> Id -> Token -> f ()
checkAnds Id
id Token
rhs
T_Pipeline _ _ list :: [Token]
list | Bool -> Bool
not ([Token] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null [Token]
list) -> Id -> Token -> f ()
checkAnds Id
id ([Token] -> Token
forall a. [a] -> a
last [Token]
list)
cmd :: Token
cmd -> Bool -> f () -> f ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (Token -> Bool
isTest Token
cmd) (f () -> f ()) -> f () -> f ()
forall a b. (a -> b) -> a -> b
$
Id -> Code -> String -> Fix -> f ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> Fix -> m ()
errWithFix Id
id 2265 "Use && for logical AND. Single & will background and return true." (Fix -> f ()) -> Fix -> f ()
forall a b. (a -> b) -> a -> b
$
([Replacement] -> Fix
fixWith [Id -> Parameters -> Code -> String -> Replacement
replaceEnd Id
id Parameters
params 0 "&"])
isTest :: Token -> Bool
isTest t :: Token
t =
case Token
t of
T_Condition {} -> Bool
True
T_SimpleCommand {} -> Token
t Token -> String -> Bool
`isCommand` "test"
T_Redirecting _ _ t :: Token
t -> Token -> Bool
isTest Token
t
T_Annotation _ _ t :: Token
t -> Token -> Bool
isTest Token
t
_ -> Bool
False
prop_checkComparisonWithLeadingX1 :: Bool
prop_checkComparisonWithLeadingX1 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkComparisonWithLeadingX "[ x$foo = xlol ]"
prop_checkComparisonWithLeadingX2 :: Bool
prop_checkComparisonWithLeadingX2 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkComparisonWithLeadingX "test x$foo = xlol"
prop_checkComparisonWithLeadingX3 :: Bool
prop_checkComparisonWithLeadingX3 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkComparisonWithLeadingX "[ $foo = xbar ]"
prop_checkComparisonWithLeadingX4 :: Bool
prop_checkComparisonWithLeadingX4 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkComparisonWithLeadingX "test $foo = xbar"
prop_checkComparisonWithLeadingX5 :: Bool
prop_checkComparisonWithLeadingX5 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkComparisonWithLeadingX "[ \"x$foo\" = 'xlol' ]"
prop_checkComparisonWithLeadingX6 :: Bool
prop_checkComparisonWithLeadingX6 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkComparisonWithLeadingX "[ x\"$foo\" = x'lol' ]"
checkComparisonWithLeadingX :: Parameters -> Token -> m ()
checkComparisonWithLeadingX params :: Parameters
params t :: Token
t =
case Token
t of
TC_Binary id :: Id
id typ :: ConditionType
typ op :: String
op lhs :: Token
lhs rhs :: Token
rhs | String
op String -> String -> Bool
forall a. Eq a => a -> a -> Bool
== "=" Bool -> Bool -> Bool
|| String
op String -> String -> Bool
forall a. Eq a => a -> a -> Bool
== "==" ->
Token -> Token -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Token -> Token -> m ()
check Token
lhs Token
rhs
T_SimpleCommand _ _ [cmd :: Token
cmd, lhs :: Token
lhs, op :: Token
op, rhs :: Token
rhs] |
Token -> Maybe String
getLiteralString Token
cmd Maybe String -> Maybe String -> Bool
forall a. Eq a => a -> a -> Bool
== String -> Maybe String
forall a. a -> Maybe a
Just "test" Bool -> Bool -> Bool
&&
Token -> Maybe String
getLiteralString Token
op Maybe String -> [Maybe String] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [String -> Maybe String
forall a. a -> Maybe a
Just "=", String -> Maybe String
forall a. a -> Maybe a
Just "=="] ->
Token -> Token -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Token -> Token -> m ()
check Token
lhs Token
rhs
_ -> () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
where
msg :: String
msg = "Avoid x-prefix in comparisons as it no longer serves a purpose."
check :: Token -> Token -> m ()
check lhs :: Token
lhs rhs :: Token
rhs = Maybe (m ()) -> m ()
forall (t :: * -> *) (m :: * -> *) a.
(Foldable t, Monad m) =>
t (m a) -> m ()
sequence_ (Maybe (m ()) -> m ()) -> Maybe (m ()) -> m ()
forall a b. (a -> b) -> a -> b
$ do
Replacement
l <- Token -> Maybe Replacement
fixLeadingX Token
lhs
Replacement
r <- Token -> Maybe Replacement
fixLeadingX Token
rhs
m () -> Maybe (m ())
forall (m :: * -> *) a. Monad m => a -> m a
return (m () -> Maybe (m ())) -> m () -> Maybe (m ())
forall a b. (a -> b) -> a -> b
$ Id -> Code -> String -> Fix -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> Fix -> m ()
styleWithFix (Token -> Id
getId Token
lhs) 2268 String
msg (Fix -> m ()) -> Fix -> m ()
forall a b. (a -> b) -> a -> b
$ [Replacement] -> Fix
fixWith [Replacement
l, Replacement
r]
fixLeadingX :: Token -> Maybe Replacement
fixLeadingX token :: Token
token =
case Token -> [Token]
getWordParts Token
token of
T_Literal id :: Id
id ('x':_):_ ->
case Token
token of
T_NormalWord _ [T_Literal id :: Id
id "x"] ->
Replacement -> Maybe Replacement
forall (m :: * -> *) a. Monad m => a -> m a
return (Replacement -> Maybe Replacement)
-> Replacement -> Maybe Replacement
forall a b. (a -> b) -> a -> b
$ Id -> Parameters -> Code -> String -> Replacement
replaceStart Id
id Parameters
params 1 "\"\""
_ -> Replacement -> Maybe Replacement
forall (m :: * -> *) a. Monad m => a -> m a
return (Replacement -> Maybe Replacement)
-> Replacement -> Maybe Replacement
forall a b. (a -> b) -> a -> b
$ Id -> Parameters -> Code -> String -> Replacement
replaceStart Id
id Parameters
params 1 ""
T_SingleQuoted id :: Id
id ('x':_):_ ->
Replacement -> Maybe Replacement
forall (m :: * -> *) a. Monad m => a -> m a
return (Replacement -> Maybe Replacement)
-> Replacement -> Maybe Replacement
forall a b. (a -> b) -> a -> b
$ Id -> Parameters -> Code -> String -> Replacement
replaceStart Id
id Parameters
params 2 "'"
_ -> Maybe Replacement
forall a. Maybe a
Nothing
prop_checkAssignToSelf1 :: Bool
prop_checkAssignToSelf1 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkAssignToSelf "x=$x"
prop_checkAssignToSelf2 :: Bool
prop_checkAssignToSelf2 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkAssignToSelf "x=${x}"
prop_checkAssignToSelf3 :: Bool
prop_checkAssignToSelf3 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkAssignToSelf "x=\"$x\""
prop_checkAssignToSelf4 :: Bool
prop_checkAssignToSelf4 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verifyNot Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkAssignToSelf "x=$x mycmd"
checkAssignToSelf :: p -> Token -> m ()
checkAssignToSelf _ t :: Token
t =
case Token
t of
T_SimpleCommand _ vars :: [Token]
vars [] -> (Token -> m ()) -> [Token] -> m ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ Token -> m ()
forall (m :: * -> *). MonadWriter [TokenComment] m => Token -> m ()
check [Token]
vars
_ -> () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
where
check :: Token -> f ()
check t :: Token
t =
case Token
t of
T_Assignment id :: Id
id Assign name :: String
name [] t :: Token
t ->
case Token -> [Token]
getWordParts Token
t of
[T_DollarBraced _ _ b :: Token
b] -> do
Bool -> f () -> f ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (String -> Maybe String
forall a. a -> Maybe a
Just String
name Maybe String -> Maybe String -> Bool
forall a. Eq a => a -> a -> Bool
== Token -> Maybe String
getLiteralString Token
b) (f () -> f ()) -> f () -> f ()
forall a b. (a -> b) -> a -> b
$
Id -> f ()
forall (m :: * -> *). MonadWriter [TokenComment] m => Id -> m ()
msg Id
id
_ -> () -> f ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
_ -> () -> f ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
msg :: Id -> m ()
msg id :: Id
id = Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
info Id
id 2269 "This variable is assigned to itself, so the assignment does nothing."
prop_checkEqualsInCommand1a :: Bool
prop_checkEqualsInCommand1a = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> [Code] -> String -> Bool
verifyCodes Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkEqualsInCommand [2277] "#!/bin/bash\n0='foo'"
prop_checkEqualsInCommand2a :: Bool
prop_checkEqualsInCommand2a = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> [Code] -> String -> Bool
verifyCodes Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkEqualsInCommand [2278] "#!/bin/ksh \n$0='foo'"
prop_checkEqualsInCommand3a :: Bool
prop_checkEqualsInCommand3a = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> [Code] -> String -> Bool
verifyCodes Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkEqualsInCommand [2279] "#!/bin/dash\n${0}='foo'"
prop_checkEqualsInCommand4a :: Bool
prop_checkEqualsInCommand4a = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> [Code] -> String -> Bool
verifyCodes Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkEqualsInCommand [2280] "#!/bin/sh \n0='foo'"
prop_checkEqualsInCommand1b :: Bool
prop_checkEqualsInCommand1b = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> [Code] -> String -> Bool
verifyCodes Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkEqualsInCommand [2270] "1='foo'"
prop_checkEqualsInCommand2b :: Bool
prop_checkEqualsInCommand2b = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> [Code] -> String -> Bool
verifyCodes Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkEqualsInCommand [2270] "${2}='foo'"
prop_checkEqualsInCommand1c :: Bool
prop_checkEqualsInCommand1c = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> [Code] -> String -> Bool
verifyCodes Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkEqualsInCommand [2271] "var$((n+1))=value"
prop_checkEqualsInCommand2c :: Bool
prop_checkEqualsInCommand2c = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> [Code] -> String -> Bool
verifyCodes Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkEqualsInCommand [2271] "var${x}=value"
prop_checkEqualsInCommand3c :: Bool
prop_checkEqualsInCommand3c = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> [Code] -> String -> Bool
verifyCodes Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkEqualsInCommand [2271] "var$((cmd))x='foo'"
prop_checkEqualsInCommand4c :: Bool
prop_checkEqualsInCommand4c = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> [Code] -> String -> Bool
verifyCodes Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkEqualsInCommand [2271] "$(cmd)='foo'"
prop_checkEqualsInCommand1d :: Bool
prop_checkEqualsInCommand1d = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> [Code] -> String -> Bool
verifyCodes Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkEqualsInCommand [2273] "======="
prop_checkEqualsInCommand2d :: Bool
prop_checkEqualsInCommand2d = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> [Code] -> String -> Bool
verifyCodes Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkEqualsInCommand [2274] "======= Here ======="
prop_checkEqualsInCommand3d :: Bool
prop_checkEqualsInCommand3d = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> [Code] -> String -> Bool
verifyCodes Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkEqualsInCommand [2275] "foo\n=42"
prop_checkEqualsInCommand1e :: Bool
prop_checkEqualsInCommand1e = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> [Code] -> String -> Bool
verifyCodes Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkEqualsInCommand [] "--foo=bar"
prop_checkEqualsInCommand2e :: Bool
prop_checkEqualsInCommand2e = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> [Code] -> String -> Bool
verifyCodes Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkEqualsInCommand [] "$(cmd)'=foo'"
prop_checkEqualsInCommand3e :: Bool
prop_checkEqualsInCommand3e = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> [Code] -> String -> Bool
verifyCodes Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkEqualsInCommand [2276] "var${x}/=value"
prop_checkEqualsInCommand4e :: Bool
prop_checkEqualsInCommand4e = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> [Code] -> String -> Bool
verifyCodes Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkEqualsInCommand [2276] "${}=value"
prop_checkEqualsInCommand5e :: Bool
prop_checkEqualsInCommand5e = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> [Code] -> String -> Bool
verifyCodes Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkEqualsInCommand [2276] "${#x}=value"
prop_checkEqualsInCommand1f :: Bool
prop_checkEqualsInCommand1f = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> [Code] -> String -> Bool
verifyCodes Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkEqualsInCommand [2281] "$var=foo"
prop_checkEqualsInCommand2f :: Bool
prop_checkEqualsInCommand2f = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> [Code] -> String -> Bool
verifyCodes Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkEqualsInCommand [2281] "$a=$b"
prop_checkEqualsInCommand3f :: Bool
prop_checkEqualsInCommand3f = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> [Code] -> String -> Bool
verifyCodes Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkEqualsInCommand [2281] "${var}=foo"
prop_checkEqualsInCommand4f :: Bool
prop_checkEqualsInCommand4f = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> [Code] -> String -> Bool
verifyCodes Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkEqualsInCommand [2281] "${var[42]}=foo"
prop_checkEqualsInCommand5f :: Bool
prop_checkEqualsInCommand5f = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> [Code] -> String -> Bool
verifyCodes Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkEqualsInCommand [2281] "$var+=foo"
prop_checkEqualsInCommand1g :: Bool
prop_checkEqualsInCommand1g = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> [Code] -> String -> Bool
verifyCodes Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Parameters -> Token -> m ()
checkEqualsInCommand [2282] "411toppm=true"
checkEqualsInCommand :: Parameters -> Token -> m ()
checkEqualsInCommand params :: Parameters
params originalToken :: Token
originalToken =
case Token
originalToken of
T_SimpleCommand _ _ (word :: Token
word:_) -> Token -> m ()
forall (m :: * -> *). MonadWriter [TokenComment] m => Token -> m ()
check Token
word
_ -> () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
where
hasEquals :: Token -> Bool
hasEquals t :: Token
t =
case Token
t of
T_Literal _ s :: String
s -> '=' Char -> String -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` String
s
_ -> Bool
False
check :: Token -> m ()
check t :: Token
t@(T_NormalWord _ list :: [Token]
list) | (Token -> Bool) -> [Token] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any Token -> Bool
hasEquals [Token]
list =
case (Token -> Bool) -> [Token] -> ([Token], [Token])
forall a. (a -> Bool) -> [a] -> ([a], [a])
break Token -> Bool
hasEquals [Token]
list of
(leading :: [Token]
leading, (eq :: Token
eq:_)) -> Token -> [Token] -> Token -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Token -> [Token] -> Token -> m ()
msg Token
t ([Token] -> [Token]
stripSinglePlus [Token]
leading) Token
eq
_ -> () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
check _ = () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
stripSinglePlus :: [Token] -> [Token]
stripSinglePlus l :: [Token]
l =
case [Token] -> [Token]
forall a. [a] -> [a]
reverse [Token]
l of
(T_Literal _ "+"):rest :: [Token]
rest -> [Token] -> [Token]
forall a. [a] -> [a]
reverse [Token]
rest
_ -> [Token]
l
positionalAssignmentRe :: Regex
positionalAssignmentRe = String -> Regex
mkRegex "^[0-9][0-9]?="
positionalMsg :: Id -> m ()
positionalMsg id :: Id
id =
Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
err Id
id 2270 "To assign positional parameters, use 'set -- first second ..' (or use [ ] to compare)."
indirectionMsg :: Id -> m ()
indirectionMsg id :: Id
id =
Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
err Id
id 2271 "For indirection, use arrays, declare \"var$n=value\", or (for sh) read/eval."
badComparisonMsg :: Id -> m ()
badComparisonMsg id :: Id
id =
Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
err Id
id 2272 "Command name contains ==. For comparison, use [ \"$var\" = value ]."
conflictMarkerMsg :: Id -> m ()
conflictMarkerMsg id :: Id
id =
Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
err Id
id 2273 "Sequence of ===s found. Merge conflict or intended as a commented border?"
borderMsg :: Id -> m ()
borderMsg id :: Id
id =
Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
err Id
id 2274 "Command name starts with ===. Intended as a commented border?"
prefixMsg :: Id -> m ()
prefixMsg id :: Id
id =
Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
err Id
id 2275 "Command name starts with =. Bad line break?"
genericMsg :: Id -> m ()
genericMsg id :: Id
id =
Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
err Id
id 2276 "This is interpreted as a command name containing '='. Bad assignment or comparison?"
assign0Msg :: Id -> Fix -> m ()
assign0Msg id :: Id
id bashfix :: Fix
bashfix =
case Parameters -> Shell
shellType Parameters
params of
Bash -> Id -> Code -> String -> Fix -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> Fix -> m ()
errWithFix Id
id 2277 "Use BASH_ARGV0 to assign to $0 in bash (or use [ ] to compare)." Fix
bashfix
Ksh -> Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
err Id
id 2278 "$0 can't be assigned in Ksh (but it does reflect the current function)."
Dash -> Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
err Id
id 2279 "$0 can't be assigned in Dash. This becomes a command name."
_ -> Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
err Id
id 2280 "$0 can't be assigned this way, and there is no portable alternative."
leadingNumberMsg :: Id -> m ()
leadingNumberMsg id :: Id
id =
Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
err Id
id 2282 "Variable names can't start with numbers, so this is interpreted as a command."
isExpansion :: Token -> Bool
isExpansion t :: Token
t =
case Token
t of
T_Arithmetic {} -> Bool
True
_ -> Token -> Bool
isQuoteableExpansion Token
t
isConflictMarker :: Token -> Bool
isConflictMarker cmd :: Token
cmd = Bool -> Maybe Bool -> Bool
forall a. a -> Maybe a -> a
fromMaybe Bool
False (Maybe Bool -> Bool) -> Maybe Bool -> Bool
forall a b. (a -> b) -> a -> b
$ do
String
str <- Token -> Maybe String
getUnquotedLiteral Token
cmd
Bool -> Maybe ()
forall (f :: * -> *). Alternative f => Bool -> f ()
guard (Bool -> Maybe ()) -> Bool -> Maybe ()
forall a b. (a -> b) -> a -> b
$ (Char -> Bool) -> String -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all (Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
== '=') String
str
Bool -> Maybe ()
forall (f :: * -> *). Alternative f => Bool -> f ()
guard (Bool -> Maybe ()) -> Bool -> Maybe ()
forall a b. (a -> b) -> a -> b
$ String -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length String
str Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= 4 Bool -> Bool -> Bool
&& String -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length String
str Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
<= 12
Bool -> Maybe Bool
forall (m :: * -> *) a. Monad m => a -> m a
return Bool
True
mayBeVariableName :: [Token] -> Bool
mayBeVariableName l :: [Token]
l = Bool -> Maybe Bool -> Bool
forall a. a -> Maybe a -> a
fromMaybe Bool
False (Maybe Bool -> Bool) -> Maybe Bool -> Bool
forall a b. (a -> b) -> a -> b
$ do
Bool -> Maybe ()
forall (f :: * -> *). Alternative f => Bool -> f ()
guard (Bool -> Maybe ()) -> (Bool -> Bool) -> Bool -> Maybe ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Bool -> Bool
not (Bool -> Maybe ()) -> Bool -> Maybe ()
forall a b. (a -> b) -> a -> b
$ (Token -> Bool) -> [Token] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any Token -> Bool
isQuotes [Token]
l
Bool -> Maybe ()
forall (f :: * -> *). Alternative f => Bool -> f ()
guard (Bool -> Maybe ()) -> (Bool -> Bool) -> Bool -> Maybe ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Bool -> Bool
not (Bool -> Maybe ()) -> Bool -> Maybe ()
forall a b. (a -> b) -> a -> b
$ (Token -> Bool) -> [Token] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any Token -> Bool
willBecomeMultipleArgs [Token]
l
String
str <- (Token -> Maybe String) -> Token -> Maybe String
forall (m :: * -> *).
Monad m =>
(Token -> m String) -> Token -> m String
getLiteralStringExt (\_ -> String -> Maybe String
forall a. a -> Maybe a
Just "x") (Id -> [Token] -> Token
T_NormalWord (Int -> Id
Id 0) [Token]
l)
Bool -> Maybe Bool
forall (m :: * -> *) a. Monad m => a -> m a
return (Bool -> Maybe Bool) -> Bool -> Maybe Bool
forall a b. (a -> b) -> a -> b
$ String -> Bool
isVariableName String
str
isLeadingNumberVar :: String -> Bool
isLeadingNumberVar s :: String
s =
let lead :: String
lead = (Char -> Bool) -> String -> String
forall a. (a -> Bool) -> [a] -> [a]
takeWhile (Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
/= '=') String
s
in Bool -> Bool
not (String -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null String
lead) Bool -> Bool -> Bool
&& Char -> Bool
isDigit (String -> Char
forall a. [a] -> a
head String
lead)
Bool -> Bool -> Bool
&& (Char -> Bool) -> String -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all Char -> Bool
isVariableChar String
lead Bool -> Bool -> Bool
&& Bool -> Bool
not ((Char -> Bool) -> String -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all Char -> Bool
isDigit String
lead)
msg :: Token -> [Token] -> Token -> m ()
msg cmd :: Token
cmd leading :: [Token]
leading (T_Literal litId :: Id
litId s :: String
s) = do
case [Token]
leading of
[] | "-" String -> String -> Bool
forall a. Eq a => [a] -> [a] -> Bool
`isPrefixOf` String
s ->
() -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
[] | "=" String -> String -> Bool
forall a. Eq a => [a] -> [a] -> Bool
`isPrefixOf` String
s ->
case Token
originalToken of
T_SimpleCommand _ [] [word] | Token -> Bool
isConflictMarker Token
word ->
Id -> m ()
forall (m :: * -> *). MonadWriter [TokenComment] m => Id -> m ()
conflictMarkerMsg (Token -> Id
getId Token
originalToken)
_ | "===" String -> String -> Bool
forall a. Eq a => [a] -> [a] -> Bool
`isPrefixOf` String
s -> Id -> m ()
forall (m :: * -> *). MonadWriter [TokenComment] m => Id -> m ()
borderMsg (Token -> Id
getId Token
originalToken)
_ -> Id -> m ()
forall (m :: * -> *). MonadWriter [TokenComment] m => Id -> m ()
prefixMsg (Token -> Id
getId Token
cmd)
_ | "==" String -> String -> Bool
forall a. Eq a => [a] -> [a] -> Bool
`isInfixOf` String
s ->
Id -> m ()
forall (m :: * -> *). MonadWriter [TokenComment] m => Id -> m ()
badComparisonMsg (Token -> Id
getId Token
cmd)
[T_DollarBraced id :: Id
id braced :: Bool
braced l :: Token
l] | "=" String -> String -> Bool
forall a. Eq a => [a] -> [a] -> Bool
`isPrefixOf` String
s -> do
let variableStr :: String
variableStr = [String] -> String
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat ([String] -> String) -> [String] -> String
forall a b. (a -> b) -> a -> b
$ Token -> [String]
oversimplify Token
l
let variableReference :: String
variableReference = String -> String
getBracedReference String
variableStr
let variableModifier :: String
variableModifier = String -> String
getBracedModifier String
variableStr
let isPlain :: Bool
isPlain = String -> Bool
isVariableName String
variableStr
let isPositional :: Bool
isPositional = (Char -> Bool) -> String -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all Char -> Bool
isDigit String
variableStr
let isArray :: Bool
isArray = String
variableReference String -> String -> Bool
forall a. Eq a => a -> a -> Bool
/= ""
Bool -> Bool -> Bool
&& "[" String -> String -> Bool
forall a. Eq a => [a] -> [a] -> Bool
`isPrefixOf` String
variableModifier
Bool -> Bool -> Bool
&& "]" String -> String -> Bool
forall a. Eq a => [a] -> [a] -> Bool
`isSuffixOf` String
variableModifier
case () of
_ | String
variableStr String -> String -> Bool
forall a. Eq a => a -> a -> Bool
== "" ->
Id -> m ()
forall (m :: * -> *). MonadWriter [TokenComment] m => Id -> m ()
genericMsg (Token -> Id
getId Token
cmd)
_ | "#" String -> String -> Bool
forall a. Eq a => [a] -> [a] -> Bool
`isPrefixOf` String
variableStr ->
Id -> m ()
forall (m :: * -> *). MonadWriter [TokenComment] m => Id -> m ()
genericMsg (Token -> Id
getId Token
cmd)
_ | String
variableStr String -> String -> Bool
forall a. Eq a => a -> a -> Bool
== "0" ->
Id -> Fix -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Fix -> m ()
assign0Msg Id
id (Fix -> m ()) -> Fix -> m ()
forall a b. (a -> b) -> a -> b
$ [Replacement] -> Fix
fixWith [Id -> Parameters -> String -> Replacement
replaceToken Id
id Parameters
params "BASH_ARGV0"]
_ | Bool
isPositional ->
Id -> m ()
forall (m :: * -> *). MonadWriter [TokenComment] m => Id -> m ()
positionalMsg Id
id
_ | Bool
isArray Bool -> Bool -> Bool
|| Bool
isPlain ->
Id -> Code -> String -> Fix -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> Fix -> m ()
errWithFix Id
id 2281
("Don't use " String -> String -> String
forall a. [a] -> [a] -> [a]
++ (if Bool
braced then "${}" else "$") String -> String -> String
forall a. [a] -> [a] -> [a]
++ " on the left side of assignments.") (Fix -> m ()) -> Fix -> m ()
forall a b. (a -> b) -> a -> b
$
[Replacement] -> Fix
fixWith ([Replacement] -> Fix) -> [Replacement] -> Fix
forall a b. (a -> b) -> a -> b
$
if Bool
braced
then [ Id -> Parameters -> Code -> String -> Replacement
replaceStart Id
id Parameters
params 2 "", Id -> Parameters -> Code -> String -> Replacement
replaceEnd Id
id Parameters
params 1 "" ]
else [ Id -> Parameters -> Code -> String -> Replacement
replaceStart Id
id Parameters
params 1 "" ]
_ -> Id -> m ()
forall (m :: * -> *). MonadWriter [TokenComment] m => Id -> m ()
indirectionMsg Id
id
[] | String
s String -> Regex -> Bool
`matches` Regex
positionalAssignmentRe ->
if "0=" String -> String -> Bool
forall a. Eq a => [a] -> [a] -> Bool
`isPrefixOf` String
s
then
Id -> Fix -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Fix -> m ()
assign0Msg Id
litId (Fix -> m ()) -> Fix -> m ()
forall a b. (a -> b) -> a -> b
$ [Replacement] -> Fix
fixWith [Id -> Parameters -> Code -> String -> Replacement
replaceStart Id
litId Parameters
params 1 "BASH_ARGV0"]
else
Id -> m ()
forall (m :: * -> *). MonadWriter [TokenComment] m => Id -> m ()
positionalMsg Id
litId
[] | String -> Bool
isLeadingNumberVar String
s ->
Id -> m ()
forall (m :: * -> *). MonadWriter [TokenComment] m => Id -> m ()
leadingNumberMsg (Token -> Id
getId Token
cmd)
(_:_) | [Token] -> Bool
mayBeVariableName [Token]
leading Bool -> Bool -> Bool
&& ((Char -> Bool) -> String -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all Char -> Bool
isVariableChar (String -> Bool) -> String -> Bool
forall a b. (a -> b) -> a -> b
$ (Char -> Bool) -> String -> String
forall a. (a -> Bool) -> [a] -> [a]
takeWhile (Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
/= '=') String
s) ->
Id -> m ()
forall (m :: * -> *). MonadWriter [TokenComment] m => Id -> m ()
indirectionMsg (Token -> Id
getId Token
cmd)
_ -> Id -> m ()
forall (m :: * -> *). MonadWriter [TokenComment] m => Id -> m ()
genericMsg (Token -> Id
getId Token
cmd)
prop_checkSecondArgIsComparison1 :: Bool
prop_checkSecondArgIsComparison1 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkSecondArgIsComparison "foo = $bar"
prop_checkSecondArgIsComparison2 :: Bool
prop_checkSecondArgIsComparison2 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkSecondArgIsComparison "$foo = $bar"
prop_checkSecondArgIsComparison3 :: Bool
prop_checkSecondArgIsComparison3 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkSecondArgIsComparison "2f == $bar"
prop_checkSecondArgIsComparison4 :: Bool
prop_checkSecondArgIsComparison4 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkSecondArgIsComparison "'var' =$bar"
prop_checkSecondArgIsComparison5 :: Bool
prop_checkSecondArgIsComparison5 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkSecondArgIsComparison "foo ='$bar'"
prop_checkSecondArgIsComparison6 :: Bool
prop_checkSecondArgIsComparison6 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkSecondArgIsComparison "$foo =$bar"
prop_checkSecondArgIsComparison7 :: Bool
prop_checkSecondArgIsComparison7 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkSecondArgIsComparison "2f ==$bar"
prop_checkSecondArgIsComparison8 :: Bool
prop_checkSecondArgIsComparison8 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkSecondArgIsComparison "'var' =$bar"
prop_checkSecondArgIsComparison9 :: Bool
prop_checkSecondArgIsComparison9 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkSecondArgIsComparison "var += $(foo)"
prop_checkSecondArgIsComparison10 :: Bool
prop_checkSecondArgIsComparison10 = (Parameters -> Token -> WriterT [TokenComment] Identity ())
-> String -> Bool
verify Parameters -> Token -> WriterT [TokenComment] Identity ()
forall (m :: * -> *) p.
MonadWriter [TokenComment] m =>
p -> Token -> m ()
checkSecondArgIsComparison "var +=$(foo)"
checkSecondArgIsComparison :: p -> Token -> m ()
checkSecondArgIsComparison _ t :: Token
t =
case Token
t of
T_SimpleCommand _ _ (lhs :: Token
lhs:arg :: Token
arg:_) -> Maybe (m ()) -> m ()
forall (t :: * -> *) (m :: * -> *) a.
(Foldable t, Monad m) =>
t (m a) -> m ()
sequence_ (Maybe (m ()) -> m ()) -> Maybe (m ()) -> m ()
forall a b. (a -> b) -> a -> b
$ do
String
argString <- Token -> Maybe String
getLeadingUnquotedString Token
arg
case String
argString of
'=':'=':'=':'=':_ -> Maybe (m ())
forall a. Maybe a
Nothing
'+':'=':_ ->
m () -> Maybe (m ())
forall (m :: * -> *) a. Monad m => a -> m a
return (m () -> Maybe (m ())) -> m () -> Maybe (m ())
forall a b. (a -> b) -> a -> b
$ Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
err (Token -> Id
headId Token
t) 2285 (String -> m ()) -> String -> m ()
forall a b. (a -> b) -> a -> b
$
"Remove spaces around += to assign (or quote '+=' if literal)."
'=':'=':_ ->
m () -> Maybe (m ())
forall (m :: * -> *) a. Monad m => a -> m a
return (m () -> Maybe (m ())) -> m () -> Maybe (m ())
forall a b. (a -> b) -> a -> b
$ Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
err (Token -> Id
getId Token
t) 2284 (String -> m ()) -> String -> m ()
forall a b. (a -> b) -> a -> b
$
"Use [ x = y ] to compare values (or quote '==' if literal)."
'=':_ ->
m () -> Maybe (m ())
forall (m :: * -> *) a. Monad m => a -> m a
return (m () -> Maybe (m ())) -> m () -> Maybe (m ())
forall a b. (a -> b) -> a -> b
$ Id -> Code -> String -> m ()
forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
err (Token -> Id
headId Token
arg) 2283 (String -> m ()) -> String -> m ()
forall a b. (a -> b) -> a -> b
$
"Remove spaces around = to assign (or use [ ] to compare, or quote '=' if literal)."
_ -> Maybe (m ())
forall a. Maybe a
Nothing
_ -> () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
where
headId :: Token -> Id
headId t :: Token
t =
case Token
t of
T_NormalWord _ (x :: Token
x:_) -> Token -> Id
getId Token
x
_ -> Token -> Id
getId Token
t
return []
runTests :: IO Bool
runTests = $( [| $(forAllProperties) (quickCheckWithResult (stdArgs { maxSuccess = 1 }) ) |])