From f6ba500d6b5c80f6ec790d7bcde93418bbf064ff Mon Sep 17 00:00:00 2001 From: Benjamin Gordon Date: Fri, 31 May 2019 10:42:53 -0600 Subject: [PATCH] Add support for basic shflags semantics The shflags command-line flags library creates variables at runtime with a few well-defined functions. This causes shellcheck to spit out lots of warnings about unassigned variables, as well as miss warnings about unused flag variables. We can address this with two parts: 1. Pretend that the shflags global variables are predefined like other shell variables so that shellcheck doesn't expect users to set them. 2. Treat DEFINE_string, DEFINE_int, etc. as new commands that create variables, similar to the existing read, local, mapfile, etc. Part 1 can theoretically be addresssed without this by following sourced files, but that doesn't help if people are otherwise not following external sources. The new behavior is on by default, similar to automatic bats test behavior. Addresses #1597 --- CHANGELOG.md | 1 + src/ShellCheck/Analytics.hs | 2 ++ src/ShellCheck/AnalyzerLib.hs | 11 +++++++++++ src/ShellCheck/Data.hs | 8 ++++++++ 4 files changed, 22 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e074894..28fbb4d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ - Source paths: Use `-P dir1:dir2` or a `source-path=dir1` directive to specify search paths for sourced files. - json1 format like --format=json but treats tabs as single characters +- Recognize FLAGS variables created by the shflags library. - SC2154: Also warn about unassigned uppercase variables (optional) - SC2252: Warn about `[ $a != x ] || [ $a != y ]`, similar to SC2055 - SC2251: Inform about ineffectual ! in front of commands diff --git a/src/ShellCheck/Analytics.hs b/src/ShellCheck/Analytics.hs index e1523fd..9377fa0 100644 --- a/src/ShellCheck/Analytics.hs +++ b/src/ShellCheck/Analytics.hs @@ -2081,6 +2081,8 @@ prop_checkUnused38= verifyTree checkUnusedAssignments "(( a=42 ))" prop_checkUnused39= verifyNotTree checkUnusedAssignments "declare -x -f foo" prop_checkUnused40= verifyNotTree checkUnusedAssignments "arr=(1 2); num=2; echo \"${arr[@]:num}\"" prop_checkUnused41= verifyNotTree checkUnusedAssignments "@test 'foo' {\ntrue\n}\n" +prop_checkUnused42= verifyNotTree checkUnusedAssignments "DEFINE_string foo '' ''; echo \"${FLAGS_foo}\"" +prop_checkUnused43= verifyTree checkUnusedAssignments "DEFINE_string foo '' ''" checkUnusedAssignments params t = execWriter (mapM_ warnFor unused) where flow = variableFlow params diff --git a/src/ShellCheck/AnalyzerLib.hs b/src/ShellCheck/AnalyzerLib.hs index 388f871..508b6ee 100644 --- a/src/ShellCheck/AnalyzerLib.hs +++ b/src/ShellCheck/AnalyzerLib.hs @@ -606,6 +606,11 @@ getModifiedVariableCommand base@(T_SimpleCommand _ _ (T_NormalWord _ (T_Literal "mapfile" -> maybeToList $ getMapfileArray base rest "readarray" -> maybeToList $ getMapfileArray base rest + "DEFINE_boolean" -> maybeToList $ getFlagVariable rest + "DEFINE_float" -> maybeToList $ getFlagVariable rest + "DEFINE_integer" -> maybeToList $ getFlagVariable rest + "DEFINE_string" -> maybeToList $ getFlagVariable rest + _ -> [] where flags = map snd $ getAllFlags base @@ -679,6 +684,12 @@ getModifiedVariableCommand base@(T_SimpleCommand _ _ (T_NormalWord _ (T_Literal map (getLiteralArray . snd) (filter (\(x,_) -> getLiteralString x == Just "-a") (zip (args) (tail args))) + -- get the FLAGS_ variable created by a shflags DEFINE_ call + getFlagVariable (n:v:_) = return (base, n, flagName n, DataString $ SourceFrom [v]) + where + flagName varName@(T_NormalWord _ _) = "FLAGS_" ++ (onlyLiteralString varName) + getFlagVariable _ = fail "Invalid flag definition" + getModifiedVariableCommand _ = [] getIndexReferences s = fromMaybe [] $ do diff --git a/src/ShellCheck/Data.hs b/src/ShellCheck/Data.hs index 2eedeeb..1394c04 100644 --- a/src/ShellCheck/Data.hs +++ b/src/ShellCheck/Data.hs @@ -36,6 +36,11 @@ internalVariables = [ -- Ksh , ".sh.version" + + -- shflags + , "FLAGS_ARGC", "FLAGS_ARGV", "FLAGS_ERROR", "FLAGS_FALSE", "FLAGS_HELP", + "FLAGS_PARENT", "FLAGS_RESERVED", "FLAGS_TRUE", "FLAGS_VERSION", + "flags_error", "flags_return" ] specialVariablesWithoutSpaces = [ @@ -45,6 +50,9 @@ variablesWithoutSpaces = specialVariablesWithoutSpaces ++ [ "BASHPID", "BASH_ARGC", "BASH_LINENO", "BASH_SUBSHELL", "EUID", "LINENO", "OPTIND", "PPID", "RANDOM", "SECONDS", "SHELLOPTS", "SHLVL", "UID", "COLUMNS", "HISTFILESIZE", "HISTSIZE", "LINES" + + -- shflags + , "FLAGS_ERROR", "FLAGS_FALSE", "FLAGS_TRUE" ] specialVariables = specialVariablesWithoutSpaces ++ ["@", "*"]