2 * coded by Ketmar // Invisible Vector <ketmar@ketmar.no-ip.org>
3 * Understanding is not required. Only obedience.
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, version 3 of the License ONLY.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 module gaem
.anal
.uninit
is aliced
;
20 import gaem
.anal
.utils
;
23 // ////////////////////////////////////////////////////////////////////////// //
24 void analUninited (NodeFunc fn
) {
27 Loc
[string
] vdecls
; // for error messages
29 int argvar (string s
) {
31 case "argument0": return 0;
32 case "argument1": return 1;
33 case "argument2": return 2;
34 case "argument3": return 3;
35 case "argument4": return 4;
36 case "argument5": return 5;
37 case "argument6": return 6;
38 case "argument7": return 7;
39 case "argument8": return 8;
40 case "argument9": return 9;
41 case "argument10": return 10;
42 case "argument11": return 11;
43 case "argument12": return 12;
44 case "argument13": return 13;
45 case "argument14": return 14;
46 case "argument15": return 15;
47 case "self": return 16;
48 case "other": return 17;
54 void warning(A
...) (Loc loc
, A args
) {
56 writeln(loc
, ": ", args
);
57 if (fn
.pp
!is null) fn
.pp
.printCaret(loc
);
60 // collect var declarations (gml is not properly scoped)
61 visitNodes(fn
.ebody
, (Node n
) {
62 if (auto vd
= cast(NodeVarDecl
)n
) {
63 foreach (immutable idx
, string name
; vd
.names
) {
65 if (vd
.asGlobal
) message(fn
, vd
.locs
[idx
], "conflicting variable '", name
, "' declaration (previous at ", vdecls
[name
].toStringNoFile
, ")");
66 } else if (name
in globals
) {
67 if (!vd
.asGlobal
) message(fn
, vd
.locs
[idx
], "conflicting variable '", name
, "' declaration (previous at ", vdecls
[name
].toStringNoFile
, ")");
69 vdecls
[name
] = vd
.locs
[idx
];
77 return VisitRes
.Continue
;
80 void findUninitialized () {
84 void processExpr (Node n
, bool asAss
=false) {
85 if (n
is null) return;
86 visitNodes(n
, (Node nn
) {
87 if (auto id
= cast(NodeId
)nn
) {
88 if (argvar(id
.name
) < 0) {
89 if (!asAss
&& id
.name
in locals
&& id
.name
!in inited
) {
90 message(fn
, nn
.loc
, "using uninitialized variable; declared at ", vdecls
[id
.name
].toStringNoFile
);
93 inited
[id
.name
] = true;
95 return VisitRes
.SkipChildren
;
97 if (auto n
= cast(NodeFCall
)nn
) {
98 if (cast(NodeId
)n
.fe
is null) message(fn
, n
.loc
, "invalid function call");
99 if (n
.args
.length
> 16) message(fn
, n
.loc
, "too many arguments in function call");
100 foreach (immutable idx
, Node a
; n
.args
) {
101 // no assignments allowed there
104 return VisitRes
.SkipChildren
;
106 return VisitRes
.Continue
;
110 void processStatement (Node nn
) {
111 if (nn
is null) return;
112 return selectNode
!void(nn
,
115 foreach (Node st
; n
.stats
) {
116 if (cast(NodeStatementBreakCont
)st
!is null) break;
117 processStatement(st
);
118 if (cast(NodeReturn
)st
!is null) break;
121 (NodeStatementEmpty n
) {},
122 (NodeStatementAss n
) {
123 if (cast(NodeId
)n
.el
is null && cast(NodeDot
)n
.el
is null && cast(NodeIndex
)n
.el
is null) {
124 message(fn
, nn
.loc
, "assignment to rvalue");
127 processExpr(n
.er
); // it is calculated first
128 if (auto did
= cast(NodeId
)n
.el
) {
129 inited
[did
.name
] = true;
130 used
[did
.name
] = true;
132 processExpr(n
.el
, asAss
:true);
135 (NodeStatementExpr n
) { processExpr(n
.e
); },
136 (NodeReturn n
) { processExpr(n
.e
); },
138 processExpr(n
.e
); // can't contain assignments
139 // body can be executed zero times, so...
140 auto before
= inited
.dup
;
141 processStatement(n
.ebody
);
146 auto before
= inited
.dup
;
147 processStatement(n
.et
);
148 auto tset
= inited
.dup
;
150 processStatement(n
.ef
);
151 // now copy to `before` all items that are set both in `tset` and in `inited`
152 foreach (string name
; inited
.byKey
) {
153 if (name
in tset
) before
[name
] = true;
157 (NodeStatementBreakCont n
) {},
159 processStatement(n
.einit
);
160 // "next" and "cond" can't contain assignments, so it's safe here
161 processExpr(n
.econd
);
162 processStatement(n
.enext
);
163 // yet body can be executed zero times, so...
164 auto before
= inited
.dup
;
165 processStatement(n
.ebody
);
169 // "cond" can't contain assignments, so it's safe here
170 processExpr(n
.econd
);
171 // yet body can be executed zero times, so...
172 auto before
= inited
.dup
;
173 processStatement(n
.ebody
);
177 // "cond" can't contain assignments, so it's safe here
178 processExpr(n
.econd
);
179 // body is guaranteed to execute at least one time
180 processStatement(n
.ebody
);
183 // "count" can't contain assignments, so it's safe here
184 processExpr(n
.ecount
);
185 // yet body can be executed zero times, so...
186 auto before
= inited
.dup
;
187 processStatement(n
.ebody
);
191 // "expr" can't contain assignments, so it's safe here
193 auto before
= inited
.dup
;
194 foreach (ref ci
; n
.cases
) {
195 processExpr(ci
.e
); // can't contain assignments
197 if (ci
.st
!is null) {
199 processStatement(ci
.st
);
204 () { assert(0, "unimplemented node: "~typeid(nn
).name
); },
208 processStatement(fn
.ebody
);
210 // now show unused locals
211 static struct Info
{ Loc loc
; string name
; }
213 foreach (string name
; locals
.keys
) {
215 //message(fn, vdecls[name], "unused local '", name, "'");
216 unusedLocs
~= Info(vdecls
[name
], name
);
220 import std
.algorithm
: sort
;
221 unusedLocs
.sort
!((ref a
, ref b
) { if (a
.loc
.line
< b
.loc
.line
) return true; if (a
.loc
.line
> b
.loc
.line
) return false; return (a
.loc
.col
< b
.loc
.col
); });
222 foreach (ref nfo
; unusedLocs
) message(fn
, nfo
.loc
, "unused local '", nfo
.name
, "'");