mirror of
https://github.com/koalaman/shellcheck
synced 2025-07-08 05:51:09 -07:00
Add support for source-path directives (fixes #1577)
This commit is contained in:
parent
aa4b24e458
commit
d9e419d60f
7 changed files with 67 additions and 17 deletions
|
@ -146,6 +146,7 @@ data Annotation =
|
|||
DisableComment Integer
|
||||
| SourceOverride String
|
||||
| ShellOverride String
|
||||
| SourcePath String
|
||||
deriving (Show, Eq)
|
||||
data ConditionType = DoubleBracket | SingleBracket deriving (Show, Eq)
|
||||
|
||||
|
|
|
@ -215,6 +215,7 @@ prop_worksWhenSourcingWithDashDash =
|
|||
prop_worksWhenDotting =
|
||||
null $ checkWithIncludes [("lib", "bar=1")] ". lib; echo \"$bar\""
|
||||
|
||||
-- FIXME: This should really be giving [1093], "recursively sourced"
|
||||
prop_noInfiniteSourcing =
|
||||
[] == checkWithIncludes [("lib", "source lib")] "source lib"
|
||||
|
||||
|
@ -236,6 +237,12 @@ prop_recursiveAnalysis =
|
|||
prop_recursiveParsing =
|
||||
[1037] == checkRecursive [("lib", "echo \"$10\"")] "source lib"
|
||||
|
||||
prop_nonRecursiveAnalysis =
|
||||
[] == checkWithIncludes [("lib", "echo $1")] "source lib"
|
||||
|
||||
prop_nonRecursiveParsing =
|
||||
[] == checkWithIncludes [("lib", "echo \"$10\"")] "source lib"
|
||||
|
||||
prop_sourceDirectiveDoesntFollowFile =
|
||||
null $ checkWithIncludes
|
||||
[("foo", "source bar"), ("bar", "baz=3")]
|
||||
|
@ -342,17 +349,26 @@ prop_brokenRcGetsWarning = result == [1134, 2086]
|
|||
|
||||
prop_sourcePathRedirectsName = result == [2086]
|
||||
where
|
||||
f "dir/myscript" "lib" = return "foo/lib"
|
||||
f "dir/myscript" _ "lib" = return "foo/lib"
|
||||
result = checkWithIncludesAndSourcePath [("foo/lib", "echo $1")] f emptyCheckSpec {
|
||||
csScript = "#!/bin/bash\nsource lib",
|
||||
csFilename = "dir/myscript",
|
||||
csCheckSourced = True
|
||||
}
|
||||
|
||||
prop_sourcePathAddsAnnotation = result == [2086]
|
||||
where
|
||||
f "dir/myscript" ["mypath"] "lib" = return "foo/lib"
|
||||
result = checkWithIncludesAndSourcePath [("foo/lib", "echo $1")] f emptyCheckSpec {
|
||||
csScript = "#!/bin/bash\n# shellcheck source-path=mypath\nsource lib",
|
||||
csFilename = "dir/myscript",
|
||||
csCheckSourced = True
|
||||
}
|
||||
|
||||
prop_sourcePathRedirectsDirective = result == [2086]
|
||||
where
|
||||
f "dir/myscript" "lib" = return "foo/lib"
|
||||
f _ _ = return "/dev/null"
|
||||
f "dir/myscript" _ "lib" = return "foo/lib"
|
||||
f _ _ _ = return "/dev/null"
|
||||
result = checkWithIncludesAndSourcePath [("foo/lib", "echo $1")] f emptyCheckSpec {
|
||||
csScript = "#!/bin/bash\n# shellcheck source=lib\nsource kittens",
|
||||
csFilename = "dir/myscript",
|
||||
|
|
|
@ -73,8 +73,12 @@ import qualified Data.Map as Map
|
|||
data SystemInterface m = SystemInterface {
|
||||
-- Read a file by filename, or return an error
|
||||
siReadFile :: String -> m (Either ErrorMessage String),
|
||||
-- Given the current script and a sourced file, find the sourced file
|
||||
siFindSource :: String -> String -> m FilePath,
|
||||
-- Given:
|
||||
-- the current script,
|
||||
-- a list of source-path annotations in effect,
|
||||
-- and a sourced file,
|
||||
-- find the sourced file
|
||||
siFindSource :: String -> [String] -> String -> m FilePath,
|
||||
-- Get the configuration file (name, contents) for a filename
|
||||
siGetConfig :: String -> m (Maybe (FilePath, String))
|
||||
}
|
||||
|
@ -297,7 +301,7 @@ mockedSystemInterface files = SystemInterface {
|
|||
case filter ((== file) . fst) files of
|
||||
[] -> return $ Left "File not included in mock."
|
||||
[(_, contents)] -> return $ Right contents
|
||||
fs _ file = return file
|
||||
fs _ _ file = return file
|
||||
|
||||
mockRcFile rcfile mock = mock {
|
||||
siGetConfig = const . return $ Just (".shellcheckrc", rcfile)
|
||||
|
|
|
@ -264,6 +264,15 @@ shouldIgnoreCode code = do
|
|||
disabling' (DisableComment n) = code == n
|
||||
disabling' _ = False
|
||||
|
||||
getCurrentAnnotations includeSource =
|
||||
concatMap get . takeWhile (not . isBoundary) <$> getCurrentContexts
|
||||
where
|
||||
get (ContextAnnotation list) = list
|
||||
get _ = []
|
||||
isBoundary (ContextSource _) = not includeSource
|
||||
isBoundary _ = False
|
||||
|
||||
|
||||
shouldFollow file = do
|
||||
context <- getCurrentContexts
|
||||
if any isThisFile context
|
||||
|
@ -966,7 +975,7 @@ readAnnotationWithoutPrefix = do
|
|||
where
|
||||
readKey = do
|
||||
keyPos <- getPosition
|
||||
key <- many1 letter
|
||||
key <- many1 (letter <|> char '-')
|
||||
char '=' <|> fail "Expected '=' after directive key"
|
||||
annotations <- case key of
|
||||
"disable" -> readCode `sepBy` char ','
|
||||
|
@ -980,6 +989,10 @@ readAnnotationWithoutPrefix = do
|
|||
filename <- many1 $ noneOf " \n"
|
||||
return [SourceOverride filename]
|
||||
|
||||
"source-path" -> do
|
||||
dirname <- many1 $ noneOf " \n"
|
||||
return [SourcePath dirname]
|
||||
|
||||
"shell" -> do
|
||||
pos <- getPosition
|
||||
shell <- many1 $ noneOf " \n"
|
||||
|
@ -2079,6 +2092,7 @@ readSource t@(T_Redirecting _ _ (T_SimpleCommand cmdId _ (cmd:file':rest'))) = d
|
|||
proceed <- shouldFollow filename
|
||||
if not proceed
|
||||
then do
|
||||
-- FIXME: This actually gets squashed without -a
|
||||
parseNoteAtId (getId file) InfoC 1093
|
||||
"This file appears to be recursively sourced. Ignoring."
|
||||
return t
|
||||
|
@ -2089,7 +2103,8 @@ readSource t@(T_Redirecting _ _ (T_SimpleCommand cmdId _ (cmd:file':rest'))) = d
|
|||
then return (Right "")
|
||||
else do
|
||||
currentScript <- Mr.asks currentFilename
|
||||
filename' <- system $ siFindSource sys currentScript filename
|
||||
paths <- mapMaybe getSourcePath <$> getCurrentAnnotations True
|
||||
filename' <- system $ siFindSource sys currentScript paths filename
|
||||
system $ siReadFile sys filename'
|
||||
case input of
|
||||
Left err -> do
|
||||
|
@ -2118,6 +2133,11 @@ readSource t@(T_Redirecting _ _ (T_SimpleCommand cmdId _ (cmd:file':rest'))) = d
|
|||
x -> file
|
||||
getFile file _ = file
|
||||
|
||||
getSourcePath t =
|
||||
case t of
|
||||
SourcePath x -> Just x
|
||||
_ -> Nothing
|
||||
|
||||
subRead name script =
|
||||
withContext (ContextSource name) $
|
||||
inSeparateContext $
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue