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

wurstscript / WurstScript / 209

25 Oct 2023 11:42AM CUT coverage: 63.746% (-0.01%) from 63.756%
209

Pull #1081

circleci

Frotty
WIP
Pull Request #1081: More performance improvements

17267 of 27087 relevant lines covered (63.75%)

0.64 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.wurstio.utils.FileUtils;
5
import de.peeeq.wurstscript.WLogger;
6
import de.peeeq.wurstscript.ast.*;
7
import de.peeeq.wurstscript.attributes.CofigOverridePackages;
8
import de.peeeq.wurstscript.attributes.CompileError;
9
import de.peeeq.wurstscript.attributes.ImplicitFuncs;
10
import de.peeeq.wurstscript.attributes.names.DefLink;
11
import de.peeeq.wurstscript.attributes.names.FuncLink;
12
import de.peeeq.wurstscript.attributes.names.NameLink;
13
import de.peeeq.wurstscript.attributes.names.VarLink;
14
import de.peeeq.wurstscript.gui.ProgressHelper;
15
import de.peeeq.wurstscript.types.*;
16
import de.peeeq.wurstscript.utils.Utils;
17
import de.peeeq.wurstscript.validation.controlflow.DataflowAnomalyAnalysis;
18
import de.peeeq.wurstscript.validation.controlflow.ReturnsAnalysis;
19
import io.vavr.Tuple2;
20
import org.eclipse.jdt.annotation.Nullable;
21

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

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

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

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

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

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

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

63

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

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

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

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

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

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

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

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

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

154
                }
1✔
155
            }
156
        }
1✔
157
    }
1✔
158

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

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

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

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

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

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

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

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

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

456
    }
1✔
457

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

631
        return true;
1✔
632
    }
633

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

637
    }
1✔
638

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

642
    }
1✔
643

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

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

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

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

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

675
    }
1✔
676

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

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

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

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

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

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

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

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

759
    }
1✔
760

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

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

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

780
    }
1✔
781

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

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

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

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

814
    }
1✔
815

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

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

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

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

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

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

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

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

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

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

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

874
    }
1✔
875

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

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

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

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

912
            }
1✔
913

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

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

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

939
            }
1✔
940

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

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

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

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

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

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

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

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

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

1066
    }
1✔
1067

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

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

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

1102
    }
1✔
1103

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1325
        checkFuncDefDeprecated(stmtCall);
1✔
1326

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

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

1354
    }
1✔
1355

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1574

1575
                }
1576
            }
1577
        }
1578

1579
    }
1✔
1580

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1729
    }
1✔
1730

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1993

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

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

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

2019
    }
1✔
2020

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

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

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

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

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

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

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

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

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

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

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

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

2185
        }
2186
    }
1✔
2187

2188

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

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

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

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

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

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

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

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

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

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

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

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

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

2378
    }
1✔
2379

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

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

2402
    private void checkParameter(WParameter param) {
2403
        if (param.attrTyp() instanceof WurstTypeArray) {
1✔
2404
            param.addError("Cannot use arrays as parameters.");
×
2405
        }
2406
    }
1✔
2407
}
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