From 5c01b6c7f5f77380e24cd54f3e1494aa20351c0e Mon Sep 17 00:00:00 2001 From: Vidar Holen Date: Sun, 16 Apr 2017 18:11:00 -0700 Subject: [PATCH] Parse empty [ ] conditionals --- ShellCheck/AST.hs | 2 ++ ShellCheck/Analytics.hs | 7 +++++++ ShellCheck/Parser.hs | 10 ++++++++-- 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/ShellCheck/AST.hs b/ShellCheck/AST.hs index 961a897..fec6247 100644 --- a/ShellCheck/AST.hs +++ b/ShellCheck/AST.hs @@ -48,6 +48,7 @@ data Token = | TC_Nullary Id ConditionType Token | TC_Or Id ConditionType String Token Token | TC_Unary Id ConditionType String Token + | TC_Empty Id ConditionType | T_AND_IF Id | T_AndIf Id Token Token | T_Arithmetic Id Token @@ -372,6 +373,7 @@ getId t = case t of T_CoProcBody id _ -> id T_Include id _ _ -> id T_UnparsedIndex id _ _ -> id + TC_Empty id _ -> id blank :: Monad m => Token -> m () blank = const $ return () diff --git a/ShellCheck/Analytics.hs b/ShellCheck/Analytics.hs index 094e226..cf5f9f9 100644 --- a/ShellCheck/Analytics.hs +++ b/ShellCheck/Analytics.hs @@ -163,6 +163,7 @@ nodeChecks = [ ,checkSplittingInArrays ,checkRedirectionToNumber ,checkGlobAsCommand + ,checkEmptyCondition ] @@ -2819,5 +2820,11 @@ checkGlobAsCommand _ t = case t of warn (getId first) 2211 "This is a glob used as a command name. Was it supposed to be in ${..}, array, or is it missing quoting?" _ -> return () +prop_checkEmptyCondition1 = verify checkEmptyCondition "if [ ]; then ..; fi" +prop_checkEmptyCondition2 = verifyNot checkEmptyCondition "[ foo -o bar ]" +checkEmptyCondition _ t = case t of + TC_Empty id _ -> style id 2212 "Use 'false' instead of empty [/[[ conditionals." + _ -> return () + return [] runTests = $( [| $(forAllProperties) (quickCheckWithResult (stdArgs { maxSuccess = 1 }) ) |]) diff --git a/ShellCheck/Parser.hs b/ShellCheck/Parser.hs index 9f1c2fa..ba0bcab 100644 --- a/ShellCheck/Parser.hs +++ b/ShellCheck/Parser.hs @@ -848,11 +848,13 @@ prop_readCondition14= isOk readCondition "[ foo '>' bar ]" prop_readCondition15= isOk readCondition "[ foo \">=\" bar ]" prop_readCondition16= isOk readCondition "[ foo \\< bar ]" prop_readCondition17= isOk readCondition "[[ ${file::1} = [-.\\|/\\\\] ]]" +prop_readCondition18= isOk readCondition "[ ]" readCondition = called "test expression" $ do opos <- getPosition id <- getNextId open <- try (string "[[") <|> string "[" let single = open == "[" + let typ = if single then SingleBracket else DoubleBracket pos <- getPosition space <- allspacing @@ -864,7 +866,11 @@ readCondition = called "test expression" $ do when (single && '\n' `elem` space) $ parseProblemAt pos ErrorC 1080 "You need \\ before line feeds to break lines in [ ]." - condition <- readConditionContents single + condition <- readConditionContents single <|> do + guard . not . null $ space + lookAhead $ string "]" + id <- getNextIdAt pos + return $ TC_Empty id typ cpos <- getPosition close <- try (string "]]") <|> string "]" <|> fail "Expected test to end here (don't wrap commands in []/[[]])" @@ -872,7 +878,7 @@ readCondition = called "test expression" $ do when (open == "[" && close /= "]" ) $ parseProblemAt opos ErrorC 1034 "Did you mean [[ ?" spacing many readCmdWord -- Read and throw away remainders to get then/do warnings. Fixme? - return $ T_Condition id (if single then SingleBracket else DoubleBracket) condition + return $ T_Condition id typ condition readAnnotationPrefix = do char '#'