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

wurstscript / WurstScript / 271

29 Sep 2025 12:12PM UTC coverage: 64.649% (+2.4%) from 62.222%
271

Pull #1096

circleci

Frotty
Merge branch 'perf-improvements' of https://github.com/wurstscript/WurstScript into perf-improvements
Pull Request #1096: Perf improvements

18202 of 28155 relevant lines covered (64.65%)

0.65 hits per line

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

92.18
de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/names/NameResolution.java
1
package de.peeeq.wurstscript.attributes.names;
2

3
import com.google.common.collect.ImmutableCollection;
4
import com.google.common.collect.ImmutableList;
5
import com.google.common.collect.Lists;
6
import de.peeeq.wurstscript.ast.*;
7
import de.peeeq.wurstscript.jassIm.ImExpr;
8
import de.peeeq.wurstscript.jassIm.ImFunction;
9
import de.peeeq.wurstscript.jassIm.JassIm;
10
import de.peeeq.wurstscript.translation.imtranslation.ImTranslator;
11
import de.peeeq.wurstscript.types.*;
12
import de.peeeq.wurstscript.utils.Utils;
13
import org.eclipse.jdt.annotation.Nullable;
14

15
import java.util.List;
16
import java.util.Optional;
17

18
public class NameResolution {
×
19

20
    public static ImmutableCollection<FuncLink> lookupFuncsNoConfig(Element node, String name, boolean showErrors) {
21
        StructureDef nearestStructureDef = node.attrNearestStructureDef();
1✔
22
        if (nearestStructureDef != null) {
1✔
23
            // inside a class one can write foo instead of this.foo()
24
            // so the receiver type is implicitly given by the enclosing class
25
            WurstType receiverType = nearestStructureDef.attrTyp();
1✔
26
            ImmutableCollection<FuncLink> funcs = node.lookupMemberFuncs(receiverType, name, showErrors);
1✔
27
            if (!funcs.isEmpty()) {
1✔
28
                return funcs;
1✔
29
            }
30
        }
31

32

33
        List<FuncLink> result = Lists.newArrayList();
1✔
34
        WScope scope = node.attrNearestScope();
1✔
35
        while (scope != null) {
1✔
36
            for (DefLink n : scope.attrNameLinks().get(name)) {
1✔
37
                if (n instanceof FuncLink && n.getReceiverType() == null) {
1✔
38
                    if (!result.contains(n)) {
1✔
39
                        result.add((FuncLink) n);
1✔
40
                    }
41
                }
42
            }
1✔
43
            scope = nextScope(scope);
1✔
44
        }
45
        return removeDuplicates(result);
1✔
46
    }
47

48
    public static ImmutableCollection<FuncLink> lookupFuncs(Element e, String name, boolean showErrors) {
49
        final ImmutableCollection<FuncLink> raw = e.lookupFuncsNoConfig(name, showErrors);
1✔
50

51
        if (raw != null) {
1✔
52
            final java.util.Collection<FuncLink> c = raw;
1✔
53
            final int n = c.size();
1✔
54
            if (n == 0) return ImmutableList.of();
1✔
55
            if (n == 1) {
1✔
56
                // avoid builder/array allocs
57
                final FuncLink only = c.iterator().next();
1✔
58
                return ImmutableList.of(only.withConfigDef());
1✔
59
            }
60
            final ImmutableList.Builder<FuncLink> b = ImmutableList.builderWithExpectedSize(n);
1✔
61
            for (FuncLink f : c) b.add(f.withConfigDef());
1✔
62
            return b.build();
1✔
63
        }
64

65
        // Fallback if not a Collection (unknown size)
66
        ImmutableList.Builder<FuncLink> b = ImmutableList.builder();
×
67
        for (FuncLink f : raw) b.add(f.withConfigDef());
×
68
        return b.build();
×
69
    }
70

71

72
    private static <T extends NameLink> ImmutableCollection<T> removeDuplicates(List<T> nameLinks) {
73
        List<T> result = Lists.newArrayList();
1✔
74
        nextLink:
75
        for (T nl : nameLinks) {
1✔
76
            for (T other : result) {
1✔
77
                if (other.getDef() == nl.getDef()) {
1✔
78
                    continue nextLink;
1✔
79
                }
80
            }
1✔
81
            result.add(nl);
1✔
82
        }
1✔
83
        return ImmutableList.copyOf(result);
1✔
84
    }
85

86
    private static @Nullable WScope nextScope(WScope scope) {
87
        Element parent = scope.getParent();
1✔
88
        if (parent == null) {
1✔
89
            return null;
1✔
90
        }
91
        WScope currentScope = scope;
1✔
92
        if (currentScope instanceof ModuleInstanciation) {
1✔
93
            ModuleInstanciation moduleInstanciation = (ModuleInstanciation) currentScope;
1✔
94
            // for module instanciations the next scope is the package in which
95
            // the module was defined
96
            return nextScope(moduleInstanciation.attrModuleOrigin());
1✔
97
        }
98
        return parent.attrNearestScope();
1✔
99
    }
100

101
    public static ImmutableCollection<FuncLink> lookupMemberFuncs(Element node, WurstType receiverType, String name, boolean showErrors) {
102
        List<FuncLink> result = Lists.newArrayList();
1✔
103
        addMemberMethods(node, receiverType, name, result);
1✔
104

105
        WScope scope = node.attrNearestScope();
1✔
106
        while (scope != null) {
1✔
107
            for (DefLink n : scope.attrNameLinks().get(name)) {
1✔
108
                if (!(n instanceof FuncLink)) {
1✔
109
                    continue;
1✔
110
                }
111
                DefLink n2 = matchDefLinkReceiver(n, receiverType, node, false);
1✔
112
                if (n2 != null) {
1✔
113
                    FuncLink f = (FuncLink) n2;
1✔
114
                    result.add(f);
1✔
115
                }
116
            }
1✔
117
            scope = nextScope(scope);
1✔
118
        }
119
        return removeDuplicates(result);
1✔
120
    }
121

122
    public static void addMemberMethods(Element node,
123
                                        WurstType receiverType, String name, List<FuncLink> result) {
124
        receiverType.addMemberMethods(node, name, result);
1✔
125
    }
1✔
126

127
    public static NameLink lookupVarNoConfig(Element node, String name, boolean showErrors) {
128
        NameLink privateCandidate = null;
1✔
129
        List<NameLink> candidates = Lists.newArrayList();
1✔
130

131
        for (WScope scope = node.attrNearestScope(); scope != null; scope = nextScope(scope)) {
1✔
132

133
            if (scope instanceof LoopStatementWithVarDef) {
1✔
134
                LoopStatementWithVarDef loop = (LoopStatementWithVarDef) scope;
1✔
135
                // only consider this scope if node is in the body:
136
                if (!Utils.elementContained(Optional.of(node), loop.getBody())) {
1✔
137
                    continue;
1✔
138
                }
139
            }
140

141
            if (scope instanceof StructureDef) {
1✔
142
                StructureDef nearestStructureDef = (StructureDef) scope;
1✔
143
                // inside a class one can write foo instead of this.foo()
144
                // so the receiver type is implicitly given by the enclosing class
145
                WurstTypeNamedScope receiverType = (WurstTypeNamedScope) nearestStructureDef.attrTyp();
1✔
146
                for (DefLink link : receiverType.nameLinks(name)) {
1✔
147
                    if (!(link instanceof FuncLink)) {
1✔
148
                        return link;
1✔
149
                    }
150
                }
1✔
151
            }
152
            for (DefLink n : scope.attrNameLinks().get(name)) {
1✔
153
                WurstType n_receiverType = n.getReceiverType();
1✔
154
                if (n instanceof VarLink && n_receiverType == null) {
1✔
155

156
                    if (n.getVisibility() != Visibility.PRIVATE_OTHER
1✔
157
                            && n.getVisibility() != Visibility.PROTECTED_OTHER) {
1✔
158
                        candidates.add(n);
1✔
159
                    } else if (privateCandidate == null) {
1✔
160
                        privateCandidate = n;
1✔
161
                    }
162

163
                } else if (n instanceof TypeDefLink) {
1✔
164
                    candidates.add(n);
1✔
165
                }
166

167
            }
1✔
168
            if (candidates.size() > 0) {
1✔
169
                if (showErrors && candidates.size() > 1) {
1✔
170
                    node.addError("Reference to variable " + name + " is ambiguous. Alternatives are:\n"
1✔
171
                            + Utils.printAlternatives(candidates));
1✔
172
                }
173
                return candidates.get(0);
1✔
174
            }
175
        }
176
        if (showErrors) {
1✔
177
            if (privateCandidate == null) {
1✔
178
                node.addError("Could not find variable " + name + ".");
×
179
            } else {
180
                node.addError(Utils.printElementWithSource(Optional.of(privateCandidate.getDef()))
×
181
                        + " is not visible inside this package. If you want to access it, declare it public.");
182
                return privateCandidate;
×
183
            }
184
        }
185
        return null;
1✔
186
    }
187

188
    public static NameLink lookupMemberVar(Element node, WurstType receiverType, String name, boolean showErrors) {
189
        WScope scope = node.attrNearestScope();
1✔
190
        while (scope != null) {
1✔
191
            for (DefLink n : scope.attrNameLinks().get(name)) {
1✔
192
                if (!(n instanceof VarLink)) {
1✔
193
                    continue;
1✔
194
                }
195
                DefLink n2 = matchDefLinkReceiver(n, receiverType, node, showErrors);
1✔
196
                if (n2 != null) {
1✔
197
                    return n2;
1✔
198
                }
199
            }
1✔
200
            scope = nextScope(scope);
1✔
201
        }
202

203
        if (receiverType instanceof WurstTypeClassOrInterface) {
1✔
204
            WurstTypeClassOrInterface ct = (WurstTypeClassOrInterface) receiverType;
1✔
205
            for (DefLink n : ct.nameLinks().get(name)) {
1✔
206
                if (n instanceof VarLink || n instanceof TypeDefLink) {
1✔
207
                    if (n.getVisibility().isPublic()) {
1✔
208
                        return n;
1✔
209
                    }
210
                }
211
            }
×
212
        } else if (receiverType instanceof WurstTypeArray && name.equals("length")) {
1✔
213
            // special lookup for length
214
            WurstTypeArray wta = (WurstTypeArray) receiverType;
1✔
215
            if (wta.getDimensions() > 0) {
1✔
216
                int size = wta.getSize(0);
1✔
217
                return new OtherLink(Visibility.PUBLIC, name, WurstTypeInt.instance()) {
1✔
218
                    @Override
219
                    public ImExpr translate(NameRef e, ImTranslator t, ImFunction f) {
220
                        return JassIm.ImIntVal(size);
1✔
221
                    }
222
                };
223
            }
224
        }
225

226
        return null;
1✔
227
    }
228

229
    public static DefLink matchDefLinkReceiver(DefLink n, WurstType receiverType, Element node, boolean showErrors) {
230
        WurstType n_receiverType = n.getReceiverType();
1✔
231
        if (n_receiverType == null) {
1✔
232
            return null;
1✔
233
        }
234
        VariableBinding mapping = receiverType.matchAgainstSupertype(n_receiverType, node, VariableBinding.emptyMapping().withTypeVariables(n.getTypeParams()), VariablePosition.RIGHT);
1✔
235
        if (mapping == null) {
1✔
236
            return null;
1✔
237
        }
238
        if (showErrors) {
1✔
239
            if (n.getVisibility() == Visibility.PRIVATE_OTHER) {
1✔
240
                node.addError(Utils.printElement(n.getDef()) + " is private and cannot be used here.");
×
241
            } else if (n.getVisibility() == Visibility.PROTECTED_OTHER) {
1✔
242
                node.addError(Utils.printElement(n.getDef()) + " is protected and cannot be used here.");
×
243
            }
244
        }
245
        return n.withTypeArgBinding(node, mapping);
1✔
246
    }
247

248
    public static @Nullable TypeDef lookupType(Element node, String name, boolean showErrors) {
249

250
        NameLink privateCandidate = null;
1✔
251
        List<NameLink> candidates = Lists.newArrayList();
1✔
252

253
        WScope scope = node.attrNearestScope();
1✔
254
        while (scope != null) {
1✔
255
            for (NameLink n : scope.attrTypeNameLinks().get(name)) {
1✔
256
                if (n.getDef() instanceof TypeDef) {
1✔
257
                    if (n.getVisibility() != Visibility.PRIVATE_OTHER
1✔
258
                            && n.getVisibility() != Visibility.PROTECTED_OTHER) {
1✔
259
                        candidates.add(n);
1✔
260
                    } else if (privateCandidate == null) {
1✔
261
                        privateCandidate = n;
1✔
262
                    }
263
                }
264
            }
1✔
265
            if (candidates.size() > 0) {
1✔
266
                if (showErrors && candidates.size() > 1) {
1✔
267
                    node.addError("Reference to type " + name + " is ambiguous. Alternatives are:\n"
×
268
                            + Utils.printAlternatives(candidates));
×
269
                }
270
                return (TypeDef) candidates.get(0).getDef();
1✔
271
            }
272
            scope = nextScope(scope);
1✔
273
        }
274
        if (showErrors) {
1✔
275
            if (privateCandidate == null) {
1✔
276
                node.addError("Could not find type " + name + ".");
1✔
277
            } else {
278
                node.addError(Utils.printElementWithSource(Optional.of(privateCandidate.getDef()))
1✔
279
                        + " is not visible inside this package. If you want to access it, declare it public.");
280
                return (TypeDef) privateCandidate.getDef();
1✔
281
            }
282
        }
283
        return null;
1✔
284
    }
285

286
    public static PackageLink lookupPackage(Element node, String name, boolean showErrors) {
287
        WScope scope = node.attrNearestScope();
1✔
288
        while (scope != null) {
1✔
289
            for (NameLink n : scope.attrNameLinks().get(name)) {
1✔
290
                if (n instanceof PackageLink) {
1✔
291
                    return (PackageLink) n;
1✔
292
                }
293
            }
×
294
            scope = nextScope(scope);
1✔
295
        }
296
        return null;
1✔
297
    }
298

299
    public static ImmutableCollection<FuncLink> lookupFuncsShort(Element elem, String name) {
300
        return lookupFuncs(elem, name, true);
1✔
301
    }
302

303
    public static ImmutableCollection<FuncLink> lookupMemberFuncsShort(Element elem, WurstType receiverType, String name) {
304
        return lookupMemberFuncs(elem, receiverType, name, true);
1✔
305
    }
306

307
    public static NameLink lookupVarShort(Element node, String name) {
308
        return lookupVar(node, name, true);
×
309
    }
310

311
    public static NameLink lookupMemberVarShort(Element node, WurstType receiverType, String name) {
312
        return lookupMemberVar(node, receiverType, name, true);
1✔
313
    }
314

315
    public static @Nullable TypeDef lookupTypeShort(Element node, String name) {
316
        return lookupType(node, name, true);
1✔
317
    }
318

319
    public static PackageLink lookupPackageShort(Element node, String name) {
320
        return lookupPackage(node, name, true);
1✔
321
    }
322

323
    public static NameLink lookupVar(Element e, String name, boolean showErrors) {
324
        NameLink v = e.lookupVarNoConfig(name, showErrors);
1✔
325
        if (v != null) {
1✔
326
            NameDef actual = v.getDef().attrConfigActualNameDef();
1✔
327
            return v.withDef(actual);
1✔
328
        }
329
        return null;
1✔
330
    }
331

332

333
}
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