diff --git a/src/ShellCheck/Analytics.hs b/src/ShellCheck/Analytics.hs index 6230f5b..78b5366 100644 --- a/src/ShellCheck/Analytics.hs +++ b/src/ShellCheck/Analytics.hs @@ -484,7 +484,7 @@ checkShebang params (T_Annotation _ list t) = checkShebang params (T_Script id sb _) = execWriter $ do unless (shellTypeSpecified params) $ do when (sb == "") $ - err id 2148 "Tips depend on target shell and yours is unknown. Add a shebang." + err id 2148 "Tips depend on target shell and yours is unknown. Add a shebang or a .bash, .ksh, .dash extension to the filename." when (executableFromShebang sb == "ash") $ warn id 2187 "Ash scripts will be checked as Dash. Add '# shellcheck shell=dash' to silence." unless (null sb) $ do diff --git a/src/ShellCheck/AnalyzerLib.hs b/src/ShellCheck/AnalyzerLib.hs index 37a96ad..b2b4edd 100644 --- a/src/ShellCheck/AnalyzerLib.hs +++ b/src/ShellCheck/AnalyzerLib.hs @@ -175,7 +175,7 @@ makeCommentWithFix severity id code str fix = makeParameters spec = let params = Parameters { rootNode = root, - shellType = fromMaybe (determineShell root) $ asShellType spec, + shellType = fromMaybe (determineShell (asFallbackShell spec) root) $ asShellType spec, hasSetE = containsSetE root, hasLastpipe = case shellType params of @@ -184,7 +184,7 @@ makeParameters spec = Sh -> False Ksh -> True, - shellTypeSpecified = isJust $ asShellType spec, + shellTypeSpecified = isJust (asShellType spec) || isJust (asFallbackShell spec), parentMap = getParentTree root, variableFlow = getVariableFlow params root, tokenPositions = asTokenPositions spec @@ -227,11 +227,13 @@ prop_determineShell4 = determineShellTest "#!/bin/ksh\n#shellcheck shell=sh\nfoo prop_determineShell5 = determineShellTest "#shellcheck shell=sh\nfoo" == Sh prop_determineShell6 = determineShellTest "#! /bin/sh" == Sh prop_determineShell7 = determineShellTest "#! /bin/ash" == Dash +prop_determineShell8 = determineShellTest' (Just Ksh) "#!/bin/sh" == Sh -determineShellTest = determineShell . fromJust . prRoot . pScript -determineShell t = fromMaybe Bash $ do +determineShellTest = determineShellTest' Nothing +determineShellTest' fallbackShell = determineShell fallbackShell . fromJust . prRoot . pScript +determineShell fallbackShell t = fromMaybe Bash $ do shellString <- foldl mplus Nothing $ getCandidates t - shellForExecutable shellString + shellForExecutable shellString `mplus` fallbackShell where forAnnotation t = case t of diff --git a/src/ShellCheck/Checker.hs b/src/ShellCheck/Checker.hs index 10074e3..ac51ddd 100644 --- a/src/ShellCheck/Checker.hs +++ b/src/ShellCheck/Checker.hs @@ -48,6 +48,16 @@ tokenToPosition startMap t = fromMaybe fail $ do where fail = error "Internal shellcheck error: id doesn't exist. Please report!" +shellFromFilename filename = foldl mplus Nothing candidates + where + shellExtensions = [(".ksh", Ksh) + ,(".bash", Bash) + ,(".dash", Dash)] + -- The `.sh` is too generic to determine the shell: + -- We fallback to Bash in this case and emit SC2148 if there is no shebang + candidates = + map (\(ext,sh) -> if ext `isSuffixOf` filename then Just sh else Nothing) shellExtensions + checkScript :: Monad m => SystemInterface m -> CheckSpec -> m CheckResult checkScript sys spec = do results <- checkScript (csScript spec) @@ -69,6 +79,7 @@ checkScript sys spec = do as { asScript = root, asShellType = csShellTypeOverride spec, + asFallbackShell = shellFromFilename $ csFilename spec, asCheckSourced = csCheckSourced spec, asExecutionMode = Executed, asTokenPositions = tokenPositions diff --git a/src/ShellCheck/Interface.hs b/src/ShellCheck/Interface.hs index dd53f6f..285042f 100644 --- a/src/ShellCheck/Interface.hs +++ b/src/ShellCheck/Interface.hs @@ -25,7 +25,7 @@ module ShellCheck.Interface , CheckResult(crFilename, crComments) , ParseSpec(psFilename, psScript, psCheckSourced, psShellTypeOverride) , ParseResult(prComments, prTokenPositions, prRoot) - , AnalysisSpec(asScript, asShellType, asExecutionMode, asCheckSourced, asTokenPositions) + , AnalysisSpec(asScript, asShellType, asFallbackShell, asExecutionMode, asCheckSourced, asTokenPositions) , AnalysisResult(arComments) , FormatterOptions(foColorOption, foWikiLinkCount) , Shell(Ksh, Sh, Bash, Dash) @@ -138,6 +138,7 @@ newParseResult = ParseResult { data AnalysisSpec = AnalysisSpec { asScript :: Token, asShellType :: Maybe Shell, + asFallbackShell :: Maybe Shell, asExecutionMode :: ExecutionMode, asCheckSourced :: Bool, asTokenPositions :: Map.Map Id (Position, Position) @@ -146,6 +147,7 @@ data AnalysisSpec = AnalysisSpec { newAnalysisSpec token = AnalysisSpec { asScript = token, asShellType = Nothing, + asFallbackShell = Nothing, asExecutionMode = Executed, asCheckSourced = False, asTokenPositions = Map.empty