|
18 | 18 | import java.util.function.Predicate; |
19 | 19 | import java.util.stream.Collectors; |
20 | 20 |
|
| 21 | +import static de.peeeq.wurstscript.attributes.names.NameResolution.lookupMemberFuncs; |
| 22 | + |
21 | 23 |
|
22 | 24 | /** |
23 | 25 | * this attribute find the variable definition for every variable reference |
@@ -64,19 +66,61 @@ public static FuncLink calculate(final ExprFuncRef node) { |
64 | 66 | return getExtensionFunction(node.getLeft(), node.getRight(), node.getOp()); |
65 | 67 | } |
66 | 68 |
|
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 | + } |
72 | 95 |
|
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 | + } |
76 | 108 | } |
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; |
78 | 116 | } |
79 | 117 |
|
| 118 | + public static @Nullable FunctionDefinition calculateDef(final ExprMemberMethod node) { |
| 119 | + var fl = node.attrFuncLink(); |
| 120 | + return fl == null ? null : fl.getDef(); |
| 121 | + } |
| 122 | + |
| 123 | + |
80 | 124 | public static @Nullable FuncLink calculate(final ExprFunctionCall node) { |
81 | 125 | FuncLink result = searchFunction(node.getFuncName(), node, argumentTypes(node)); |
82 | 126 |
|
|
0 commit comments