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, either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 module gaem
.anal
.uninit
is aliced
;
21 import gaem
.anal
.utils
;
24 // ////////////////////////////////////////////////////////////////////////// //
25 void analUninited (NodeFunc fn
) {
28 Loc
[string
] vdecls
; // for error messages
30 int argvar (string s
) {
32 case "argument0": return 0;
33 case "argument1": return 1;
34 case "argument2": return 2;
35 case "argument3": return 3;
36 case "argument4": return 4;
37 case "argument5": return 5;
38 case "argument6": return 6;
39 case "argument7": return 7;
40 case "argument8": return 8;
41 case "argument9": return 9;
42 case "argument10": return 10;
43 case "argument11": return 11;
44 case "argument12": return 12;
45 case "argument13": return 13;
46 case "argument14": return 14;
47 case "argument15": return 15;
48 case "self": return 16;
49 case "other": return 17;
55 void warning(A
...) (Loc loc
, A args
) {
57 writeln(loc
, ": ", args
);
58 if (fn
.pp
!is null) fn
.pp
.printCaret(loc
);
61 // collect var declarations (gml is not properly scoped)
62 visitNodes(fn
.ebody
, (Node n
) {
63 if (auto vd
= cast(NodeVarDecl
)n
) {
64 foreach (immutable idx
, string name
; vd
.names
) {
66 if (vd
.asGlobal
) message(fn
, vd
.locs
[idx
], "conflicting variable '", name
, "' declaration (previous at ", vdecls
[name
].toStringNoFile
, ")");
67 } else if (name
in globals
) {
68 if (!vd
.asGlobal
) message(fn
, vd
.locs
[idx
], "conflicting variable '", name
, "' declaration (previous at ", vdecls
[name
].toStringNoFile
, ")");
70 vdecls
[name
] = vd
.locs
[idx
];
78 return VisitRes
.Continue
;
81 void findUninitialized () {
85 void processExpr (Node n
, bool asAss
=false) {
86 if (n
is null) return;
87 visitNodes(n
, (Node nn
) {
88 if (auto id
= cast(NodeId
)nn
) {
89 if (argvar(id
.name
) < 0) {
90 if (!asAss
&& id
.name
in locals
&& id
.name
!in inited
) {
91 message(fn
, nn
.loc
, "using uninitialized variable; declared at ", vdecls
[id
.name
].toStringNoFile
);
94 inited
[id
.name
] = true;
96 return VisitRes
.SkipChildren
;
98 if (auto n
= cast(NodeFCall
)nn
) {
99 if (cast(NodeId
)n
.fe
is null) message(fn
, n
.loc
, "invalid function call");
100 if (n
.args
.length
> 16) message(fn
, n
.loc
, "too many arguments in function call");
101 foreach (immutable idx
, Node a
; n
.args
) {
102 // no assignments allowed there
105 return VisitRes
.SkipChildren
;
107 return VisitRes
.Continue
;
111 void processStatement (Node nn
) {
112 if (nn
is null) return;
113 return selectNode
!void(nn
,
116 foreach (Node st
; n
.stats
) {
117 if (cast(NodeStatementBreakCont
)st
!is null) break;
118 processStatement(st
);
119 if (cast(NodeReturn
)st
!is null) break;
122 (NodeStatementEmpty n
) {},
123 (NodeStatementAss n
) {
124 if (cast(NodeId
)n
.el
is null && cast(NodeDot
)n
.el
is null && cast(NodeIndex
)n
.el
is null) {
125 message(fn
, nn
.loc
, "assignment to rvalue");
128 processExpr(n
.er
); // it is calculated first
129 if (auto did
= cast(NodeId
)n
.el
) {
130 inited
[did
.name
] = true;
131 used
[did
.name
] = true;
133 processExpr(n
.el
, asAss
:true);
136 (NodeStatementExpr n
) { processExpr(n
.e
); },
137 (NodeReturn n
) { processExpr(n
.e
); },
139 processExpr(n
.e
); // can't contain assignments
140 // body can be executed zero times, so...
141 auto before
= inited
.dup
;
142 processStatement(n
.ebody
);
147 auto before
= inited
.dup
;
148 processStatement(n
.et
);
149 auto tset
= inited
.dup
;
151 processStatement(n
.ef
);
152 // now copy to `before` all items that are set both in `tset` and in `inited`
153 foreach (string name
; inited
.byKey
) {
154 if (name
in tset
) before
[name
] = true;
158 (NodeStatementBreakCont n
) {},
160 processStatement(n
.einit
);
161 // "next" and "cond" can't contain assignments, so it's safe here
162 processExpr(n
.econd
);
163 processStatement(n
.enext
);
164 // yet body can be executed zero times, so...
165 auto before
= inited
.dup
;
166 processStatement(n
.ebody
);
170 // "cond" can't contain assignments, so it's safe here
171 processExpr(n
.econd
);
172 // yet body can be executed zero times, so...
173 auto before
= inited
.dup
;
174 processStatement(n
.ebody
);
178 // "cond" can't contain assignments, so it's safe here
179 processExpr(n
.econd
);
180 // body is guaranteed to execute at least one time
181 processStatement(n
.ebody
);
184 // "count" can't contain assignments, so it's safe here
185 processExpr(n
.ecount
);
186 // yet body can be executed zero times, so...
187 auto before
= inited
.dup
;
188 processStatement(n
.ebody
);
192 // "expr" can't contain assignments, so it's safe here
194 auto before
= inited
.dup
;
195 foreach (ref ci
; n
.cases
) {
196 processExpr(ci
.e
); // can't contain assignments
198 if (ci
.st
!is null) {
200 processStatement(ci
.st
);
205 () { assert(0, "unimplemented node: "~typeid(nn
).name
); },
209 processStatement(fn
.ebody
);
211 // now show unused locals
212 static struct Info
{ Loc loc
; string name
; }
214 foreach (string name
; locals
.keys
) {
216 //message(fn, vdecls[name], "unused local '", name, "'");
217 unusedLocs
~= Info(vdecls
[name
], name
);
221 import std
.algorithm
: sort
;
222 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
); });
223 foreach (ref nfo
; unusedLocs
) message(fn
, nfo
.loc
, "unused local '", nfo
.name
, "'");