Skip to content

Commit b43c9ce

Browse files
committed
Correctly collect overloads in class hierachy, fixes #863
1 parent f4e4dd1 commit b43c9ce

3 files changed

Lines changed: 107 additions & 9 deletions

File tree

de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrFuncDef.java

Lines changed: 53 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
import java.util.function.Predicate;
1919
import java.util.stream.Collectors;
2020

21+
import static de.peeeq.wurstscript.attributes.names.NameResolution.lookupMemberFuncs;
22+
2123

2224
/**
2325
* this attribute find the variable definition for every variable reference
@@ -64,19 +66,61 @@ public static FuncLink calculate(final ExprFuncRef node) {
6466
return getExtensionFunction(node.getLeft(), node.getRight(), node.getOp());
6567
}
6668

67-
public static @Nullable FuncLink calculate(final ExprMemberMethod node) {
68-
69-
Expr left = node.getLeft();
70-
WurstType leftType = left.attrTyp();
71-
String funcName = node.getFuncName();
69+
public static de.peeeq.wurstscript.attributes.names.FuncLink calculate(final ExprMemberMethod node) {
70+
// collect candidates
71+
WurstType recvT = node.getLeft().attrTyp();
72+
String name = node.getFuncName();
73+
var cands = de.peeeq.wurstscript.attributes.names.NameResolution
74+
.lookupMemberFuncs(node, recvT, name, /*showErrors=*/false);
75+
76+
if (cands.isEmpty()) return null;
77+
78+
// resolve with arguments using the same rules as signatures:
79+
// build a (sig, link) pair for each candidate and pick the best match.
80+
java.util.List<WurstType> argTypes = de.peeeq.wurstscript.attributes.AttrFuncDef.argumentTypesPre(node);
81+
de.peeeq.wurstscript.attributes.names.FuncLink bestLink = null;
82+
de.peeeq.wurstscript.types.FunctionSignature bestSig = null;
83+
84+
// Pass 1: exact matches only
85+
for (var f : cands) {
86+
var sig = de.peeeq.wurstscript.types.FunctionSignature.fromNameLink(f);
87+
var matched = sig.matchAgainstArgs(argTypes, node);
88+
if (matched != null) {
89+
// choose the first exact match; if you want “most specific” tie-break, add it here
90+
bestLink = f;
91+
bestSig = matched;
92+
break;
93+
}
94+
}
7295

73-
@Nullable FuncLink result = searchMemberFunc(node, leftType, funcName, argumentTypes(node));
74-
if (result == null) {
75-
node.addError("The method " + funcName + " is undefined for receiver of type " + leftType);
96+
if (bestLink == null) {
97+
// Pass 2: best-effort (to align with diagnostics), pick the lowest badness
98+
int bestBadness = Integer.MAX_VALUE;
99+
for (var f : cands) {
100+
var sig = de.peeeq.wurstscript.types.FunctionSignature.fromNameLink(f);
101+
var res = sig.tryMatchAgainstArgs(argTypes, node.getArgs(), node);
102+
if (res.getBadness() < bestBadness) {
103+
bestBadness = res.getBadness();
104+
bestLink = f;
105+
bestSig = res.getSig();
106+
}
107+
}
76108
}
77-
return result;
109+
110+
if (bestLink == null) return null;
111+
112+
// IMPORTANT: carry the mapping from the matched signature back into the link,
113+
// so downstream types are fully bound at the call site.
114+
var bound = bestLink.withTypeArgBinding(node, bestSig.getMapping());
115+
return bound;
78116
}
79117

118+
public static @Nullable FunctionDefinition calculateDef(final ExprMemberMethod node) {
119+
var fl = node.attrFuncLink();
120+
return fl == null ? null : fl.getDef();
121+
}
122+
123+
80124
public static @Nullable FuncLink calculate(final ExprFunctionCall node) {
81125
FuncLink result = searchFunction(node.getFuncName(), node, argumentTypes(node));
82126

de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/attributes/AttrPossibleFunctionSignatures.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import com.google.common.collect.Lists;
66
import de.peeeq.wurstscript.ast.*;
77
import de.peeeq.wurstscript.attributes.names.FuncLink;
8+
import de.peeeq.wurstscript.attributes.names.NameResolution;
89
import de.peeeq.wurstscript.types.FunctionSignature;
910
import de.peeeq.wurstscript.types.FunctionSignature.ArgsMatchResult;
1011
import de.peeeq.wurstscript.types.VariableBinding;
@@ -117,6 +118,28 @@ private static ImmutableCollection<FunctionSignature> findBestSignature(StmtCall
117118
return out.build();
118119
}
119120

121+
public static com.google.common.collect.ImmutableCollection<FunctionSignature> calculate(ExprMemberMethod mm) {
122+
// 1) Collect all member candidates (includes inherited overloads)
123+
WurstType recvT = mm.getLeft().attrTyp();
124+
com.google.common.collect.ImmutableCollection<FuncLink> fs =
125+
NameResolution.lookupMemberFuncs(mm, recvT, mm.getFuncName(), /*showErrors=*/false);
126+
127+
// 2) Convert to signatures using the bound link (receiver/params/return + type-arg binding)
128+
com.google.common.collect.ImmutableList.Builder<FunctionSignature> out = com.google.common.collect.ImmutableList.builder();
129+
for (FuncLink f : fs) {
130+
FunctionSignature sig = FunctionSignature.fromNameLink(f);
131+
132+
// Apply any explicit/given type-args at the call-site to the signature mapping:
133+
VariableBinding mapping = givenBinding(mm, sig.getDefinitionTypeVariables());
134+
sig = sig.setTypeArgs(mm, mapping);
135+
136+
out.add(sig);
137+
}
138+
139+
// 3) Reuse the common “pick best” logic (exact matches first, then best-errors)
140+
return findBestSignature(mm, out.build());
141+
}
142+
120143

121144
public static ImmutableCollection<FunctionSignature> calculate(ExprNewObject fc) {
122145
TypeDef typeDef = fc.attrTypeDef();

de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/BugTests.java

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1473,4 +1473,35 @@ public void callingDestroyThisInConstructor() {
14731473
"endpackage");
14741474
}
14751475

1476+
@Test
1477+
public void overloadsHiddenBySubclassName_onlyZeroArgSeen_errorsOnArgs() {
1478+
testAssertOkLines(true,
1479+
"package Test",
1480+
"native testSuccess()",
1481+
"native println(string s)",
1482+
"class A",
1483+
" function func(int i)",
1484+
" println(\"func_int...\")",
1485+
"",
1486+
" function func(string s)",
1487+
" println(\"func_string...\")",
1488+
"",
1489+
"class B extends A",
1490+
" function func()",
1491+
" println(\"func_noarg\")",
1492+
"",
1493+
" function func(bool b)",
1494+
" println(\"func_boolean...\")",
1495+
"",
1496+
"init",
1497+
" let b = new B()",
1498+
" b.func()",
1499+
" b.func(true)",
1500+
" b.func(1)",
1501+
" b.func(\"1\")",
1502+
" testSuccess()"
1503+
);
1504+
}
1505+
1506+
14761507
}

0 commit comments

Comments
 (0)