Directives after the shebang now apply to the entire script.
Also adds support for the shell= directive.
This commit is contained in:
parent
6af1aeb259
commit
944313c6ba
|
@ -129,7 +129,11 @@ data Token =
|
||||||
| T_Include Id Token Token -- . & source: SimpleCommand T_Script
|
| T_Include Id Token Token -- . & source: SimpleCommand T_Script
|
||||||
deriving (Show)
|
deriving (Show)
|
||||||
|
|
||||||
data Annotation = DisableComment Integer | SourceOverride String deriving (Show, Eq)
|
data Annotation =
|
||||||
|
DisableComment Integer
|
||||||
|
| SourceOverride String
|
||||||
|
| ShellOverride String
|
||||||
|
deriving (Show, Eq)
|
||||||
data ConditionType = DoubleBracket | SingleBracket deriving (Show, Eq)
|
data ConditionType = DoubleBracket | SingleBracket deriving (Show, Eq)
|
||||||
|
|
||||||
-- This is an abomination.
|
-- This is an abomination.
|
||||||
|
|
|
@ -115,14 +115,32 @@ checkList l t = concatMap (\f -> f t) l
|
||||||
|
|
||||||
getCode (TokenComment _ (Comment _ c _)) = c
|
getCode (TokenComment _ (Comment _ c _)) = c
|
||||||
|
|
||||||
prop_determineShell0 = determineShell (T_Script (Id 0) "#!/bin/sh" []) == Sh
|
prop_determineShell0 = determineShell (fromJust $ pScript "#!/bin/sh") == Sh
|
||||||
prop_determineShell1 = determineShell (T_Script (Id 0) "#!/usr/bin/env ksh" []) == Ksh
|
prop_determineShell1 = determineShell (fromJust $ pScript "#!/usr/bin/env ksh") == Ksh
|
||||||
prop_determineShell2 = determineShell (T_Script (Id 0) "" []) == Bash
|
prop_determineShell2 = determineShell (fromJust $ pScript "") == Bash
|
||||||
prop_determineShell3 = determineShell (T_Script (Id 0) "#!/bin/sh -e" []) == Sh
|
prop_determineShell3 = determineShell (fromJust $ pScript "#!/bin/sh -e") == Sh
|
||||||
determineShell (T_Script _ shebang _) = fromMaybe Bash . shellForExecutable $ shellFor shebang
|
prop_determineShell4 = determineShell (fromJust $ pScript
|
||||||
where shellFor s | "/env " `isInfixOf` s = head (drop 1 (words s)++[""])
|
"#!/bin/ksh\n#shellcheck shell=sh\nfoo") == Sh
|
||||||
shellFor s | ' ' `elem` s = shellFor $ takeWhile (/= ' ') s
|
prop_determineShell5 = determineShell (fromJust $ pScript
|
||||||
shellFor s = reverse . takeWhile (/= '/') . reverse $ s
|
"#shellcheck shell=sh\nfoo") == Sh
|
||||||
|
determineShell t = fromMaybe Bash $ do
|
||||||
|
shellString <- foldl mplus Nothing $ getCandidates t
|
||||||
|
shellForExecutable shellString
|
||||||
|
where
|
||||||
|
forAnnotation t =
|
||||||
|
case t of
|
||||||
|
(ShellOverride s) -> return s
|
||||||
|
_ -> fail ""
|
||||||
|
getCandidates :: Token -> [Maybe String]
|
||||||
|
getCandidates t@(T_Script {}) = [Just $ fromShebang t]
|
||||||
|
getCandidates (T_Annotation _ annotations s) =
|
||||||
|
map forAnnotation annotations ++
|
||||||
|
[Just $ fromShebang s]
|
||||||
|
fromShebang (T_Script _ s t) = shellFor s
|
||||||
|
|
||||||
|
shellFor s | "/env " `isInfixOf` s = head (drop 1 (words s)++[""])
|
||||||
|
shellFor s | ' ' `elem` s = shellFor $ takeWhile (/= ' ') s
|
||||||
|
shellFor s = reverse . takeWhile (/= '/') . reverse $ s
|
||||||
|
|
||||||
-- Checks that are run on each node in the AST
|
-- Checks that are run on each node in the AST
|
||||||
runNodeAnalysis f p t = execWriter (doAnalysis (f p) t)
|
runNodeAnalysis f p t = execWriter (doAnalysis (f p) t)
|
||||||
|
@ -310,14 +328,16 @@ defaultSpec root = AnalysisSpec {
|
||||||
checkNode f = producesComments (runNodeAnalysis f)
|
checkNode f = producesComments (runNodeAnalysis f)
|
||||||
producesComments :: (Parameters -> Token -> [TokenComment]) -> String -> Maybe Bool
|
producesComments :: (Parameters -> Token -> [TokenComment]) -> String -> Maybe Bool
|
||||||
producesComments f s = do
|
producesComments f s = do
|
||||||
root <- prRoot pResult
|
root <- pScript s
|
||||||
return . not . null $ runList (defaultSpec root) [f]
|
return . not . null $ runList (defaultSpec root) [f]
|
||||||
where
|
|
||||||
|
pScript s =
|
||||||
|
let
|
||||||
pSpec = ParseSpec {
|
pSpec = ParseSpec {
|
||||||
psFilename = "script",
|
psFilename = "script",
|
||||||
psScript = s
|
psScript = s
|
||||||
}
|
}
|
||||||
pResult = runIdentity $ parseScript (mockedSystemInterface []) pSpec
|
in prRoot . runIdentity $ parseScript (mockedSystemInterface []) pSpec
|
||||||
|
|
||||||
-- Copied from https://wiki.haskell.org/Edit_distance
|
-- Copied from https://wiki.haskell.org/Edit_distance
|
||||||
dist :: Eq a => [a] -> [a] -> Int
|
dist :: Eq a => [a] -> [a] -> Int
|
||||||
|
@ -554,12 +574,14 @@ indexOfSublists sub = f 0
|
||||||
|
|
||||||
prop_checkShebangParameters1 = verifyTree checkShebangParameters "#!/usr/bin/env bash -x\necho cow"
|
prop_checkShebangParameters1 = verifyTree checkShebangParameters "#!/usr/bin/env bash -x\necho cow"
|
||||||
prop_checkShebangParameters2 = verifyNotTree checkShebangParameters "#! /bin/sh -l "
|
prop_checkShebangParameters2 = verifyNotTree checkShebangParameters "#! /bin/sh -l "
|
||||||
|
checkShebangParameters p (T_Annotation _ _ t) = checkShebangParameters p t
|
||||||
checkShebangParameters _ (T_Script id sb _) =
|
checkShebangParameters _ (T_Script id sb _) =
|
||||||
[makeComment ErrorC id 2096 "On most OS, shebangs can only specify a single parameter." | length (words sb) > 2]
|
[makeComment ErrorC id 2096 "On most OS, shebangs can only specify a single parameter." | length (words sb) > 2]
|
||||||
|
|
||||||
prop_checkShebang1 = verifyNotTree checkShebang "#!/usr/bin/env bash -x\necho cow"
|
prop_checkShebang1 = verifyNotTree checkShebang "#!/usr/bin/env bash -x\necho cow"
|
||||||
prop_checkShebang2 = verifyNotTree checkShebang "#! /bin/sh -l "
|
prop_checkShebang2 = verifyNotTree checkShebang "#! /bin/sh -l "
|
||||||
prop_checkShebang3 = verifyTree checkShebang "ls -l"
|
prop_checkShebang3 = verifyTree checkShebang "ls -l"
|
||||||
|
checkShebang params (T_Annotation _ _ t) = checkShebang params t
|
||||||
checkShebang params (T_Script id sb _) =
|
checkShebang params (T_Script id sb _) =
|
||||||
[makeComment ErrorC id 2148 "Tips depend on target shell and yours is unknown. Add a shebang."
|
[makeComment ErrorC id 2148 "Tips depend on target shell and yours is unknown. Add a shebang."
|
||||||
| not (shellTypeSpecified params) && sb == "" ]
|
| not (shellTypeSpecified params) && sb == "" ]
|
||||||
|
|
|
@ -772,7 +772,7 @@ prop_readAnnotation3 = isOk readAnnotation "# shellcheck disable=SC1234 source=/
|
||||||
readAnnotation = called "shellcheck annotation" $ do
|
readAnnotation = called "shellcheck annotation" $ do
|
||||||
try readAnnotationPrefix
|
try readAnnotationPrefix
|
||||||
many1 linewhitespace
|
many1 linewhitespace
|
||||||
values <- many1 (readDisable <|> readSourceOverride)
|
values <- many1 (readDisable <|> readSourceOverride <|> readShellOverride)
|
||||||
linefeed
|
linefeed
|
||||||
many linewhitespace
|
many linewhitespace
|
||||||
return $ concat values
|
return $ concat values
|
||||||
|
@ -789,6 +789,14 @@ readAnnotation = called "shellcheck annotation" $ do
|
||||||
filename <- many1 $ noneOf " \n"
|
filename <- many1 $ noneOf " \n"
|
||||||
return [SourceOverride filename]
|
return [SourceOverride filename]
|
||||||
|
|
||||||
|
readShellOverride = forKey "shell" $ do
|
||||||
|
pos <- getPosition
|
||||||
|
shell <- many1 $ noneOf " \n"
|
||||||
|
when (isNothing $ shellForExecutable shell) $
|
||||||
|
parseNoteAt pos ErrorC 1103
|
||||||
|
"This shell type is unknown. Use e.g. sh or bash."
|
||||||
|
return [ShellOverride shell]
|
||||||
|
|
||||||
forKey s p = do
|
forKey s p = do
|
||||||
try $ string s
|
try $ string s
|
||||||
char '='
|
char '='
|
||||||
|
@ -2334,6 +2342,7 @@ ifParse p t f =
|
||||||
|
|
||||||
prop_readShebang1 = isOk readShebang "#!/bin/sh\n"
|
prop_readShebang1 = isOk readShebang "#!/bin/sh\n"
|
||||||
prop_readShebang2 = isWarning readShebang "!# /bin/sh\n"
|
prop_readShebang2 = isWarning readShebang "!# /bin/sh\n"
|
||||||
|
prop_readShebang3 = isNotOk readShebang "#shellcheck shell=/bin/sh\n"
|
||||||
readShebang = do
|
readShebang = do
|
||||||
try readCorrect <|> try readSwapped
|
try readCorrect <|> try readSwapped
|
||||||
str <- many $ noneOf "\r\n"
|
str <- many $ noneOf "\r\n"
|
||||||
|
@ -2378,9 +2387,11 @@ readScript = do
|
||||||
verifyShell pos (getShell sb)
|
verifyShell pos (getShell sb)
|
||||||
if isValidShell (getShell sb) /= Just False
|
if isValidShell (getShell sb) /= Just False
|
||||||
then do
|
then do
|
||||||
commands <- readCompoundListOrEmpty
|
annotationId <- getNextId
|
||||||
|
annotations <- readAnnotations
|
||||||
|
commands <- withAnnotations annotations readCompoundListOrEmpty
|
||||||
verifyEof
|
verifyEof
|
||||||
return $ T_Script id sb commands
|
return $ T_Annotation annotationId annotations $ T_Script id sb commands
|
||||||
else do
|
else do
|
||||||
many anyChar
|
many anyChar
|
||||||
return $ T_Script id sb []
|
return $ T_Script id sb []
|
||||||
|
|
Loading…
Reference in New Issue