Adds a #shellcheck source=file directive to override source statements.
This commit is contained in:
parent
ccb6bf1ed5
commit
a01862bc12
|
@ -128,7 +128,7 @@ 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 deriving (Show, Eq)
|
data Annotation = DisableComment Integer | SourceOverride 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.
|
||||||
|
|
|
@ -86,11 +86,13 @@ getErrors sys spec =
|
||||||
where
|
where
|
||||||
getCode (PositionedComment _ (Comment _ code _)) = code
|
getCode (PositionedComment _ (Comment _ code _)) = code
|
||||||
|
|
||||||
check str =
|
check = checkWithIncludes []
|
||||||
|
|
||||||
|
checkWithIncludes includes src =
|
||||||
getErrors
|
getErrors
|
||||||
(mockedSystemInterface [])
|
(mockedSystemInterface includes)
|
||||||
emptyCheckSpec {
|
emptyCheckSpec {
|
||||||
csScript = str,
|
csScript = src,
|
||||||
csExcludedWarnings = [2148]
|
csExcludedWarnings = [2148]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -124,45 +126,34 @@ prop_optionDisablesIssue2 =
|
||||||
csExcludedWarnings = [2148, 1037]
|
csExcludedWarnings = [2148, 1037]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
prop_canParseDevNull =
|
||||||
|
[] == check "source /dev/null"
|
||||||
|
|
||||||
prop_failsWhenNotSourcing =
|
prop_failsWhenNotSourcing =
|
||||||
[1091, 2154] == getErrors
|
[1091, 2154] == check "source lol; echo \"$bar\""
|
||||||
(mockedSystemInterface [])
|
|
||||||
emptyCheckSpec {
|
|
||||||
csScript = "source lob; echo \"$bar\"",
|
|
||||||
csExcludedWarnings = [2148]
|
|
||||||
}
|
|
||||||
|
|
||||||
prop_worksWhenSourcing =
|
prop_worksWhenSourcing =
|
||||||
null $ getErrors
|
null $ checkWithIncludes [("lib", "bar=1")] "source lib; echo \"$bar\""
|
||||||
(mockedSystemInterface [("lib", "bar=1")])
|
|
||||||
emptyCheckSpec {
|
|
||||||
csScript = "source lib; echo \"$bar\"",
|
|
||||||
csExcludedWarnings = [2148]
|
|
||||||
}
|
|
||||||
|
|
||||||
prop_worksWhenDotting =
|
prop_worksWhenDotting =
|
||||||
null $ getErrors
|
null $ checkWithIncludes [("lib", "bar=1")] ". lib; echo \"$bar\""
|
||||||
(mockedSystemInterface [("lib", "bar=1")])
|
|
||||||
emptyCheckSpec {
|
|
||||||
csScript = ". lib; echo \"$bar\"",
|
|
||||||
csExcludedWarnings = [2148]
|
|
||||||
}
|
|
||||||
|
|
||||||
prop_noInfiniteSourcing =
|
prop_noInfiniteSourcing =
|
||||||
[] == getErrors
|
[] == checkWithIncludes [("lib", "source lib")] "source lib"
|
||||||
(mockedSystemInterface [("lib", "source lib")])
|
|
||||||
emptyCheckSpec {
|
|
||||||
csScript = "source lib",
|
|
||||||
csExcludedWarnings = [2148]
|
|
||||||
}
|
|
||||||
|
|
||||||
prop_canSourceBadSyntax =
|
prop_canSourceBadSyntax =
|
||||||
[1094, 2086] == getErrors
|
[1094, 2086] == checkWithIncludes [("lib", "for f; do")] "source lib; echo $1"
|
||||||
(mockedSystemInterface [("lib", "for f; do")])
|
|
||||||
emptyCheckSpec {
|
prop_cantSourceDynamic =
|
||||||
csScript = "source lib; echo $1",
|
[1090] == checkWithIncludes [("lib", "")] ". \"$1\""
|
||||||
csExcludedWarnings = [2148]
|
|
||||||
}
|
prop_canSourceDynamicWhenRedirected =
|
||||||
|
null $ checkWithIncludes [("lib", "")] "#shellcheck source=lib\n. \"$1\""
|
||||||
|
|
||||||
|
prop_sourceDirectiveDoesntFollowFile =
|
||||||
|
null $ checkWithIncludes
|
||||||
|
[("foo", "source bar"), ("bar", "baz=3")]
|
||||||
|
"#shellcheck source=foo\n. \"$1\"; echo \"$baz\""
|
||||||
|
|
||||||
return []
|
return []
|
||||||
runTests = $quickCheckAll
|
runTests = $quickCheckAll
|
||||||
|
|
|
@ -32,6 +32,7 @@ import Data.Char
|
||||||
import Data.Functor
|
import Data.Functor
|
||||||
import Data.List (isPrefixOf, isInfixOf, isSuffixOf, partition, sortBy, intercalate, nub)
|
import Data.List (isPrefixOf, isInfixOf, isSuffixOf, partition, sortBy, intercalate, nub)
|
||||||
import Data.Maybe
|
import Data.Maybe
|
||||||
|
import Data.Monoid
|
||||||
import Debug.Trace
|
import Debug.Trace
|
||||||
import GHC.Exts (sortWith)
|
import GHC.Exts (sortWith)
|
||||||
import Prelude hiding (readList)
|
import Prelude hiding (readList)
|
||||||
|
@ -191,6 +192,7 @@ shouldIgnoreCode code = do
|
||||||
disabling (ContextSource _) = True -- Don't add messages for sourced files
|
disabling (ContextSource _) = True -- Don't add messages for sourced files
|
||||||
disabling _ = False
|
disabling _ = False
|
||||||
disabling' (DisableComment n) = code == n
|
disabling' (DisableComment n) = code == n
|
||||||
|
disabling' _ = False
|
||||||
|
|
||||||
shouldFollow file = do
|
shouldFollow file = do
|
||||||
context <- getCurrentContexts
|
context <- getCurrentContexts
|
||||||
|
@ -209,6 +211,18 @@ shouldFollow file = do
|
||||||
isThisFile (ContextSource name) | name == file = True
|
isThisFile (ContextSource name) | name == file = True
|
||||||
isThisFile _= False
|
isThisFile _= False
|
||||||
|
|
||||||
|
getSourceOverride = do
|
||||||
|
context <- getCurrentContexts
|
||||||
|
return . msum . map findFile $ takeWhile isSameFile context
|
||||||
|
where
|
||||||
|
isSameFile (ContextSource _) = False
|
||||||
|
isSameFile _ = True
|
||||||
|
|
||||||
|
findFile (ContextAnnotation list) = msum $ map getFile list
|
||||||
|
findFile _ = Nothing
|
||||||
|
getFile (SourceOverride str) = Just str
|
||||||
|
getFile _ = Nothing
|
||||||
|
|
||||||
-- Store potential parse problems outside of parsec
|
-- Store potential parse problems outside of parsec
|
||||||
|
|
||||||
data SystemState = SystemState {
|
data SystemState = SystemState {
|
||||||
|
@ -722,10 +736,11 @@ readAnnotationPrefix = do
|
||||||
|
|
||||||
prop_readAnnotation1 = isOk readAnnotation "# shellcheck disable=1234,5678\n"
|
prop_readAnnotation1 = isOk readAnnotation "# shellcheck disable=1234,5678\n"
|
||||||
prop_readAnnotation2 = isOk readAnnotation "# shellcheck disable=SC1234 disable=SC5678\n"
|
prop_readAnnotation2 = isOk readAnnotation "# shellcheck disable=SC1234 disable=SC5678\n"
|
||||||
|
prop_readAnnotation3 = isOk readAnnotation "# shellcheck disable=SC1234 source=/dev/null disable=SC5678\n"
|
||||||
readAnnotation = called "shellcheck annotation" $ do
|
readAnnotation = called "shellcheck annotation" $ do
|
||||||
try readAnnotationPrefix
|
try readAnnotationPrefix
|
||||||
many1 linewhitespace
|
many1 linewhitespace
|
||||||
values <- many1 readDisable
|
values <- many1 (readDisable <|> readSourceOverride)
|
||||||
linefeed
|
linefeed
|
||||||
many linewhitespace
|
many linewhitespace
|
||||||
return $ concat values
|
return $ concat values
|
||||||
|
@ -737,6 +752,11 @@ readAnnotation = called "shellcheck annotation" $ do
|
||||||
optional $ string "SC"
|
optional $ string "SC"
|
||||||
int <- many1 digit
|
int <- many1 digit
|
||||||
return $ DisableComment (read int)
|
return $ DisableComment (read int)
|
||||||
|
|
||||||
|
readSourceOverride = forKey "source" $ do
|
||||||
|
filename <- many1 $ noneOf " \n"
|
||||||
|
return [SourceOverride filename]
|
||||||
|
|
||||||
forKey s p = do
|
forKey s p = do
|
||||||
try $ string s
|
try $ string s
|
||||||
char '='
|
char '='
|
||||||
|
@ -1480,11 +1500,12 @@ readSimpleCommand = called "simple command" $ do
|
||||||
|
|
||||||
readSource :: Monad m => SourcePos -> Token -> SCParser m Token
|
readSource :: Monad m => SourcePos -> Token -> SCParser m Token
|
||||||
readSource pos t@(T_Redirecting _ _ (T_SimpleCommand _ _ (cmd:file:_))) = do
|
readSource pos t@(T_Redirecting _ _ (T_SimpleCommand _ _ (cmd:file:_))) = do
|
||||||
let literalFile = getLiteralString file
|
override <- getSourceOverride
|
||||||
|
let literalFile = override `mplus` getLiteralString file
|
||||||
case literalFile of
|
case literalFile of
|
||||||
Nothing -> do
|
Nothing -> do
|
||||||
parseNoteAt pos InfoC 1090
|
parseNoteAt pos WarningC 1090
|
||||||
"This source will be skipped since it's not constant."
|
"Can't follow non-constant source. Use a directive to specify location."
|
||||||
return t
|
return t
|
||||||
Just filename -> do
|
Just filename -> do
|
||||||
proceed <- shouldFollow filename
|
proceed <- shouldFollow filename
|
||||||
|
@ -1495,7 +1516,10 @@ readSource pos t@(T_Redirecting _ _ (T_SimpleCommand _ _ (cmd:file:_))) = do
|
||||||
return t
|
return t
|
||||||
else do
|
else do
|
||||||
sys <- Mr.ask
|
sys <- Mr.ask
|
||||||
input <- system $ siReadFile sys filename
|
input <-
|
||||||
|
if filename == "/dev/null" -- always allow /dev/null
|
||||||
|
then return (Right "")
|
||||||
|
else system $ siReadFile sys filename
|
||||||
case input of
|
case input of
|
||||||
Left err -> do
|
Left err -> do
|
||||||
parseNoteAt pos InfoC 1091 $
|
parseNoteAt pos InfoC 1091 $
|
||||||
|
|
|
@ -58,7 +58,8 @@ not warn at all, as `ksh` supports decimals in arithmetic contexts.
|
||||||
|
|
||||||
: Follow 'source' statements even when the file is not specified as input.
|
: Follow 'source' statements even when the file is not specified as input.
|
||||||
By default, `shellcheck` will only follow files specified on the command
|
By default, `shellcheck` will only follow files specified on the command
|
||||||
line. This option allows following any file the script may `source`.
|
line (plus `/dev/null`). This option allows following any file the script
|
||||||
|
may `source`.
|
||||||
|
|
||||||
# FORMATS
|
# FORMATS
|
||||||
|
|
||||||
|
@ -125,7 +126,12 @@ For example, to suppress SC2035 about using `./*.jpg`:
|
||||||
# shellcheck disable=SC2035
|
# shellcheck disable=SC2035
|
||||||
echo "Files: " *.jpg
|
echo "Files: " *.jpg
|
||||||
|
|
||||||
Here a shell brace group is used to suppress on multiple lines:
|
To tell ShellCheck where to look for an otherwise dynamically determined file:
|
||||||
|
|
||||||
|
# shellcheck source=./lib.sh
|
||||||
|
source "$(find_install_dir)/lib.sh"
|
||||||
|
|
||||||
|
Here a shell brace group is used to suppress a warning on multiple lines:
|
||||||
|
|
||||||
# shellcheck disable=SC2016
|
# shellcheck disable=SC2016
|
||||||
{
|
{
|
||||||
|
@ -140,6 +146,11 @@ Valid keys are:
|
||||||
The command can be a simple command like `echo foo`, or a compound command
|
The command can be a simple command like `echo foo`, or a compound command
|
||||||
like a function definition, subshell block or loop.
|
like a function definition, subshell block or loop.
|
||||||
|
|
||||||
|
**source**
|
||||||
|
: Overrides the filename included by a `source`/`.` statement. This can be
|
||||||
|
used to tell shellcheck where to look for a file whose name is determined
|
||||||
|
at runtime, or to skip a source by telling it to use `/dev/null`.
|
||||||
|
|
||||||
|
|
||||||
# AUTHOR
|
# AUTHOR
|
||||||
ShellCheck is written and maintained by Vidar Holen.
|
ShellCheck is written and maintained by Vidar Holen.
|
||||||
|
|
Loading…
Reference in New Issue