{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE TemplateHaskell #-}
module ShellCheck.AnalyzerLib where
import ShellCheck.AST
import ShellCheck.ASTLib
import qualified ShellCheck.CFGAnalysis as CF
import ShellCheck.Data
import ShellCheck.Interface
import ShellCheck.Parser
import ShellCheck.Prelude
import ShellCheck.Regex
import Control.Arrow (first)
import Control.DeepSeq
import Control.Monad
import Control.Monad.Identity
import Control.Monad.RWS
import Control.Monad.State
import Control.Monad.Writer
import Data.Char
import Data.List
import Data.Maybe
import Data.Semigroup
import qualified Data.Map as Map
import Test.QuickCheck.All (forAllProperties)
import Test.QuickCheck.Test (maxSuccess, quickCheckWithResult, stdArgs)
type Analysis = AnalyzerM ()
type AnalyzerM a = RWS Parameters [TokenComment] Cache a
nullCheck :: b -> RWST Parameters [TokenComment] Cache Identity ()
nullCheck = forall a b. a -> b -> a
const forall a b. (a -> b) -> a -> b
$ forall (m :: * -> *) a. Monad m => a -> m a
return ()
data Checker = Checker {
Checker -> Root -> RWST Parameters [TokenComment] Cache Identity ()
perScript :: Root -> Analysis,
Checker
-> Token -> RWST Parameters [TokenComment] Cache Identity ()
perToken :: Token -> Analysis
}
runChecker :: Parameters -> Checker -> [TokenComment]
runChecker :: Parameters -> Checker -> [TokenComment]
runChecker Parameters
params Checker
checker = [TokenComment]
notes
where
root :: Token
root = Parameters -> Token
rootNode Parameters
params
check :: Root -> RWST Parameters [TokenComment] Cache Identity ()
check = Checker -> Root -> RWST Parameters [TokenComment] Cache Identity ()
perScript Checker
checker forall a.
(a -> RWST Parameters [TokenComment] Cache Identity ())
-> (a -> RWST Parameters [TokenComment] Cache Identity ())
-> a
-> RWST Parameters [TokenComment] Cache Identity ()
`composeAnalyzers` (\(Root Token
x) -> forall (f :: * -> *) a. Functor f => f a -> f ()
void forall a b. (a -> b) -> a -> b
$ forall (m :: * -> *).
Monad m =>
(Token -> m ()) -> Token -> m Token
doAnalysis (Checker
-> Token -> RWST Parameters [TokenComment] Cache Identity ()
perToken Checker
checker) Token
x)
notes :: [TokenComment]
notes = forall a b. (a, b) -> b
snd forall a b. (a -> b) -> a -> b
$ forall r w s a. RWS r w s a -> r -> s -> (a, w)
evalRWS (Root -> RWST Parameters [TokenComment] Cache Identity ()
check forall a b. (a -> b) -> a -> b
$ Token -> Root
Root Token
root) Parameters
params Cache
Cache
instance Semigroup Checker where
<> :: Checker -> Checker -> Checker
(<>) Checker
x Checker
y = Checker {
perScript :: Root -> RWST Parameters [TokenComment] Cache Identity ()
perScript = Checker -> Root -> RWST Parameters [TokenComment] Cache Identity ()
perScript Checker
x forall a.
(a -> RWST Parameters [TokenComment] Cache Identity ())
-> (a -> RWST Parameters [TokenComment] Cache Identity ())
-> a
-> RWST Parameters [TokenComment] Cache Identity ()
`composeAnalyzers` Checker -> Root -> RWST Parameters [TokenComment] Cache Identity ()
perScript Checker
y,
perToken :: Token -> RWST Parameters [TokenComment] Cache Identity ()
perToken = Checker
-> Token -> RWST Parameters [TokenComment] Cache Identity ()
perToken Checker
x forall a.
(a -> RWST Parameters [TokenComment] Cache Identity ())
-> (a -> RWST Parameters [TokenComment] Cache Identity ())
-> a
-> RWST Parameters [TokenComment] Cache Identity ()
`composeAnalyzers` Checker
-> Token -> RWST Parameters [TokenComment] Cache Identity ()
perToken Checker
y
}
instance Monoid Checker where
mempty :: Checker
mempty = Checker {
perScript :: Root -> RWST Parameters [TokenComment] Cache Identity ()
perScript = forall {b}. b -> RWST Parameters [TokenComment] Cache Identity ()
nullCheck,
perToken :: Token -> RWST Parameters [TokenComment] Cache Identity ()
perToken = forall {b}. b -> RWST Parameters [TokenComment] Cache Identity ()
nullCheck
}
mappend :: Checker -> Checker -> Checker
mappend = forall a. Semigroup a => a -> a -> a
(Data.Semigroup.<>)
composeAnalyzers :: (a -> Analysis) -> (a -> Analysis) -> a -> Analysis
composeAnalyzers :: forall a.
(a -> RWST Parameters [TokenComment] Cache Identity ())
-> (a -> RWST Parameters [TokenComment] Cache Identity ())
-> a
-> RWST Parameters [TokenComment] Cache Identity ()
composeAnalyzers a -> RWST Parameters [TokenComment] Cache Identity ()
f a -> RWST Parameters [TokenComment] Cache Identity ()
g a
x = a -> RWST Parameters [TokenComment] Cache Identity ()
f a
x forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> a -> RWST Parameters [TokenComment] Cache Identity ()
g a
x
data Parameters = Parameters {
Parameters -> Bool
hasLastpipe :: Bool,
Parameters -> Bool
hasInheritErrexit :: Bool,
Parameters -> Bool
hasSetE :: Bool,
Parameters -> Bool
hasPipefail :: Bool,
Parameters -> [StackData]
variableFlow :: [StackData],
Parameters -> Map Id Token
idMap :: Map.Map Id Token,
Parameters -> Map Id Token
parentMap :: Map.Map Id Token,
Parameters -> Shell
shellType :: Shell,
Parameters -> Bool
shellTypeSpecified :: Bool,
Parameters -> Token
rootNode :: Token,
Parameters -> Map Id (Position, Position)
tokenPositions :: Map.Map Id (Position, Position),
Parameters -> CFGAnalysis
cfgAnalysis :: CF.CFGAnalysis
} deriving (Int -> Parameters -> ShowS
[Parameters] -> ShowS
Parameters -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Parameters] -> ShowS
$cshowList :: [Parameters] -> ShowS
show :: Parameters -> String
$cshow :: Parameters -> String
showsPrec :: Int -> Parameters -> ShowS
$cshowsPrec :: Int -> Parameters -> ShowS
Show)
data Cache = Cache {}
data Scope = SubshellScope String | NoneScope deriving (Int -> Scope -> ShowS
[Scope] -> ShowS
Scope -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Scope] -> ShowS
$cshowList :: [Scope] -> ShowS
show :: Scope -> String
$cshow :: Scope -> String
showsPrec :: Int -> Scope -> ShowS
$cshowsPrec :: Int -> Scope -> ShowS
Show, Scope -> Scope -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Scope -> Scope -> Bool
$c/= :: Scope -> Scope -> Bool
== :: Scope -> Scope -> Bool
$c== :: Scope -> Scope -> Bool
Eq)
data StackData =
StackScope Scope
| StackScopeEnd
| Assignment (Token, Token, String, DataType)
| Reference (Token, Token, String)
deriving (Int -> StackData -> ShowS
[StackData] -> ShowS
StackData -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [StackData] -> ShowS
$cshowList :: [StackData] -> ShowS
show :: StackData -> String
$cshow :: StackData -> String
showsPrec :: Int -> StackData -> ShowS
$cshowsPrec :: Int -> StackData -> ShowS
Show)
data DataType = DataString DataSource | DataArray DataSource
deriving (Int -> DataType -> ShowS
[DataType] -> ShowS
DataType -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [DataType] -> ShowS
$cshowList :: [DataType] -> ShowS
show :: DataType -> String
$cshow :: DataType -> String
showsPrec :: Int -> DataType -> ShowS
$cshowsPrec :: Int -> DataType -> ShowS
Show)
data DataSource =
SourceFrom [Token]
| SourceExternal
| SourceDeclaration
| SourceInteger
| SourceChecked
deriving (Int -> DataSource -> ShowS
[DataSource] -> ShowS
DataSource -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [DataSource] -> ShowS
$cshowList :: [DataSource] -> ShowS
show :: DataSource -> String
$cshow :: DataSource -> String
showsPrec :: Int -> DataSource -> ShowS
$cshowsPrec :: Int -> DataSource -> ShowS
Show)
data VariableState = Dead Token String | Alive deriving (Int -> VariableState -> ShowS
[VariableState] -> ShowS
VariableState -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [VariableState] -> ShowS
$cshowList :: [VariableState] -> ShowS
show :: VariableState -> String
$cshow :: VariableState -> String
showsPrec :: Int -> VariableState -> ShowS
$cshowsPrec :: Int -> VariableState -> ShowS
Show)
defaultSpec :: ParseResult -> AnalysisSpec
defaultSpec ParseResult
pr = AnalysisSpec
spec {
asShellType :: Maybe Shell
asShellType = forall a. Maybe a
Nothing,
asCheckSourced :: Bool
asCheckSourced = Bool
False,
asExecutionMode :: ExecutionMode
asExecutionMode = ExecutionMode
Executed,
asTokenPositions :: Map Id (Position, Position)
asTokenPositions = ParseResult -> Map Id (Position, Position)
prTokenPositions ParseResult
pr
} where spec :: AnalysisSpec
spec = Token -> AnalysisSpec
newAnalysisSpec (forall a. HasCallStack => Maybe a -> a
fromJust forall a b. (a -> b) -> a -> b
$ ParseResult -> Maybe Token
prRoot ParseResult
pr)
pScript :: String -> ParseResult
pScript String
s =
let
pSpec :: ParseSpec
pSpec = ParseSpec
newParseSpec {
psFilename :: String
psFilename = String
"script",
psScript :: String
psScript = String
s
}
in forall a. Identity a -> a
runIdentity forall a b. (a -> b) -> a -> b
$ forall (m :: * -> *).
Monad m =>
SystemInterface m -> ParseSpec -> m ParseResult
parseScript ([(String, String)] -> SystemInterface Identity
mockedSystemInterface []) ParseSpec
pSpec
producesComments :: Checker -> String -> Maybe Bool
Checker
c 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
forall (m :: * -> *) a. Monad m => a -> m a
return forall b c a. (b -> c) -> (a -> b) -> a -> c
. Bool -> Bool
not forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (t :: * -> *) a. Foldable t => t a -> Bool
null forall a b. (a -> b) -> a -> b
$ AnalysisSpec -> Parameters -> [TokenComment] -> [TokenComment]
filterByAnnotation AnalysisSpec
spec Parameters
params forall a b. (a -> b) -> a -> b
$ Parameters -> Checker -> [TokenComment]
runChecker Parameters
params Checker
c
makeComment :: Severity -> Id -> Code -> String -> TokenComment
Severity
severity Id
id Code
code String
note =
TokenComment
newTokenComment {
tcId :: Id
tcId = Id
id,
tcComment :: Comment
tcComment = Comment
newComment {
cSeverity :: Severity
cSeverity = Severity
severity,
cCode :: Code
cCode = Code
code,
cMessage :: String
cMessage = String
note
}
}
a
note = a
note forall a b. NFData a => a -> b -> b
`deepseq` forall w (m :: * -> *). MonadWriter w m => w -> m ()
tell [a
note]
warn :: MonadWriter [TokenComment] m => Id -> Code -> String -> m ()
warn :: forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> m ()
warn Id
id Code
code String
str = forall {a} {m :: * -> *}.
(NFData a, MonadWriter [a] m) =>
a -> m ()
addComment forall a b. (a -> b) -> a -> b
$ Severity -> Id -> Code -> String -> TokenComment
makeComment Severity
WarningC Id
id Code
code String
str
err :: Id -> Code -> String -> m ()
err Id
id Code
code String
str = forall {a} {m :: * -> *}.
(NFData a, MonadWriter [a] m) =>
a -> m ()
addComment forall a b. (a -> b) -> a -> b
$ Severity -> Id -> Code -> String -> TokenComment
makeComment Severity
ErrorC Id
id Code
code String
str
info :: Id -> Code -> String -> m ()
info Id
id Code
code String
str = forall {a} {m :: * -> *}.
(NFData a, MonadWriter [a] m) =>
a -> m ()
addComment forall a b. (a -> b) -> a -> b
$ Severity -> Id -> Code -> String -> TokenComment
makeComment Severity
InfoC Id
id Code
code String
str
style :: Id -> Code -> String -> m ()
style Id
id Code
code String
str = forall {a} {m :: * -> *}.
(NFData a, MonadWriter [a] m) =>
a -> m ()
addComment forall a b. (a -> b) -> a -> b
$ Severity -> Id -> Code -> String -> TokenComment
makeComment Severity
StyleC Id
id Code
code String
str
errWithFix :: MonadWriter [TokenComment] m => Id -> Code -> String -> Fix -> m ()
errWithFix :: forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> Fix -> m ()
errWithFix = forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Severity -> Id -> Code -> String -> Fix -> m ()
addCommentWithFix Severity
ErrorC
warnWithFix :: MonadWriter [TokenComment] m => Id -> Code -> String -> Fix -> m ()
warnWithFix :: forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> Fix -> m ()
warnWithFix = forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Severity -> Id -> Code -> String -> Fix -> m ()
addCommentWithFix Severity
WarningC
infoWithFix :: MonadWriter [TokenComment] m => Id -> Code -> String -> Fix -> m ()
infoWithFix :: forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> Fix -> m ()
infoWithFix = forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Severity -> Id -> Code -> String -> Fix -> m ()
addCommentWithFix Severity
InfoC
styleWithFix :: MonadWriter [TokenComment] m => Id -> Code -> String -> Fix -> m ()
styleWithFix :: forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Id -> Code -> String -> Fix -> m ()
styleWithFix = forall (m :: * -> *).
MonadWriter [TokenComment] m =>
Severity -> Id -> Code -> String -> Fix -> m ()
addCommentWithFix Severity
StyleC
addCommentWithFix :: MonadWriter [TokenComment] m => Severity -> Id -> Code -> String -> Fix -> m ()
Severity
severity Id
id Code
code String
str Fix
fix =
forall {a} {m :: * -> *}.
(NFData a, MonadWriter [a] m) =>
a -> m ()
addComment forall a b. (a -> b) -> a -> b
$ Severity -> Id -> Code -> String -> Fix -> TokenComment
makeCommentWithFix Severity
severity Id
id Code
code String
str Fix
fix
makeCommentWithFix :: Severity -> Id -> Code -> String -> Fix -> TokenComment
Severity
severity Id
id Code
code String
str Fix
fix =
let comment :: TokenComment
comment = Severity -> Id -> Code -> String -> TokenComment
makeComment Severity
severity Id
id Code
code String
str
withFix :: TokenComment
withFix = TokenComment
comment {
tcFix :: Maybe Fix
tcFix = if forall (t :: * -> *) a. Foldable t => t a -> Bool
null (Fix -> [Replacement]
fixReplacements Fix
fix) then forall a. Maybe a
Nothing else forall a. a -> Maybe a
Just Fix
fix
}
in forall a. NFData a => a -> a
force TokenComment
withFix
makeParameters :: AnalysisSpec -> Parameters
makeParameters AnalysisSpec
spec = Parameters
params
where
params :: Parameters
params = Parameters {
rootNode :: Token
rootNode = Token
root,
shellType :: Shell
shellType = forall a. a -> Maybe a -> a
fromMaybe (Maybe Shell -> Token -> Shell
determineShell (AnalysisSpec -> Maybe Shell
asFallbackShell AnalysisSpec
spec) Token
root) forall a b. (a -> b) -> a -> b
$ AnalysisSpec -> Maybe Shell
asShellType AnalysisSpec
spec,
hasSetE :: Bool
hasSetE = Token -> Bool
containsSetE Token
root,
hasLastpipe :: Bool
hasLastpipe =
case Parameters -> Shell
shellType Parameters
params of
Shell
Bash -> String -> Token -> Bool
isOptionSet String
"lastpipe" Token
root
Shell
Dash -> Bool
False
Shell
Sh -> Bool
False
Shell
Ksh -> Bool
True,
hasInheritErrexit :: Bool
hasInheritErrexit =
case Parameters -> Shell
shellType Parameters
params of
Shell
Bash -> String -> Token -> Bool
isOptionSet String
"inherit_errexit" Token
root
Shell
Dash -> Bool
True
Shell
Sh -> Bool
True
Shell
Ksh -> Bool
False,
hasPipefail :: Bool
hasPipefail =
case Parameters -> Shell
shellType Parameters
params of
Shell
Bash -> String -> Token -> Bool
isOptionSet String
"pipefail" Token
root
Shell
Dash -> Bool
True
Shell
Sh -> Bool
True
Shell
Ksh -> String -> Token -> Bool
isOptionSet String
"pipefail" Token
root,
shellTypeSpecified :: Bool
shellTypeSpecified = forall a. Maybe a -> Bool
isJust (AnalysisSpec -> Maybe Shell
asShellType AnalysisSpec
spec) Bool -> Bool -> Bool
|| forall a. Maybe a -> Bool
isJust (AnalysisSpec -> Maybe Shell
asFallbackShell AnalysisSpec
spec),
idMap :: Map Id Token
idMap = Token -> Map Id Token
getTokenMap Token
root,
parentMap :: Map Id Token
parentMap = Token -> Map Id Token
getParentTree Token
root,
variableFlow :: [StackData]
variableFlow = Parameters -> Token -> [StackData]
getVariableFlow Parameters
params Token
root,
tokenPositions :: Map Id (Position, Position)
tokenPositions = AnalysisSpec -> Map Id (Position, Position)
asTokenPositions AnalysisSpec
spec,
cfgAnalysis :: CFGAnalysis
cfgAnalysis = CFGParameters -> Token -> CFGAnalysis
CF.analyzeControlFlow CFGParameters
cfParams Token
root
}
cfParams :: CFGParameters
cfParams = CF.CFGParameters {
cfLastpipe :: Bool
CF.cfLastpipe = Parameters -> Bool
hasLastpipe Parameters
params,
cfPipefail :: Bool
CF.cfPipefail = Parameters -> Bool
hasPipefail Parameters
params
}
root :: Token
root = AnalysisSpec -> Token
asScript AnalysisSpec
spec
containsSetE :: Token -> Bool
containsSetE Token
root = forall a. Maybe a -> Bool
isNothing forall a b. (a -> b) -> a -> b
$ forall (m :: * -> *).
Monad m =>
(Token -> m ()) -> Token -> m Token
doAnalysis (forall (f :: * -> *). Alternative f => Bool -> f ()
guard forall b c a. (b -> c) -> (a -> b) -> a -> c
. Bool -> Bool
not forall b c a. (b -> c) -> (a -> b) -> a -> c
. Token -> Bool
isSetE) Token
root
where
isSetE :: Token -> Bool
isSetE Token
t =
case Token
t of
T_Script Id
_ (T_Literal Id
_ String
str) [Token]
_ -> String
str String -> Regex -> Bool
`matches` Regex
re
T_SimpleCommand {} ->
Token
t Token -> String -> Bool
`isUnqualifiedCommand` String
"set" Bool -> Bool -> Bool
&&
(String
"errexit" forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` Token -> [String]
oversimplify Token
t Bool -> Bool -> Bool
||
String
"e" forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` forall a b. (a -> b) -> [a] -> [b]
map forall a b. (a, b) -> b
snd (Token -> [(Token, String)]
getAllFlags Token
t))
Token
_ -> Bool
False
re :: Regex
re = String -> Regex
mkRegex String
"[[:space:]]-[^-]*e"
containsSetOption :: String -> Token -> Bool
containsSetOption String
opt Token
root = forall a. Maybe a -> Bool
isNothing forall a b. (a -> b) -> a -> b
$ forall (m :: * -> *).
Monad m =>
(Token -> m ()) -> Token -> m Token
doAnalysis (forall (f :: * -> *). Alternative f => Bool -> f ()
guard forall b c a. (b -> c) -> (a -> b) -> a -> c
. Bool -> Bool
not forall b c a. (b -> c) -> (a -> b) -> a -> c
. Token -> Bool
isPipefail) Token
root
where
isPipefail :: Token -> Bool
isPipefail Token
t =
case Token
t of
T_SimpleCommand {} ->
Token
t Token -> String -> Bool
`isUnqualifiedCommand` String
"set" Bool -> Bool -> Bool
&&
(String
opt forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` Token -> [String]
oversimplify Token
t Bool -> Bool -> Bool
||
String
"o" forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` forall a b. (a -> b) -> [a] -> [b]
map forall a b. (a, b) -> b
snd (Token -> [(Token, String)]
getAllFlags Token
t))
Token
_ -> Bool
False
containsShopt :: String -> Token -> Bool
containsShopt String
shopt Token
root =
forall a. Maybe a -> Bool
isNothing forall a b. (a -> b) -> a -> b
$ forall (m :: * -> *).
Monad m =>
(Token -> m ()) -> Token -> m Token
doAnalysis (forall (f :: * -> *). Alternative f => Bool -> f ()
guard forall b c a. (b -> c) -> (a -> b) -> a -> c
. Bool -> Bool
not forall b c a. (b -> c) -> (a -> b) -> a -> c
. Token -> Bool
isShoptLastPipe) Token
root
where
isShoptLastPipe :: Token -> Bool
isShoptLastPipe Token
t =
case Token
t of
T_SimpleCommand {} ->
Token
t Token -> String -> Bool
`isUnqualifiedCommand` String
"shopt" Bool -> Bool -> Bool
&&
(String
shopt forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` Token -> [String]
oversimplify Token
t)
Token
_ -> Bool
False
isOptionSet :: String -> Token -> Bool
isOptionSet String
opt Token
root = String -> Token -> Bool
containsShopt String
opt Token
root Bool -> Bool -> Bool
|| String -> Token -> Bool
containsSetOption String
opt Token
root
prop_determineShell0 :: Bool
prop_determineShell0 = String -> Shell
determineShellTest String
"#!/bin/sh" forall a. Eq a => a -> a -> Bool
== Shell
Sh
prop_determineShell1 :: Bool
prop_determineShell1 = String -> Shell
determineShellTest String
"#!/usr/bin/env ksh" forall a. Eq a => a -> a -> Bool
== Shell
Ksh
prop_determineShell2 :: Bool
prop_determineShell2 = String -> Shell
determineShellTest String
"" forall a. Eq a => a -> a -> Bool
== Shell
Bash
prop_determineShell3 :: Bool
prop_determineShell3 = String -> Shell
determineShellTest String
"#!/bin/sh -e" forall a. Eq a => a -> a -> Bool
== Shell
Sh
prop_determineShell4 :: Bool
prop_determineShell4 = String -> Shell
determineShellTest String
"#!/bin/ksh\n#shellcheck shell=sh\nfoo" forall a. Eq a => a -> a -> Bool
== Shell
Sh
prop_determineShell5 :: Bool
prop_determineShell5 = String -> Shell
determineShellTest String
"#shellcheck shell=sh\nfoo" forall a. Eq a => a -> a -> Bool
== Shell
Sh
prop_determineShell6 :: Bool
prop_determineShell6 = String -> Shell
determineShellTest String
"#! /bin/sh" forall a. Eq a => a -> a -> Bool
== Shell
Sh
prop_determineShell7 :: Bool
prop_determineShell7 = String -> Shell
determineShellTest String
"#! /bin/ash" forall a. Eq a => a -> a -> Bool
== Shell
Dash
prop_determineShell8 :: Bool
prop_determineShell8 = Maybe Shell -> String -> Shell
determineShellTest' (forall a. a -> Maybe a
Just Shell
Ksh) String
"#!/bin/sh" forall a. Eq a => a -> a -> Bool
== Shell
Sh
prop_determineShell9 :: Bool
prop_determineShell9 = String -> Shell
determineShellTest String
"#!/bin/env -S dash -x" forall a. Eq a => a -> a -> Bool
== Shell
Dash
prop_determineShell10 :: Bool
prop_determineShell10 = String -> Shell
determineShellTest String
"#!/bin/env --split-string= dash -x" forall a. Eq a => a -> a -> Bool
== Shell
Dash
prop_determineShell11 :: Bool
prop_determineShell11 = String -> Shell
determineShellTest String
"#!/bin/busybox sh" forall a. Eq a => a -> a -> Bool
== Shell
Dash
prop_determineShell12 :: Bool
prop_determineShell12 = String -> Shell
determineShellTest String
"#!/bin/busybox ash" forall a. Eq a => a -> a -> Bool
== Shell
Dash
determineShellTest :: String -> Shell
determineShellTest = Maybe Shell -> String -> Shell
determineShellTest' forall a. Maybe a
Nothing
determineShellTest' :: Maybe Shell -> String -> Shell
determineShellTest' Maybe Shell
fallbackShell = Maybe Shell -> Token -> Shell
determineShell Maybe Shell
fallbackShell forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. HasCallStack => Maybe a -> a
fromJust forall b c a. (b -> c) -> (a -> b) -> a -> c
. ParseResult -> Maybe Token
prRoot forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> ParseResult
pScript
determineShell :: Maybe Shell -> Token -> Shell
determineShell Maybe Shell
fallbackShell Token
t = forall a. a -> Maybe a -> a
fromMaybe Shell
Bash forall a b. (a -> b) -> a -> b
$
String -> Maybe Shell
shellForExecutable String
shellString forall (m :: * -> *) a. MonadPlus m => m a -> m a -> m a
`mplus` Maybe Shell
fallbackShell
where
shellString :: String
shellString = Token -> String
getCandidate Token
t
getCandidate :: Token -> String
getCandidate :: Token -> String
getCandidate t :: Token
t@T_Script {} = Token -> String
fromShebang Token
t
getCandidate (T_Annotation Id
_ [Annotation]
annotations Token
s) =
forall {a}. a -> [a] -> a
headOrDefault (Token -> String
fromShebang Token
s) [String
s | ShellOverride String
s <- [Annotation]
annotations]
fromShebang :: Token -> String
fromShebang (T_Script Id
_ (T_Literal Id
_ String
s) [Token]
_) = ShowS
executableFromShebang String
s
getParentTree :: Token -> Map.Map Id Token
getParentTree :: Token -> Map Id Token
getParentTree Token
t =
forall a b. (a, b) -> b
snd forall a b. (a -> b) -> a -> b
$ forall s a. State s a -> s -> s
execState (forall (m :: * -> *).
Monad m =>
(Token -> m ()) -> (Token -> m ()) -> Token -> m Token
doStackAnalysis forall {a} {d} {m :: * -> *}. MonadState ([a], d) m => a -> m ()
pre forall {m :: * -> *} {a}.
MonadState ([a], Map Id a) m =>
Token -> m ()
post Token
t) ([], forall k a. Map k a
Map.empty)
where
pre :: a -> m ()
pre a
t = forall s (m :: * -> *). MonadState s m => (s -> s) -> m ()
modify (forall (a :: * -> * -> *) b c d.
Arrow a =>
a b c -> a (b, d) (c, d)
first ((:) a
t))
post :: Token -> m ()
post Token
t = do
([a]
x, Map Id a
map) <- forall s (m :: * -> *). MonadState s m => m s
get
case [a]
x of
a
_:[a]
rest -> case [a]
rest of [] -> forall s (m :: * -> *). MonadState s m => s -> m ()
put ([a]
rest, Map Id a
map)
(a
x:[a]
_) -> forall s (m :: * -> *). MonadState s m => s -> m ()
put ([a]
rest, forall k a. Ord k => k -> a -> Map k a -> Map k a
Map.insert (Token -> Id
getId Token
t) a
x Map Id a
map)
getTokenMap :: Token -> Map.Map Id Token
getTokenMap :: Token -> Map Id Token
getTokenMap Token
t =
forall s a. State s a -> s -> s
execState (forall (m :: * -> *).
Monad m =>
(Token -> m ()) -> Token -> m Token
doAnalysis forall {m :: * -> *}. MonadState (Map Id Token) m => Token -> m ()
f Token
t) forall k a. Map k a
Map.empty
where
f :: Token -> m ()
f Token
t = forall s (m :: * -> *). MonadState s m => (s -> s) -> m ()
modify (forall k a. Ord k => k -> a -> Map k a -> Map k a
Map.insert (Token -> Id
getId Token
t) Token
t)
isStrictlyQuoteFree :: Shell -> Map Id Token -> Token -> Bool
isStrictlyQuoteFree = Bool -> Shell -> Map Id Token -> Token -> Bool
isQuoteFreeNode Bool
True
isQuoteFree :: Shell -> Map Id Token -> Token -> Bool
isQuoteFree = Bool -> Shell -> Map Id Token -> Token -> Bool
isQuoteFreeNode Bool
False
isQuoteFreeNode :: Bool -> Shell -> Map Id Token -> Token -> Bool
isQuoteFreeNode Bool
strict Shell
shell Map Id Token
tree Token
t =
Token -> Bool
isQuoteFreeElement Token
t Bool -> Bool -> Bool
||
(forall a. a -> Maybe a -> a
fromMaybe Bool
False forall a b. (a -> b) -> a -> b
$ forall (t :: * -> *) (m :: * -> *) a.
(Foldable t, MonadPlus m) =>
t (m a) -> m a
msum forall a b. (a -> b) -> a -> b
$ forall a b. (a -> b) -> [a] -> [b]
map Token -> Maybe Bool
isQuoteFreeContext forall a b. (a -> b) -> a -> b
$ forall a. Int -> [a] -> [a]
drop Int
1 forall a b. (a -> b) -> a -> b
$ Map Id Token -> Token -> [Token]
getPath Map Id Token
tree Token
t)
where
isQuoteFreeElement :: Token -> Bool
isQuoteFreeElement Token
t =
case Token
t of
T_Assignment {} -> Token -> Bool
assignmentIsQuoting Token
t
T_FdRedirect {} -> Bool
True
Token
_ -> Bool
False
isQuoteFreeContext :: Token -> Maybe Bool
isQuoteFreeContext Token
t =
case Token
t of
TC_Nullary Id
_ ConditionType
DoubleBracket Token
_ -> forall (m :: * -> *) a. Monad m => a -> m a
return Bool
True
TC_Unary Id
_ ConditionType
DoubleBracket String
_ Token
_ -> forall (m :: * -> *) a. Monad m => a -> m a
return Bool
True
TC_Binary Id
_ ConditionType
DoubleBracket String
_ Token
_ Token
_ -> forall (m :: * -> *) a. Monad m => a -> m a
return Bool
True
TA_Sequence {} -> forall (m :: * -> *) a. Monad m => a -> m a
return Bool
True
T_Arithmetic {} -> forall (m :: * -> *) a. Monad m => a -> m a
return Bool
True
T_Assignment {} -> forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ Token -> Bool
assignmentIsQuoting Token
t
T_Redirecting {} -> forall (m :: * -> *) a. Monad m => a -> m a
return Bool
False
T_DoubleQuoted Id
_ [Token]
_ -> forall (m :: * -> *) a. Monad m => a -> m a
return Bool
True
T_DollarDoubleQuoted Id
_ [Token]
_ -> forall (m :: * -> *) a. Monad m => a -> m a
return Bool
True
T_CaseExpression {} -> forall (m :: * -> *) a. Monad m => a -> m a
return Bool
True
T_HereDoc {} -> forall (m :: * -> *) a. Monad m => a -> m a
return Bool
True
T_DollarBraced {} -> forall (m :: * -> *) a. Monad m => a -> m a
return Bool
True
T_ForIn {} -> forall (m :: * -> *) a. Monad m => a -> m a
return (Bool -> Bool
not Bool
strict)
T_SelectIn {} -> forall (m :: * -> *) a. Monad m => a -> m a
return (Bool -> Bool
not Bool
strict)
Token
_ -> forall a. Maybe a
Nothing
assignmentIsQuoting :: Token -> Bool
assignmentIsQuoting Token
t = Bool
shellParsesParamsAsAssignments Bool -> Bool -> Bool
|| Bool -> Bool
not (Token -> Bool
isAssignmentParamToCommand Token
t)
shellParsesParamsAsAssignments :: Bool
shellParsesParamsAsAssignments = Shell
shell forall a. Eq a => a -> a -> Bool
/= Shell
Sh
isAssignmentParamToCommand :: Token -> Bool
isAssignmentParamToCommand (T_Assignment Id
id AssignmentMode
_ String
_ [Token]
_ Token
_) =
case forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup Id
id Map Id Token
tree of
Just (T_SimpleCommand Id
_ [Token]
_ (Token
_:[Token]
args)) -> Id
id forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` (forall a b. (a -> b) -> [a] -> [b]
map Token -> Id
getId [Token]
args)
Maybe Token
_ -> Bool
False
isParamTo :: Map.Map Id Token -> String -> Token -> Bool
isParamTo :: Map Id Token -> String -> Token -> Bool
isParamTo Map Id Token
tree String
cmd =
Token -> Bool
go
where
go :: Token -> Bool
go Token
x = case forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup (Token -> Id
getId Token
x) Map Id Token
tree of
Maybe Token
Nothing -> Bool
False
Just Token
parent -> Token -> Bool
check Token
parent
check :: Token -> Bool
check Token
t =
case Token
t of
T_SingleQuoted Id
_ String
_ -> Token -> Bool
go Token
t
T_DoubleQuoted Id
_ [Token]
_ -> Token -> Bool
go Token
t
T_NormalWord Id
_ [Token]
_ -> Token -> Bool
go Token
t
T_SimpleCommand {} -> Token -> String -> Bool
isCommand Token
t String
cmd
T_Redirecting {} -> Token -> String -> Bool
isCommand Token
t String
cmd
Token
_ -> Bool
False
getClosestCommand :: Map.Map Id Token -> Token -> Maybe Token
getClosestCommand :: Map Id Token -> Token -> Maybe Token
getClosestCommand Map Id Token
tree Token
t =
forall a. (a -> Maybe Bool) -> [a] -> Maybe a
findFirst Token -> Maybe Bool
findCommand forall a b. (a -> b) -> a -> b
$ Map Id Token -> Token -> [Token]
getPath Map Id Token
tree Token
t
where
findCommand :: Token -> Maybe Bool
findCommand Token
t =
case Token
t of
T_Redirecting {} -> forall (m :: * -> *) a. Monad m => a -> m a
return Bool
True
T_Script {} -> forall (m :: * -> *) a. Monad m => a -> m a
return Bool
False
Token
_ -> forall a. Maybe a
Nothing
getClosestCommandM :: Token -> m (Maybe Token)
getClosestCommandM Token
t = do
Parameters
params <- forall r (m :: * -> *). MonadReader r m => m r
ask
forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ Map Id Token -> Token -> Maybe Token
getClosestCommand (Parameters -> Map Id Token
parentMap Parameters
params) Token
t
usedAsCommandName :: Map Id Token -> Token -> Bool
usedAsCommandName Map Id Token
tree Token
token = Id -> [Token] -> Bool
go (Token -> Id
getId Token
token) (forall a. [a] -> [a]
tail forall a b. (a -> b) -> a -> b
$ Map Id Token -> Token -> [Token]
getPath Map Id Token
tree Token
token)
where
go :: Id -> [Token] -> Bool
go Id
currentId (T_NormalWord Id
id [Token
word]:[Token]
rest)
| Id
currentId forall a. Eq a => a -> a -> Bool
== Token -> Id
getId Token
word = Id -> [Token] -> Bool
go Id
id [Token]
rest
go Id
currentId (T_DoubleQuoted Id
id [Token
word]:[Token]
rest)
| Id
currentId forall a. Eq a => a -> a -> Bool
== Token -> Id
getId Token
word = Id -> [Token] -> Bool
go Id
id [Token]
rest
go Id
currentId (t :: Token
t@(T_SimpleCommand Id
_ [Token]
_ (Token
word:[Token]
_)):[Token]
_) =
Token -> Id
getId Token
word forall a. Eq a => a -> a -> Bool
== Id
currentId Bool -> Bool -> Bool
|| Token -> Id
getId (Token -> Token
getCommandTokenOrThis Token
t) forall a. Eq a => a -> a -> Bool
== Id
currentId
go Id
_ [Token]
_ = Bool
False
getPathM :: Token -> m [Token]
getPathM Token
t = do
Parameters
params <- forall r (m :: * -> *). MonadReader r m => m r
ask
forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ Map Id Token -> Token -> [Token]
getPath (Parameters -> Map Id Token
parentMap Parameters
params) Token
t
isParentOf :: Map Id Token -> Token -> Token -> Bool
isParentOf Map Id Token
tree Token
parent Token
child =
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
elem (Token -> Id
getId Token
parent) forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b. (a -> b) -> [a] -> [b]
map Token -> Id
getId forall a b. (a -> b) -> a -> b
$ Map Id Token -> Token -> [Token]
getPath Map Id Token
tree Token
child
parents :: Parameters -> Token -> [Token]
parents Parameters
params = Map Id Token -> Token -> [Token]
getPath (Parameters -> Map Id Token
parentMap Parameters
params)
findFirst :: (a -> Maybe Bool) -> [a] -> Maybe a
findFirst :: forall a. (a -> Maybe Bool) -> [a] -> Maybe a
findFirst a -> Maybe Bool
p = forall (t :: * -> *) a b.
Foldable t =>
(a -> b -> b) -> b -> t a -> b
foldr a -> Maybe a -> Maybe a
go forall a. Maybe a
Nothing
where
go :: a -> Maybe a -> Maybe a
go a
x Maybe a
acc =
case a -> Maybe Bool
p a
x of
Just Bool
True -> forall (m :: * -> *) a. Monad m => a -> m a
return a
x
Just Bool
False -> forall a. Maybe a
Nothing
Maybe Bool
Nothing -> Maybe a
acc
tokenIsJustCommandOutput :: Token -> Bool
tokenIsJustCommandOutput Token
t = case Token
t of
T_NormalWord Id
id [T_DollarExpansion Id
_ [Token]
cmds] -> [Token] -> Bool
check [Token]
cmds
T_NormalWord Id
id [T_DoubleQuoted Id
_ [T_DollarExpansion Id
_ [Token]
cmds]] -> [Token] -> Bool
check [Token]
cmds
T_NormalWord Id
id [T_Backticked Id
_ [Token]
cmds] -> [Token] -> Bool
check [Token]
cmds
T_NormalWord Id
id [T_DoubleQuoted Id
_ [T_Backticked Id
_ [Token]
cmds]] -> [Token] -> Bool
check [Token]
cmds
Token
_ -> Bool
False
where
check :: [Token] -> Bool
check [Token
x] = Bool -> Bool
not forall a b. (a -> b) -> a -> b
$ Token -> Bool
isOnlyRedirection Token
x
check [Token]
_ = Bool
False
getVariableFlow :: Parameters -> Token -> [StackData]
getVariableFlow Parameters
params Token
t =
forall a. [a] -> [a]
reverse forall a b. (a -> b) -> a -> b
$ forall s a. State s a -> s -> s
execState (forall (m :: * -> *).
Monad m =>
(Token -> m ()) -> (Token -> m ()) -> Token -> m Token
doStackAnalysis forall {m :: * -> *}. MonadState [StackData] m => Token -> m ()
startScope forall {m :: * -> *}. MonadState [StackData] m => Token -> m ()
endScope Token
t) []
where
startScope :: Token -> m ()
startScope Token
t =
let scopeType :: Scope
scopeType = Parameters -> Token -> Scope
leadType Parameters
params Token
t
in do
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (Scope
scopeType forall a. Eq a => a -> a -> Bool
/= Scope
NoneScope) forall a b. (a -> b) -> a -> b
$ forall s (m :: * -> *). MonadState s m => (s -> s) -> m ()
modify (Scope -> StackData
StackScope Scope
scopeTypeforall a. a -> [a] -> [a]
:)
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (Token -> Bool
assignFirst Token
t) forall a b. (a -> b) -> a -> b
$ forall {m :: * -> *}. MonadState [StackData] m => Token -> m ()
setWritten Token
t
endScope :: Token -> m ()
endScope Token
t =
let scopeType :: Scope
scopeType = Parameters -> Token -> Scope
leadType Parameters
params Token
t
in do
forall {m :: * -> *}. MonadState [StackData] m => Token -> m ()
setRead Token
t
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
unless (Token -> Bool
assignFirst Token
t) forall a b. (a -> b) -> a -> b
$ forall {m :: * -> *}. MonadState [StackData] m => Token -> m ()
setWritten Token
t
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (Scope
scopeType forall a. Eq a => a -> a -> Bool
/= Scope
NoneScope) forall a b. (a -> b) -> a -> b
$ forall s (m :: * -> *). MonadState s m => (s -> s) -> m ()
modify (StackData
StackScopeEndforall a. a -> [a] -> [a]
:)
assignFirst :: Token -> Bool
assignFirst T_ForIn {} = Bool
True
assignFirst T_SelectIn {} = Bool
True
assignFirst (T_BatsTest {}) = Bool
True
assignFirst Token
_ = Bool
False
setRead :: Token -> m ()
setRead Token
t =
let read :: [(Token, Token, String)]
read = Map Id Token -> Token -> [(Token, Token, String)]
getReferencedVariables (Parameters -> Map Id Token
parentMap Parameters
params) Token
t
in forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ (\(Token, Token, String)
v -> forall s (m :: * -> *). MonadState s m => (s -> s) -> m ()
modify ((Token, Token, String) -> StackData
Reference (Token, Token, String)
vforall a. a -> [a] -> [a]
:)) [(Token, Token, String)]
read
setWritten :: Token -> m ()
setWritten Token
t =
let written :: [(Token, Token, String, DataType)]
written = Token -> [(Token, Token, String, DataType)]
getModifiedVariables Token
t
in forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ (\(Token, Token, String, DataType)
v -> forall s (m :: * -> *). MonadState s m => (s -> s) -> m ()
modify ((Token, Token, String, DataType) -> StackData
Assignment (Token, Token, String, DataType)
vforall a. a -> [a] -> [a]
:)) [(Token, Token, String, DataType)]
written
leadType :: Parameters -> Token -> Scope
leadType Parameters
params Token
t =
case Token
t of
T_DollarExpansion Id
_ [Token]
_ -> String -> Scope
SubshellScope String
"$(..) expansion"
T_Backticked Id
_ [Token]
_ -> String -> Scope
SubshellScope String
"`..` expansion"
T_Backgrounded Id
_ Token
_ -> String -> Scope
SubshellScope String
"backgrounding &"
T_Subshell Id
_ [Token]
_ -> String -> Scope
SubshellScope String
"(..) group"
T_BatsTest {} -> String -> Scope
SubshellScope String
"@bats test"
T_CoProcBody Id
_ Token
_ -> String -> Scope
SubshellScope String
"coproc"
T_Redirecting {} ->
if Maybe Bool
causesSubshell forall a. Eq a => a -> a -> Bool
== forall a. a -> Maybe a
Just Bool
True
then String -> Scope
SubshellScope String
"pipeline"
else Scope
NoneScope
Token
_ -> Scope
NoneScope
where
parentPipeline :: Maybe Token
parentPipeline = do
Token
parent <- forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup (Token -> Id
getId Token
t) (Parameters -> Map Id Token
parentMap Parameters
params)
case Token
parent of
T_Pipeline {} -> forall (m :: * -> *) a. Monad m => a -> m a
return Token
parent
Token
_ -> forall a. Maybe a
Nothing
causesSubshell :: Maybe Bool
causesSubshell = do
(T_Pipeline Id
_ [Token]
_ [Token]
list) <- Maybe Token
parentPipeline
forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ case [Token]
list of
Token
_:Token
_:[Token]
_ -> Bool -> Bool
not (Parameters -> Bool
hasLastpipe Parameters
params) Bool -> Bool -> Bool
|| Token -> Id
getId (forall a. [a] -> a
last [Token]
list) forall a. Eq a => a -> a -> Bool
/= Token -> Id
getId Token
t
[Token]
_ -> Bool
False
getModifiedVariables :: Token -> [(Token, Token, String, DataType)]
getModifiedVariables Token
t =
case Token
t of
T_SimpleCommand Id
_ [Token]
vars [] ->
[(Token
x, Token
x, String
name, (DataSource -> DataType) -> Token -> DataType
dataTypeFrom DataSource -> DataType
DataString Token
w) | x :: Token
x@(T_Assignment Id
id AssignmentMode
_ String
name [Token]
_ Token
w) <- [Token]
vars]
T_SimpleCommand {} ->
Token -> [(Token, Token, String, DataType)]
getModifiedVariableCommand Token
t
TA_Unary Id
_ String
op v :: Token
v@(TA_Variable Id
_ String
name [Token]
_) | String
"--" forall a. Eq a => [a] -> [a] -> Bool
`isInfixOf` String
op Bool -> Bool -> Bool
|| String
"++" forall a. Eq a => [a] -> [a] -> Bool
`isInfixOf` String
op ->
[(Token
t, Token
v, String
name, DataSource -> DataType
DataString DataSource
SourceInteger)]
TA_Assignment Id
_ String
op (TA_Variable Id
_ String
name [Token]
_) Token
rhs -> do
forall (f :: * -> *). Alternative f => Bool -> f ()
guard forall a b. (a -> b) -> a -> b
$ String
op forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [String
"=", String
"*=", String
"/=", String
"%=", String
"+=", String
"-=", String
"<<=", String
">>=", String
"&=", String
"^=", String
"|="]
forall (m :: * -> *) a. Monad m => a -> m a
return (Token
t, Token
t, String
name, DataSource -> DataType
DataString DataSource
SourceInteger)
T_BatsTest {} -> [
(Token
t, Token
t, String
"lines", DataSource -> DataType
DataArray DataSource
SourceExternal),
(Token
t, Token
t, String
"status", DataSource -> DataType
DataString DataSource
SourceInteger),
(Token
t, Token
t, String
"output", DataSource -> DataType
DataString DataSource
SourceExternal)
]
TC_Unary Id
id ConditionType
_ String
"-v" Token
token -> forall a. Maybe a -> [a]
maybeToList forall a b. (a -> b) -> a -> b
$ do
String
str <- Token -> Maybe String
getVariableForTestDashV Token
token
forall (m :: * -> *) a. Monad m => a -> m a
return (Token
t, Token
token, String
str, DataSource -> DataType
DataString DataSource
SourceChecked)
TC_Unary Id
_ ConditionType
_ String
"-n" Token
token -> forall {a}. a -> Token -> [(a, Token, String, DataType)]
markAsChecked Token
t Token
token
TC_Unary Id
_ ConditionType
_ String
"-z" Token
token -> forall {a}. a -> Token -> [(a, Token, String, DataType)]
markAsChecked Token
t Token
token
TC_Nullary Id
_ ConditionType
_ Token
token -> forall {a}. a -> Token -> [(a, Token, String, DataType)]
markAsChecked Token
t Token
token
T_DollarBraced Id
_ Bool
_ Token
l -> forall a. Maybe a -> [a]
maybeToList forall a b. (a -> b) -> a -> b
$ do
let string :: String
string = forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat forall a b. (a -> b) -> a -> b
$ Token -> [String]
oversimplify Token
l
let modifier :: String
modifier = ShowS
getBracedModifier String
string
forall (f :: * -> *). Alternative f => Bool -> f ()
guard forall a b. (a -> b) -> a -> b
$ forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any (forall a. Eq a => [a] -> [a] -> Bool
`isPrefixOf` String
modifier) [String
"=", String
":="]
forall (m :: * -> *) a. Monad m => a -> m a
return (Token
t, Token
t, ShowS
getBracedReference String
string, DataSource -> DataType
DataString forall a b. (a -> b) -> a -> b
$ [Token] -> DataSource
SourceFrom [Token
l])
T_FdRedirect Id
_ (Char
'{':String
var) Token
op ->
[(Token
t, Token
t, forall a. (a -> Bool) -> [a] -> [a]
takeWhile (forall a. Eq a => a -> a -> Bool
/= Char
'}') String
var, DataSource -> DataType
DataString DataSource
SourceInteger) | Bool -> Bool
not forall a b. (a -> b) -> a -> b
$ Token -> Bool
isClosingFileOp Token
op]
T_CoProc Id
_ Maybe String
name Token
_ ->
[(Token
t, Token
t, forall a. a -> Maybe a -> a
fromMaybe String
"COPROC" Maybe String
name, DataSource -> DataType
DataArray DataSource
SourceInteger)]
T_ForIn Id
id String
str [] [Token]
_ -> [(Token
t, Token
t, String
str, DataSource -> DataType
DataString DataSource
SourceExternal)]
T_ForIn Id
id String
str [Token]
words [Token]
_ -> [(Token
t, Token
t, String
str, DataSource -> DataType
DataString forall a b. (a -> b) -> a -> b
$ [Token] -> DataSource
SourceFrom [Token]
words)]
T_SelectIn Id
id String
str [Token]
words [Token]
_ -> [(Token
t, Token
t, String
str, DataSource -> DataType
DataString forall a b. (a -> b) -> a -> b
$ [Token] -> DataSource
SourceFrom [Token]
words)]
Token
_ -> []
where
markAsChecked :: a -> Token -> [(a, Token, String, DataType)]
markAsChecked a
place Token
token = forall a b. (a -> Maybe b) -> [a] -> [b]
mapMaybe (forall {a}. a -> Token -> Maybe (a, Token, String, DataType)
f a
place) forall a b. (a -> b) -> a -> b
$ Token -> [Token]
getWordParts Token
token
f :: a -> Token -> Maybe (a, Token, String, DataType)
f a
place Token
t = case Token
t of
T_DollarBraced Id
_ Bool
_ Token
l ->
let str :: String
str = ShowS
getBracedReference forall a b. (a -> b) -> a -> b
$ forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat forall a b. (a -> b) -> a -> b
$ Token -> [String]
oversimplify Token
l in do
forall (f :: * -> *). Alternative f => Bool -> f ()
guard forall a b. (a -> b) -> a -> b
$ String -> Bool
isVariableName String
str
forall (m :: * -> *) a. Monad m => a -> m a
return (a
place, Token
t, String
str, DataSource -> DataType
DataString DataSource
SourceChecked)
Token
_ -> forall a. Maybe a
Nothing
getReferencedVariableCommand :: Token -> [(Token, Token, String)]
getReferencedVariableCommand base :: Token
base@(T_SimpleCommand Id
_ [Token]
_ (T_NormalWord Id
_ (T_Literal Id
_ String
x:[Token]
_):[Token]
rest)) =
case String
x of
String
"declare" -> [(Token, Token, String)]
forDeclare
String
"typeset" -> [(Token, Token, String)]
forDeclare
String
"export" -> if String
"f" forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [String]
flags
then []
else forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap Token -> [(Token, Token, String)]
getReference [Token]
rest
String
"local" -> if String
"x" forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [String]
flags
then forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap Token -> [(Token, Token, String)]
getReference [Token]
rest
else []
String
"trap" ->
case [Token]
rest of
Token
head:[Token]
_ -> forall a b. (a -> b) -> [a] -> [b]
map (\String
x -> (Token
base, Token
head, String
x)) forall a b. (a -> b) -> a -> b
$ Token -> [String]
getVariablesFromLiteralToken Token
head
[Token]
_ -> []
String
"alias" -> [(Token
base, Token
token, String
name) | Token
token <- [Token]
rest, String
name <- Token -> [String]
getVariablesFromLiteralToken Token
token]
String
_ -> []
where
forDeclare :: [(Token, Token, String)]
forDeclare =
if
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any (forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [String]
flags) [String
"x", String
"p"] Bool -> Bool -> Bool
&&
(Bool -> Bool
not forall a b. (a -> b) -> a -> b
$ forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any (forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [String]
flags) [String
"f", String
"F"])
then forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap Token -> [(Token, Token, String)]
getReference [Token]
rest
else []
getReference :: Token -> [(Token, Token, String)]
getReference t :: Token
t@(T_Assignment Id
_ AssignmentMode
_ String
name [Token]
_ Token
value) = [(Token
t, Token
t, String
name)]
getReference t :: Token
t@(T_NormalWord Id
_ [T_Literal Id
_ String
name]) | Bool -> Bool
not (String
"-" forall a. Eq a => [a] -> [a] -> Bool
`isPrefixOf` String
name) = [(Token
t, Token
t, String
name)]
getReference Token
_ = []
flags :: [String]
flags = forall a b. (a -> b) -> [a] -> [b]
map forall a b. (a, b) -> b
snd forall a b. (a -> b) -> a -> b
$ Token -> [(Token, String)]
getAllFlags Token
base
getReferencedVariableCommand Token
_ = []
getModifiedVariableCommand :: Token -> [(Token, Token, String, DataType)]
getModifiedVariableCommand base :: Token
base@(T_SimpleCommand Id
id [Token]
cmdPrefix (T_NormalWord Id
_ (T_Literal Id
_ String
x:[Token]
_):[Token]
rest)) =
forall a. (a -> Bool) -> [a] -> [a]
filter (\(Token
_,Token
_,String
s,DataType
_) -> Bool -> Bool
not (String
"-" forall a. Eq a => [a] -> [a] -> Bool
`isPrefixOf` String
s)) forall a b. (a -> b) -> a -> b
$
case String
x of
String
"builtin" ->
Token -> [(Token, Token, String, DataType)]
getModifiedVariableCommand forall a b. (a -> b) -> a -> b
$ Id -> [Token] -> [Token] -> Token
T_SimpleCommand Id
id [Token]
cmdPrefix [Token]
rest
String
"read" ->
let fallback :: [(Token, Token, String, DataType)]
fallback = forall a. [Maybe a] -> [a]
catMaybes forall a b. (a -> b) -> a -> b
$ forall a. (a -> Bool) -> [a] -> [a]
takeWhile forall a. Maybe a -> Bool
isJust (forall a. [a] -> [a]
reverse forall a b. (a -> b) -> a -> b
$ forall a b. (a -> b) -> [a] -> [b]
map Token -> Maybe (Token, Token, String, DataType)
getLiteral [Token]
rest)
in forall a. a -> Maybe a -> a
fromMaybe [(Token, Token, String, DataType)]
fallback forall a b. (a -> b) -> a -> b
$ do
[(String, (Token, Token))]
parsed <- String -> [Token] -> Maybe [(String, (Token, Token))]
getGnuOpts String
flagsForRead [Token]
rest
case forall a b. Eq a => a -> [(a, b)] -> Maybe b
lookup String
"a" [(String, (Token, Token))]
parsed of
Just (Token
_, Token
var) -> (forall a. a -> [a] -> [a]
:[]) forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Token -> Maybe (Token, Token, String, DataType)
getLiteralArray Token
var
Maybe (Token, Token)
Nothing -> forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ forall a. [Maybe a] -> [a]
catMaybes forall a b. (a -> b) -> a -> b
$
forall a b. (a -> b) -> [a] -> [b]
map (Token -> Maybe (Token, Token, String, DataType)
getLiteral forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b. (a, b) -> b
snd forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b. (a, b) -> b
snd) forall a b. (a -> b) -> a -> b
$ forall a. (a -> Bool) -> [a] -> [a]
filter (forall (t :: * -> *) a. Foldable t => t a -> Bool
null forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b. (a, b) -> a
fst) [(String, (Token, Token))]
parsed
String
"getopts" ->
case [Token]
rest of
Token
opts:Token
var:[Token]
_ -> forall a. Maybe a -> [a]
maybeToList forall a b. (a -> b) -> a -> b
$ Token -> Maybe (Token, Token, String, DataType)
getLiteral Token
var
[Token]
_ -> []
String
"let" -> forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap Token -> [(Token, Token, String, DataType)]
letParamToLiteral [Token]
rest
String
"export" ->
if String
"f" forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [String]
flags then [] else forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap Token -> [(Token, Token, String, DataType)]
getModifierParamString [Token]
rest
String
"declare" -> [(Token, Token, String, DataType)]
forDeclare
String
"typeset" -> [(Token, Token, String, DataType)]
forDeclare
String
"local" -> forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap Token -> [(Token, Token, String, DataType)]
getModifierParamString [Token]
rest
String
"readonly" ->
if forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any (forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [String]
flags) [String
"f", String
"p"]
then []
else forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap Token -> [(Token, Token, String, DataType)]
getModifierParamString [Token]
rest
String
"set" -> forall a. Maybe a -> [a]
maybeToList forall a b. (a -> b) -> a -> b
$ do
[Token]
params <- [Token] -> Maybe [Token]
getSetParams [Token]
rest
forall (m :: * -> *) a. Monad m => a -> m a
return (Token
base, Token
base, String
"@", DataSource -> DataType
DataString forall a b. (a -> b) -> a -> b
$ [Token] -> DataSource
SourceFrom [Token]
params)
String
"printf" -> forall a. Maybe a -> [a]
maybeToList forall a b. (a -> b) -> a -> b
$ [Token] -> Maybe (Token, Token, String, DataType)
getPrintfVariable [Token]
rest
String
"wait" -> forall a. Maybe a -> [a]
maybeToList forall a b. (a -> b) -> a -> b
$ [Token] -> Maybe (Token, Token, String, DataType)
getWaitVariable [Token]
rest
String
"mapfile" -> forall a. Maybe a -> [a]
maybeToList forall a b. (a -> b) -> a -> b
$ Token -> [Token] -> Maybe (Token, Token, String, DataType)
getMapfileArray Token
base [Token]
rest
String
"readarray" -> forall a. Maybe a -> [a]
maybeToList forall a b. (a -> b) -> a -> b
$ Token -> [Token] -> Maybe (Token, Token, String, DataType)
getMapfileArray Token
base [Token]
rest
String
"DEFINE_boolean" -> forall a. Maybe a -> [a]
maybeToList forall a b. (a -> b) -> a -> b
$ [Token] -> Maybe (Token, Token, String, DataType)
getFlagVariable [Token]
rest
String
"DEFINE_float" -> forall a. Maybe a -> [a]
maybeToList forall a b. (a -> b) -> a -> b
$ [Token] -> Maybe (Token, Token, String, DataType)
getFlagVariable [Token]
rest
String
"DEFINE_integer" -> forall a. Maybe a -> [a]
maybeToList forall a b. (a -> b) -> a -> b
$ [Token] -> Maybe (Token, Token, String, DataType)
getFlagVariable [Token]
rest
String
"DEFINE_string" -> forall a. Maybe a -> [a]
maybeToList forall a b. (a -> b) -> a -> b
$ [Token] -> Maybe (Token, Token, String, DataType)
getFlagVariable [Token]
rest
String
_ -> []
where
flags :: [String]
flags = forall a b. (a -> b) -> [a] -> [b]
map forall a b. (a, b) -> b
snd forall a b. (a -> b) -> a -> b
$ Token -> [(Token, String)]
getAllFlags Token
base
stripEquals :: ShowS
stripEquals String
s = forall a. Int -> [a] -> [a]
drop Int
1 forall a b. (a -> b) -> a -> b
$ forall a. (a -> Bool) -> [a] -> [a]
dropWhile (forall a. Eq a => a -> a -> Bool
/= Char
'=') String
s
stripEqualsFrom :: Token -> Token
stripEqualsFrom (T_NormalWord Id
id1 (T_Literal Id
id2 String
s:[Token]
rs)) =
Id -> [Token] -> Token
T_NormalWord Id
id1 (Id -> String -> Token
T_Literal Id
id2 (ShowS
stripEquals String
s)forall a. a -> [a] -> [a]
:[Token]
rs)
stripEqualsFrom (T_NormalWord Id
id1 [T_DoubleQuoted Id
id2 [T_Literal Id
id3 String
s]]) =
Id -> [Token] -> Token
T_NormalWord Id
id1 [Id -> [Token] -> Token
T_DoubleQuoted Id
id2 [Id -> String -> Token
T_Literal Id
id3 (ShowS
stripEquals String
s)]]
stripEqualsFrom Token
t = Token
t
forDeclare :: [(Token, Token, String, DataType)]
forDeclare = if forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any (forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [String]
flags) [String
"F", String
"f", String
"p"] then [] else [(Token, Token, String, DataType)]
declaredVars
declaredVars :: [(Token, Token, String, DataType)]
declaredVars = forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap ((DataSource -> DataType)
-> Token -> [(Token, Token, String, DataType)]
getModifierParam DataSource -> DataType
defaultType) [Token]
rest
where
defaultType :: DataSource -> DataType
defaultType = if forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any (forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [String]
flags) [String
"a", String
"A"] then DataSource -> DataType
DataArray else DataSource -> DataType
DataString
getLiteralOfDataType :: Token -> d -> Maybe (Token, Token, String, d)
getLiteralOfDataType Token
t d
d = do
String
s <- Token -> Maybe String
getLiteralString Token
t
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (String
"-" forall a. Eq a => [a] -> [a] -> Bool
`isPrefixOf` String
s) forall a b. (a -> b) -> a -> b
$ forall (m :: * -> *) a. MonadFail m => String -> m a
fail String
"argument"
forall (m :: * -> *) a. Monad m => a -> m a
return (Token
base, Token
t, String
s, d
d)
getLiteral :: Token -> Maybe (Token, Token, String, DataType)
getLiteral Token
t = forall {d}. Token -> d -> Maybe (Token, Token, String, d)
getLiteralOfDataType Token
t (DataSource -> DataType
DataString DataSource
SourceExternal)
getLiteralArray :: Token -> Maybe (Token, Token, String, DataType)
getLiteralArray Token
t = forall {d}. Token -> d -> Maybe (Token, Token, String, d)
getLiteralOfDataType Token
t (DataSource -> DataType
DataArray DataSource
SourceExternal)
getModifierParamString :: Token -> [(Token, Token, String, DataType)]
getModifierParamString = (DataSource -> DataType)
-> Token -> [(Token, Token, String, DataType)]
getModifierParam DataSource -> DataType
DataString
getModifierParam :: (DataSource -> DataType)
-> Token -> [(Token, Token, String, DataType)]
getModifierParam DataSource -> DataType
def t :: Token
t@(T_Assignment Id
_ AssignmentMode
_ String
name [Token]
_ Token
value) =
[(Token
base, Token
t, String
name, (DataSource -> DataType) -> Token -> DataType
dataTypeFrom DataSource -> DataType
def Token
value)]
getModifierParam DataSource -> DataType
def t :: Token
t@T_NormalWord {} = forall a. Maybe a -> [a]
maybeToList forall a b. (a -> b) -> a -> b
$ do
String
name <- Token -> Maybe String
getLiteralString Token
t
forall (f :: * -> *). Alternative f => Bool -> f ()
guard forall a b. (a -> b) -> a -> b
$ String -> Bool
isVariableName String
name
forall (m :: * -> *) a. Monad m => a -> m a
return (Token
base, Token
t, String
name, DataSource -> DataType
def DataSource
SourceDeclaration)
getModifierParam DataSource -> DataType
_ Token
_ = []
letParamToLiteral :: Token -> [(Token, Token, String, DataType)]
letParamToLiteral Token
token =
if forall (t :: * -> *) a. Foldable t => t a -> Bool
null String
var
then []
else [(Token
base, Token
token, String
var, DataSource -> DataType
DataString forall a b. (a -> b) -> a -> b
$ [Token] -> DataSource
SourceFrom [Token -> Token
stripEqualsFrom Token
token])]
where var :: String
var = forall a. (a -> Bool) -> [a] -> [a]
takeWhile Char -> Bool
isVariableChar forall a b. (a -> b) -> a -> b
$ forall a. (a -> Bool) -> [a] -> [a]
dropWhile (forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` String
"+-") forall a b. (a -> b) -> a -> b
$ forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat forall a b. (a -> b) -> a -> b
$ Token -> [String]
oversimplify Token
token
getSetParams :: [Token] -> Maybe [Token]
getSetParams (Token
t:Token
_:[Token]
rest) | Token -> Maybe String
getLiteralString Token
t forall a. Eq a => a -> a -> Bool
== forall a. a -> Maybe a
Just String
"-o" = [Token] -> Maybe [Token]
getSetParams [Token]
rest
getSetParams (Token
t:[Token]
rest) =
let s :: Maybe String
s = Token -> Maybe String
getLiteralString Token
t in
case Maybe String
s of
Just String
"--" -> forall (m :: * -> *) a. Monad m => a -> m a
return [Token]
rest
Just (Char
'-':String
_) -> [Token] -> Maybe [Token]
getSetParams [Token]
rest
Maybe String
_ -> forall (m :: * -> *) a. Monad m => a -> m a
return (Token
tforall a. a -> [a] -> [a]
:forall a. a -> Maybe a -> a
fromMaybe [] ([Token] -> Maybe [Token]
getSetParams [Token]
rest))
getSetParams [] = forall a. Maybe a
Nothing
getPrintfVariable :: [Token] -> Maybe (Token, Token, String, DataType)
getPrintfVariable [Token]
list = forall {t :: * -> *} {b} {a}.
(Foldable t, Eq b) =>
b
-> DataSource
-> Maybe (t (b, (a, Token)))
-> Maybe (Token, Token, String, DataType)
getFlagAssignedVariable String
"v" ([Token] -> DataSource
SourceFrom [Token]
list) forall a b. (a -> b) -> a -> b
$ String -> [Token] -> Maybe [(String, (Token, Token))]
getBsdOpts String
"v:" [Token]
list
getWaitVariable :: [Token] -> Maybe (Token, Token, String, DataType)
getWaitVariable [Token]
list = forall {t :: * -> *} {b} {a}.
(Foldable t, Eq b) =>
b
-> DataSource
-> Maybe (t (b, (a, Token)))
-> Maybe (Token, Token, String, DataType)
getFlagAssignedVariable String
"p" DataSource
SourceInteger forall a b. (a -> b) -> a -> b
$ forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ [Token] -> [(String, (Token, Token))]
getGenericOpts [Token]
list
getFlagAssignedVariable :: b
-> DataSource
-> Maybe (t (b, (a, Token)))
-> Maybe (Token, Token, String, DataType)
getFlagAssignedVariable b
str DataSource
dataSource Maybe (t (b, (a, Token)))
maybeFlags = do
t (b, (a, Token))
flags <- Maybe (t (b, (a, Token)))
maybeFlags
(b
_, (a
flag, Token
value)) <- forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Maybe a
find ((forall a. Eq a => a -> a -> Bool
== b
str) forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b. (a, b) -> a
fst) t (b, (a, Token))
flags
String
variableName <- forall (m :: * -> *).
Monad m =>
(Token -> m String) -> Token -> m String
getLiteralStringExt (forall a b. a -> b -> a
const forall a b. (a -> b) -> a -> b
$ forall (m :: * -> *) a. Monad m => a -> m a
return String
"!") Token
value
let (String
baseName, String
index) = forall a. (a -> Bool) -> [a] -> ([a], [a])
span (forall a. Eq a => a -> a -> Bool
/= Char
'[') String
variableName
forall (m :: * -> *) a. Monad m => a -> m a
return (Token
base, Token
value, String
baseName, (if forall (t :: * -> *) a. Foldable t => t a -> Bool
null String
index then DataSource -> DataType
DataString else DataSource -> DataType
DataArray) DataSource
dataSource)
getMapfileArray :: Token -> [Token] -> Maybe (Token, Token, String, DataType)
getMapfileArray Token
base [Token]
rest = Maybe (Token, Token, String, DataType)
parseArgs forall (m :: * -> *) a. MonadPlus m => m a -> m a -> m a
`mplus` Maybe (Token, Token, String, DataType)
fallback
where
parseArgs :: Maybe (Token, Token, String, DataType)
parseArgs :: Maybe (Token, Token, String, DataType)
parseArgs = do
[(String, (Token, Token))]
args <- String -> [Token] -> Maybe [(String, (Token, Token))]
getGnuOpts String
"d:n:O:s:u:C:c:t" [Token]
rest
case [Token
y | (String
"",(Token
_,Token
y)) <- [(String, (Token, Token))]
args] of
[] ->
forall (m :: * -> *) a. Monad m => a -> m a
return (Token
base, Token
base, String
"MAPFILE", DataSource -> DataType
DataArray DataSource
SourceExternal)
Token
first:[Token]
_ -> do
String
name <- Token -> Maybe String
getLiteralString Token
first
forall (f :: * -> *). Alternative f => Bool -> f ()
guard forall a b. (a -> b) -> a -> b
$ String -> Bool
isVariableName String
name
forall (m :: * -> *) a. Monad m => a -> m a
return (Token
base, Token
first, String
name, DataSource -> DataType
DataArray DataSource
SourceExternal)
fallback :: Maybe (Token, Token, String, DataType)
fallback :: Maybe (Token, Token, String, DataType)
fallback = do
(String
name, Token
token) <- forall a. [a] -> Maybe a
listToMaybe forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b. (a -> Maybe b) -> [a] -> [b]
mapMaybe Token -> Maybe (String, Token)
f forall a b. (a -> b) -> a -> b
$ forall a. [a] -> [a]
reverse [Token]
rest
forall (m :: * -> *) a. Monad m => a -> m a
return (Token
base, Token
token, String
name, DataSource -> DataType
DataArray DataSource
SourceExternal)
f :: Token -> Maybe (String, Token)
f Token
arg = do
String
name <- Token -> Maybe String
getLiteralString Token
arg
forall (f :: * -> *). Alternative f => Bool -> f ()
guard forall a b. (a -> b) -> a -> b
$ String -> Bool
isVariableName String
name
forall (m :: * -> *) a. Monad m => a -> m a
return (String
name, Token
arg)
getFlagVariable :: [Token] -> Maybe (Token, Token, String, DataType)
getFlagVariable (Token
n:Token
v:[Token]
_) = do
String
name <- Token -> Maybe String
getLiteralString Token
n
forall (m :: * -> *) a. Monad m => a -> m a
return (Token
base, Token
n, String
"FLAGS_" forall a. [a] -> [a] -> [a]
++ String
name, DataSource -> DataType
DataString forall a b. (a -> b) -> a -> b
$ DataSource
SourceExternal)
getFlagVariable [Token]
_ = forall a. Maybe a
Nothing
getModifiedVariableCommand Token
_ = []
getVariableForTestDashV :: Token -> Maybe String
getVariableForTestDashV :: Token -> Maybe String
getVariableForTestDashV Token
t = do
String
str <- forall a. (a -> Bool) -> [a] -> [a]
takeWhile (Char
'[' forall a. Eq a => a -> a -> Bool
/=) forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall (m :: * -> *).
Monad m =>
(Token -> m String) -> Token -> m String
getLiteralStringExt forall {m :: * -> *}. Monad m => Token -> m String
toStr Token
t
forall (f :: * -> *). Alternative f => Bool -> f ()
guard forall a b. (a -> b) -> a -> b
$ String -> Bool
isVariableName String
str
forall (m :: * -> *) a. Monad m => a -> m a
return String
str
where
toStr :: Token -> m String
toStr (T_Glob Id
_ String
s) = forall (m :: * -> *) a. Monad m => a -> m a
return String
s
toStr Token
_ = forall (m :: * -> *) a. Monad m => a -> m a
return String
"\0"
getReferencedVariables :: Map Id Token -> Token -> [(Token, Token, String)]
getReferencedVariables Map Id Token
parents Token
t =
case Token
t of
T_DollarBraced Id
id Bool
_ Token
l -> let str :: String
str = forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat forall a b. (a -> b) -> a -> b
$ Token -> [String]
oversimplify Token
l in
(Token
t, Token
t, ShowS
getBracedReference String
str) forall a. a -> [a] -> [a]
:
forall a b. (a -> b) -> [a] -> [b]
map (\String
x -> (Token
l, Token
l, String
x)) (
String -> [String]
getIndexReferences String
str
forall a. [a] -> [a] -> [a]
++ String -> [String]
getOffsetReferences (ShowS
getBracedModifier String
str))
TA_Variable Id
id String
name [Token]
_ ->
if Token -> Bool
isArithmeticAssignment Token
t
then []
else [(Token
t, Token
t, String
name)]
T_Assignment Id
id AssignmentMode
mode String
str [Token]
_ Token
word ->
[(Token
t, Token
t, String
str) | AssignmentMode
mode forall a. Eq a => a -> a -> Bool
== AssignmentMode
Append] forall a. [a] -> [a] -> [a]
++ forall {b}. String -> b -> Token -> [(b, b, String)]
specialReferences String
str Token
t Token
word
TC_Unary Id
id ConditionType
_ String
"-v" Token
token -> forall {a}. a -> Token -> [(a, Token, String)]
getIfReference Token
t Token
token
TC_Unary Id
id ConditionType
_ String
"-R" Token
token -> forall {a}. a -> Token -> [(a, Token, String)]
getIfReference Token
t Token
token
TC_Binary Id
id ConditionType
DoubleBracket String
op Token
lhs Token
rhs ->
if String -> Bool
isDereferencingBinaryOp String
op
then forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap (forall {a}. a -> Token -> [(a, Token, String)]
getIfReference Token
t) [Token
lhs, Token
rhs]
else []
T_BatsTest {} -> [
(Token
t, Token
t, String
"lines"),
(Token
t, Token
t, String
"status"),
(Token
t, Token
t, String
"output")
]
T_FdRedirect Id
_ (Char
'{':String
var) Token
op ->
[(Token
t, Token
t, forall a. (a -> Bool) -> [a] -> [a]
takeWhile (forall a. Eq a => a -> a -> Bool
/= Char
'}') String
var) | Token -> Bool
isClosingFileOp Token
op]
Token
x -> Token -> [(Token, Token, String)]
getReferencedVariableCommand Token
x
where
specialReferences :: String -> b -> Token -> [(b, b, String)]
specialReferences String
name b
base Token
word =
if String
name forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [
String
"PS1", String
"PS2", String
"PS3", String
"PS4",
String
"PROMPT_COMMAND"
]
then
forall a b. (a -> b) -> [a] -> [b]
map (\String
x -> (b
base, b
base, String
x)) forall a b. (a -> b) -> a -> b
$
Token -> [String]
getVariablesFromLiteralToken Token
word
else []
literalizer :: Token -> [String]
literalizer Token
t = case Token
t of
T_Glob Id
_ String
s -> forall (m :: * -> *) a. Monad m => a -> m a
return String
s
Token
_ -> []
getIfReference :: a -> Token -> [(a, Token, String)]
getIfReference a
context Token
token = forall a. Maybe a -> [a]
maybeToList forall a b. (a -> b) -> a -> b
$ do
String
str <- Token -> Maybe String
getVariableForTestDashV Token
token
forall (m :: * -> *) a. Monad m => a -> m a
return (a
context, Token
token, ShowS
getBracedReference String
str)
isArithmeticAssignment :: Token -> Bool
isArithmeticAssignment Token
t = case Map Id Token -> Token -> [Token]
getPath Map Id Token
parents Token
t of
Token
this: TA_Assignment Id
_ String
"=" Token
lhs Token
_ :[Token]
_ -> Token
lhs forall a. Eq a => a -> a -> Bool
== Token
t
[Token]
_ -> Bool
False
isDereferencingBinaryOp :: String -> Bool
isDereferencingBinaryOp = (forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [String
"-eq", String
"-ne", String
"-lt", String
"-le", String
"-gt", String
"-ge"])
dataTypeFrom :: (DataSource -> DataType) -> Token -> DataType
dataTypeFrom DataSource -> DataType
defaultType Token
v = (case Token
v of T_Array {} -> DataSource -> DataType
DataArray; Token
_ -> DataSource -> DataType
defaultType) forall a b. (a -> b) -> a -> b
$ [Token] -> DataSource
SourceFrom [Token
v]
isCommand :: Token -> String -> Bool
isCommand Token
token String
str = Token -> (String -> Bool) -> Bool
isCommandMatch Token
token (\String
cmd -> String
cmd forall a. Eq a => a -> a -> Bool
== String
str Bool -> Bool -> Bool
|| (Char
'/' forall a. a -> [a] -> [a]
: String
str) forall a. Eq a => [a] -> [a] -> Bool
`isSuffixOf` String
cmd)
isUnqualifiedCommand :: Token -> String -> Bool
isUnqualifiedCommand Token
token String
str = Token -> (String -> Bool) -> Bool
isCommandMatch Token
token (forall a. Eq a => a -> a -> Bool
== String
str)
isCommandMatch :: Token -> (String -> Bool) -> Bool
isCommandMatch Token
token String -> Bool
matcher = forall b a. b -> (a -> b) -> Maybe a -> b
maybe Bool
False
String -> Bool
matcher (Token -> Maybe String
getCommandName Token
token)
isConfusedGlobRegex :: String -> Bool
isConfusedGlobRegex :: String -> Bool
isConfusedGlobRegex (Char
'*':String
_) = Bool
True
isConfusedGlobRegex [Char
x,Char
'*'] | Char
x forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`notElem` String
"\\." = Bool
True
isConfusedGlobRegex String
_ = Bool
False
getVariablesFromLiteralToken :: Token -> [String]
getVariablesFromLiteralToken Token
token =
String -> [String]
getVariablesFromLiteral (String -> Token -> String
getLiteralStringDef String
" " Token
token)
prop_getVariablesFromLiteral1 :: Bool
prop_getVariablesFromLiteral1 =
String -> [String]
getVariablesFromLiteral String
"$foo${bar//a/b}$BAZ" forall a. Eq a => a -> a -> Bool
== [String
"foo", String
"bar", String
"BAZ"]
getVariablesFromLiteral :: String -> [String]
getVariablesFromLiteral String
string =
forall a b. (a -> b) -> [a] -> [b]
map forall a. [a] -> a
head forall a b. (a -> b) -> a -> b
$ Regex -> String -> [[String]]
matchAllSubgroups Regex
variableRegex String
string
where
variableRegex :: Regex
variableRegex = String -> Regex
mkRegex String
"\\$\\{?([A-Za-z0-9_]+)"
whenShell :: t Shell -> m () -> m ()
whenShell t Shell
l m ()
c = do
Parameters
params <- forall r (m :: * -> *). MonadReader r m => m r
ask
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (Parameters -> Shell
shellType Parameters
params forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` t Shell
l ) m ()
c
filterByAnnotation :: AnalysisSpec -> Parameters -> [TokenComment] -> [TokenComment]
filterByAnnotation AnalysisSpec
asSpec Parameters
params =
forall a. (a -> Bool) -> [a] -> [a]
filter (Bool -> Bool
not forall b c a. (b -> c) -> (a -> b) -> a -> c
. TokenComment -> Bool
shouldIgnore)
where
token :: Token
token = AnalysisSpec -> Token
asScript AnalysisSpec
asSpec
shouldIgnore :: TokenComment -> Bool
shouldIgnore TokenComment
note =
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any (Code -> Token -> Bool
shouldIgnoreFor (TokenComment -> Code
getCode TokenComment
note)) forall a b. (a -> b) -> a -> b
$
Map Id Token -> Token -> [Token]
getPath Map Id Token
parents (Id -> Token
T_Bang forall a b. (a -> b) -> a -> b
$ TokenComment -> Id
tcId TokenComment
note)
shouldIgnoreFor :: Code -> Token -> Bool
shouldIgnoreFor Code
_ T_Include {} = Bool -> Bool
not forall a b. (a -> b) -> a -> b
$ AnalysisSpec -> Bool
asCheckSourced AnalysisSpec
asSpec
shouldIgnoreFor Code
code Token
t = Code -> Token -> Bool
isAnnotationIgnoringCode Code
code Token
t
parents :: Map Id Token
parents = Parameters -> Map Id Token
parentMap Parameters
params
getCode :: TokenComment -> Code
getCode = Comment -> Code
cCode forall b c a. (b -> c) -> (a -> b) -> a -> c
. TokenComment -> Comment
tcComment
shouldIgnoreCode :: Parameters -> Code -> Token -> Bool
shouldIgnoreCode Parameters
params Code
code Token
t =
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any (Code -> Token -> Bool
isAnnotationIgnoringCode Code
code) forall a b. (a -> b) -> a -> b
$
Map Id Token -> Token -> [Token]
getPath (Parameters -> Map Id Token
parentMap Parameters
params) Token
t
isCountingReference :: Token -> Bool
isCountingReference (T_DollarBraced Id
id Bool
_ Token
token) =
case forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat forall a b. (a -> b) -> a -> b
$ Token -> [String]
oversimplify Token
token of
Char
'#':String
_ -> Bool
True
String
_ -> Bool
False
isCountingReference Token
_ = Bool
False
isQuotedAlternativeReference :: Token -> Bool
isQuotedAlternativeReference Token
t =
case Token
t of
T_DollarBraced Id
_ Bool
_ Token
l ->
ShowS
getBracedModifier (forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat forall a b. (a -> b) -> a -> b
$ Token -> [String]
oversimplify Token
l) String -> Regex -> Bool
`matches` Regex
re
Token
_ -> Bool
False
where
re :: Regex
re = String -> Regex
mkRegex String
"(^|\\]):?\\+"
supportsArrays :: Shell -> Bool
supportsArrays Shell
Bash = Bool
True
supportsArrays Shell
Ksh = Bool
True
supportsArrays Shell
_ = Bool
False
isBashLike :: Parameters -> Bool
isBashLike :: Parameters -> Bool
isBashLike Parameters
params =
case Parameters -> Shell
shellType Parameters
params of
Shell
Bash -> Bool
True
Shell
Ksh -> Bool
True
Shell
Dash -> Bool
False
Shell
Sh -> Bool
False
isTrueAssignmentSource :: DataType -> Bool
isTrueAssignmentSource DataType
c =
case DataType
c of
DataString DataSource
SourceChecked -> Bool
False
DataString DataSource
SourceDeclaration -> Bool
False
DataArray DataSource
SourceChecked -> Bool
False
DataArray DataSource
SourceDeclaration -> Bool
False
DataType
_ -> Bool
True
modifiesVariable :: Parameters -> Token -> String -> Bool
modifiesVariable Parameters
params Token
token String
name =
forall (t :: * -> *). Foldable t => t Bool -> Bool
or forall a b. (a -> b) -> a -> b
$ forall a b. (a -> b) -> [a] -> [b]
map StackData -> Bool
check [StackData]
flow
where
flow :: [StackData]
flow = Parameters -> Token -> [StackData]
getVariableFlow Parameters
params Token
token
check :: StackData -> Bool
check StackData
t =
case StackData
t of
Assignment (Token
_, Token
_, String
n, DataType
source) -> DataType -> Bool
isTrueAssignmentSource DataType
source Bool -> Bool -> Bool
&& String
n forall a. Eq a => a -> a -> Bool
== String
name
StackData
_ -> Bool
False
return []
runTests :: IO Bool
runTests = $( [| $(forAllProperties) (quickCheckWithResult (stdArgs { maxSuccess = 1 }) ) |])