1{-
2    Copyright 2012-2019 Vidar Holen
3
4    This file is part of ShellCheck.
5    https://www.shellcheck.net
6
7    ShellCheck is free software: you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation, either version 3 of the License, or
10    (at your option) any later version.
11
12    ShellCheck is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    You should have received a copy of the GNU General Public License
18    along with this program.  If not, see <https://www.gnu.org/licenses/>.
19-}
20{-# LANGUAGE DeriveGeneric, DeriveAnyClass, DeriveTraversable, PatternSynonyms #-}
21module ShellCheck.AST where
22
23import GHC.Generics (Generic)
24import Control.Monad.Identity
25import Control.DeepSeq
26import Text.Parsec
27import qualified ShellCheck.Regex as Re
28import Prelude hiding (id)
29
30newtype Id = Id Int deriving (Show, Eq, Ord, Generic, NFData)
31
32data Quoted = Quoted | Unquoted deriving (Show, Eq)
33data Dashed = Dashed | Undashed deriving (Show, Eq)
34data AssignmentMode = Assign | Append deriving (Show, Eq)
35newtype FunctionKeyword = FunctionKeyword Bool deriving (Show, Eq)
36newtype FunctionParentheses = FunctionParentheses Bool deriving (Show, Eq)
37data CaseType = CaseBreak | CaseFallThrough | CaseContinue deriving (Show, Eq)
38
39newtype Root = Root Token
40data Token = OuterToken Id (InnerToken Token) deriving (Show)
41
42data InnerToken t =
43    Inner_TA_Binary String t t
44    | Inner_TA_Assignment String t t
45    | Inner_TA_Variable String [t]
46    | Inner_TA_Expansion [t]
47    | Inner_TA_Sequence [t]
48    | Inner_TA_Trinary t t t
49    | Inner_TA_Unary String t
50    | Inner_TC_And ConditionType String t t
51    | Inner_TC_Binary ConditionType String t t
52    | Inner_TC_Group ConditionType t
53    | Inner_TC_Nullary ConditionType t
54    | Inner_TC_Or ConditionType String t t
55    | Inner_TC_Unary ConditionType String t
56    | Inner_TC_Empty ConditionType
57    | Inner_T_AND_IF
58    | Inner_T_AndIf t t
59    | Inner_T_Arithmetic t
60    | Inner_T_Array [t]
61    | Inner_T_IndexedElement [t] t
62    -- Store the index as string, and parse as arithmetic or string later
63    | Inner_T_UnparsedIndex SourcePos String
64    | Inner_T_Assignment AssignmentMode String [t] t
65    | Inner_T_Backgrounded t
66    | Inner_T_Backticked [t]
67    | Inner_T_Bang
68    | Inner_T_Banged t
69    | Inner_T_BraceExpansion [t]
70    | Inner_T_BraceGroup [t]
71    | Inner_T_CLOBBER
72    | Inner_T_Case
73    | Inner_T_CaseExpression t [(CaseType, [t], [t])]
74    | Inner_T_Condition ConditionType t
75    | Inner_T_DGREAT
76    | Inner_T_DLESS
77    | Inner_T_DLESSDASH
78    | Inner_T_DSEMI
79    | Inner_T_Do
80    | Inner_T_DollarArithmetic t
81    | Inner_T_DollarBraced Bool t
82    | Inner_T_DollarBracket t
83    | Inner_T_DollarDoubleQuoted [t]
84    | Inner_T_DollarExpansion [t]
85    | Inner_T_DollarSingleQuoted String
86    | Inner_T_DollarBraceCommandExpansion [t]
87    | Inner_T_Done
88    | Inner_T_DoubleQuoted [t]
89    | Inner_T_EOF
90    | Inner_T_Elif
91    | Inner_T_Else
92    | Inner_T_Esac
93    | Inner_T_Extglob String [t]
94    | Inner_T_FdRedirect String t
95    | Inner_T_Fi
96    | Inner_T_For
97    | Inner_T_ForArithmetic t t t [t]
98    | Inner_T_ForIn String [t] [t]
99    | Inner_T_Function FunctionKeyword FunctionParentheses String t
100    | Inner_T_GREATAND
101    | Inner_T_Glob String
102    | Inner_T_Greater
103    | Inner_T_HereDoc Dashed Quoted String [t]
104    | Inner_T_HereString t
105    | Inner_T_If
106    | Inner_T_IfExpression [([t],[t])] [t]
107    | Inner_T_In
108    | Inner_T_IoFile t t
109    | Inner_T_IoDuplicate t String
110    | Inner_T_LESSAND
111    | Inner_T_LESSGREAT
112    | Inner_T_Lbrace
113    | Inner_T_Less
114    | Inner_T_Literal String
115    | Inner_T_Lparen
116    | Inner_T_NEWLINE
117    | Inner_T_NormalWord [t]
118    | Inner_T_OR_IF
119    | Inner_T_OrIf t t
120    | Inner_T_ParamSubSpecialChar String -- e.g. '%' in ${foo%bar}  or '/' in ${foo/bar/baz}
121    | Inner_T_Pipeline [t] [t] -- [Pipe separators] [Commands]
122    | Inner_T_ProcSub String [t]
123    | Inner_T_Rbrace
124    | Inner_T_Redirecting [t] t
125    | Inner_T_Rparen
126    | Inner_T_Script t [t] -- Shebang T_Literal, followed by script.
127    | Inner_T_Select
128    | Inner_T_SelectIn String [t] [t]
129    | Inner_T_Semi
130    | Inner_T_SimpleCommand [t] [t]
131    | Inner_T_SingleQuoted String
132    | Inner_T_Subshell [t]
133    | Inner_T_Then
134    | Inner_T_Until
135    | Inner_T_UntilExpression [t] [t]
136    | Inner_T_While
137    | Inner_T_WhileExpression [t] [t]
138    | Inner_T_Annotation [Annotation] t
139    | Inner_T_Pipe String
140    | Inner_T_CoProc (Maybe String) t
141    | Inner_T_CoProcBody t
142    | Inner_T_Include t
143    | Inner_T_SourceCommand t t
144    | Inner_T_BatsTest t t
145    deriving (Show, Eq, Functor, Foldable, Traversable)
146
147data Annotation =
148    DisableComment Integer Integer -- [from, to)
149    | EnableComment String
150    | SourceOverride String
151    | ShellOverride String
152    | SourcePath String
153    | ExternalSources Bool
154    deriving (Show, Eq)
155data ConditionType = DoubleBracket | SingleBracket deriving (Show, Eq)
156
157pattern T_AND_IF id = OuterToken id Inner_T_AND_IF
158pattern T_Bang id = OuterToken id Inner_T_Bang
159pattern T_Case id = OuterToken id Inner_T_Case
160pattern TC_Empty id typ = OuterToken id (Inner_TC_Empty typ)
161pattern T_CLOBBER id = OuterToken id Inner_T_CLOBBER
162pattern T_DGREAT id = OuterToken id Inner_T_DGREAT
163pattern T_DLESS id = OuterToken id Inner_T_DLESS
164pattern T_DLESSDASH id = OuterToken id Inner_T_DLESSDASH
165pattern T_Do id = OuterToken id Inner_T_Do
166pattern T_DollarSingleQuoted id str = OuterToken id (Inner_T_DollarSingleQuoted str)
167pattern T_Done id = OuterToken id Inner_T_Done
168pattern T_DSEMI id = OuterToken id Inner_T_DSEMI
169pattern T_Elif id = OuterToken id Inner_T_Elif
170pattern T_Else id = OuterToken id Inner_T_Else
171pattern T_EOF id = OuterToken id Inner_T_EOF
172pattern T_Esac id = OuterToken id Inner_T_Esac
173pattern T_Fi id = OuterToken id Inner_T_Fi
174pattern T_For id = OuterToken id Inner_T_For
175pattern T_Glob id str = OuterToken id (Inner_T_Glob str)
176pattern T_GREATAND id = OuterToken id Inner_T_GREATAND
177pattern T_Greater id = OuterToken id Inner_T_Greater
178pattern T_If id = OuterToken id Inner_T_If
179pattern T_In id = OuterToken id Inner_T_In
180pattern T_Lbrace id = OuterToken id Inner_T_Lbrace
181pattern T_Less id = OuterToken id Inner_T_Less
182pattern T_LESSAND id = OuterToken id Inner_T_LESSAND
183pattern T_LESSGREAT id = OuterToken id Inner_T_LESSGREAT
184pattern T_Literal id str = OuterToken id (Inner_T_Literal str)
185pattern T_Lparen id = OuterToken id Inner_T_Lparen
186pattern T_NEWLINE id = OuterToken id Inner_T_NEWLINE
187pattern T_OR_IF id = OuterToken id Inner_T_OR_IF
188pattern T_ParamSubSpecialChar id str = OuterToken id (Inner_T_ParamSubSpecialChar str)
189pattern T_Pipe id str = OuterToken id (Inner_T_Pipe str)
190pattern T_Rbrace id = OuterToken id Inner_T_Rbrace
191pattern T_Rparen id = OuterToken id Inner_T_Rparen
192pattern T_Select id = OuterToken id Inner_T_Select
193pattern T_Semi id = OuterToken id Inner_T_Semi
194pattern T_SingleQuoted id str = OuterToken id (Inner_T_SingleQuoted str)
195pattern T_Then id = OuterToken id Inner_T_Then
196pattern T_UnparsedIndex id pos str = OuterToken id (Inner_T_UnparsedIndex pos str)
197pattern T_Until id = OuterToken id Inner_T_Until
198pattern T_While id = OuterToken id Inner_T_While
199pattern TA_Assignment id op t1 t2 = OuterToken id (Inner_TA_Assignment op t1 t2)
200pattern TA_Binary id op t1 t2 = OuterToken id (Inner_TA_Binary op t1 t2)
201pattern TA_Expansion id t = OuterToken id (Inner_TA_Expansion t)
202pattern T_AndIf id t u = OuterToken id (Inner_T_AndIf t u)
203pattern T_Annotation id anns t = OuterToken id (Inner_T_Annotation anns t)
204pattern T_Arithmetic id c = OuterToken id (Inner_T_Arithmetic c)
205pattern T_Array id t = OuterToken id (Inner_T_Array t)
206pattern TA_Sequence id l = OuterToken id (Inner_TA_Sequence l)
207pattern T_Assignment id mode var indices value = OuterToken id (Inner_T_Assignment mode var indices value)
208pattern TA_Trinary id t1 t2 t3 = OuterToken id (Inner_TA_Trinary t1 t2 t3)
209pattern TA_Unary id op t1 = OuterToken id (Inner_TA_Unary op t1)
210pattern TA_Variable id str t = OuterToken id (Inner_TA_Variable str t)
211pattern T_Backgrounded id l = OuterToken id (Inner_T_Backgrounded l)
212pattern T_Backticked id list = OuterToken id (Inner_T_Backticked list)
213pattern T_Banged id l = OuterToken id (Inner_T_Banged l)
214pattern T_BatsTest id name t = OuterToken id (Inner_T_BatsTest name t)
215pattern T_BraceExpansion id list = OuterToken id (Inner_T_BraceExpansion list)
216pattern T_BraceGroup id l = OuterToken id (Inner_T_BraceGroup l)
217pattern TC_And id typ str t1 t2 = OuterToken id (Inner_TC_And typ str t1 t2)
218pattern T_CaseExpression id word cases = OuterToken id (Inner_T_CaseExpression word cases)
219pattern TC_Binary id typ op lhs rhs = OuterToken id (Inner_TC_Binary typ op lhs rhs)
220pattern TC_Group id typ token = OuterToken id (Inner_TC_Group typ token)
221pattern TC_Nullary id typ token = OuterToken id (Inner_TC_Nullary typ token)
222pattern T_Condition id typ token = OuterToken id (Inner_T_Condition typ token)
223pattern T_CoProcBody id t = OuterToken id (Inner_T_CoProcBody t)
224pattern T_CoProc id var body = OuterToken id (Inner_T_CoProc var body)
225pattern TC_Or id typ str t1 t2 = OuterToken id (Inner_TC_Or typ str t1 t2)
226pattern TC_Unary id typ op token = OuterToken id (Inner_TC_Unary typ op token)
227pattern T_DollarArithmetic id c = OuterToken id (Inner_T_DollarArithmetic c)
228pattern T_DollarBraceCommandExpansion id list = OuterToken id (Inner_T_DollarBraceCommandExpansion list)
229pattern T_DollarBraced id braced op = OuterToken id (Inner_T_DollarBraced braced op)
230pattern T_DollarBracket id c = OuterToken id (Inner_T_DollarBracket c)
231pattern T_DollarDoubleQuoted id list = OuterToken id (Inner_T_DollarDoubleQuoted list)
232pattern T_DollarExpansion id list = OuterToken id (Inner_T_DollarExpansion list)
233pattern T_DoubleQuoted id list = OuterToken id (Inner_T_DoubleQuoted list)
234pattern T_Extglob id str l = OuterToken id (Inner_T_Extglob str l)
235pattern T_FdRedirect id v t = OuterToken id (Inner_T_FdRedirect v t)
236pattern T_ForArithmetic id a b c group = OuterToken id (Inner_T_ForArithmetic a b c group)
237pattern T_ForIn id v w l = OuterToken id (Inner_T_ForIn v w l)
238pattern T_Function id a b name body = OuterToken id (Inner_T_Function a b name body)
239pattern T_HereDoc id d q str l = OuterToken id (Inner_T_HereDoc d q str l)
240pattern T_HereString id word = OuterToken id (Inner_T_HereString word)
241pattern T_IfExpression id conditions elses = OuterToken id (Inner_T_IfExpression conditions elses)
242pattern T_Include id script = OuterToken id (Inner_T_Include script)
243pattern T_IndexedElement id indices t = OuterToken id (Inner_T_IndexedElement indices t)
244pattern T_IoDuplicate id op num = OuterToken id (Inner_T_IoDuplicate op num)
245pattern T_IoFile id op file = OuterToken id (Inner_T_IoFile op file)
246pattern T_NormalWord id list = OuterToken id (Inner_T_NormalWord list)
247pattern T_OrIf id t u = OuterToken id (Inner_T_OrIf t u)
248pattern T_Pipeline id l1 l2 = OuterToken id (Inner_T_Pipeline l1 l2)
249pattern T_ProcSub id typ l = OuterToken id (Inner_T_ProcSub typ l)
250pattern T_Redirecting id redirs cmd = OuterToken id (Inner_T_Redirecting redirs cmd)
251pattern T_Script id shebang list = OuterToken id (Inner_T_Script shebang list)
252pattern T_SelectIn id v w l = OuterToken id (Inner_T_SelectIn v w l)
253pattern T_SimpleCommand id vars cmds = OuterToken id (Inner_T_SimpleCommand vars cmds)
254pattern T_SourceCommand id includer t_include = OuterToken id (Inner_T_SourceCommand includer t_include)
255pattern T_Subshell id l = OuterToken id (Inner_T_Subshell l)
256pattern T_UntilExpression id c l = OuterToken id (Inner_T_UntilExpression c l)
257pattern T_WhileExpression id c l = OuterToken id (Inner_T_WhileExpression c l)
258
259{-# COMPLETE T_AND_IF, T_Bang, T_Case, TC_Empty, T_CLOBBER, T_DGREAT, T_DLESS, T_DLESSDASH, T_Do, T_DollarSingleQuoted, T_Done, T_DSEMI, T_Elif, T_Else, T_EOF, T_Esac, T_Fi, T_For, T_Glob, T_GREATAND, T_Greater, T_If, T_In, T_Lbrace, T_Less, T_LESSAND, T_LESSGREAT, T_Literal, T_Lparen, T_NEWLINE, T_OR_IF, T_ParamSubSpecialChar, T_Pipe, T_Rbrace, T_Rparen, T_Select, T_Semi, T_SingleQuoted, T_Then, T_UnparsedIndex, T_Until, T_While, TA_Assignment, TA_Binary, TA_Expansion, T_AndIf, T_Annotation, T_Arithmetic, T_Array, TA_Sequence, T_Assignment, TA_Trinary, TA_Unary, TA_Variable, T_Backgrounded, T_Backticked, T_Banged, T_BatsTest, T_BraceExpansion, T_BraceGroup, TC_And, T_CaseExpression, TC_Binary, TC_Group, TC_Nullary, T_Condition, T_CoProcBody, T_CoProc, TC_Or, TC_Unary, T_DollarArithmetic, T_DollarBraceCommandExpansion, T_DollarBraced, T_DollarBracket, T_DollarDoubleQuoted, T_DollarExpansion, T_DoubleQuoted, T_Extglob, T_FdRedirect, T_ForArithmetic, T_ForIn, T_Function, T_HereDoc, T_HereString, T_IfExpression, T_Include, T_IndexedElement, T_IoDuplicate, T_IoFile, T_NormalWord, T_OrIf, T_Pipeline, T_ProcSub, T_Redirecting, T_Script, T_SelectIn, T_SimpleCommand, T_SourceCommand, T_Subshell, T_UntilExpression, T_WhileExpression #-}
260
261instance Eq Token where
262    OuterToken _ a == OuterToken _ b = a == b
263
264analyze :: Monad m => (Token -> m ()) -> (Token -> m ()) -> (Token -> m Token) -> Token -> m Token
265analyze f g i =
266    round
267  where
268    round t@(OuterToken id it) = do
269        f t
270        newIt <- traverse round it
271        g t
272        i (OuterToken id newIt)
273
274getId :: Token -> Id
275getId (OuterToken id _) = id
276
277blank :: Monad m => Token -> m ()
278blank = const $ return ()
279doAnalysis :: Monad m => (Token -> m ()) -> Token -> m Token
280doAnalysis f = analyze f blank return
281doStackAnalysis :: Monad m => (Token -> m ()) -> (Token -> m ()) -> Token -> m Token
282doStackAnalysis startToken endToken = analyze startToken endToken return
283doTransform :: (Token -> Token) -> Token -> Token
284doTransform i = runIdentity . analyze blank blank (return . i)
285
286