diff --git a/ShellCheck/Analytics.hs b/ShellCheck/Analytics.hs index e97f26a..d059847 100644 --- a/ShellCheck/Analytics.hs +++ b/ShellCheck/Analytics.hs @@ -155,6 +155,7 @@ isGlob _ = False isConfusedGlobRegex ('*':_) = True +isConfusedGlobRegex [x,'*'] | x /= '\\' = True isConfusedGlobRegex _ = False isPotentiallyConfusedGlobRegex = @@ -588,10 +589,10 @@ prop_checkGlobbedRegex2a = verify checkGlobbedRegex "[[ $foo =~ \\#* ]]" prop_checkGlobbedRegex3 = verifyNot checkGlobbedRegex "[[ $foo =~ $foo ]]" prop_checkGlobbedRegex4 = verifyNot checkGlobbedRegex "[[ $foo =~ ^c.* ]]" checkGlobbedRegex (TC_Binary _ DoubleBracket "=~" _ rhs) = - case rhs of - T_NormalWord id ((T_Glob _ "*"):_) -> warn id $ "=~ is for regex. Use == for globs." - T_NormalWord id ([(T_Literal _ [c]), (T_Glob _ "*")]) -> warn id $ "=~ is for regex. Either ^anchor$ this, or treat it as a glob with == ." - _ -> return () + let s = concat $ deadSimple rhs in + if isConfusedGlobRegex s + then warn (getId rhs) $ "=~ is for regex. Use == for globs." + else return () checkGlobbedRegex _ = return () diff --git a/ShellCheck/Parser.hs b/ShellCheck/Parser.hs index a495faf..3ad73d7 100644 --- a/ShellCheck/Parser.hs +++ b/ShellCheck/Parser.hs @@ -291,8 +291,11 @@ readConditionContents single = do ) (do pos <- getPosition + isRegex <- regexOperatorAhead op <- readCondBinaryOp - y <- readCondWord <|> ( (parseProblemAt pos ErrorC $ "Expected another argument for this operator.") >> mzero) + y <- if isRegex + then readRegex + else readCondWord <|> ( (parseProblemAt pos ErrorC $ "Expected another argument for this operator.") >> mzero) return (x `op` y) ) <|> (return $ TC_Noary id typ x) @@ -316,6 +319,28 @@ readConditionContents single = do isEscaped _ = False xor x y = x && not y || not x && y + -- Currently a bit of a hack since parsing rules are obscure + regexOperatorAhead = (lookAhead $ do + try (string "=~") <|> try (string "~=") + return True) + <|> return False + readRegex = called "regex" $ do + id <- getNextId + parts <- many1 (readGroup <|> readSingleQuoted <|> readDoubleQuoted <|> readDollar <|> readNormalLiteral "( " <|> readGlobLiteral) + disregard spacing + return $ T_NormalWord id parts + where + readGlobLiteral = do + id <- getNextId + s <- many1 extglobStart + return $ T_Literal id s + readGroup = do -- Fixme: account for vars and quotes in groups + id <- getNextId + char '(' + s <- readGenericLiteral (char ')') + char ')' + return $ T_Literal id $ "(" ++ s ++ ")" + readCondTerm = readCondNot <|> readCondExpr readCondNot = do id <- getNextId @@ -505,6 +530,8 @@ readArithmeticContents = prop_readCondition = isOk readCondition "[ \\( a = b \\) -a \\( c = d \\) ]" prop_readCondition2 = isOk readCondition "[[ (a = b) || (c = d) ]]" prop_readCondition3 = isOk readCondition "[[ $c = [[:alpha:].~-] ]]" +prop_readCondition4 = isOk readCondition "[[ $c =~ *foo* ]]" +prop_readCondition5 = isOk readCondition "[[ $c =~ f( ]] )* ]]" readCondition = called "test expression" $ do opos <- getPosition id <- getNextId