mirror of
https://github.com/koalaman/shellcheck.git
synced 2025-08-21 16:13:17 +08:00
Allow empty scripts, $()s and <()s. Also improves related error messages.
This commit is contained in:
@@ -771,14 +771,14 @@ readDollarBracedLiteral = do
|
|||||||
|
|
||||||
prop_readProcSub1 = isOk readProcSub "<(echo test | wc -l)"
|
prop_readProcSub1 = isOk readProcSub "<(echo test | wc -l)"
|
||||||
prop_readProcSub2 = isOk readProcSub "<( if true; then true; fi )"
|
prop_readProcSub2 = isOk readProcSub "<( if true; then true; fi )"
|
||||||
|
prop_readProcSub3 = isOk readProcSub "<( # nothing here \n)"
|
||||||
readProcSub = called "process substitution" $ do
|
readProcSub = called "process substitution" $ do
|
||||||
id <- getNextId
|
id <- getNextId
|
||||||
dir <- try $ do
|
dir <- try $ do
|
||||||
x <- oneOf "<>"
|
x <- oneOf "<>"
|
||||||
char '('
|
char '('
|
||||||
return [x]
|
return [x]
|
||||||
allspacing
|
list <- readCompoundListOrEmpty
|
||||||
list <- readCompoundList
|
|
||||||
allspacing
|
allspacing
|
||||||
char ')'
|
char ')'
|
||||||
return $ T_ProcSub id dir list
|
return $ T_ProcSub id dir list
|
||||||
@@ -843,19 +843,23 @@ readBackTicked = called "backtick expansion" $ do
|
|||||||
suggestForgotClosingQuote startPos endPos "backtick expansion"
|
suggestForgotClosingQuote startPos endPos "backtick expansion"
|
||||||
|
|
||||||
-- Result positions may be off due to escapes
|
-- Result positions may be off due to escapes
|
||||||
result <- subParse subStart readTermOrNone (unEscape subString)
|
result <- subParse subStart subParser (unEscape subString)
|
||||||
return $ T_Backticked id result
|
return $ T_Backticked id result
|
||||||
where
|
where
|
||||||
unEscape [] = []
|
unEscape [] = []
|
||||||
unEscape ('\\':x:rest) | x `elem` "$`\\" = x : unEscape rest
|
unEscape ('\\':x:rest) | x `elem` "$`\\" = x : unEscape rest
|
||||||
unEscape ('\\':'\n':rest) = unEscape rest
|
unEscape ('\\':'\n':rest) = unEscape rest
|
||||||
unEscape (c:rest) = c : unEscape rest
|
unEscape (c:rest) = c : unEscape rest
|
||||||
|
subParser = do
|
||||||
|
cmds <- readCompoundListOrEmpty
|
||||||
|
verifyEof
|
||||||
|
return cmds
|
||||||
backtick =
|
backtick =
|
||||||
disregard (char '`') <|> do
|
disregard (char '`') <|> do
|
||||||
pos <- getPosition
|
pos <- getPosition
|
||||||
char '´'
|
char '´'
|
||||||
parseProblemAt pos ErrorC 1077
|
parseProblemAt pos ErrorC 1077
|
||||||
"For command expansion, the tick should slant left (` vs ´)."
|
"For command expansion, the tick should slant left (` vs ´). Use $(..) instead."
|
||||||
|
|
||||||
subParse pos parser input = do
|
subParse pos parser input = do
|
||||||
lastPosition <- getPosition
|
lastPosition <- getPosition
|
||||||
@@ -1132,11 +1136,13 @@ readDollarBraced = called "parameter expansion" $ do
|
|||||||
char '}'
|
char '}'
|
||||||
return $ T_DollarBraced id word
|
return $ T_DollarBraced id word
|
||||||
|
|
||||||
prop_readDollarExpansion = isOk readDollarExpansion "$(echo foo; ls\n)"
|
prop_readDollarExpansion1= isOk readDollarExpansion "$(echo foo; ls\n)"
|
||||||
|
prop_readDollarExpansion2= isOk readDollarExpansion "$( )"
|
||||||
|
prop_readDollarExpansion3= isOk readDollarExpansion "$( command \n#comment \n)"
|
||||||
readDollarExpansion = called "command expansion" $ do
|
readDollarExpansion = called "command expansion" $ do
|
||||||
id <- getNextId
|
id <- getNextId
|
||||||
try (string "$(")
|
try (string "$(")
|
||||||
cmds <- readCompoundList
|
cmds <- readCompoundListOrEmpty
|
||||||
char ')' <?> "end of $(..) expression"
|
char ')' <?> "end of $(..) expression"
|
||||||
return $ T_DollarExpansion id cmds
|
return $ T_DollarExpansion id cmds
|
||||||
|
|
||||||
@@ -1845,6 +1851,9 @@ readCompoundCommand = do
|
|||||||
|
|
||||||
|
|
||||||
readCompoundList = readTerm
|
readCompoundList = readTerm
|
||||||
|
readCompoundListOrEmpty = do
|
||||||
|
allspacing
|
||||||
|
readTerm <|> return []
|
||||||
|
|
||||||
readCmdPrefix = many1 (readIoRedirect <|> readAssignmentWord)
|
readCmdPrefix = many1 (readIoRedirect <|> readAssignmentWord)
|
||||||
readCmdSuffix = many1 (readIoRedirect <|> readCmdWord)
|
readCmdSuffix = many1 (readIoRedirect <|> readCmdWord)
|
||||||
@@ -2048,10 +2057,25 @@ readShebang = do
|
|||||||
parseProblemAt pos ErrorC 1084
|
parseProblemAt pos ErrorC 1084
|
||||||
"Use #!, not !#, for the shebang."
|
"Use #!, not !#, for the shebang."
|
||||||
|
|
||||||
|
verifyEof = eof <|> choice [
|
||||||
|
ifParsable g_Lparen $
|
||||||
|
parseProblem ErrorC 1088 "Parsing stopped here. Invalid use of parentheses?",
|
||||||
|
|
||||||
|
ifParsable readKeyword $
|
||||||
|
parseProblem ErrorC 1089 "Parsing stopped here. Is this keyword correctly matched up?",
|
||||||
|
|
||||||
|
parseProblem ErrorC 1070 "Parsing stopped here. Mismatched keywords or invalid parentheses?"
|
||||||
|
]
|
||||||
|
where
|
||||||
|
ifParsable p action = do
|
||||||
|
try (lookAhead p)
|
||||||
|
action
|
||||||
|
|
||||||
prop_readScript1 = isOk readScript "#!/bin/bash\necho hello world\n"
|
prop_readScript1 = isOk readScript "#!/bin/bash\necho hello world\n"
|
||||||
prop_readScript2 = isWarning readScript "#!/bin/bash\r\necho hello world\n"
|
prop_readScript2 = isWarning readScript "#!/bin/bash\r\necho hello world\n"
|
||||||
prop_readScript3 = isWarning readScript "#!/bin/bash\necho hello\xA0world"
|
prop_readScript3 = isWarning readScript "#!/bin/bash\necho hello\xA0world"
|
||||||
prop_readScript4 = isWarning readScript "#!/usr/bin/perl\nfoo=("
|
prop_readScript4 = isWarning readScript "#!/usr/bin/perl\nfoo=("
|
||||||
|
prop_readScript5 = isOk readScript "#!/bin/bash\n#This is an empty script\n\n"
|
||||||
readScript = do
|
readScript = do
|
||||||
id <- getNextId
|
id <- getNextId
|
||||||
pos <- getPosition
|
pos <- getPosition
|
||||||
@@ -2062,19 +2086,13 @@ readScript = do
|
|||||||
sb <- option "" readShebang
|
sb <- option "" readShebang
|
||||||
verifyShell pos (getShell sb)
|
verifyShell pos (getShell sb)
|
||||||
if isValidShell (getShell sb) /= Just False
|
if isValidShell (getShell sb) /= Just False
|
||||||
then
|
then do
|
||||||
do {
|
commands <- readCompoundListOrEmpty
|
||||||
allspacing;
|
verifyEof
|
||||||
commands <- readTerm;
|
return $ T_Script id sb commands
|
||||||
eof <|> parseProblem ErrorC 1070 "Parsing stopped here because of parsing errors.";
|
|
||||||
return $ T_Script id sb commands;
|
|
||||||
} <|> do {
|
|
||||||
parseProblem WarningC 1014 "Couldn't read any commands.";
|
|
||||||
return $ T_Script id sb []
|
|
||||||
}
|
|
||||||
else do
|
else do
|
||||||
many anyChar
|
many anyChar
|
||||||
return $ T_Script id sb [];
|
return $ T_Script id sb []
|
||||||
|
|
||||||
where
|
where
|
||||||
basename s = reverse . takeWhile (/= '/') . reverse $ s
|
basename s = reverse . takeWhile (/= '/') . reverse $ s
|
||||||
|
Reference in New Issue
Block a user