1 /**
2  * Utility to visit every variable in an expression.
3  *
4  * Copyright:   Copyright (C) 1999-2023 by The D Language Foundation, All Rights Reserved
5  * Authors:     $(LINK2 https://www.digitalmars.com, Walter Bright)
6  * License:     $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
7  * Source:      $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/foreachvar.d, _foreachvar.d)
8  * Documentation:  https://dlang.org/phobos/dmd_foreachvar.html
9  * Coverage:    https://codecov.io/gh/dlang/dmd/src/master/src/dmd/foreachvar.d
10  */
11 
12 module dmd.foreachvar;
13 
14 import core.stdc.stdio;
15 import core.stdc.stdlib;
16 import core.stdc.string;
17 
18 import dmd.arraytypes;
19 import dmd.astenums;
20 import dmd.attrib;
21 import dmd.dclass;
22 import dmd.declaration;
23 import dmd.dstruct;
24 import dmd.dsymbol;
25 import dmd.dsymbolsem;
26 import dmd.dtemplate;
27 import dmd.errors;
28 import dmd.expression;
29 import dmd.func;
30 import dmd.id;
31 import dmd.identifier;
32 import dmd.init;
33 import dmd.initsem;
34 import dmd.mtype;
35 import dmd.postordervisitor;
36 import dmd.printast;
37 import dmd.root.array;
38 import dmd.rootobject;
39 import dmd.statement;
40 import dmd.tokens;
41 import dmd.visitor;
42 
43 /*********************************************
44  * Visit each Expression in e, and call dgVar() on each variable declared in it.
45  * Params:
46  *      e = expression tree to visit
47  *      dgVar = call when a variable is declared
48  */
49 void foreachVar(Expression e, void delegate(VarDeclaration) dgVar)
50 {
51     if (!e)
52         return;
53 
54     extern (C++) final class VarWalker : StoppableVisitor
55     {
56         alias visit = typeof(super).visit;
57         extern (D) void delegate(VarDeclaration) dgVar;
58 
59         extern (D) this(void delegate(VarDeclaration) dgVar) scope @safe
60         {
61             this.dgVar = dgVar;
62         }
63 
64         override void visit(Expression e)
65         {
66         }
67 
68         override void visit(ErrorExp e)
69         {
70         }
71 
72         override void visit(DeclarationExp e)
73         {
74             VarDeclaration v = e.declaration.isVarDeclaration();
75             if (!v)
76                 return;
77             if (TupleDeclaration td = v.toAlias().isTupleDeclaration())
78                 td.foreachVar((s) { dgVar(s.isVarDeclaration()); });
79             else
80                 dgVar(v);
81             Dsymbol s = v.toAlias();
82             if (s == v && !v.isStatic() && v._init)
83             {
84                 if (auto ie = v._init.isExpInitializer())
85                     ie.exp.foreachVar(dgVar);
86             }
87         }
88 
89         override void visit(IndexExp e)
90         {
91             if (e.lengthVar)
92                 dgVar(e.lengthVar);
93         }
94 
95         override void visit(SliceExp e)
96         {
97             if (e.lengthVar)
98                 dgVar(e.lengthVar);
99         }
100     }
101 
102     scope VarWalker v = new VarWalker(dgVar);
103     walkPostorder(e, v);
104 }
105 
106 /***************
107  * Transitively walk Statement s, pass Expressions to dgExp(), VarDeclarations to dgVar().
108  * Params:
109  *      s = Statement to traverse
110  *      dgExp = delegate to pass found Expressions to
111  *      dgVar = delegate to pass found VarDeclarations to
112  */
113 void foreachExpAndVar(Statement s,
114         void delegate(Expression) dgExp,
115         void delegate(VarDeclaration) dgVar)
116 {
117     void visit(Statement s)
118     {
119         void visitExp(ExpStatement s)
120         {
121             if (s.exp)
122                 dgExp(s.exp);
123         }
124 
125         void visitDtorExp(DtorExpStatement s)
126         {
127             if (s.exp)
128                 dgExp(s.exp);
129         }
130 
131         void visitIf(IfStatement s)
132         {
133             dgExp(s.condition);
134             visit(s.ifbody);
135             visit(s.elsebody);
136         }
137 
138         void visitDo(DoStatement s)
139         {
140             dgExp(s.condition);
141             visit(s._body);
142         }
143 
144         void visitFor(ForStatement s)
145         {
146             visit(s._init);
147             if (s.condition)
148                 dgExp(s.condition);
149             if (s.increment)
150                 dgExp(s.increment);
151             visit(s._body);
152         }
153 
154         void visitSwitch(SwitchStatement s)
155         {
156             dgExp(s.condition);
157             // Note that the body contains the Case and Default
158             // statements, so we only need to compile the expressions
159             foreach (cs; *s.cases)
160             {
161                 dgExp(cs.exp);
162             }
163             visit(s._body);
164         }
165 
166         void visitCase(CaseStatement s)
167         {
168             visit(s.statement);
169         }
170 
171         void visitReturn(ReturnStatement s)
172         {
173             if (s.exp)
174                 dgExp(s.exp);
175         }
176 
177         void visitCompound(CompoundStatement s)
178         {
179             if (s.statements)
180             {
181                 foreach (s2; *s.statements)
182                 {
183                     visit(s2);
184                 }
185             }
186         }
187 
188         void visitCompoundDeclaration(CompoundDeclarationStatement s)
189         {
190             visitCompound(s);
191         }
192 
193         void visitUnrolledLoop(UnrolledLoopStatement s)
194         {
195             foreach (s2; *s.statements)
196             {
197                 visit(s2);
198             }
199         }
200 
201         void visitScope(ScopeStatement s)
202         {
203             visit(s.statement);
204         }
205 
206         void visitDefault(DefaultStatement s)
207         {
208             visit(s.statement);
209         }
210 
211         void visitWith(WithStatement s)
212         {
213             // If it is with(Enum) {...}, just execute the body.
214             if (s.exp.op == EXP.scope_ || s.exp.op == EXP.type)
215             {
216             }
217             else
218             {
219                 dgVar(s.wthis);
220                 dgExp(s.exp);
221             }
222             visit(s._body);
223         }
224 
225         void visitTryCatch(TryCatchStatement s)
226         {
227             visit(s._body);
228             foreach (ca; *s.catches)
229             {
230                 if (ca.var)
231                     dgVar(ca.var);
232                 visit(ca.handler);
233             }
234         }
235 
236         void visitTryFinally(TryFinallyStatement s)
237         {
238             visit(s._body);
239             visit(s.finalbody);
240         }
241 
242         void visitThrow(ThrowStatement s)
243         {
244             dgExp(s.exp);
245         }
246 
247         void visitLabel(LabelStatement s)
248         {
249             visit(s.statement);
250         }
251 
252         if (!s)
253             return;
254 
255         final switch (s.stmt)
256         {
257             case STMT.Exp:                 visitExp(s.isExpStatement()); break;
258             case STMT.DtorExp:             visitDtorExp(s.isDtorExpStatement()); break;
259             case STMT.Compound:            visitCompound(s.isCompoundStatement()); break;
260             case STMT.CompoundDeclaration: visitCompoundDeclaration(s.isCompoundDeclarationStatement()); break;
261             case STMT.UnrolledLoop:        visitUnrolledLoop(s.isUnrolledLoopStatement()); break;
262             case STMT.Scope:               visitScope(s.isScopeStatement()); break;
263             case STMT.Do:                  visitDo(s.isDoStatement()); break;
264             case STMT.For:                 visitFor(s.isForStatement()); break;
265             case STMT.If:                  visitIf(s.isIfStatement()); break;
266             case STMT.Switch:              visitSwitch(s.isSwitchStatement()); break;
267             case STMT.Case:                visitCase(s.isCaseStatement()); break;
268             case STMT.Default:             visitDefault(s.isDefaultStatement()); break;
269             case STMT.Return:              visitReturn(s.isReturnStatement()); break;
270             case STMT.With:                visitWith(s.isWithStatement()); break;
271             case STMT.TryCatch:            visitTryCatch(s.isTryCatchStatement()); break;
272             case STMT.TryFinally:          visitTryFinally(s.isTryFinallyStatement()); break;
273             case STMT.Throw:               visitThrow(s.isThrowStatement()); break;
274             case STMT.Label:               visitLabel(s.isLabelStatement()); break;
275 
276             case STMT.CompoundAsm:
277             case STMT.Asm:
278             case STMT.InlineAsm:
279             case STMT.GccAsm:
280 
281             case STMT.Break:
282             case STMT.Continue:
283             case STMT.GotoDefault:
284             case STMT.GotoCase:
285             case STMT.SwitchError:
286             case STMT.Goto:
287             case STMT.Pragma:
288             case STMT.Import:
289             case STMT.Error:
290                 break;          // ignore these
291 
292             case STMT.ScopeGuard:
293             case STMT.Foreach:
294             case STMT.ForeachRange:
295             case STMT.Debug:
296             case STMT.CaseRange:
297             case STMT.StaticForeach:
298             case STMT.StaticAssert:
299             case STMT.Conditional:
300             case STMT.While:
301             case STMT.Forwarding:
302             case STMT.Mixin:
303             case STMT.Peel:
304             case STMT.Synchronized:
305                 assert(0);              // should have been rewritten
306         }
307     }
308 
309     visit(s);
310 }