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

wurstscript / WurstScript / 266

29 Sep 2025 09:10AM UTC coverage: 62.244% (+0.02%) from 62.222%
266

Pull #1096

circleci

Frotty
Optimize imports
Pull Request #1096: Perf improvements

17480 of 28083 relevant lines covered (62.24%)

0.62 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
        // Pull once
50
        final ImmutableCollection<FuncLink> raw = e.lookupFuncsNoConfig(name, showErrors);
1✔
51

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

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

73

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

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

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

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

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

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

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

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

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

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

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

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

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

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

228
        return null;
1✔
229
    }
230

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

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

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

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

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

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

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

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

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

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

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

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

334

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