From a4b9cec9f00ba31e8b48c379296d2bb1d4bb3638 Mon Sep 17 00:00:00 2001 From: Tito Sacchi Date: Mon, 14 Jan 2019 14:16:12 +0100 Subject: [PATCH] Fix #1369 (Use file extension to detect shell) The precedence order that is used to determine the shell is the following: 1. ShellCheck directive 2. Shebang 3. File extension A new field `asFallbackShell` has been added to the record type `AnalysisSpec`. --- src/ShellCheck/AnalyzerLib.hs | 10 ++++++---- src/ShellCheck/Checker.hs | 10 ++++++++++ src/ShellCheck/Interface.hs | 4 +++- 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/src/ShellCheck/AnalyzerLib.hs b/src/ShellCheck/AnalyzerLib.hs index 37a96ad0f..5248d9404 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 @@ -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 10074e380..67bd1c30f 100644 --- a/src/ShellCheck/Checker.hs +++ b/src/ShellCheck/Checker.hs @@ -48,6 +48,15 @@ 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) + ,(".sh", Sh) + ,(".bash", Bash) + ,(".dash", Dash)] + 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 +78,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 dd53f6f37..285042ff5 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