From 505ff7832fb611bc43332bf994be0c2b853e505a Mon Sep 17 00:00:00 2001 From: Vladimir Panteleev Date: Mon, 22 May 2017 05:12:50 +0000 Subject: [PATCH] Recognize bash's `shopt -s lastpipe` Fixes #732. --- ShellCheck/Analytics.hs | 5 +++-- ShellCheck/AnalyzerLib.hs | 20 ++++++++++++++------ 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/ShellCheck/Analytics.hs b/ShellCheck/Analytics.hs index 77206e9..b4abc47 100644 --- a/ShellCheck/Analytics.hs +++ b/ShellCheck/Analytics.hs @@ -1521,6 +1521,7 @@ prop_subshellAssignmentCheck15 = verifyNotTree subshellAssignmentCheck "#!/bin/k prop_subshellAssignmentCheck16 = verifyNotTree subshellAssignmentCheck "(set -e); echo $@" prop_subshellAssignmentCheck17 = verifyNotTree subshellAssignmentCheck "foo=${ { bar=$(baz); } 2>&1; }; echo $foo $bar" prop_subshellAssignmentCheck18 = verifyTree subshellAssignmentCheck "( exec {n}>&2; ); echo $n" +prop_subshellAssignmentCheck19 = verifyNotTree subshellAssignmentCheck "#!/bin/bash\nshopt -s lastpipe; echo a | read -r b; echo \"$b\"" subshellAssignmentCheck params t = let flow = variableFlow params check = findSubshelled flow [("oops",[])] Map.empty @@ -2105,7 +2106,7 @@ checkLoopKeywordScope params t | where name = getCommandName t path = let p = getPath (parentMap params) t in filter relevant p - subshellType t = case leadType (shellType params) (parentMap params) t of + subshellType t' = case leadType (shellType params) (parentMap params) t' t of NoneScope -> Nothing SubshellScope str -> return str relevant t = isLoop t || isFunction t || isJust (subshellType t) @@ -2167,7 +2168,7 @@ checkUnpassedInFunctions params root = functions = execWriter $ doAnalysis (tell . maybeToList . findFunction) root findFunction t@(T_Function id _ _ name body) = - let flow = getVariableFlow (shellType params) (parentMap params) body + let flow = getVariableFlow (shellType params) (parentMap params) body root in if any (isPositionalReference t) flow && not (any isPositionalAssignment flow) then return t diff --git a/ShellCheck/AnalyzerLib.hs b/ShellCheck/AnalyzerLib.hs index 605b015..e82949d 100644 --- a/ShellCheck/AnalyzerLib.hs +++ b/ShellCheck/AnalyzerLib.hs @@ -145,7 +145,7 @@ makeParameters spec = shellTypeSpecified = isJust $ asShellType spec, parentMap = getParentTree root, variableFlow = - getVariableFlow (shellType params) (parentMap params) root + getVariableFlow (shellType params) (parentMap params) root root } in params where root = asScript spec @@ -337,18 +337,18 @@ tokenIsJustCommandOutput t = case t of check _ = False -- TODO: Replace this with a proper Control Flow Graph -getVariableFlow shell parents t = +getVariableFlow shell parents t root = let (_, stack) = runState (doStackAnalysis startScope endScope t) [] in reverse stack where startScope t = - let scopeType = leadType shell parents t + let scopeType = leadType shell parents t root in do when (scopeType /= NoneScope) $ modify (StackScope scopeType:) when (assignFirst t) $ setWritten t endScope t = - let scopeType = leadType shell parents t + let scopeType = leadType shell parents t root in do setRead t unless (assignFirst t) $ setWritten t @@ -367,7 +367,7 @@ getVariableFlow shell parents t = in mapM_ (\v -> modify (Assignment v:)) written -leadType shell parents t = +leadType shell parents t root = case t of T_DollarExpansion _ _ -> SubshellScope "$(..) expansion" T_Backticked _ _ -> SubshellScope "`..` expansion" @@ -396,11 +396,19 @@ leadType shell parents t = lastCreatesSubshell = case shell of - Bash -> True + Bash -> not hasShoptLastPipe Dash -> True Sh -> True Ksh -> False + hasShoptLastPipe = isNothing $ doAnalysis (guard . not . isShoptLastPipe) root + isShoptLastPipe t = + case t of + T_SimpleCommand {} -> + t `isUnqualifiedCommand` "shopt" && + ("lastpipe" `elem` oversimplify t) + _ -> False + getModifiedVariables t = case t of T_SimpleCommand _ vars [] ->