mirror of
https://github.com/koalaman/shellcheck
synced 2025-07-12 16:13:19 -07:00
Allow specifying external-sources=true in shellcheckrc (fixes #1818)
This commit is contained in:
parent
64733cc110
commit
4e703e5c61
12 changed files with 145 additions and 31 deletions
|
@ -150,6 +150,7 @@ data Annotation =
|
|||
| SourceOverride String
|
||||
| ShellOverride String
|
||||
| SourcePath String
|
||||
| ExternalSources Bool
|
||||
deriving (Show, Eq)
|
||||
data ConditionType = DoubleBracket | SingleBracket deriving (Show, Eq)
|
||||
|
||||
|
|
|
@ -156,6 +156,11 @@ checkWithIncludesAndSourcePath includes mapper = getErrors
|
|||
siFindSource = mapper
|
||||
}
|
||||
|
||||
checkWithRcIncludesAndSourcePath rc includes mapper = getErrors
|
||||
(mockRcFile rc $ mockedSystemInterface includes) {
|
||||
siFindSource = mapper
|
||||
}
|
||||
|
||||
prop_findsParseIssue = check "echo \"$12\"" == [1037]
|
||||
|
||||
prop_commentDisablesParseIssue1 =
|
||||
|
@ -384,7 +389,7 @@ prop_canEnableOptionalsWithRc = result == [2244]
|
|||
|
||||
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",
|
||||
|
@ -393,7 +398,7 @@ prop_sourcePathRedirectsName = result == [2086]
|
|||
|
||||
prop_sourcePathAddsAnnotation = result == [2086]
|
||||
where
|
||||
f "dir/myscript" ["mypath"] "lib" = return "foo/lib"
|
||||
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",
|
||||
|
@ -402,13 +407,75 @@ prop_sourcePathAddsAnnotation = result == [2086]
|
|||
|
||||
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",
|
||||
csCheckSourced = True
|
||||
}
|
||||
|
||||
prop_rcCanAllowExternalSources = result == [2086]
|
||||
where
|
||||
f "dir/myscript" (Just True) _ "mylib" = return "resolved/mylib"
|
||||
f a b c d = error $ show ("Unexpected", a, b, c, d)
|
||||
result = checkWithRcIncludesAndSourcePath "external-sources=true" [("resolved/mylib", "echo $1")] f emptyCheckSpec {
|
||||
csScript = "#!/bin/bash\nsource mylib",
|
||||
csFilename = "dir/myscript",
|
||||
csCheckSourced = True
|
||||
}
|
||||
|
||||
prop_rcCanDenyExternalSources = result == [2086]
|
||||
where
|
||||
f "dir/myscript" (Just False) _ "mylib" = return "resolved/mylib"
|
||||
f a b c d = error $ show ("Unexpected", a, b, c, d)
|
||||
result = checkWithRcIncludesAndSourcePath "external-sources=false" [("resolved/mylib", "echo $1")] f emptyCheckSpec {
|
||||
csScript = "#!/bin/bash\nsource mylib",
|
||||
csFilename = "dir/myscript",
|
||||
csCheckSourced = True
|
||||
}
|
||||
|
||||
prop_rcCanLeaveExternalSourcesUnspecified = result == [2086]
|
||||
where
|
||||
f "dir/myscript" Nothing _ "mylib" = return "resolved/mylib"
|
||||
f a b c d = error $ show ("Unexpected", a, b, c, d)
|
||||
result = checkWithRcIncludesAndSourcePath "" [("resolved/mylib", "echo $1")] f emptyCheckSpec {
|
||||
csScript = "#!/bin/bash\nsource mylib",
|
||||
csFilename = "dir/myscript",
|
||||
csCheckSourced = True
|
||||
}
|
||||
|
||||
prop_fileCanDisableExternalSources = result == [2006, 2086]
|
||||
where
|
||||
f "dir/myscript" (Just True) _ "withExternal" = return "withExternal"
|
||||
f "dir/myscript" (Just False) _ "withoutExternal" = return "withoutExternal"
|
||||
f a b c d = error $ show ("Unexpected", a, b, c, d)
|
||||
result = checkWithRcIncludesAndSourcePath "external-sources=true" [("withExternal", "echo $1"), ("withoutExternal", "_=`foo`")] f emptyCheckSpec {
|
||||
csScript = "#!/bin/bash\ntrue\nsource withExternal\n# shellcheck external-sources=false\nsource withoutExternal",
|
||||
csFilename = "dir/myscript",
|
||||
csCheckSourced = True
|
||||
}
|
||||
|
||||
prop_fileCannotEnableExternalSources = result == [1144]
|
||||
where
|
||||
f "dir/myscript" Nothing _ "foo" = return "foo"
|
||||
f a b c d = error $ show ("Unexpected", a, b, c, d)
|
||||
result = checkWithRcIncludesAndSourcePath "" [("foo", "true")] f emptyCheckSpec {
|
||||
csScript = "#!/bin/bash\n# shellcheck external-sources=true\nsource foo",
|
||||
csFilename = "dir/myscript",
|
||||
csCheckSourced = True
|
||||
}
|
||||
|
||||
prop_fileCannotEnableExternalSources2 = result == [1144]
|
||||
where
|
||||
f "dir/myscript" (Just False) _ "foo" = return "foo"
|
||||
f a b c d = error $ show ("Unexpected", a, b, c, d)
|
||||
result = checkWithRcIncludesAndSourcePath "external-sources=false" [("foo", "true")] f emptyCheckSpec {
|
||||
csScript = "#!/bin/bash\n# shellcheck external-sources=true\nsource foo",
|
||||
csFilename = "dir/myscript",
|
||||
csCheckSourced = True
|
||||
}
|
||||
|
||||
|
||||
return []
|
||||
runTests = $quickCheckAll
|
||||
|
|
|
@ -48,7 +48,7 @@ outputResults cr sys =
|
|||
fileGroups = groupWith sourceFile comments
|
||||
outputGroup group = do
|
||||
let filename = sourceFile (head group)
|
||||
result <- (siReadFile sys) filename
|
||||
result <- siReadFile sys (Just True) filename
|
||||
let contents = either (const "") id result
|
||||
outputFile filename contents group
|
||||
|
||||
|
|
|
@ -90,7 +90,7 @@ reportResult foundIssues reportedIssues color result sys = do
|
|||
mapM_ output $ M.toList fixmap
|
||||
where
|
||||
output (name, fix) = do
|
||||
file <- (siReadFile sys) name
|
||||
file <- siReadFile sys (Just True) name
|
||||
case file of
|
||||
Right contents -> do
|
||||
putStrLn $ formatDoc color $ makeDiff name contents fix
|
||||
|
|
|
@ -43,7 +43,7 @@ outputAll cr sys = mapM_ f groups
|
|||
f :: [PositionedComment] -> IO ()
|
||||
f group = do
|
||||
let filename = sourceFile (head group)
|
||||
result <- (siReadFile sys) filename
|
||||
result <- siReadFile sys (Just True) filename
|
||||
let contents = either (const "") id result
|
||||
outputResult filename contents group
|
||||
|
||||
|
|
|
@ -117,7 +117,7 @@ collectResult ref cr sys = mapM_ f groups
|
|||
f :: [PositionedComment] -> IO ()
|
||||
f group = do
|
||||
let filename = sourceFile (head group)
|
||||
result <- siReadFile sys filename
|
||||
result <- siReadFile sys (Just True) filename
|
||||
let contents = either (const "") id result
|
||||
let comments' = makeNonVirtual comments contents
|
||||
modifyIORef ref (\x -> comments' ++ x)
|
||||
|
|
|
@ -121,7 +121,7 @@ outputResult options ref result sys = do
|
|||
|
||||
outputForFile color sys comments = do
|
||||
let fileName = sourceFile (head comments)
|
||||
result <- (siReadFile sys) fileName
|
||||
result <- siReadFile sys (Just True) fileName
|
||||
let contents = either (const "") id result
|
||||
let fileLinesList = lines contents
|
||||
let lineCount = length fileLinesList
|
||||
|
|
|
@ -73,14 +73,18 @@ 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:
|
||||
-- What annotations say about including external files (if anything)
|
||||
-- A resolved filename from siFindSource
|
||||
-- Read the file or return an error
|
||||
siReadFile :: Maybe Bool -> String -> m (Either ErrorMessage String),
|
||||
-- | Given:
|
||||
-- the current script,
|
||||
-- what annotations say about including external files (if anything)
|
||||
-- a list of source-path annotations in effect,
|
||||
-- and a sourced file,
|
||||
-- find the sourced file
|
||||
siFindSource :: String -> [String] -> String -> m FilePath,
|
||||
siFindSource :: String -> Maybe Bool -> [String] -> String -> m FilePath,
|
||||
-- | Get the configuration file (name, contents) for a filename
|
||||
siGetConfig :: String -> m (Maybe (FilePath, String))
|
||||
}
|
||||
|
@ -313,11 +317,11 @@ mockedSystemInterface files = SystemInterface {
|
|||
siGetConfig = const $ return Nothing
|
||||
}
|
||||
where
|
||||
rf file = return $
|
||||
rf _ file = return $
|
||||
case find ((== file) . fst) files of
|
||||
Nothing -> Left "File not included in mock."
|
||||
Just (_, contents) -> Right contents
|
||||
fs _ _ file = return file
|
||||
fs _ _ _ file = return file
|
||||
|
||||
mockRcFile rcfile mock = mock {
|
||||
siGetConfig = const . return $ Just (".shellcheckrc", rcfile)
|
||||
|
|
|
@ -987,9 +987,9 @@ prop_readAnnotation7 = isOk readAnnotation "# shellcheck disable=SC1000,SC2000-S
|
|||
readAnnotation = called "shellcheck directive" $ do
|
||||
try readAnnotationPrefix
|
||||
many1 linewhitespace
|
||||
readAnnotationWithoutPrefix
|
||||
readAnnotationWithoutPrefix True
|
||||
|
||||
readAnnotationWithoutPrefix = do
|
||||
readAnnotationWithoutPrefix sandboxed = do
|
||||
values <- many1 readKey
|
||||
optional readAnyComment
|
||||
void linefeed <|> eof <|> do
|
||||
|
@ -1035,6 +1035,21 @@ readAnnotationWithoutPrefix = do
|
|||
"This shell type is unknown. Use e.g. sh or bash."
|
||||
return [ShellOverride shell]
|
||||
|
||||
"external-sources" -> do
|
||||
pos <- getPosition
|
||||
value <- many1 letter
|
||||
case value of
|
||||
"true" ->
|
||||
if sandboxed
|
||||
then do
|
||||
parseNoteAt pos ErrorC 1144 "external-sources can only be enabled in .shellcheckrc, not in individual files."
|
||||
return []
|
||||
else return [ExternalSources True]
|
||||
"false" -> return [ExternalSources False]
|
||||
_ -> do
|
||||
parseNoteAt pos ErrorC 1145 "Unknown external-sources value. Expected true/false."
|
||||
return []
|
||||
|
||||
_ -> do
|
||||
parseNoteAt keyPos WarningC 1107 "This directive is unknown. It will be ignored."
|
||||
anyChar `reluctantlyTill` whitespace
|
||||
|
@ -2176,10 +2191,12 @@ readSource t@(T_Redirecting _ _ (T_SimpleCommand cmdId _ (cmd:file':rest'))) = d
|
|||
if filename == "/dev/null" -- always allow /dev/null
|
||||
then return (Right "", filename)
|
||||
else do
|
||||
allAnnotations <- getCurrentAnnotations True
|
||||
currentScript <- Mr.asks currentFilename
|
||||
paths <- mapMaybe getSourcePath <$> getCurrentAnnotations True
|
||||
resolved <- system $ siFindSource sys currentScript paths filename
|
||||
contents <- system $ siReadFile sys resolved
|
||||
let paths = mapMaybe getSourcePath allAnnotations
|
||||
let externalSources = listToMaybe $ mapMaybe getExternalSources allAnnotations
|
||||
resolved <- system $ siFindSource sys currentScript externalSources paths filename
|
||||
contents <- system $ siReadFile sys externalSources resolved
|
||||
return (contents, resolved)
|
||||
case input of
|
||||
Left err -> do
|
||||
|
@ -2213,6 +2230,11 @@ readSource t@(T_Redirecting _ _ (T_SimpleCommand cmdId _ (cmd:file':rest'))) = d
|
|||
SourcePath x -> Just x
|
||||
_ -> Nothing
|
||||
|
||||
getExternalSources t =
|
||||
case t of
|
||||
ExternalSources b -> Just b
|
||||
_ -> Nothing
|
||||
|
||||
-- If the word has a single expansion as the directory, try stripping it
|
||||
-- This affects `$foo/bar` but not `${foo}-dir/bar` or `/foo/$file`
|
||||
stripDynamicPrefix word =
|
||||
|
@ -3202,7 +3224,7 @@ prop_readConfigKVs4 = isOk readConfigKVs "\n\n\n\n\t \n"
|
|||
prop_readConfigKVs5 = isOk readConfigKVs "# shellcheck accepts annotation-like comments in rc files\ndisable=1234"
|
||||
readConfigKVs = do
|
||||
anySpacingOrComment
|
||||
annotations <- many (readAnnotationWithoutPrefix <* anySpacingOrComment)
|
||||
annotations <- many (readAnnotationWithoutPrefix False <* anySpacingOrComment)
|
||||
eof
|
||||
return $ concat annotations
|
||||
anySpacingOrComment =
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue