• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

wurstscript / WurstScript / 228

29 Nov 2023 05:00PM UTC coverage: 62.48% (-0.09%) from 62.574%
228

push

circleci

web-flow
Show dialog for choosing game path, cleanup (#1083)

* show dialog for choosing game path

* cleanup code

* remove logs and refactor

* remove confusing mpq error, make some mpq loads readonly

17295 of 27681 relevant lines covered (62.48%)

0.62 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

82.13
de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/validation/WurstValidator.java
1
package de.peeeq.wurstscript.validation;
2

3
import com.google.common.collect.*;
4
import de.peeeq.wurstscript.WLogger;
5
import de.peeeq.wurstscript.ast.*;
6
import de.peeeq.wurstscript.attributes.CofigOverridePackages;
7
import de.peeeq.wurstscript.attributes.CompileError;
8
import de.peeeq.wurstscript.attributes.ImplicitFuncs;
9
import de.peeeq.wurstscript.attributes.names.DefLink;
10
import de.peeeq.wurstscript.attributes.names.FuncLink;
11
import de.peeeq.wurstscript.attributes.names.NameLink;
12
import de.peeeq.wurstscript.attributes.names.VarLink;
13
import de.peeeq.wurstscript.gui.ProgressHelper;
14
import de.peeeq.wurstscript.types.*;
15
import de.peeeq.wurstscript.utils.Utils;
16
import de.peeeq.wurstscript.validation.controlflow.DataflowAnomalyAnalysis;
17
import de.peeeq.wurstscript.validation.controlflow.ReturnsAnalysis;
18
import io.vavr.Tuple2;
19
import org.eclipse.jdt.annotation.Nullable;
20

21
import java.util.*;
22
import java.util.Map.Entry;
23
import java.util.stream.Collectors;
24

25
import static de.peeeq.wurstscript.attributes.SmallHelpers.superArgs;
26

27
/**
28
 * this class validates a wurstscript program
29
 * <p>
30
 * it has visit methods for different elements in the AST and checks whether
31
 * these are correct
32
 * <p>
33
 * the validation phase might not find all errors, code transformation and
34
 * optimization phases might detect other errors because they do a more
35
 * sophisticated analysis of the program
36
 * <p>
37
 * also note that many cases are already caught by the calculation of the
38
 * attributes
39
 */
40
public class WurstValidator {
41

42
    private final WurstModel prog;
43
    private int functionCount;
44
    private int visitedFunctions;
45
    private final Multimap<WScope, WScope> calledFunctions = HashMultimap.create();
1✔
46
    private @Nullable Element lastElement = null;
1✔
47
    private final HashSet<String> trveWrapperFuncs = new HashSet<>();
1✔
48
    private final HashMap<String, HashSet<FunctionCall>> wrapperCalls = new HashMap<>();
1✔
49

50
    public WurstValidator(WurstModel root) {
1✔
51
        this.prog = root;
1✔
52
    }
1✔
53

54
    public void validate(Collection<CompilationUnit> toCheck) {
55
        try {
56
            functionCount = countFunctions();
1✔
57
            visitedFunctions = 0;
1✔
58

59
            prog.getErrorHandler().setProgress("Checking wurst types",
1✔
60
                    ProgressHelper.getValidatorPercent(visitedFunctions, functionCount));
1✔
61

62

63
            for (CompilationUnit cu : toCheck) {
1✔
64
                walkTree(cu);
1✔
65
            }
1✔
66
            prog.getErrorHandler().setProgress("Post checks", 0.55);
1✔
67
            postChecks(toCheck);
1✔
68
        } catch (RuntimeException e) {
×
69
            WLogger.severe(e);
×
70
            Element le = lastElement;
×
71
            if (le != null) {
×
72
                le.addError("Encountered compiler bug near element " + Utils.printElement(le) + ":\n"
×
73
                        + Utils.printException(e));
×
74
            } else {
75
                // rethrow
76
                throw e;
×
77
            }
78
        }
1✔
79
    }
1✔
80

81
    /**
82
     * checks done after walking the tree
83
     */
84
    private void postChecks(Collection<CompilationUnit> toCheck) {
85
        checkUnusedImports(toCheck);
1✔
86
        ValidateGlobalsUsage.checkGlobalsUsage(toCheck);
1✔
87
        ValidateClassMemberUsage.checkClassMembers(toCheck);
1✔
88
        ValidateLocalUsage.checkLocalsUsage(toCheck);
1✔
89

90
        trveWrapperFuncs.forEach(wrapper -> {
1✔
91
            if (wrapperCalls.containsKey(wrapper)) {
1✔
92
                wrapperCalls.get(wrapper).forEach(call -> {
1✔
93
                    if (call.getArgs().size() > 1 && call.getArgs().get(1) instanceof ExprStringVal) {
1✔
94
                        ExprStringVal varName = (ExprStringVal) call.getArgs().get(1);
1✔
95
                        TRVEHelper.protectedVariables.add(varName.getValS());
1✔
96
                        WLogger.info("keep: " + varName.getValS());
1✔
97
                    } else {
1✔
98
                        call.addError("Map contains TriggerRegisterVariableEvent with non-constant arguments. Can't be optimized.");
×
99
                    }
100
                });
1✔
101
            }
102
        });
1✔
103
    }
1✔
104

105
    private void checkUnusedImports(Collection<CompilationUnit> toCheck) {
106
        for (CompilationUnit cu : toCheck) {
1✔
107
            for (WPackage p : cu.getPackages()) {
1✔
108
                checkUnusedImports(p);
1✔
109
            }
1✔
110
        }
1✔
111
    }
1✔
112

113
    private void checkUnusedImports(WPackage p) {
114
        Set<PackageOrGlobal> used = Sets.newLinkedHashSet();
1✔
115

116
        collectUsedPackages(used, p.getElements());
1✔
117

118
        // String usedToStr =
119
        // used.stream().map(Utils::printElement).sorted().collect(Collectors.joining(",
120
        // "));
121
        // System.out.println("used = " + usedToStr);
122

123
        // contributed packages for each import
124
        Map<WImport, Set<WPackage>> contributions = new HashMap<>();
1✔
125
        for (WImport imp : p.getImports()) {
1✔
126
            Set<WPackage> contributedPackages = contributedPackages(imp.attrImportedPackage(), used, new HashSet<>());
1✔
127
            contributions.put(imp, contributedPackages);
1✔
128
            // System.out.println( imp.getPackagename() + " contributes = " +
129
            // contributedPackages.stream().map(Utils::printElement).sorted().collect(Collectors.joining(",
130
            // ")));
131
        }
1✔
132

133
        // check for imports, which only contribute a subset of some other
134
        // import
135
        for (WImport imp : p.getImports()) {
1✔
136
            if (imp.attrImportedPackage() == null || imp.getIsPublic() || imp.getPackagename().equals("Wurst")) {
1✔
137
                continue;
1✔
138
            }
139
            Set<WPackage> impContributions = contributions.get(imp);
1✔
140
            if (impContributions.isEmpty()) {
1✔
141
                imp.addWarning("The import " + imp.getPackagename() + " is never used");
1✔
142
            } else {
143
                for (WImport imp2 : p.getImports()) {
1✔
144
                    if (imp == imp2) {
1✔
145
                        continue;
1✔
146
                    }
147
                    if (contributions.get(imp2).containsAll(impContributions)) {
1✔
148
                        imp.addWarning("The import " + imp.getPackagename()
1✔
149
                                + " can be removed, because it is already included in " + imp2.getPackagename() + ".");
1✔
150
                        break;
1✔
151
                    }
152

153
                }
1✔
154
            }
155
        }
1✔
156
    }
1✔
157

158
    private Set<WPackage> contributedPackages(WPackage p, Set<PackageOrGlobal> used, Set<WPackage> visited) {
159
        if (p == null) {
1✔
160
            return Collections.emptySet();
1✔
161
        }
162
        visited.add(p);
1✔
163
        Set<WPackage> result = new HashSet<>();
1✔
164
        if (used.contains(p)) {
1✔
165
            result.add(p);
1✔
166
        }
167
        for (WImport imp : p.getImports()) {
1✔
168
            WPackage imported = imp.attrImportedPackage();
1✔
169
            if (imp.getPackagename().equals("Wurst") || visited.contains(imported)) {
1✔
170
                continue;
1✔
171
            }
172
            if (imp.getIsPublic()) {
1✔
173
                result.addAll(contributedPackages(imported, used, visited));
1✔
174
            }
175
        }
1✔
176
        return result;
1✔
177
    }
178

179
    private WPackage getConfiguredPackage(Element e) {
180
        PackageOrGlobal p = e.attrNearestPackage();
1✔
181
        if(p instanceof WPackage) {
1✔
182
            if (p.getModel().attrConfigOverridePackages().containsValue(p)) {
1✔
183
                for(WPackage k : p.getModel().attrConfigOverridePackages().keySet()) {
1✔
184
                    if(p.getModel().attrConfigOverridePackages().get(k).equals(p)) {
1✔
185
                        return k;
1✔
186
                    }
187
                }
×
188
            }
189
        }
190
        return null;
×
191
    }
192

193
    private void collectUsedPackages(Set<PackageOrGlobal> used, Element e) {
194
        for (int i = 0; i < e.size(); i++) {
1✔
195
            collectUsedPackages(used, e.get(i));
1✔
196
        }
197

198
        if (e instanceof FuncRef) {
1✔
199
            FuncRef fr = (FuncRef) e;
1✔
200
            FuncLink link = fr.attrFuncLink();
1✔
201
            if (link != null) {
1✔
202
                used.add(link.getDef().attrNearestPackage());
1✔
203
                if(link.getDef().attrHasAnnotation("@config")) {
1✔
204
                    WPackage configPackage = getConfiguredPackage(link.getDef());
1✔
205
                    if(configPackage != null) {
1✔
206
                        used.add(configPackage);
1✔
207
                    }
208
                }
209
            }
210
        }
211
        if (e instanceof NameRef) {
1✔
212
            NameRef nr = (NameRef) e;
1✔
213
            NameLink def = nr.attrNameLink();
1✔
214
            if (def != null) {
1✔
215
                used.add(def.getDef().attrNearestPackage());
1✔
216
                if(def.getDef().attrHasAnnotation("@config")) {
1✔
217
                    WPackage configPackage = getConfiguredPackage(def.getDef());
1✔
218
                    if(configPackage != null) {
1✔
219
                        used.add(configPackage);
1✔
220
                    }
221
                }
222
            }
223
        }
224
        if (e instanceof TypeRef) {
1✔
225
            TypeRef t = (TypeRef) e;
1✔
226
            TypeDef def = t.attrTypeDef();
1✔
227
            if (def != null) {
1✔
228
                used.add(def.attrNearestPackage());
1✔
229
            }
230
        }
231
        if (e instanceof ExprBinary) {
1✔
232
            ExprBinary binop = (ExprBinary) e;
1✔
233
            FuncLink def = binop.attrFuncLink();
1✔
234
            if (def != null) {
1✔
235
                used.add(def.getDef().attrNearestPackage());
1✔
236
            }
237
        }
238
        if (e instanceof Expr) {
1✔
239
            WurstType typ = ((Expr) e).attrTyp();
1✔
240
            if (typ instanceof WurstTypeNamedScope) {
1✔
241
                WurstTypeNamedScope ns = (WurstTypeNamedScope) typ;
1✔
242
                NamedScope def = ns.getDef();
1✔
243
                if (def != null) {
1✔
244
                    used.add(def.attrNearestPackage());
1✔
245
                }
246
            } else if (typ instanceof WurstTypeTuple) {
1✔
247
                TupleDef def = ((WurstTypeTuple) typ).getTupleDef();
1✔
248
                used.add(def.attrNearestPackage());
1✔
249
            }
250
        }
251
        if (e instanceof ModuleUse) {
1✔
252
            ModuleUse mu = (ModuleUse) e;
1✔
253
            @Nullable ModuleDef def = mu.attrModuleDef();
1✔
254
            if (def != null) {
1✔
255
                used.add(def.attrNearestPackage());
1✔
256
            }
257
        }
258
    }
1✔
259

260
    private void walkTree(Element e) {
261
        lastElement = e;
1✔
262
        check(e);
1✔
263
        lastElement = null;
1✔
264
        for (int i = 0; i < e.size(); i++) {
1✔
265
            walkTree(e.get(i));
1✔
266
        }
267
    }
1✔
268

269
    private void check(Element e) {
270
        try {
271
            if (e instanceof Annotation)
1✔
272
                checkAnnotation((Annotation) e);
1✔
273
            if (e instanceof AstElementWithTypeParameters)
1✔
274
                checkTypeParameters((AstElementWithTypeParameters) e);
1✔
275
            if (e instanceof AstElementWithNameId)
1✔
276
                checkName((AstElementWithNameId) e);
1✔
277
            if (e instanceof ClassDef) {
1✔
278
                checkAbstractMethods((ClassDef) e);
1✔
279
                visit((ClassDef) e);
1✔
280
            }
281
            if (e instanceof ClassOrModule)
1✔
282
                checkConstructorsUnique((ClassOrModule) e);
1✔
283
            if (e instanceof CompilationUnit)
1✔
284
                checkPackageName((CompilationUnit) e);
1✔
285
            if (e instanceof ConstructorDef) {
1✔
286
                checkConstructor((ConstructorDef) e);
1✔
287
                checkConstructorSuperCall((ConstructorDef) e);
1✔
288
            }
289
            if (e instanceof ExprBinary)
1✔
290
                visit((ExprBinary) e);
1✔
291
            if (e instanceof ExprClosure)
1✔
292
                checkClosure((ExprClosure) e);
1✔
293
            if (e instanceof ExprEmpty)
1✔
294
                checkExprEmpty((ExprEmpty) e);
1✔
295
            if (e instanceof ExprIntVal)
1✔
296
                checkIntVal((ExprIntVal) e);
1✔
297
            if (e instanceof ExprFuncRef)
1✔
298
                checkFuncRef((ExprFuncRef) e);
1✔
299
            if (e instanceof ExprFunctionCall) {
1✔
300
                checkBannedFunctions((ExprFunctionCall) e);
1✔
301
                visit((ExprFunctionCall) e);
1✔
302
            }
303
            if (e instanceof ExprMemberMethod)
1✔
304
                visit((ExprMemberMethod) e);
1✔
305
            if (e instanceof ExprMemberVar)
1✔
306
                checkMemberVar((ExprMemberVar) e);
1✔
307
            if (e instanceof ExprMemberArrayVar)
1✔
308
                checkMemberArrayVar((ExprMemberArrayVar) e);
1✔
309
            if (e instanceof ExprNewObject) {
1✔
310
                checkNewObj((ExprNewObject) e);
1✔
311
                visit((ExprNewObject) e);
1✔
312
            }
313
            if (e instanceof ExprNull)
1✔
314
                checkExprNull((ExprNull) e);
1✔
315
            if (e instanceof ExprVarAccess)
1✔
316
                visit((ExprVarAccess) e);
1✔
317
            if (e instanceof ExprVarArrayAccess)
1✔
318
                checkArrayAccess((ExprVarArrayAccess) e);
1✔
319
            if (e instanceof ExtensionFuncDef)
1✔
320
                visit((ExtensionFuncDef) e);
1✔
321
            if (e instanceof FuncDef)
1✔
322
                visit((FuncDef) e);
1✔
323
            if (e instanceof FuncRef)
1✔
324
                checkFuncRef((FuncRef) e);
1✔
325
            if (e instanceof FunctionLike)
1✔
326
                checkUninitializedVars((FunctionLike) e);
1✔
327
            if (e instanceof GlobalVarDef)
1✔
328
                visit((GlobalVarDef) e);
1✔
329
            if (e instanceof HasModifier)
1✔
330
                checkModifiers((HasModifier) e);
1✔
331
            if (e instanceof HasTypeArgs)
1✔
332
                checkTypeBinding((HasTypeArgs) e);
1✔
333
            if (e instanceof InterfaceDef)
1✔
334
                checkInterfaceDef((InterfaceDef) e);
1✔
335
            if (e instanceof LocalVarDef) {
1✔
336
                checkLocalShadowing((LocalVarDef) e);
1✔
337
                visit((LocalVarDef) e);
1✔
338
            }
339
            if (e instanceof Modifiers)
1✔
340
                visit((Modifiers) e);
1✔
341
            if (e instanceof ModuleDef)
1✔
342
                visit((ModuleDef) e);
1✔
343
            if (e instanceof NameDef) {
1✔
344
                nameDefsMustNotBeNamedAfterJassNativeTypes((NameDef) e);
1✔
345
                checkConfigOverride((NameDef) e);
1✔
346
            }
347
            if (e instanceof NameRef) {
1✔
348
                checkImplicitParameter((NameRef) e);
1✔
349
                checkNameRef((NameRef) e);
1✔
350
            }
351
            if (e instanceof StmtCall)
1✔
352
                checkCall((StmtCall) e);
1✔
353
            if (e instanceof ExprDestroy)
1✔
354
                visit((ExprDestroy) e);
1✔
355
            if (e instanceof StmtForRange)
1✔
356
                checkForRange((StmtForRange) e);
1✔
357
            if (e instanceof StmtIf)
1✔
358
                visit((StmtIf) e);
1✔
359
            if (e instanceof StmtReturn)
1✔
360
                visit((StmtReturn) e);
1✔
361
            if (e instanceof StmtSet)
1✔
362
                checkStmtSet((StmtSet) e);
1✔
363
            if (e instanceof StmtWhile)
1✔
364
                visit((StmtWhile) e);
1✔
365
            if (e instanceof SwitchStmt)
1✔
366
                checkSwitch((SwitchStmt) e);
1✔
367
            if (e instanceof TypeExpr)
1✔
368
                checkTypeExpr((TypeExpr) e);
1✔
369
            if (e instanceof TypeExprArray)
1✔
370
                checkCodeArrays((TypeExprArray) e);
1✔
371
            if (e instanceof TupleDef)
1✔
372
                checkTupleDef((TupleDef) e);
1✔
373
            if (e instanceof VarDef)
1✔
374
                checkVarDef((VarDef) e);
1✔
375
            if (e instanceof WImport)
1✔
376
                visit((WImport) e);
1✔
377
            if (e instanceof WPackage)
1✔
378
                checkPackage((WPackage) e);
1✔
379
            if (e instanceof WParameter) {
1✔
380
                checkParameter((WParameter) e);
1✔
381
                visit((WParameter) e);
1✔
382
            }
383
            if (e instanceof WScope)
1✔
384
                checkForDuplicateNames((WScope) e);
1✔
385
            if (e instanceof WStatement)
1✔
386
                checkReachability((WStatement) e);
1✔
387
            if (e instanceof WurstModel)
1✔
388
                checkForDuplicatePackages((WurstModel) e);
×
389
            if (e instanceof WStatements) {
1✔
390
                checkForInvalidStmts((WStatements) e);
1✔
391
                checkForEmptyBlocks((WStatements) e);
1✔
392
            }
393
            if (e instanceof StmtExitwhen)
1✔
394
                visit((StmtExitwhen) e);
1✔
395
        } catch (CyclicDependencyError cde) {
×
396
            cde.printStackTrace();
×
397
            Element element = cde.getElement();
×
398
            String attr = cde.getAttributeName().replaceFirst("^attr", "");
×
399
            WLogger.info(Utils.printElementWithSource(Optional.of(element))
×
400
                + " depends on itself when evaluating attribute "
401
                + attr);
402
            WLogger.info(cde);
×
403
            throw new CompileError(element.attrSource(),
×
404
                    Utils.printElement(element) + " depends on itself when evaluating attribute " + attr);
×
405
        }
1✔
406
    }
1✔
407

408
    private void checkAbstractMethods(ClassDef c) {
409
        ImmutableMultimap<String, DefLink> nameLinks = c.attrNameLinks();
1✔
410
        if (!c.attrIsAbstract()) {
1✔
411
            StringBuilder toImplement = new StringBuilder();
1✔
412
            // should have no abstract methods
413
            for (DefLink link : nameLinks.values()) {
1✔
414
                NameDef f = link.getDef();
1✔
415
                if (f.attrIsAbstract()) {
1✔
416
                    if (f.attrNearestStructureDef() == c) {
1✔
417
                        Element loc = f.getModifiers().stream()
1✔
418
                                .filter(m -> m instanceof ModAbstract)
1✔
419
                                .<Element>map(x -> x)
1✔
420
                                .findFirst()
1✔
421
                                .orElse(f);
1✔
422
                        loc.addError("Non-abstract class " + c.getName() + " cannot have abstract functions like " + f.getName());
×
423
                    } else if (link instanceof FuncLink) {
1✔
424
                        toImplement.append("\n    ");
1✔
425
                        toImplement.append(((FuncLink) link).printFunctionTemplate());
1✔
426
                    }
427
                }
428
            }
1✔
429
            if (toImplement.length() > 0) {
1✔
430
                c.addError("Non-abstract class " + c.getName() + " must implement the following functions:" + toImplement);
1✔
431
            }
432
        }
433
    }
1✔
434

435
    private void visit(StmtExitwhen exitwhen) {
436
        Element parent = exitwhen.getParent();
1✔
437
        while (!(parent instanceof FunctionDefinition)) {
1✔
438
            if (parent instanceof StmtForEach) {
1✔
439
                StmtForEach forEach = (StmtForEach) parent;
1✔
440
                if (forEach.getIn().tryGetNameDef().attrIsVararg()) {
1✔
441
                    exitwhen.addError("Cannot use break in vararg for each loops.");
×
442
                }
443
                return;
×
444
            } else if (parent instanceof LoopStatement) {
1✔
445
                return;
1✔
446
            }
447
            parent = parent.getParent();
1✔
448
        }
449
        exitwhen.addError("Break is not allowed outside of loop statements.");
×
450
    }
×
451

452
    private void checkTupleDef(TupleDef e) {
453
        checkTupleDefCycle(e, new ArrayList<>());
1✔
454

455
    }
1✔
456

457
    private boolean checkTupleDefCycle(TupleDef e, ArrayList<TupleDef> tuples) {
458
        if (tuples.contains(e)) {
1✔
459
            return true;
1✔
460
        }
461
        tuples.add(e);
1✔
462
        try {
463
            for (WParameter param : e.getParameters()) {
1✔
464
                WurstType t = param.getTyp().attrTyp();
1✔
465
                if (t instanceof WurstTypeTuple) {
1✔
466
                    WurstTypeTuple tt = (WurstTypeTuple) t;
1✔
467
                    TupleDef tDef = tt.getTupleDef();
1✔
468
                    if (checkTupleDefCycle(tDef, tuples)) {
1✔
469
                        param.addError("Parameter " + param.getName() + " is recursive. This is not allowed for tuples.");
×
470
                        return true;
×
471
                    }
472
                }
473
            }
1✔
474
            return false;
1✔
475
        } finally {
476
            tuples.remove(e);
1✔
477
        }
478
    }
479

480
    private void checkForInvalidStmts(WStatements stmts) {
481
        for (WStatement s : stmts) {
1✔
482
            if (s instanceof ExprVarAccess) {
1✔
483
                ExprVarAccess ev = (ExprVarAccess) s;
1✔
484
                s.addError("Use of variable " + ev.getVarName() + " is an incomplete statement.");
×
485
            }
486
        }
1✔
487
    }
1✔
488

489
    private void checkForEmptyBlocks(WStatements e) {
490
        Element parent = e.getParent();
1✔
491
        // some parent cases to ignore:
492
        if (parent instanceof OnDestroyDef
1✔
493
            || parent instanceof ConstructorDef
494
            || parent instanceof FunctionDefinition
495
            || parent instanceof SwitchDefaultCaseStatements
496
            || parent instanceof SwitchCase) {
497
            return;
1✔
498
        }
499
        if (parent instanceof ExprStatementsBlock) {
1✔
500
            // for blocks in closures, we have StartFunction and EndFunction statements, so must be > 2 to be nonempty
501
            if (e.size() > 2) {
1✔
502
                return;
1✔
503
            }
504
            parent.getParent().addWarning("This function has an empty body. Write 'skip' if you intend to leave it empty.");
1✔
505
            return;
1✔
506
        }
507
        if (!e.isEmpty()) {
1✔
508
            return;
1✔
509
        }
510
        if (Utils.isJassCode(parent)) {
1✔
511
            // no warnings in Jass code
512
            return;
1✔
513
        }
514

515
        if (parent instanceof StmtIf) {
1✔
516
            StmtIf stmtIf = (StmtIf) parent;
1✔
517
            if (e == stmtIf.getElseBlock() && stmtIf.getHasElse()) {
1✔
518
                parent.addWarning("This if-statement has an empty else-block.");
1✔
519
            } else if (e == stmtIf.getThenBlock()) {
1✔
520
                parent.addWarning("This if-statement has an empty then-block. Write 'skip' if you intend to leave it empty.");
1✔
521
            }
522
            return;
1✔
523
        }
524

525
        parent.addWarning("This statement (" + Utils.printElement(parent) + ") contains an empty block. Write 'skip' if you intend to leave it empty.");
1✔
526
    }
1✔
527

528
    private void checkName(AstElementWithNameId e) {
529
        String name = e.getNameId().getName();
1✔
530
        TypeDef def = e.lookupType(name, false);
1✔
531

532
        if (def != e && def instanceof NativeType) {
1✔
533
            e.addError(
1✔
534
                    "The name '" + name + "' is already used as a native type in " + Utils.printPos(def.getSource()));
1✔
535
        } else if (!e.attrSource().getFile().endsWith(".j")) {
1✔
536
            switch (name) {
1✔
537
                case "int":
538
                case "integer":
539
                case "real":
540
                case "code":
541
                case "boolean":
542
                case "string":
543
                case "handle":
544
                    e.addError("The name '" + name + "' is a built-in type and cannot be used here.");
×
545
            }
546
        }
547
    }
1✔
548

549
    private void checkConfigOverride(NameDef e) {
550
        if (!e.hasAnnotation("@config")) {
1✔
551
            return;
1✔
552
        }
553
        PackageOrGlobal nearestPackage = e.attrNearestPackage();
1✔
554
        if (!(nearestPackage instanceof WPackage)) {
1✔
555
            e.addError("Annotation @config can only be used in packages.");
×
556
            return;
×
557
        }
558
        WPackage configPackage = (WPackage) nearestPackage;
1✔
559
        if (!configPackage.getName().endsWith(CofigOverridePackages.CONFIG_POSTFIX)) {
1✔
560
            e.addError(
×
561
                    "Annotation @config can only be used in config packages (package name has to end with '_config').");
562
            return;
×
563
        }
564

565
        WPackage origPackage = CofigOverridePackages.getOriginalPackage(configPackage);
1✔
566
        if (origPackage == null) {
1✔
567
            return;
×
568
        }
569

570
        if (e instanceof GlobalVarDef) {
1✔
571
            GlobalVarDef v = (GlobalVarDef) e;
1✔
572
            NameLink origVar = origPackage.getElements().lookupVarNoConfig(v.getName(), false);
1✔
573
            if (origVar == null) {
1✔
574
                e.addError("Could not find var " + v.getName() + " in configured package.");
×
575
                return;
×
576
            }
577

578
            if (!v.attrTyp().equalsType(origVar.getTyp(), v)) {
1✔
579
                e.addError("Configured variable must have type " + origVar.getTyp() + " but the found type is "
1✔
580
                        + v.attrTyp() + ".");
1✔
581
                return;
×
582
            }
583

584
            if (!origVar.getDef().hasAnnotation("@configurable")) {
1✔
585
                e.addWarning("The configured variable " + v.getName() + " is not marked with @configurable.\n"
1✔
586
                        + "It is still possible to configure this var but it is not recommended.");
587
            }
588

589
        } else if (e instanceof FuncDef) {
1✔
590
            FuncDef funcDef = (FuncDef) e;
1✔
591
            Collection<FuncLink> funcs = origPackage.getElements().lookupFuncsNoConfig(funcDef.getName(), false);
1✔
592
            FuncDef configuredFunc = null;
1✔
593
            for (NameLink nameLink : funcs) {
1✔
594
                if (nameLink.getDef() instanceof FuncDef) {
1✔
595
                    FuncDef f = (FuncDef) nameLink.getDef();
1✔
596
                    if (equalSignatures(funcDef, f)) {
1✔
597
                        configuredFunc = f;
1✔
598
                        break;
1✔
599
                    }
600
                }
601
            }
1✔
602
            if (configuredFunc == null) {
1✔
603
                funcDef.addError("Could not find a function " + funcDef.getName()
×
604
                        + " with the same signature in the configured package.");
605
            } else {
606
                if (!configuredFunc.hasAnnotation("@configurable")) {
1✔
607
                    e.addWarning("The configured function " + funcDef.getName() + " is not marked with @configurable.\n"
1✔
608
                            + "It is still possible to configure this function but it is not recommended.");
609
                }
610
            }
611

612
        } else {
1✔
613
            e.addError("Configuring " + Utils.printElement(e) + " is not supported by Wurst.");
×
614
        }
615
    }
1✔
616

617
    private boolean equalSignatures(FuncDef f, FuncDef g) {
618
        if (f.getParameters().size() != g.getParameters().size()) {
1✔
619
            return false;
×
620
        }
621
        if (!f.attrReturnTyp().equalsType(g.attrReturnTyp(), f)) {
1✔
622
            return false;
×
623
        }
624
        for (int i = 0; i < f.getParameters().size(); i++) {
1✔
625
            if (!f.getParameters().get(i).attrTyp().equalsType(g.getParameters().get(i).attrTyp(), f)) {
1✔
626
                return false;
1✔
627
            }
628
        }
629

630
        return true;
1✔
631
    }
632

633
    private void checkExprEmpty(ExprEmpty e) {
634
        e.addError("Incomplete expression...");
1✔
635

636
    }
1✔
637

638
    private void checkMemberArrayVar(ExprMemberArrayVar e) {
639
        // TODO Auto-generated method stub
640

641
    }
1✔
642

643
    private void checkNameRef(NameRef e) {
644
        if (e.getVarName().isEmpty()) {
1✔
645
            e.addError("Missing variable name.");
×
646
        }
647
    }
1✔
648

649
    private void checkPackage(WPackage p) {
650
        checkForDuplicateImports(p);
1✔
651
        p.attrInitDependencies();
1✔
652
    }
1✔
653

654
    private void checkTypeExpr(TypeExpr e) {
655
        if (e instanceof TypeExprResolved) {
1✔
656
            return;
1✔
657
        }
658
        if (e.isModuleUseTypeArg()) {
1✔
659
            return;
×
660
        }
661

662
        TypeDef typeDef = e.attrTypeDef();
1✔
663
        // check that modules are not used as normal types
664
        if (e.attrTypeDef() instanceof ModuleDef) {
1✔
665
            ModuleDef md = (ModuleDef) e.attrTypeDef();
1✔
666
            checkModuleTypeUsedCorrectly(e, md);
1✔
667
        }
668

669
        if (typeDef instanceof TypeParamDef) { // references a type parameter
1✔
670
            TypeParamDef tp = (TypeParamDef) typeDef;
1✔
671
            checkTypeparamsUsedCorrectly(e, tp);
1✔
672
        }
673

674
    }
1✔
675

676
    /**
677
     * Checks that module types are only used in valid places
678
     */
679
    private void checkModuleTypeUsedCorrectly(TypeExpr e, ModuleDef md) {
680
        if (e instanceof TypeExprThis) {
1✔
681
            // thistype is allowed, because it is translated to a real type when used
682
            return;
×
683
        }
684
        if (e.getParent() instanceof TypeExprThis) {
1✔
685
            TypeExprThis parent = (TypeExprThis) e.getParent();
1✔
686
            if (parent.getScopeType() == e) {
1✔
687
                // ModuleName.thistype is allowed
688
                // TODO (maybe check here that it is a parent)
689
                return;
1✔
690
            }
691
        }
692
        if (e instanceof TypeExprSimple) {
1✔
693
            TypeExprSimple tes = (TypeExprSimple) e;
1✔
694
            if (tes.getScopeType() instanceof TypeExpr) {
1✔
695
                TypeExpr scopeType = (TypeExpr) tes.getScopeType();
×
696
                if (scopeType instanceof TypeExprThis
×
697
                        || scopeType.attrTypeDef() instanceof ModuleDef) {
×
698
                    // thistype.A etc. is allowed
699
                    return;
×
700
                }
701
            }
702
        }
703
        e.addError("Cannot use module type " + md.getName() + " in this context.");
×
704
    }
×
705

706
    /**
707
     * check that type parameters are used in correct contexts:
708
     */
709
    private void checkTypeparamsUsedCorrectly(TypeExpr e, TypeParamDef tp) {
710
        if (tp.isStructureDefTypeParam()) { // typeParamDef is for
1✔
711
            // structureDef
712
            if (tp.attrNearestStructureDef() instanceof ModuleDef) {
1✔
713
                // in modules we can also type-params in static contexts
714
                return;
1✔
715
            }
716

717
            if (!e.attrIsDynamicContext()) {
1✔
718
                e.addError("Type variables must not be used in static contexts.");
×
719
            }
720
        }
721
    }
1✔
722

723
    private void checkClosure(ExprClosure e) {
724
        WurstType expectedTyp = e.attrExpectedTypAfterOverloading();
1✔
725
        if (expectedTyp instanceof WurstTypeCode) {
1✔
726
            // TODO check if no vars are captured
727
            if (!e.attrCapturedVariables().isEmpty()) {
1✔
728
                for (Entry<Element, VarDef> elem : e.attrCapturedVariables().entries()) {
1✔
729
                    elem.getKey().addError("Cannot capture local variable '" + elem.getValue().getName()
×
730
                            + "' in anonymous function. This is only possible with closures.");
731
                }
×
732
            }
733
        } else if (expectedTyp instanceof WurstTypeUnknown || expectedTyp instanceof WurstTypeClosure) {
1✔
734
            e.addError("Closures can only be used when a interface or class type is given.");
×
735

736
        } else if (!(expectedTyp instanceof WurstTypeClass
1✔
737
                || expectedTyp instanceof WurstTypeInterface)) {
738
            e.addError("Closures can only be used when a interface or class type is given, " + "but at this position a "
×
739
                    + expectedTyp + " is expected.");
740
        }
741
        e.attrCapturedVariables();
1✔
742

743
        if (e.getImplementation() instanceof ExprStatementsBlock) {
1✔
744
            ExprStatementsBlock block = (ExprStatementsBlock) e.getImplementation();
1✔
745
            new DataflowAnomalyAnalysis(false).execute(block);
1✔
746
        }
747

748
        if (expectedTyp instanceof WurstTypeClass) {
1✔
749
            WurstTypeClass ct = (WurstTypeClass) expectedTyp;
1✔
750

751
            ClassDef cd = ct.getClassDef();
1✔
752
            if (cd.getConstructors().stream().noneMatch(constr -> constr.getParameters().isEmpty())) {
1✔
753
                e.addError("No default constructor for class " + ct
×
754
                        + " found, so it cannot be instantiated using an anonymous function.");
755
            }
756
        }
757

758
    }
1✔
759

760
    private void checkConstructorsUnique(ClassOrModule c) {
761
        List<ConstructorDef> constrs = c.getConstructors();
1✔
762

763
        for (int i = 0; i < constrs.size() - 1; i++) {
1✔
764
            ConstructorDef c1 = constrs.get(i);
1✔
765
            for (int j = i + 1; i < constrs.size(); i++) {
1✔
766
                ConstructorDef c2 = constrs.get(j);
1✔
767
                if (c1.getParameters().size() != c2.getParameters().size()) {
1✔
768
                    continue;
1✔
769
                }
770

771
                if (!parametersTypeDisjunct(c1.getParameters(), c2.getParameters())) {
1✔
772
                    c2.addError(
1✔
773
                            "Duplicate constructor, an other constructor with similar types is already defined in line "
774
                                    + c1.attrSource().getLine());
1✔
775
                }
776
            }
777
        }
778

779
    }
1✔
780

781
    private boolean parametersTypeDisjunct(WParameters params1, WParameters params2) {
782
        for (int i = 0; i < params1.size(); i++) {
1✔
783
            WurstType t1 = params1.get(i).attrTyp();
1✔
784
            WurstType t2 = params2.get(i).attrTyp();
1✔
785
            if (!t1.isSubtypeOf(t2, params1) && !t2.isSubtypeOf(t1, params2)) {
1✔
786
                return true;
1✔
787
            }
788
        }
789
        return false;
1✔
790
    }
791

792
    private void checkImplicitParameter(NameRef e) {
793
        e.attrImplicitParameter();
1✔
794
    }
1✔
795

796
    private void checkTypeParameters(AstElementWithTypeParameters e) {
797
        for (TypeParamDef ta : e.getTypeParameters()) {
1✔
798
            if (ta.getName().contains("<") || ta.getName().startsWith("#")) {
1✔
799
                ta.addError("Type parameter must be a simple name ");
×
800
            } else {
801
                checkTypeName(ta, ta.getName());
1✔
802
            }
803
            ta.attrTyp();
1✔
804
        }
1✔
805
    }
1✔
806

807
    private void checkExprNull(ExprNull e) {
808
        if (!Utils.isJassCode(e) && e.attrExpectedTyp() instanceof WurstTypeUnknown) {
1✔
809
            e.addError(
×
810
                    "Cannot use 'null' constant here because " + "the compiler cannot infer which kind of null it is.");
811
        }
812

813
    }
1✔
814

815
    private void checkForRange(StmtForRange e) {
816
        if (!(e.getLoopVar().attrTyp().isSubtypeOf(WurstTypeInt.instance(), e))) {
1✔
817
            e.getLoopVar().addError("For-loop variable must be int.");
×
818
        }
819
        if (!(e.getTo().attrTyp().isSubtypeOf(WurstTypeInt.instance(), e))) {
1✔
820
            e.getLoopVar().addError("For-loop target must be int.");
×
821
        }
822
        if (!(e.getStep().attrTyp().isSubtypeOf(WurstTypeInt.instance(), e))) {
1✔
823
            e.getLoopVar().addError("For-loop step must be int.");
×
824
        }
825
    }
1✔
826

827
    private void checkIntVal(ExprIntVal e) {
828
        // check range? ...
829
    }
1✔
830

831
    private int countFunctions() {
832
        final int[] functionCount = new int[1];
1✔
833
        prog.accept(new WurstModel.DefaultVisitor() {
1✔
834

835
            @Override
836
            public void visit(FuncDef f) {
837
                super.visit(f);
1✔
838
                functionCount[0]++;
1✔
839
            }
1✔
840
        });
841
        return functionCount[0];
1✔
842
    }
843

844
    private void checkStmtSet(StmtSet s) {
845
        NameLink nameLink = s.getUpdatedExpr().attrNameLink();
1✔
846
        if (nameLink == null) {
1✔
847
            s.getUpdatedExpr().addError("Could not find variable " + s.getUpdatedExpr().getVarName() + ".");
×
848
            return;
×
849
        }
850

851
        if (!(nameLink instanceof VarLink)) {
1✔
852
            s.getUpdatedExpr()
1✔
853
                    .addError("Invalid assignment. This is not a variable, this is a " + nameLink);
×
854
            return;
×
855
        }
856

857
        WurstType leftType = s.getUpdatedExpr().attrTyp();
1✔
858
        WurstType rightType = s.getRight().attrTyp();
1✔
859

860
        checkAssignment(Utils.isJassCode(s), s, leftType, rightType);
1✔
861

862
        checkIfAssigningToConstant(s.getUpdatedExpr());
1✔
863

864
        checkIfNoEffectAssignment(s);
1✔
865
    }
1✔
866

867
    private void checkIfNoEffectAssignment(StmtSet s) {
868
        if (refersToSameVar(s.getUpdatedExpr(), s.getRight())) {
1✔
869
            s.addWarning("The assignment to " + Utils.printElement(s.getUpdatedExpr().attrNameDef())
1✔
870
                    + " probably has no effect.");
871
        }
872

873
    }
1✔
874

875
    private boolean refersToSameVar(OptExpr a, OptExpr b) {
876
        if (a instanceof NoExpr && b instanceof NoExpr) {
1✔
877
            return true;
1✔
878
        }
879
        if (a instanceof ExprThis && b instanceof ExprThis) {
1✔
880
            return true;
1✔
881
        }
882
        if (a instanceof NameRef && b instanceof NameRef) {
1✔
883
            NameRef va = (NameRef) a;
1✔
884
            NameRef vb = (NameRef) b;
1✔
885
            NameLink nla = va.attrNameLink();
1✔
886
            NameLink nlb = vb.attrNameLink();
1✔
887
            if (nla != null && nlb != null && nla.getDef() == nlb.getDef()
1✔
888
                    && refersToSameVar(va.attrImplicitParameter(), vb.attrImplicitParameter())) {
1✔
889
                if (va instanceof AstElementWithIndexes && vb instanceof AstElementWithIndexes) {
1✔
890
                    AstElementWithIndexes vai = (AstElementWithIndexes) va;
1✔
891
                    AstElementWithIndexes vbi = (AstElementWithIndexes) vb;
1✔
892

893
                    for (int i = 0; i < vai.getIndexes().size() && i < vbi.getIndexes().size(); i++) {
1✔
894
                        if (!refersToSameVar(vai.getIndexes().get(i), vbi.getIndexes().get(i))) {
1✔
895
                            return false;
1✔
896
                        }
897
                    }
898
                }
899
                return true;
1✔
900
            }
901
        }
902
        return false;
1✔
903
    }
904

905
    private void checkIfAssigningToConstant(final LExpr left) {
906
        left.match(new LExpr.MatcherVoid() {
1✔
907

908
            @Override
909
            public void case_ExprVarArrayAccess(ExprVarArrayAccess e) {
910

911
            }
1✔
912

913
            @Override
914
            public void case_ExprVarAccess(ExprVarAccess e) {
915
                checkVarNotConstant(e, e.attrNameLink());
1✔
916
            }
1✔
917

918
            @Override
919
            public void case_ExprMemberVarDot(ExprMemberVarDot e) {
920
                if (e.attrNameDef() instanceof WParameter) {
1✔
921
                    // we have an assignment to a tuple variable
922
                    // check whether left side is 'this' or a constant variable
923
                    if (e.getLeft() instanceof ExprThis) {
1✔
924
                        e.addError("Cannot change 'this'. Tuples are not classes.");
×
925
                    } else if (e.getLeft() instanceof NameRef) {
1✔
926
                        checkIfAssigningToConstant((NameRef) e.getLeft());
1✔
927
                    } else {
928
                        e.addError(
×
929
                                "Ok, so you are trying to assign something to the return value of a function. This wont do nothing. Tuples are not classes.");
930
                    }
931
                }
932
                checkVarNotConstant(e, e.attrNameLink());
1✔
933
            }
1✔
934

935
            @Override
936
            public void case_ExprMemberArrayVarDot(ExprMemberArrayVarDot e) {
937

938
            }
1✔
939

940
            @Override
941
            public void case_ExprMemberArrayVarDotDot(ExprMemberArrayVarDotDot e) {
942
                e.addError("Cannot assign to dot-dot-expression.");
×
943
            }
×
944

945
            @Override
946
            public void case_ExprMemberVarDotDot(ExprMemberVarDotDot e) {
947
                e.addError("Cannot assign to dot-dot-expression.");
×
948
            }
×
949
        });
950
    }
1✔
951

952
    private void checkVarNotConstant(NameRef left, @Nullable NameLink link) {
953
        if (link == null) {
1✔
954
            return;
×
955
        }
956
        NameDef var = link.getDef();
1✔
957
        if (var != null && var.attrIsConstant()) {
1✔
958
            if (var instanceof GlobalVarDef) {
1✔
959
                GlobalVarDef glob = (GlobalVarDef) var;
1✔
960
                if (glob.attrIsDynamicClassMember() && isInConstructor(left)) {
1✔
961
                    // allow to assign constant members in constructor
962
                    return;
1✔
963
                }
964
            }
965
            left.addError("Cannot assign a new value to constant " + Utils.printElement(var));
×
966
        }
967
    }
1✔
968

969
    private boolean isInConstructor(Element e) {
970
        while (e != null) {
1✔
971
            if (e instanceof ConstructorDef) {
1✔
972
                return true;
1✔
973
            }
974
            e = e.getParent();
1✔
975
        }
976
        return false;
1✔
977
    }
978

979
    private void checkAssignment(boolean isJassCode, Element pos, WurstType leftType, WurstType rightType) {
980
        if (!rightType.isSubtypeOf(leftType, pos)) {
1✔
981
            if (isJassCode) {
1✔
982
                if (leftType.isSubtypeOf(WurstTypeReal.instance(), pos)
×
983
                        && rightType.isSubtypeOf(WurstTypeInt.instance(), pos)) {
×
984
                    // special case: jass allows to assign an integer to a real
985
                    // variable
986
                    return;
×
987
                }
988
            }
989
            pos.addError("Cannot assign " + rightType + " to " + leftType);
×
990
        }
991
        if (leftType instanceof WurstTypeNamedScope) {
1✔
992
            WurstTypeNamedScope ns = (WurstTypeNamedScope) leftType;
1✔
993
            if (ns.isStaticRef()) {
1✔
994
                pos.addError("Missing variable name in variable declaration.\n" + "Cannot assign to " + leftType);
×
995
            }
996
        }
997
        if (leftType instanceof WurstTypeArray) {
1✔
998
            pos.addError("Missing array index for assignment to array variable.s");
×
999
        }
1000
        if (rightType instanceof WurstTypeVoid) {
1✔
1001
            if (pos.attrNearestPackage() instanceof WPackage) {
×
1002
                WPackage pack = (WPackage) pos.attrNearestPackage();
×
1003
                if (pack != null && !pack.getName().equals("WurstREPL")) { // allow
×
1004
                    // assigning
1005
                    // nothing
1006
                    // to
1007
                    // a
1008
                    // variable
1009
                    // in
1010
                    // the
1011
                    // Repl
1012
                    pos.addError("Function or expression returns nothing. Cannot assign nothing to a variable.");
×
1013
                }
1014
            }
1015
        }
1016
    }
1✔
1017

1018
    private void visit(LocalVarDef s) {
1019
        checkVarName(s, false);
1✔
1020
        if (s.getInitialExpr() instanceof Expr) {
1✔
1021
            Expr initial = (Expr) s.getInitialExpr();
1✔
1022
            if ((s.getOptTyp() instanceof NoTypeExpr)) {
1✔
1023
                // TODO
1024
            } else {
1025
                if (initial instanceof ExprNewObject) {
1✔
1026
                    s.addWarning("Duplicated type information. Use 'var' or 'let' instead.");
1✔
1027
                }
1028
            }
1029
            WurstType leftType = s.attrTyp();
1✔
1030
            WurstType rightType = initial.attrTyp();
1✔
1031

1032
            checkAssignment(Utils.isJassCode(s), s, leftType, rightType);
1✔
1033
        } else if (s.getInitialExpr() instanceof ArrayInitializer) {
1✔
1034
            ArrayInitializer arInit = (ArrayInitializer) s.getInitialExpr();
1✔
1035
            checkArrayInit(s, arInit);
1✔
1036
        }
1037
        checkIfRead(s);
1✔
1038
    }
1✔
1039

1040
    private void checkArrayInit(VarDef def, ArrayInitializer arInit) {
1041
        WurstType leftType = def.attrTyp();
1✔
1042
        if (leftType instanceof WurstTypeArray) {
1✔
1043
            WurstTypeArray arT = (WurstTypeArray) leftType;
1✔
1044
            if (arT.getDimensions() > 1) {
1✔
1045
                def.addError("Array initializer can only be used with one-dimensional arrays.");
×
1046
            }
1047
            if (arT.getDimensions() == 1) {
1✔
1048
                int initialValues = arInit.getValues().size();
1✔
1049
                int size = arT.getSize(0);
1✔
1050
                if (size >= 0 && size != initialValues) {
1✔
1051
                    def.addError("Array variable " + def.getName() + " is an array of size " + size + ", but is initialized with " + initialValues + " values here.");
×
1052
                }
1053

1054
            }
1055
            WurstType baseType = arT.getBaseType();
1✔
1056
            for (Expr expr : arInit.getValues()) {
1✔
1057
                if (!expr.attrTyp().isSubtypeOf(baseType, expr)) {
1✔
1058
                    expr.addError("Expected expression of type " + baseType + " in array initialization, but found " + expr.attrTyp());
×
1059
                }
1060
            }
1✔
1061
        } else {
1✔
1062
            def.addError("Array initializer can only be used with array-variables, but " + Utils.printElement(def) + " has type " + leftType);
×
1063
        }
1064

1065
    }
1✔
1066

1067
    private void checkIfRead(VarDef s) {
1068
        if (s.getName().startsWith("_")) {
1✔
1069
            // variables starting with an underscore are not read
1070
            // (same convention as in Erlang)
1071
            return;
1✔
1072
        }
1073
        if (Utils.isJassCode(s)) {
1✔
1074
            return;
1✔
1075
        }
1076
        if (s.getParent() instanceof StmtForRange) {
1✔
1077
            // it is ok, when the variable of a for-statement is not used
1078
            return;
1✔
1079
        }
1080
        WScope f = s.attrNearestScope();
1✔
1081
        if (f != null && !f.attrReadVariables().contains(s)) {
1✔
1082
            s.addWarning("The " + Utils.printElement(s) + " is never read. If intentional, prefix with \"_\" to suppress this warning.");
1✔
1083
        }
1084
    }
1✔
1085

1086
    private void checkVarName(VarDef s, boolean isConstant) {
1087
        String varName = s.getName();
1✔
1088

1089
        if (!isValidVarnameStart(varName) // first letter not lower case
1✔
1090
                && !Utils.isJassCode(s) // not in jass code
1✔
1091
                && !varName.matches("[A-Z0-9_]+") // not a constant
1✔
1092
        ) {
1093
            s.addWarning("Variable names should start with a lower case character. (" + varName + ")");
1✔
1094
        }
1095
        if (varName.equals("handle")) {
1✔
1096
            s.addError("\"handle\" is not a valid variable name");
×
1097
        } else if (varName.equals("code")) {
1✔
1098
            s.addError("\"code\" is not a valid variable name");
×
1099
        }
1100

1101
    }
1✔
1102

1103
    private boolean isValidVarnameStart(String varName) {
1104
        return varName.length() > 0 && Character.isLowerCase(varName.charAt(0)) || varName.startsWith("_");
1✔
1105
    }
1106

1107
    private void visit(WParameter p) {
1108
        checkVarName(p, false);
1✔
1109
        if (p.attrIsVararg()) {
1✔
1110
            if (p.attrNearestFuncDef().getParameters().size() != 1) {
1✔
1111
                p.addError("Vararg functions may only have one parameter");
×
1112
            }
1113
        }
1114
        checkIfParameterIsRead(p);
1✔
1115
    }
1✔
1116

1117
    private void checkIfParameterIsRead(WParameter p) {
1118
        FunctionImplementation f = p.attrNearestFuncDef();
1✔
1119
        if (f != null) {
1✔
1120
            if (p.getParent().getParent() instanceof ExprClosure) {
1✔
1121
                // closures can ignore parameters
1122
                return;
×
1123
            }
1124
            if (f.attrIsOverride()) {
1✔
1125
                // if a function is overridden it is ok to ignore parameters
1126
                return;
1✔
1127
            }
1128
            if (f.attrIsAbstract()) {
1✔
1129
                // if a function is abstract, then parameter vars are not used
1130
                return;
1✔
1131
            }
1132
            if (f.attrHasAnnotation("compiletimenative")) {
1✔
1133
                return;
1✔
1134
            }
1135
        } else {
1136
            if (p.getParent().getParent() instanceof TupleDef) {
1✔
1137
                // ignore tuples
1138
                return;
1✔
1139
            }
1140
            if (p.getParent().getParent() instanceof NativeFunc) {
1✔
1141
                // ignore native functions
1142
                return;
1✔
1143
            }
1144
        }
1145

1146
        checkIfRead(p);
1✔
1147
    }
1✔
1148

1149
    private void visit(GlobalVarDef s) {
1150
        checkVarName(s, s.attrIsConstant());
1✔
1151
        if (s.getInitialExpr() instanceof Expr) {
1✔
1152
            Expr initial = (Expr) s.getInitialExpr();
1✔
1153
            WurstType leftType = s.attrTyp();
1✔
1154
            WurstType rightType = initial.attrTyp();
1✔
1155
            checkAssignment(Utils.isJassCode(s), s, leftType, rightType);
1✔
1156
        } else if (s.getInitialExpr() instanceof ArrayInitializer) {
1✔
1157
            checkArrayInit(s, (ArrayInitializer) s.getInitialExpr());
1✔
1158
        }
1159

1160
        if (s.attrTyp() instanceof WurstTypeArray && !s.attrIsStatic() && s.attrIsDynamicClassMember()) {
1✔
1161
            // s.addError("Array variables must be static.\n" +
1162
            // "Hint: use Lists for dynamic stuff.");
1163
        }
1164
    }
1✔
1165

1166
    private void visit(StmtIf stmtIf) {
1167
        WurstType condType = stmtIf.getCond().attrTyp();
1✔
1168
        if (!(condType instanceof WurstTypeBool)) {
1✔
1169
            stmtIf.getCond().addError("If condition must be a boolean but found " + condType);
1✔
1170
        }
1171
    }
1✔
1172

1173
    private void visit(StmtWhile stmtWhile) {
1174
        WurstType condType = stmtWhile.getCond().attrTyp();
1✔
1175
        if (!(condType instanceof WurstTypeBool)) {
1✔
1176
            stmtWhile.getCond().addError("While condition must be a boolean but found " + condType);
×
1177
        }
1178
    }
1✔
1179

1180
    private void visit(ExtensionFuncDef func) {
1181
        checkFunctionName(func);
1✔
1182
        func.getExtendedType().attrTyp();
1✔
1183
    }
1✔
1184

1185
    private void checkFunctionName(FunctionDefinition f) {
1186
        if (!Utils.isJassCode(f)) {
1✔
1187
            if (!isValidVarnameStart(f.getName())) {
1✔
1188
                f.addWarning("Function names should start with an lower case character.");
×
1189
            }
1190
        }
1191
    }
1✔
1192

1193
    private void checkReturn(FunctionLike func) {
1194
        if (!func.attrHasEmptyBody()) {
1✔
1195
            new ReturnsAnalysis().execute(func);
1✔
1196
        } else { // no body, check if in interface:
1197
            if (func instanceof FunctionImplementation) {
1✔
1198
                FunctionImplementation funcDef = (FunctionImplementation) func;
1✔
1199
                if (funcDef.getReturnTyp() instanceof TypeExpr
1✔
1200
                        && !(func.attrNearestStructureDef() instanceof InterfaceDef)) {
1✔
1201
                    func.addError("Function " + funcDef.getName()
×
1202
                            + " is missing a body. Use the 'skip' statement to define an empty body.");
1203
                }
1204
            }
1205
        }
1206
    }
1✔
1207

1208
    private void checkReachability(WStatement s) {
1209
        if (s.getParent() instanceof WStatements) {
1✔
1210
            WStatements stmts = (WStatements) s.getParent();
1✔
1211
            if (s.attrPreviousStatements().isEmpty()) {
1✔
1212
                if (s.attrListIndex() > 0 || !(stmts.getParent() instanceof TranslatedToImFunction
1✔
1213
                        || stmts.getParent() instanceof ExprStatementsBlock)) {
1✔
1214
                    if (Utils.isJassCode(s)) {
1✔
1215
                        // in jass this is just a warning, because
1216
                        // the shitty code emitted by jasshelper sometimes
1217
                        // contains unreachable code
1218
                        s.addWarning("Unreachable code");
×
1219
                    } else {
1220
                        if (mightBeAffectedBySwitchThatCoversAllCases(s)) {
1✔
1221
                            // fow backwards compatibility just use a warning when
1222
                            // switch statements that handle all cases are involved:
1223
                            s.addWarning("Unreachable code");
1✔
1224
                        } else {
1225
                            s.addError("Unreachable code");
×
1226
                        }
1227
                    }
1228
                }
1229
            }
1230
        }
1231
    }
1✔
1232

1233
    private boolean mightBeAffectedBySwitchThatCoversAllCases(WStatement s) {
1234
        boolean[] containsSwitchAr = { false };
1✔
1235
        s.attrNearestNamedScope().accept(new Element.DefaultVisitor() {
1✔
1236
            @Override
1237
            public void visit(SwitchStmt switchStmt) {
1238
                if (switchStmt.calculateHandlesAllCases()) {
1✔
1239
                    containsSwitchAr[0] = true;
1✔
1240
                }
1241
            }
1✔
1242
        });
1243
        return containsSwitchAr[0];
1✔
1244
    }
1245

1246
    private void visit(FuncDef func) {
1247
        visitedFunctions++;
1✔
1248
        func.getErrorHandler().setProgress(null, ProgressHelper.getValidatorPercent(visitedFunctions, functionCount));
1✔
1249

1250
        checkFunctionName(func);
1✔
1251
        if (func.attrIsAbstract()) {
1✔
1252
            if (!func.attrHasEmptyBody()) {
1✔
1253
                func.addError("Abstract function " + func.getName() + " must not have a body.");
×
1254
            }
1255
            if (func.attrIsPrivate()) {
1✔
1256
                func.addError("Abstract functions must not be private.");
×
1257
            }
1258
        }
1259
    }
1✔
1260

1261
    private void checkUninitializedVars(FunctionLike f) {
1262
        boolean isAbstract = false;
1✔
1263
        if (f instanceof FuncDef) {
1✔
1264
            FuncDef func = (FuncDef) f;
1✔
1265
            if (func.attrIsAbstract()) {
1✔
1266
                isAbstract = true;
1✔
1267
                if (!func.attrHasEmptyBody()) {
1✔
1268
                    func.getBody().get(0)
×
1269
                            .addError("The abstract function " + func.getName() + " must not have any statements.");
×
1270
                }
1271
            }
1272
        }
1273
        if (!isAbstract) { // not abstract
1✔
1274
            checkReturn(f);
1✔
1275

1276
            if (!f.getSource().getFile().endsWith("common.j")
1✔
1277
                    && !f.getSource().getFile().endsWith("blizzard.j")
1✔
1278
                    && !f.getSource().getFile().endsWith("war3map.j")) {
1✔
1279
                new DataflowAnomalyAnalysis(Utils.isJassCode(f)).execute(f);
1✔
1280
            }
1281
        }
1282
    }
1✔
1283

1284
    private void checkCall(StmtCall call) {
1285
        String funcName;
1286
        if (call instanceof FunctionCall) {
1✔
1287
            FunctionCall fcall = (FunctionCall) call;
1✔
1288
            funcName = fcall.getFuncName();
1✔
1289
            HashSet<FunctionCall> fcalls = wrapperCalls.computeIfAbsent(funcName, (String s) -> new HashSet<>());
1✔
1290
            fcalls.add(fcall);
1✔
1291
        } else if (call instanceof ExprNewObject) {
1✔
1292
            funcName = "constructor";
1✔
1293
        } else {
1294
            throw new Error("unhandled case: " + Utils.printElement(call));
×
1295
        }
1296

1297
        call.attrCallSignature().checkSignatureCompatibility(call.attrFunctionSignature(), funcName, call);
1✔
1298
    }
1✔
1299

1300
    private void checkAnnotation(Annotation a) {
1301
        FuncLink fl = a.attrFuncLink();
1✔
1302
        if (fl != null) {
1✔
1303
            if (a.getArgs().size() < fl.getParameterTypes().size()) {
1✔
1304
                a.addWarning("not enough arguments");
1✔
1305
            } else if (a.getArgs().size() > fl.getParameterTypes().size()) {
1✔
1306
                a.addWarning("too many enough arguments");
×
1307
            } else {
1308
                for (int i = 0; i < a.getArgs().size(); i++) {
1✔
1309
                    WurstType actual = a.getArgs().get(i).attrTyp();
1✔
1310
                    WurstType expected = fl.getParameterType(i);
1✔
1311
                    if (!actual.isSubtypeOf(expected, a)) {
1✔
1312
                        a.getArgs().get(i).addWarning("Expected " + expected + " but found " + actual + ".");
×
1313
                    }
1314
                }
1315
            }
1316
        }
1317
    }
1✔
1318

1319
    private void visit(ExprFunctionCall stmtCall) {
1320
        String funcName = stmtCall.getFuncName();
1✔
1321
        // calculating the exprType should reveal most errors:
1322
        stmtCall.attrTyp();
1✔
1323

1324
        checkFuncDefDeprecated(stmtCall);
1✔
1325

1326
        if (stmtCall.attrFuncLink() != null) {
1✔
1327
            FuncLink calledFunc = stmtCall.attrFuncLink();
1✔
1328
            if (calledFunc.getDef().attrIsDynamicClassMember()) {
1✔
1329
                if (!stmtCall.attrIsDynamicContext()) {
1✔
1330
                    stmtCall.addError("Cannot call dynamic function " + funcName + " from static context.");
×
1331
                }
1332
            }
1333
            if (calledFunc.getDef() instanceof ExtensionFuncDef) {
1✔
1334
                stmtCall.addError("Extension function " + funcName + " must be called with an explicit receiver.\n"
×
1335
                        + "Try to write this." + funcName + "(...) .");
1336
            }
1337
        }
1338

1339
        // special check for filter & condition:
1340
        if (Utils.oneOf(funcName, "Condition", "Filter") && !stmtCall.getArgs().isEmpty()) {
1✔
1341
            Expr firstArg = stmtCall.getArgs().get(0);
1✔
1342
            if (firstArg instanceof ExprFuncRef) {
1✔
1343
                ExprFuncRef exprFuncRef = (ExprFuncRef) firstArg;
1✔
1344
                FuncLink f = exprFuncRef.attrFuncLink();
1✔
1345
                if (f != null) {
1✔
1346
                    if (!(f.getReturnType() instanceof WurstTypeBool) && !(f.getReturnType() instanceof WurstTypeVoid)) {
1✔
1347
                        firstArg.addError("Functions passed to Filter or Condition must return boolean or nothing.");
×
1348
                    }
1349
                }
1350
            }
1351
        }
1352

1353
    }
1✔
1354

1355
    // private void checkParams(Element where, List<Expr> args,
1356
    // FunctionDefinition calledFunc) {
1357
    // if (calledFunc == null) {
1358
    // return;
1359
    // }
1360
    // List<PscriptType> parameterTypes = calledFunc.attrParameterTypes();
1361
    // checkParams(where, args, parameterTypes);
1362
    // }
1363

1364
    @Deprecated
1365
    private void checkParams(Element where, String preMsg, List<Expr> args, FunctionSignature sig) {
1366
        checkParams(where, preMsg, args, sig.getParamTypes());
1✔
1367
    }
1✔
1368

1369
    @Deprecated
1370
    private void checkParams(Element where, String preMsg, List<Expr> args, List<WurstType> parameterTypes) {
1371
        if (args.size() > parameterTypes.size()) {
1✔
1372
            where.addError(preMsg + "Too many parameters.");
×
1373

1374
        } else if (args.size() < parameterTypes.size()) {
1✔
1375
            where.addError(preMsg + "Missing parameters.");
×
1376
        } else {
1377
            for (int i = 0; i < args.size(); i++) {
1✔
1378

1379
                WurstType actual = args.get(i).attrTyp();
1✔
1380
                WurstType expected = parameterTypes.get(i);
1✔
1381
                // if (expected instanceof AstElementWithTypeArgs)
1382
                if (!actual.isSubtypeOf(expected, where)) {
1✔
1383
                    args.get(i).addError(
×
1384
                            preMsg + "Expected " + expected + " as parameter " + (i + 1) + " but  found " + actual);
1385
                }
1386
            }
1387
        }
1388
    }
1✔
1389

1390
    private void visit(ExprBinary expr) {
1391
        FuncLink def = expr.attrFuncLink();
1✔
1392
        if (def != null) {
1✔
1393
            FunctionSignature sig = FunctionSignature.fromNameLink(def);
1✔
1394
            CallSignature callSig = new CallSignature(expr.getLeft(), Collections.singletonList(expr.getRight()));
1✔
1395
            callSig.checkSignatureCompatibility(sig, "" + expr.getOp(), expr);
1✔
1396
        }
1397
    }
1✔
1398

1399
    private void visit(ExprMemberMethod stmtCall) {
1400
        // calculating the exprType should reveal all errors:
1401
        stmtCall.attrTyp();
1✔
1402
    }
1✔
1403

1404
    private void visit(ExprNewObject stmtCall) {
1405
        stmtCall.attrTyp();
1✔
1406
        stmtCall.attrConstructorDef();
1✔
1407
    }
1✔
1408

1409
    private void visit(Modifiers modifiers) {
1410
        boolean hasVis = false;
1✔
1411
        boolean isStatic = false;
1✔
1412
        for (Modifier m : modifiers) {
1✔
1413
            if (m instanceof VisibilityModifier) {
1✔
1414
                if (hasVis) {
1✔
1415
                    m.addError("Each element can only have one visibility modifier (public, private, ...)");
×
1416
                }
1417
                hasVis = true;
1✔
1418
            } else if (m instanceof ModStatic) {
1✔
1419
                if (isStatic) {
1✔
1420
                    m.addError("double static? - what r u trying to do?");
×
1421
                }
1422
                isStatic = true;
1✔
1423
            }
1424
        }
1✔
1425
    }
1✔
1426

1427
    private void visit(StmtReturn s) {
1428
        if (s.attrNearestExprStatementsBlock() != null) {
1✔
1429
            ExprStatementsBlock e = s.attrNearestExprStatementsBlock();
1✔
1430
            if (e.getReturnStmt() != s) {
1✔
1431
                s.addError("Return in a statements block can only be at the end.");
×
1432
                return;
×
1433
            }
1434
            if (s.getReturnedObj() instanceof Expr) {
1✔
1435
                Expr expr = (Expr) s.getReturnedObj();
1✔
1436
                if (expr.attrTyp().isVoid()) {
1✔
1437
                    s.addError("Cannot return void from statements block.");
×
1438
                }
1439
            } else {
1✔
1440
                s.addError("Cannot have empty return statement in statements block.");
×
1441
            }
1442
        } else {
1✔
1443
            FunctionImplementation func = s.attrNearestFuncDef();
1✔
1444
            if (func == null) {
1✔
1445
                s.addError("return statements can only be used inside functions");
×
1446
                return;
×
1447
            }
1448
            checkReturnInFunc(s, func);
1✔
1449
        }
1450
    }
1✔
1451

1452
    private void checkReturnInFunc(StmtReturn s, FunctionImplementation func) {
1453
        WurstType returnType = func.attrReturnTyp();
1✔
1454
        if (s.getReturnedObj() instanceof Expr) {
1✔
1455
            Expr returned = (Expr) s.getReturnedObj();
1✔
1456
            if (returnType.isSubtypeOf(WurstTypeVoid.instance(), s)) {
1✔
1457
                s.addError("Cannot return a value from a function which returns nothing");
×
1458
            } else {
1459
                WurstType returnedType = returned.attrTyp();
1✔
1460
                if (!returnedType.isSubtypeOf(returnType, s)) {
1✔
1461
                    s.addError("Cannot return " + returnedType + ", expected expression of type " + returnType);
×
1462
                }
1463
            }
1464
        } else { // empty return
1✔
1465
            if (!returnType.isSubtypeOf(WurstTypeVoid.instance(), s)) {
1✔
1466
                s.addError("Missing return value");
×
1467
            }
1468
        }
1469
    }
1✔
1470

1471
    private void visit(ClassDef classDef) {
1472
        checkTypeName(classDef, classDef.getName());
1✔
1473
        if (!(classDef.getExtendedClass() instanceof NoTypeExpr) && !(classDef.getExtendedClass().attrTyp() instanceof WurstTypeClass)) {
1✔
1474
            classDef.getExtendedClass().addError("Classes may only extend other classes.");
×
1475
        }
1476
        if (classDef.isInnerClass() && !classDef.attrIsStatic()) {
1✔
1477
            classDef.addError("At the moment only static inner classes are supported.");
×
1478
        }
1479
    }
1✔
1480

1481
    private void checkTypeName(Element source, String name) {
1482
        if (!Character.isUpperCase(name.charAt(0))) {
1✔
1483
            source.addWarning("Type names should start with upper case characters.");
×
1484
        }
1485
    }
1✔
1486

1487
    private void visit(ModuleDef moduleDef) {
1488
        checkTypeName(moduleDef, moduleDef.getName());
1✔
1489
        // calculate all functions to find possible errors
1490
        moduleDef.attrNameLinks();
1✔
1491
    }
1✔
1492

1493
    private void visit(ExprDestroy stmtDestroy) {
1494
        WurstType typ = stmtDestroy.getDestroyedObj().attrTyp();
1✔
1495
        if (typ instanceof WurstTypeModule) {
1✔
1496

1497
        } else if (typ instanceof WurstTypeClass) {
1✔
1498
            WurstTypeClass c = (WurstTypeClass) typ;
1✔
1499
            checkDestroyClass(stmtDestroy, c);
1✔
1500
        } else if (typ instanceof WurstTypeInterface) {
1✔
1501
            WurstTypeInterface i = (WurstTypeInterface) typ;
1✔
1502
            checkDestroyInterface(stmtDestroy, i);
1✔
1503
        } else {
1✔
1504
            stmtDestroy.addError("Cannot destroy objects of type " + typ);
×
1505
        }
1506
    }
1✔
1507

1508
    private void checkDestroyInterface(ExprDestroy stmtDestroy, WurstTypeInterface i) {
1509
        if (i.isStaticRef()) {
1✔
1510
            stmtDestroy.addError("Cannot destroy interface " + i);
×
1511
        }
1512
    }
1✔
1513

1514
    private void checkDestroyClass(ExprDestroy stmtDestroy, WurstTypeClass c) {
1515
        if (c.isStaticRef()) {
1✔
1516
            stmtDestroy.addError("Cannot destroy class " + c);
×
1517
        }
1518
        calledFunctions.put(stmtDestroy.attrNearestScope(), c.getClassDef().getOnDestroy());
1✔
1519
    }
1✔
1520

1521
    private void visit(ExprVarAccess e) {
1522
        checkVarRef(e, e.attrIsDynamicContext());
1✔
1523
    }
1✔
1524

1525
    private void visit(WImport wImport) {
1526
        if (wImport.attrImportedPackage() == null) {
1✔
1527
            if (!wImport.getPackagename().equals("NoWurst")) {
1✔
1528
                wImport.addError("Could not find imported package " + wImport.getPackagename());
×
1529
            }
1530
            return;
1✔
1531
        }
1532
        if (!wImport.attrImportedPackage().getName().equals("Wurst")
1✔
1533
                && wImport.attrImportedPackage().getName().equals(wImport.attrNearestNamedScope().getName())) {
1✔
1534
            wImport.addError("Packages cannot import themselves");
×
1535
        }
1536
    }
1✔
1537

1538
    /**
1539
     * check if the nameRef e is accessed correctly i.e. not using a dynamic
1540
     * variable from a static context
1541
     *
1542
     * @param e
1543
     * @param dynamicContext
1544
     */
1545
    private void checkVarRef(NameRef e, boolean dynamicContext) {
1546
        NameLink link = e.attrNameLink();
1✔
1547
        if (link == null) {
1✔
1548
            return;
1✔
1549
        }
1550
        NameDef def = link.getDef();
1✔
1551
        if (def instanceof GlobalVarDef) {
1✔
1552
            GlobalVarDef g = (GlobalVarDef) def;
1✔
1553
            if (g.attrIsDynamicClassMember() && !dynamicContext) {
1✔
1554
                e.addError("Cannot reference dynamic variable " + e.getVarName() + " from static context.");
×
1555
            }
1556
        }
1557
        checkNameRefDeprecated(e, def);
1✔
1558
        if (e.attrTyp() instanceof WurstTypeNamedScope) {
1✔
1559
            WurstTypeNamedScope wtns = (WurstTypeNamedScope) e.attrTyp();
1✔
1560
            if (wtns.isStaticRef()) {
1✔
1561
                if (!isUsedAsReceiverInExprMember(e)) {
1✔
1562
                    e.addError("Reference to " + e.getVarName() + " cannot be used as an expression.");
×
1563
                } else if (e.getParent() instanceof ExprMemberMethodDotDot) {
1✔
1564
                    e.addError("Reference to " + e.getVarName()
×
1565
                            + " cannot be used with the cascade operator. Only dynamic objects are allowed.");
1566
                } else if (e.getParent() instanceof ExprMemberMethod) {
1✔
1567
                    ExprMemberMethod em = (ExprMemberMethod) e.getParent();
1✔
1568
                    if (em.attrFuncDef() instanceof ExtensionFuncDef) {
1✔
1569
                        e.addError("Reference to " + e.getVarName()
1✔
1570
                                + " can only be used for calling static methods, but not for calling extension method method '" + em.getFuncName() + "'.");
1✔
1571
                    }
1572

1573

1574
                }
1575
            }
1576
        }
1577

1578
    }
1✔
1579

1580
    private boolean isUsedAsReceiverInExprMember(Expr e) {
1581
        if (e.getParent() instanceof ExprMember) {
1✔
1582
            ExprMember em = (ExprMember) e.getParent();
1✔
1583
            return em.getLeft() == e;
1✔
1584
        } else if (e.getParent() instanceof StmtForIn) {
1✔
1585
            // if we write for x in E, then it actually calls E.iterator(), so it is used in an ExprMember
1586
            StmtForIn parent = (StmtForIn) e.getParent();
1✔
1587
            return parent.getIn() == e;
1✔
1588
        } else if (e.getParent() instanceof StmtForFrom) {
×
1589
            StmtForFrom parent = (StmtForFrom) e.getParent();
×
1590
            return parent.getIn() == e;
×
1591
        }
1592
        return false;
×
1593
    }
1594

1595
    private void checkTypeBinding(HasTypeArgs e) {
1596
        VariableBinding mapping = e.match(new HasTypeArgs.Matcher<VariableBinding>() {
1✔
1597

1598
            @Override
1599
            public VariableBinding case_ExprNewObject(ExprNewObject e) {
1600
                return e.attrTyp().getTypeArgBinding();
1✔
1601
            }
1602

1603
            @Override
1604
            public VariableBinding case_ModuleUse(ModuleUse moduleUse) {
1605
                return null;
1✔
1606
            }
1607

1608
            @Override
1609
            public VariableBinding case_TypeExprSimple(TypeExprSimple e) {
1610
                return e.attrTyp().getTypeArgBinding();
1✔
1611
            }
1612

1613
            @Override
1614
            public VariableBinding case_ExprFunctionCall(ExprFunctionCall e) {
1615
                return e.attrTyp().getTypeArgBinding();
1✔
1616
            }
1617

1618
            @Override
1619
            public VariableBinding case_ExprMemberMethodDot(ExprMemberMethodDot e) {
1620
                return e.attrTyp().getTypeArgBinding();
1✔
1621
            }
1622

1623
            @Override
1624
            public VariableBinding case_ExprMemberMethodDotDot(ExprMemberMethodDotDot e) {
1625
                return e.attrTyp().getTypeArgBinding();
1✔
1626
            }
1627
        });
1628
        if (mapping == null) {
1✔
1629
            return;
1✔
1630
        }
1631

1632
        for (Tuple2<TypeParamDef, WurstTypeBoundTypeParam> t : mapping) {
1✔
1633
            WurstTypeBoundTypeParam boundTyp = t._2();
1✔
1634
            WurstType typ = boundTyp.getBaseType();
1✔
1635

1636
            TypeParamDef tp = t._1();
1✔
1637
            if (tp.getTypeParamConstraints() instanceof TypeExprList) {
1✔
1638
                // new style generics
1639
            } else { // old style generics
1640

1641
                if (!typ.isTranslatedToInt() && !(e instanceof ModuleUse)) {
1✔
1642
                    String toIndexFuncName = ImplicitFuncs.toIndexFuncName(typ);
1✔
1643
                    String fromIndexFuncName = ImplicitFuncs.fromIndexFuncName(typ);
1✔
1644
                    Collection<FuncLink> toIndexFuncs = ImplicitFuncs.findToIndexFuncs(typ, e);
1✔
1645
                    Collection<FuncLink> fromIndexFuncs = ImplicitFuncs.findFromIndexFuncs(typ, e);
1✔
1646
                    if (toIndexFuncs.isEmpty()) {
1✔
1647
                        e.addError("Type parameters can only be bound to ints and class types, but " + "not to " + typ
×
1648
                                + ".\n" + "You can provide functions " + toIndexFuncName + " and " + fromIndexFuncName
1649
                                + " to use this type " + "with generics.");
1650
                    } else if (fromIndexFuncs.isEmpty()) {
1✔
1651
                        e.addError("Could not find function " + fromIndexFuncName + " which is required to use " + typ
×
1652
                                + " with generics.");
1653
                    } else {
1654
                        if (toIndexFuncs.size() > 1) {
1✔
1655
                            e.addError("There is more than one function named " + toIndexFuncName);
×
1656
                        }
1657
                        if (fromIndexFuncs.size() > 1) {
1✔
1658
                            e.addError("There is more than one function named " + fromIndexFuncName);
×
1659
                        }
1660
                        NameDef toIndex = Utils.getFirst(toIndexFuncs).getDef();
1✔
1661
                        if (toIndex instanceof FuncDef) {
1✔
1662
                            FuncDef toIndexF = (FuncDef) toIndex;
1✔
1663

1664
                            if (toIndexF.getParameters().size() != 1) {
1✔
1665
                                toIndexF.addError("Must have exactly one parameter");
×
1666

1667
                            } else if (!toIndexF.getParameters().get(0).attrTyp().equalsType(typ, e)) {
1✔
1668
                                toIndexF.addError("Parameter must be of type " + typ);
×
1669
                            }
1670

1671
                            WurstType returnType = toIndexF.attrReturnTyp();
1✔
1672
                            if (!returnType.equalsType(WurstTypeInt.instance(), e)) {
1✔
1673
                                toIndexF.addError("Return type must be of type int " + " but was " + returnType);
×
1674
                            }
1675
                        } else {
1✔
1676
                            toIndex.addError("This should be a function.");
×
1677
                        }
1678

1679
                        NameDef fromIndex = Utils.getFirst(fromIndexFuncs).getDef();
1✔
1680
                        if (fromIndex instanceof FuncDef) {
1✔
1681
                            FuncDef fromIndexF = (FuncDef) fromIndex;
1✔
1682

1683
                            if (fromIndexF.getParameters().size() != 1) {
1✔
1684
                                fromIndexF.addError("Must have exactly one parameter");
×
1685

1686
                            } else if (!fromIndexF.getParameters().get(0).attrTyp()
1✔
1687
                                    .equalsType(WurstTypeInt.instance(), e)) {
1✔
1688
                                fromIndexF.addError("Parameter must be of type int");
×
1689
                            }
1690

1691
                            WurstType returnType = fromIndexF.attrReturnTyp();
1✔
1692
                            if (!returnType.equalsType(typ, e)) {
1✔
1693
                                fromIndexF.addError("Return type must be of type " + typ + " but was " + returnType);
×
1694
                            }
1695

1696
                        } else {
1✔
1697
                            fromIndex.addError("This should be a function.");
×
1698
                        }
1699
                    }
1700
                }
1701
            }
1702
        }
1✔
1703
    }
1✔
1704

1705
    private void checkFuncRef(FuncRef ref) {
1706
        if (ref.getFuncName().isEmpty()) {
1✔
1707
            ref.addError("Missing function name.");
×
1708
        }
1709
        checkFuncDefDeprecated(ref);
1✔
1710
        FuncLink called = ref.attrFuncLink();
1✔
1711
        if (called == null) {
1✔
1712
            return;
1✔
1713
        }
1714
        WScope scope = ref.attrNearestFuncDef();
1✔
1715
        if (scope == null) {
1✔
1716
            scope = ref.attrNearestScope();
1✔
1717
        }
1718
        if (!(ref instanceof ExprFuncRef)) { // ExprFuncRef is not a direct call
1✔
1719
            calledFunctions.put(scope, called.getDef());
1✔
1720
        }
1721
    }
1✔
1722

1723
    private void checkNameRefDeprecated(Element trace, NameLink link) {
1724
        if (link != null) {
1✔
1725
            checkNameRefDeprecated(trace, link.getDef());
1✔
1726
        }
1727

1728
    }
1✔
1729

1730
    private void checkNameRefDeprecated(Element trace, NameDef def) {
1731
        if (def != null && def.hasAnnotation("@deprecated")) {
1✔
1732
            Annotation annotation = def.getAnnotation("@deprecated");
1✔
1733
            String msg = annotation.getAnnotationMessage();
1✔
1734
            msg = (msg == null || msg.isEmpty()) ? "It shouldn't be used and will be removed in the future." : msg;
1✔
1735
            trace.addWarning("<" + def.getName() + "> is deprecated. " + msg);
1✔
1736
        }
1737
    }
1✔
1738

1739
    private void checkFuncDefDeprecated(FuncRef ref) {
1740
        checkNameRefDeprecated(ref, ref.attrFuncLink());
1✔
1741
    }
1✔
1742

1743
    private void checkFuncRef(ExprFuncRef ref) {
1744
        FuncLink called = ref.attrFuncLink();
1✔
1745
        if (called == null) {
1✔
1746
            return;
×
1747
        }
1748
        if (ref.attrTyp() instanceof WurstTypeCode) {
1✔
1749
            if (called.getDef().attrParameterTypesIncludingReceiver().size() > 0) {
1✔
1750
                String msg = "Can only use functions without parameters in 'code' function references.";
1✔
1751
                if (called.getDef().attrIsDynamicClassMember()) {
1✔
1752
                    msg += "\nNote that " + called.getName()
1✔
1753
                            + " is a dynamic function and thus has an implicit parameter 'this'.";
1754
                }
1755
                ref.addError(msg);
×
1756
            }
1757
        }
1758
    }
1✔
1759

1760
    private void checkModifiers(final HasModifier e) {
1761
        for (final Modifier m : e.getModifiers()) {
1✔
1762
            final StringBuilder error = new StringBuilder();
1✔
1763

1764
            e.match(new HasModifier.MatcherVoid() {
1✔
1765

1766
                @Override
1767
                public void case_WParameter(WParameter wParameter) {
1768
                    check(ModConstant.class);
1✔
1769
                }
1✔
1770

1771
                @Override
1772
                public void case_WShortParameter(WShortParameter wShortParameter) {
1773
                    check(ModConstant.class);
1✔
1774
                }
1✔
1775

1776
                @Override
1777
                public void case_TypeParamDef(TypeParamDef typeParamDef) {
1778
                    error.append("Type Parameters must not have modifiers");
×
1779
                }
×
1780

1781
                @Override
1782
                public void case_NativeType(NativeType nativeType) {
1783
                    check(VisibilityPublic.class);
×
1784
                }
×
1785

1786
                @SafeVarargs
1787
                private final void check(Class<? extends Modifier>... allowed) {
1788
                    if (m instanceof WurstDoc) {
1✔
1789
                        // wurstdoc always allowed
1790
                        return;
1✔
1791
                    }
1792
                    if (m instanceof ModVararg && e.getParent() instanceof WParameters) {
1✔
1793
                        return;
1✔
1794
                    }
1795
                    boolean isAllowed = false;
1✔
1796
                    for (Class<? extends Modifier> a : allowed) {
1✔
1797
                        String modName = m.getClass().getName();
1✔
1798
                        String allowedName = a.getName();
1✔
1799
                        if (modName.startsWith(allowedName)) {
1✔
1800
                            isAllowed = true;
1✔
1801
                            break;
1✔
1802
                        }
1803
                    }
1804
                    if (!isAllowed) {
1✔
1805
                        error.append("Modifier ").append(printMod(m)).append(" not allowed for ").append(Utils.printElement(e)).append(".\n Allowed are the " +
×
1806
                                "following modifiers: ");
1807
                        boolean first = true;
×
1808
                        for (Class<? extends Modifier> c : allowed) {
×
1809
                            if (!first) {
×
1810
                                error.append(", ");
×
1811
                            }
1812
                            error.append(printMod(c));
×
1813
                            first = false;
×
1814
                        }
1815
                    }
1816
                }
1✔
1817

1818
                @Override
1819
                public void case_NativeFunc(NativeFunc nativeFunc) {
1820
                    check(VisibilityPublic.class, Annotation.class);
1✔
1821
                }
1✔
1822

1823
                @Override
1824
                public void case_ModuleInstanciation(ModuleInstanciation moduleInstanciation) {
1825
                    check(VisibilityPrivate.class, VisibilityProtected.class);
×
1826
                }
×
1827

1828
                @Override
1829
                public void case_ModuleDef(ModuleDef moduleDef) {
1830
                    check(VisibilityPublic.class);
1✔
1831
                }
1✔
1832

1833
                @Override
1834
                public void case_LocalVarDef(LocalVarDef localVarDef) {
1835
                    check(ModConstant.class);
1✔
1836
                    if (localVarDef.hasAnnotation("@compiletime")) {
1✔
1837
                        localVarDef.getAnnotation("@compiletime").addWarning("The annotation '@compiletime' has no effect on variables.");
×
1838
                    }
1839
                }
1✔
1840

1841
                @Override
1842
                public void case_GlobalVarDef(GlobalVarDef g) {
1843
                    if (g.attrNearestClassOrModule() != null) {
1✔
1844
                        check(VisibilityPrivate.class, VisibilityProtected.class, ModStatic.class, ModConstant.class,
1✔
1845
                                Annotation.class);
1846
                    } else {
1847
                        check(VisibilityPublic.class, ModConstant.class, Annotation.class);
1✔
1848
                    }
1849
                    if (g.hasAnnotation("@compiletime")) {
1✔
1850
                        g.getAnnotation("@compiletime").addWarning("The annotation '@compiletime' has no effect on variables.");
×
1851
                    }
1852
                }
1✔
1853

1854
                @Override
1855
                public void case_FuncDef(FuncDef f) {
1856
                    if (f.attrNearestStructureDef() != null) {
1✔
1857
                        if (f.attrNearestStructureDef() instanceof InterfaceDef) {
1✔
1858
                            check(VisibilityPrivate.class, VisibilityProtected.class, ModAbstract.class,
1✔
1859
                                    ModOverride.class, Annotation.class);
1860
                        } else {
1861
                            check(VisibilityPrivate.class, VisibilityProtected.class, ModAbstract.class,
1✔
1862
                                    ModOverride.class, ModStatic.class, Annotation.class);
1863
                            if (f.attrNearestStructureDef() instanceof ClassDef) {
1✔
1864
                                if (f.attrIsStatic() && f.attrIsAbstract()) {
1✔
1865
                                    f.addError("Static functions cannot be abstract.");
×
1866
                                }
1867
                            }
1868
                        }
1869
                    } else {
1870
                        check(VisibilityPublic.class, Annotation.class);
1✔
1871
                    }
1872
                    if (f.attrIsCompiletime()) {
1✔
1873
                        if (f.getParameters().size() > 0) {
1✔
1874
                            f.addError("Functions annotated '@compiletime' may not take parameters." +
×
1875
                                    "\nNote: The annotation marks functions to be executed by wurst at compiletime.");
1876
                        } else if (f.attrIsDynamicClassMember()) {
1✔
1877
                            f.addError("Functions annotated '@compiletime' must be static." +
×
1878
                                    "\nNote: The annotation marks functions to be executed by wurst at compiletime.");
1879
                        }
1880
                    }
1881
                }
1✔
1882

1883
                @Override
1884
                public void case_ExtensionFuncDef(ExtensionFuncDef extensionFuncDef) {
1885
                    check(VisibilityPublic.class, Annotation.class);
1✔
1886
                }
1✔
1887

1888
                @Override
1889
                public void case_ConstructorDef(ConstructorDef constructorDef) {
1890
                    check(VisibilityPrivate.class);
1✔
1891
                }
1✔
1892

1893
                @Override
1894
                public void case_ClassDef(ClassDef classDef) {
1895
                    check(VisibilityPublic.class, ModAbstract.class, ModStatic.class);
1✔
1896
                    if (!classDef.isInnerClass() && classDef.attrIsStatic()) {
1✔
1897
                        classDef.addError("Top-level class " + classDef.getName() + " cannot be static. "
×
1898
                                + "Only inner classes can be declared static.");
1899
                    }
1900
                }
1✔
1901

1902
                @Override
1903
                public void case_InterfaceDef(InterfaceDef interfaceDef) {
1904
                    check(VisibilityPublic.class);
1✔
1905
                }
1✔
1906

1907
                @Override
1908
                public void case_TupleDef(TupleDef tupleDef) {
1909
                    check(VisibilityPublic.class);
1✔
1910
                }
1✔
1911

1912
                @Override
1913
                public void case_WPackage(WPackage wPackage) {
1914
                    check();
×
1915
                }
×
1916

1917
                @Override
1918
                public void case_EnumDef(EnumDef enumDef) {
1919
                    check(VisibilityPublic.class);
1✔
1920
                }
1✔
1921

1922
                @Override
1923
                public void case_EnumMember(EnumMember enumMember) {
1924
                    check();
×
1925
                }
×
1926

1927
            });
1928
            if (error.length() > 0) {
1✔
1929
                if (m.attrSource().getFile().endsWith(".jurst")) {
×
1930
                    // for jurst only add a warning:
1931
                    m.addWarning(error.toString());
×
1932
                } else {
1933
                    m.addError(error.toString());
×
1934
                }
1935
            }
1936
        }
1✔
1937
    }
1✔
1938

1939
    private static String printMod(Class<? extends Modifier> c) {
1940
        String name = c.getName().toLowerCase();
×
1941
        name = name.replaceFirst("^.*\\.", "");
×
1942
        name = name.replaceAll("^(mod|visibility)", "");
×
1943
        name = name.replaceAll("impl$", "");
×
1944
        return name;
×
1945
    }
1946

1947
    private static String printMod(Modifier m) {
1948
        if (m instanceof Annotation) {
×
1949
            return ((Annotation) m).getAnnotationType();
×
1950
        }
1951
        return printMod(m.getClass());
×
1952
    }
1953

1954
    private void checkConstructor(ConstructorDef d) {
1955
        if (d.attrNearestClassOrModule() instanceof ModuleDef) {
1✔
1956
            if (d.getParameters().size() > 0) {
1✔
1957
                d.getParameters().addError("Module constructors must not have parameters.");
×
1958
            }
1959
        }
1960
        StructureDef s = d.attrNearestStructureDef();
1✔
1961
        if (s instanceof ClassDef) {
1✔
1962
            ClassDef c = (ClassDef) s;
1✔
1963
            WurstTypeClass ct = c.attrTypC();
1✔
1964
            WurstTypeClass extendedClass = ct.extendedClass();
1✔
1965
            if (extendedClass != null) {
1✔
1966
                // check if super constructor is called correctly...
1967
                // TODO check constr: get it from ct so that it has the correct type binding
1968
                ConstructorDef sc = d.attrSuperConstructor();
1✔
1969
                if (sc == null) {
1✔
1970
                    d.addError("No super constructor found.");
×
1971
                } else {
1972
                    List<WurstType> paramTypes = Lists.newArrayList();
1✔
1973
                    for (WParameter p : sc.getParameters()) {
1✔
1974
                        paramTypes.add(p.attrTyp());
1✔
1975
                    }
1✔
1976
                    if (d.getSuperConstructorCall() instanceof NoSuperConstructorCall
1✔
1977
                            && paramTypes.size() > 0) {
1✔
1978
                        c.addError("The extended class <" + extendedClass.getName() + "> does not expose a no-arg constructor. " +
×
1979
                                "You must define a constructor that calls super(..) appropriately, in this class.");
1980
                    } else {
1981
                        checkParams(d, "Incorrect call to super constructor: ", superArgs(d), paramTypes);
1✔
1982
                    }
1983
                }
1984
            }
1985
        } else {
1✔
1986
            if (d.getSuperConstructorCall() instanceof SomeSuperConstructorCall) {
1✔
1987
                d.addError("Module constructors cannot have super calls.");
×
1988
            }
1989
        }
1990
    }
1✔
1991

1992

1993
    private void checkArrayAccess(ExprVarArrayAccess ea) {
1994
        checkNameRefDeprecated(ea, ea.tryGetNameDef());
1✔
1995
        for (Expr index : ea.getIndexes()) {
1✔
1996
            if (!(index.attrTyp().isSubtypeOf(WurstTypeInt.instance(), ea))) {
1✔
1997
                index.addError("Arrayindices have to be of type int");
×
1998
            }
1999
        }
1✔
2000
    }
1✔
2001

2002
    private void checkInterfaceDef(InterfaceDef i) {
2003
        checkTypeName(i, i.getName());
1✔
2004
        // TODO check if functions are refinements
2005
    }
1✔
2006

2007
    private void checkNewObj(ExprNewObject e) {
2008
        ConstructorDef constr = e.attrConstructorDef();
1✔
2009
        if (constr != null) {
1✔
2010
            calledFunctions.put(e.attrNearestScope(), constr);
1✔
2011
            if (constr.attrNearestClassDef().attrIsAbstract()) {
1✔
2012
                e.addError("Cannot create an instance of the abstract class " + constr.attrNearestClassDef().getName());
×
2013
                return;
×
2014
            }
2015
            checkParams(e, "Wrong object creation: ", e.getArgs(), e.attrFunctionSignature());
1✔
2016
        }
2017

2018
    }
1✔
2019

2020
    private void nameDefsMustNotBeNamedAfterJassNativeTypes(NameDef n) {
2021
        PackageOrGlobal p = n.attrNearestPackage();
1✔
2022
        if (p == null) {
1✔
2023
            n.addError("Not in package or global: " + n.getName());
×
2024
        }
2025
        // checkIfTypeDefExists(n, p);
2026
        // if (p instanceof WPackage) {
2027
        // // check global scope
2028
        // p = p.getParent().attrNearestPackage();
2029
        // checkIfTypeDefExists(n, p);
2030
        // }
2031
    }
1✔
2032

2033
    private void checkMemberVar(ExprMemberVar e) {
2034
        if (e.getVarName().length() == 0) {
1✔
2035
            e.addError("Incomplete member access.");
×
2036
        }
2037
        if (e.getParent() instanceof WStatements) {
1✔
2038
            e.addError("Incomplete statement.");
×
2039
        }
2040
    }
1✔
2041

2042
    private void checkPackageName(CompilationUnit cu) {
2043
        if (cu.getPackages().size() == 1 && Utils.isWurstFile(cu.getCuInfo().getFile())) {
1✔
2044
            // only one package in a wurst file
2045
            WPackage p = cu.getPackages().get(0);
1✔
2046
            if (!Utils.fileName(cu.getCuInfo().getFile()).equals(p.getName() + ".wurst")
1✔
2047
                    && !Utils.fileName(cu.getCuInfo().getFile()).equals(p.getName() + ".jurst")) {
×
2048
                p.addError("The file must have the same name as the package " + p.getName());
×
2049
            }
2050
        }
2051
    }
1✔
2052

2053
    private void checkForDuplicatePackages(WurstModel model) {
2054
        model.attrPackages();
×
2055
    }
×
2056

2057
    private void checkBannedFunctions(ExprFunctionCall e) {
2058
        if (e.getFuncName().equals("TriggerRegisterVariableEvent")) {
1✔
2059
            if (e.getArgs().size() > 1) {
1✔
2060
                if (e.getArgs().get(1) instanceof ExprStringVal) {
1✔
2061
                    ExprStringVal varName = (ExprStringVal) e.getArgs().get(1);
1✔
2062
                    TRVEHelper.protectedVariables.add(varName.getValS());
1✔
2063
                    WLogger.info("keep: " + varName.getValS());
1✔
2064
                    return;
1✔
2065
                } else if (e.getArgs().get(1) instanceof ExprVarAccess) {
1✔
2066
                    // Check if this is a two line hook... thanks Bribe
2067
                    ExprVarAccess varAccess = (ExprVarAccess) e.getArgs().get(1);
1✔
2068
                    @Nullable FunctionImplementation nearestFunc = e.attrNearestFuncDef();
1✔
2069
                    WStatements fbody = nearestFunc.getBody();
1✔
2070
                    if (e.getParent() instanceof StmtReturn && fbody.size() <= 4 && fbody.get(fbody.size() - 2).structuralEquals(e.getParent())) {
1✔
2071
                        WParameters params = nearestFunc.getParameters();
1✔
2072
                        if (params.size() == 4 && ((TypeExprSimple) params.get(0).getTyp()).getTypeName().equals("trigger")
1✔
2073
                            && ((TypeExprSimple) params.get(1).getTyp()).getTypeName().equals("string")
1✔
2074
                            && ((TypeExprSimple) params.get(2).getTyp()).getTypeName().equals("limitop")
1✔
2075
                            && ((TypeExprSimple) params.get(3).getTyp()).getTypeName().equals("real")) {
1✔
2076
                            trveWrapperFuncs.add(nearestFunc.getName());
1✔
2077
                            WLogger.info("found wrapper: " + nearestFunc.getName());
1✔
2078
                            return;
1✔
2079
                        }
2080
                    }
2081
                }
×
2082
            } else {
2083

2084
                e.addError("Map contains TriggerRegisterVariableEvent with non-constant arguments. Can't be optimized.");
×
2085
            }
2086
        }
2087

2088
        if (e.getFuncName().equals("ExecuteFunc")) {
1✔
2089
            // executeFunc can only use constant string arguments
2090
            if (e.getArgs().size() != 1) {
1✔
2091
                e.addError("Wrong number of args");
×
2092
                return;
×
2093
            }
2094
            if (e.getArgs().get(0) instanceof ExprStringVal) {
1✔
2095
                ExprStringVal s = (ExprStringVal) e.getArgs().get(0);
1✔
2096
                String exFunc = s.getValS();
1✔
2097
                Collection<FuncLink> funcs = e.lookupFuncs(exFunc);
1✔
2098
                if (funcs.isEmpty()) {
1✔
2099
                    e.addError("Could not find function " + exFunc + ".");
×
2100
                    return;
×
2101
                }
2102
                if (funcs.size() > 1) {
1✔
2103
                    StringBuilder alternatives = new StringBuilder();
×
2104
                    for (NameLink nameLink : funcs) {
×
2105
                        alternatives.append("\n - ").append(Utils.printElementWithSource(Optional.of(nameLink.getDef())));
×
2106
                    }
×
2107
                    e.addError("Ambiguous function name: " + exFunc + ". Alternatives are: " + alternatives);
×
2108
                    return;
×
2109
                }
2110
                FuncLink func = Utils.getFirst(funcs);
1✔
2111
                if (func.getParameterTypes().size() != 0) {
1✔
2112
                    e.addError("Function " + exFunc + " must not have any parameters.");
×
2113
                }
2114
            } else {
1✔
2115
                e.addError("Wurst does only support ExecuteFunc with a single string as argument.");
×
2116
            }
2117
        }
2118
    }
1✔
2119

2120
    private boolean isViableSwitchtype(Expr expr) {
2121
        WurstType typ = expr.attrTyp();
1✔
2122
        if (typ.equalsType(WurstTypeInt.instance(), null) || typ.equalsType(WurstTypeString.instance(), null)) {
1✔
2123
            return true;
1✔
2124
        } else if (typ instanceof WurstTypeEnum) {
1✔
2125
            WurstTypeEnum wte = (WurstTypeEnum) typ;
1✔
2126
            return !wte.isStaticRef();
1✔
2127
        } else {
2128
            return false;
×
2129
        }
2130
    }
2131

2132
    private void checkSwitch(SwitchStmt s) {
2133
        if (!isViableSwitchtype(s.getExpr())) {
1✔
2134
            s.addError("The type " + s.getExpr().attrTyp()
×
2135
                    + " is not viable as switchtype.\nViable switchtypes: int, string, enum");
2136
        } else {
2137
            List<Expr> switchExprs = s.getCases().stream()
1✔
2138
                    .flatMap(e -> e.getExpressions().stream())
1✔
2139
                    .collect(Collectors.toList());
1✔
2140
            for (Expr cExpr : switchExprs) {
1✔
2141
                if (!cExpr.attrTyp().isSubtypeOf(s.getExpr().attrTyp(), cExpr)) {
1✔
2142
                    cExpr.addError("The type " + cExpr.attrTyp() + " does not match the switchtype "
1✔
2143
                            + s.getExpr().attrTyp() + ".");
1✔
2144
                }
2145
            }
1✔
2146
            for (int i = 0; i < switchExprs.size(); i++) {
1✔
2147
                Expr ei = switchExprs.get(i);
1✔
2148
                for (int j = 0; j < i; j++) {
1✔
2149
                    Expr ej = switchExprs.get(j);
1✔
2150
                    if (ei.structuralEquals(ej)) {
1✔
2151
                        ei.addError("The case " + Utils.prettyPrint(ei) + " is already handled in line " + ej.attrSource().getLine());
×
2152
                        return;
×
2153
                    }
2154
                }
2155
            }
2156
        }
2157
        for (String unhandledCase : s.calculateUnhandledCases()) {
1✔
2158
            s.addError(unhandledCase + " not covered in switchstatement and no default found.");
×
2159
        }
×
2160
        if (s.getCases().isEmpty()) {
1✔
2161
            s.addError("Switch statement without any cases.");
×
2162
        }
2163
    }
1✔
2164

2165
    public static void computeFlowAttributes(Element node) {
2166
        if (node instanceof WStatement) {
1✔
2167
            WStatement s = (WStatement) node;
1✔
2168
            s.attrNextStatements();
1✔
2169
        }
2170

2171
        // traverse childs
2172
        for (int i = 0; i < node.size(); i++) {
1✔
2173
            computeFlowAttributes(node.get(i));
1✔
2174
        }
2175
    }
1✔
2176

2177
    private void checkCodeArrays(TypeExprArray e) {
2178
        if (e.getBase() instanceof TypeExprSimple) {
1✔
2179
            TypeExprSimple base = (TypeExprSimple) e.getBase();
1✔
2180
            if (base.getTypeName().equals("code")) {
1✔
2181
                e.addError("Code arrays are not supported. Try using an array of triggers or conditionfuncs.");
×
2182
            }
2183

2184
        }
2185
    }
1✔
2186

2187

2188
    /**
2189
     * checks if func1 can override func2
2190
     */
2191
    public static boolean canOverride(FuncLink func1, FuncLink func2, boolean allowStaticOverride) {
2192
        return checkOverride(func1, func2, allowStaticOverride) == null;
1✔
2193
    }
2194

2195
    /**
2196
     * checks if func1 can override func2
2197
     * <p>
2198
     * Returns null if yes and an error message if not.
2199
     */
2200
    public static String checkOverride(FuncLink func1, FuncLink func2, boolean allowStaticOverride) {
2201
        if (!allowStaticOverride) {
1✔
2202
            if (func1.isStatic()) {
1✔
2203
                return "Static method " + func1.getName() + " cannot override other methods.";
1✔
2204
            }
2205
            if (func2.isStatic()) {
1✔
2206
                return "Static " + Utils.printElementWithSource(Optional.of(func2.getDef())) + " cannot be overridden.";
1✔
2207
            }
2208
        } else {
2209
            if (func1.isStatic() && !func2.isStatic()) {
1✔
2210
                return "Static method "
×
2211
                    + func1.getName()
×
2212
                    + " cannot override dynamic "
2213
                    + Utils.printElementWithSource(Optional.of(func2.getDef()))
×
2214
                    + ".";
2215
            } else if (!func1.isStatic() && func2.isStatic()) {
1✔
2216
                return "Method "
1✔
2217
                    + func1.getName()
1✔
2218
                    + " cannot override static "
2219
                    + Utils.printElementWithSource(Optional.of(func2.getDef()))
1✔
2220
                    + ".";
2221
            }
2222
        }
2223
        if (func1.isVarargMethod() && !func2.isVarargMethod()) {
1✔
2224
            return "Vararg method "
×
2225
                + func1.getName()
×
2226
                + " cannot override non-vararg method "
2227
                + Utils.printElementWithSource(Optional.of(func2.getDef()))
×
2228
                + ".";
2229
        }
2230
        if (!func1.isVarargMethod() && func2.isVarargMethod()) {
1✔
2231
            return "Non-vararg method "
×
2232
                + func1.getName()
×
2233
                + " cannot override vararg method "
2234
                + Utils.printElementWithSource(Optional.of(func2.getDef()))
×
2235
                + ".";
2236
        }
2237
        int paramCount2 = func2.getParameterTypes().size();
1✔
2238
        int paramCount1 = func1.getParameterTypes().size();
1✔
2239
        if (paramCount1 != paramCount2) {
1✔
2240
            return Utils.printElement(func2.getDef()) + " takes " + paramCount2
1✔
2241
                    + " parameters, but there are only " + paramCount1 + " parameters here.";
2242
        }
2243

2244
        // contravariant parametertypes
2245
        for (int i = 0; i < paramCount1; i++) {
1✔
2246
            WurstType type1 = func1.getParameterType(i);
1✔
2247
            WurstType type2 = func2.getParameterType(i);
1✔
2248
            if (!type1.isSupertypeOf(type2, func1.getDef())) {
1✔
2249
                return "Parameter " + type1 + " " + func1.getParameterName(i) + " should have type " + type2
1✔
2250
                        + " to override " + Utils.printElementWithSource(Optional.of(func2.getDef())) + ".";
1✔
2251
            }
2252
        }
2253
        // covariant return types
2254
        if (!func1.getReturnType().isSubtypeOf(func2.getReturnType(), func1.getDef())) {
1✔
2255
            return "Return type should be "
×
2256
                + func2.getReturnType()
×
2257
                + " to override "
2258
                + Utils.printElementWithSource(Optional.of(func2.getDef()))
×
2259
                + ".";
2260
        }
2261
        // no error
2262
        return null;
1✔
2263
    }
2264

2265
    private void checkForDuplicateNames(WScope scope) {
2266
        ImmutableMultimap<String, DefLink> links = scope.attrNameLinks();
1✔
2267
        for (String name : links.keySet()) {
1✔
2268
            ImmutableCollection<DefLink> nameLinks = links.get(name);
1✔
2269
            if (nameLinks.size() <= 1) {
1✔
2270
                continue;
1✔
2271
            }
2272
            @Nullable List<FuncLink> funcs = null;
1✔
2273
            @Nullable List<NameLink> other = null;
1✔
2274
            for (NameLink nl : nameLinks) {
1✔
2275
                if (nl.getDefinedIn() == scope) {
1✔
2276
                    if (nl instanceof FuncLink) {
1✔
2277
                        if (funcs == null) {
1✔
2278
                            funcs = Lists.newArrayList();
1✔
2279
                        }
2280
                        FuncLink funcLink = (FuncLink) nl;
1✔
2281
                        for (FuncLink link : funcs) {
1✔
2282
                            if (!distinctFunctions(funcLink, link)) {
1✔
2283
                                funcLink.getDef().addError(
1✔
2284
                                    "Function already defined : " + Utils.printElementWithSource(Optional.of(link.getDef())));
1✔
2285
                                link.getDef().addError(
×
2286
                                    "Function already defined : " + Utils.printElementWithSource(Optional.of(funcLink.getDef())));
×
2287
                            }
2288
                        }
1✔
2289

2290
                        funcs.add(funcLink);
1✔
2291
                    } else {
1✔
2292
                        if (other == null) {
1✔
2293
                            other = Lists.newArrayList();
1✔
2294
                        }
2295
                        other.add(nl);
1✔
2296
                    }
2297
                }
2298
            }
1✔
2299
            if (other != null && other.size() > 1) {
1✔
2300
                other.sort(Comparator.comparingInt(o -> o.getDef().attrSource().getLeftPos()));
1✔
2301
                NameLink l1 = other.get(0);
1✔
2302
                for (int j = 1; j < other.size(); j++) {
1✔
2303
                    other.get(j).getDef().addError("An element with name " + name + " already exists: "
1✔
2304
                        + Utils.printElementWithSource(Optional.of(l1.getDef())));
1✔
2305
                }
2306
            }
2307
        }
1✔
2308
    }
1✔
2309

2310
    private boolean distinctFunctions(FuncLink nl1, FuncLink nl2) {
2311
        if (receiverTypesDifferent(nl1, nl2)) {
1✔
2312
            return true;
1✔
2313
        }
2314
        FunctionDefinition f1 = nl1.getDef();
1✔
2315
        FunctionDefinition f2 = nl2.getDef();
1✔
2316
        WParameters ps1 = f1.getParameters();
1✔
2317
        WParameters ps2 = f2.getParameters();
1✔
2318
        if (ps1.size() != ps2.size()) {
1✔
2319
            return true;
1✔
2320
        }
2321
        return parametersTypeDisjunct(ps1, ps2);
1✔
2322
    }
2323

2324
    private boolean receiverTypesDifferent(FuncLink nl1, FuncLink nl2) {
2325
        if (nl1.getReceiverType() == null) {
1✔
2326
            return nl2.getReceiverType() != null;
1✔
2327
        } else {
2328
            return nl2.getReceiverType() == null || !nl1.getReceiverType().equalsType(nl2.getReceiverType(), nl1.getDef());
1✔
2329
        }
2330
    }
2331

2332
    private void checkForDuplicateImports(WPackage p) {
2333
        Set<String> imports = Sets.newLinkedHashSet();
1✔
2334
        for (WImport imp : p.getImports()) {
1✔
2335
            if (!imports.add(imp.getPackagename())) {
1✔
2336
                imp.addError("The package " + imp.getPackagename() + " is already imported.");
×
2337
            }
2338
        }
1✔
2339
    }
1✔
2340

2341
    private void checkVarDef(VarDef v) {
2342
        WurstType vtype = v.attrTyp();
1✔
2343

2344
        if (vtype instanceof WurstTypeCode && v.attrIsDynamicClassMember()) {
1✔
2345
            v.addError("Code members not allowed as dynamic class members (variable " + v.getName() + ")\n"
×
2346
                    + "Try using a trigger or conditionfunc instead.");
2347
        }
2348

2349
        if (v instanceof GlobalOrLocalVarDef) {
1✔
2350
            GlobalOrLocalVarDef g = (GlobalOrLocalVarDef) v;
1✔
2351
            if (g.attrIsConstant() && g.getInitialExpr() instanceof NoExpr && !g.attrIsDynamicClassMember()) {
1✔
2352
                g.addError("Constant variable " + g.getName() + " needs an initial value.");
×
2353
            }
2354
        }
2355

2356
        if (vtype instanceof WurstTypeArray) {
1✔
2357
            WurstTypeArray wta = (WurstTypeArray) vtype;
1✔
2358
            switch (wta.getDimensions()) {
1✔
2359
                case 0:
2360
                    v.addError("0-dimensional arrays are not allowed");
×
2361
                    break;
×
2362
                case 1:
2363
                    if (v.attrIsDynamicClassMember() && wta.getSize(0) <= 0) {
1✔
2364
                        v.addError("Array members require a fixed size greater 0.");
×
2365
                    }
2366
                    break;
2367
                default:
2368
                    v.addError("Multidimensional Arrays are not yet supported.");
×
2369
                    break;
2370
            }
2371
        }
2372

2373
        if (vtype instanceof WurstTypeNull) {
1✔
2374
            v.addError("Initial value of variable " + v.getName() + " is 'null'. Specify a concrete type.");
×
2375
        }
2376

2377
    }
1✔
2378

2379
    private void checkLocalShadowing(LocalVarDef v) {
2380
        NameLink shadowed = v.getParent().getParent().lookupVar(v.getName(), false);
1✔
2381
        if (shadowed != null) {
1✔
2382
            if (shadowed.getDef() instanceof LocalVarDef) {
1✔
2383
                v.addError("Variable " + v.getName() + " hides an other local variable with the same name.");
×
2384
            } else if (shadowed.getDef() instanceof WParameter) {
1✔
2385
                v.addError("Variable " + v.getName() + " hides a parameter with the same name.");
×
2386
            }
2387
        }
2388
    }
1✔
2389

2390
    private void checkConstructorSuperCall(ConstructorDef c) {
2391
        if (c.getSuperConstructorCall() instanceof SomeSuperConstructorCall) {
1✔
2392
            if (c.attrNearestClassDef() != null) {
1✔
2393
                ClassDef classDef = c.attrNearestClassDef();
1✔
2394
                if (classDef.getExtendedClass() instanceof NoTypeExpr) {
1✔
2395
                    c.addError("Super call in a class which extends nothing.");
×
2396
                }
2397
            }
2398
        }
2399
    }
1✔
2400

2401
    private void checkParameter(WParameter param) {
2402
        if (param.attrTyp() instanceof WurstTypeArray) {
1✔
2403
            param.addError("Cannot use arrays as parameters.");
×
2404
        }
2405
    }
1✔
2406
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc