Skip to content

Commit 79e43c4

Browse files
committed
Allow parsing arbitrary coproc names (fixes #3048)
1 parent ca65071 commit 79e43c4

File tree

4 files changed

+46
-13
lines changed

4 files changed

+46
-13
lines changed

src/ShellCheck/AST.hs

+1-1
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ data InnerToken t =
138138
| Inner_T_WhileExpression [t] [t]
139139
| Inner_T_Annotation [Annotation] t
140140
| Inner_T_Pipe String
141-
| Inner_T_CoProc (Maybe String) t
141+
| Inner_T_CoProc (Maybe Token) t
142142
| Inner_T_CoProcBody t
143143
| Inner_T_Include t
144144
| Inner_T_SourceCommand t t

src/ShellCheck/AnalyzerLib.hs

+6-2
Original file line numberDiff line numberDiff line change
@@ -559,8 +559,12 @@ getModifiedVariables t =
559559
T_FdRedirect _ ('{':var) op -> -- {foo}>&2 modifies foo
560560
[(t, t, takeWhile (/= '}') var, DataString SourceInteger) | not $ isClosingFileOp op]
561561

562-
T_CoProc _ name _ ->
563-
[(t, t, fromMaybe "COPROC" name, DataArray SourceInteger)]
562+
T_CoProc _ Nothing _ ->
563+
[(t, t, "COPROC", DataArray SourceInteger)]
564+
565+
T_CoProc _ (Just token) _ -> do
566+
name <- maybeToList $ getLiteralString token
567+
[(t, t, name, DataArray SourceInteger)]
564568

565569
--Points to 'for' rather than variable
566570
T_ForIn id str [] _ -> [(t, t, str, DataString SourceExternal)]

src/ShellCheck/CFG.hs

+11-3
Original file line numberDiff line numberDiff line change
@@ -668,10 +668,18 @@ build t = do
668668
status <- newNodeRange $ CFSetExitCode id
669669
linkRange cond status
670670

671-
T_CoProc id maybeName t -> do
672-
let name = fromMaybe "COPROC" maybeName
671+
T_CoProc id maybeNameToken t -> do
672+
-- If unspecified, "COPROC". If not a constant string, Nothing.
673+
let maybeName = case maybeNameToken of
674+
Just x -> getLiteralString x
675+
Nothing -> Just "COPROC"
676+
677+
let parentNode = case maybeName of
678+
Just str -> applySingle $ IdTagged id $ CFWriteVariable str CFValueArray
679+
Nothing -> CFStructuralNode
680+
673681
start <- newStructuralNode
674-
parent <- newNodeRange $ applySingle $ IdTagged id $ CFWriteVariable name CFValueArray
682+
parent <- newNodeRange parentNode
675683
child <- subshell id "coproc" $ build t
676684
end <- newNodeRange $ CFSetExitCode id
677685

src/ShellCheck/Parser.hs

+28-7
Original file line numberDiff line numberDiff line change
@@ -2795,17 +2795,29 @@ readFunctionDefinition = called "function" $ do
27952795
prop_readCoProc1 = isOk readCoProc "coproc foo { echo bar; }"
27962796
prop_readCoProc2 = isOk readCoProc "coproc { echo bar; }"
27972797
prop_readCoProc3 = isOk readCoProc "coproc echo bar"
2798+
prop_readCoProc4 = isOk readCoProc "coproc a=b echo bar"
2799+
prop_readCoProc5 = isOk readCoProc "coproc 'foo' { echo bar; }"
2800+
prop_readCoProc6 = isOk readCoProc "coproc \"foo$$\" { echo bar; }"
2801+
prop_readCoProc7 = isOk readCoProc "coproc 'foo' ( echo bar )"
2802+
prop_readCoProc8 = isOk readCoProc "coproc \"foo$$\" while true; do true; done"
27982803
readCoProc = called "coproc" $ do
27992804
start <- startSpan
28002805
try $ do
28012806
string "coproc"
2802-
whitespace
2807+
spacing1
28032808
choice [ try $ readCompoundCoProc start, readSimpleCoProc start ]
28042809
where
28052810
readCompoundCoProc start = do
2806-
var <- optionMaybe $
2807-
readVariableName `thenSkip` whitespace
2808-
body <- readBody readCompoundCommand
2811+
notFollowedBy2 readAssignmentWord
2812+
(var, body) <- choice [
2813+
try $ do
2814+
body <- readBody readCompoundCommand
2815+
return (Nothing, body),
2816+
try $ do
2817+
var <- readNormalWord `thenSkip` spacing
2818+
body <- readBody readCompoundCommand
2819+
return (Just var, body)
2820+
]
28092821
id <- endSpan start
28102822
return $ T_CoProc id var body
28112823
readSimpleCoProc start = do
@@ -3436,13 +3448,22 @@ isOk p s = parsesCleanly p s == Just True -- The string parses with no wa
34363448
isWarning p s = parsesCleanly p s == Just False -- The string parses with warnings
34373449
isNotOk p s = parsesCleanly p s == Nothing -- The string does not parse
34383450

3439-
parsesCleanly parser string = runIdentity $ do
3451+
-- If the parser matches the string, return Right [ParseNotes+ParseProblems]
3452+
-- If it does not match the string, return Left [ParseProblems]
3453+
getParseOutput parser string = runIdentity $ do
34403454
(res, sys) <- runParser testEnvironment
34413455
(parser >> eof >> getState) "-" string
34423456
case (res, sys) of
34433457
(Right userState, systemState) ->
3444-
return $ Just . null $ parseNotes userState ++ parseProblems systemState
3445-
(Left _, _) -> return Nothing
3458+
return $ Right $ parseNotes userState ++ parseProblems systemState
3459+
(Left _, systemState) -> return $ Left $ parseProblems systemState
3460+
3461+
-- If the parser matches the string, return Just whether it was clean (without emitting suggestions)
3462+
-- Otherwise, Nothing
3463+
parsesCleanly parser string =
3464+
case getParseOutput parser string of
3465+
Right list -> Just $ null list
3466+
Left _ -> Nothing
34463467

34473468
parseWithNotes parser = do
34483469
item <- parser

0 commit comments

Comments
 (0)