|
6 | 6 | import de.peeeq.wurstscript.WurstOperator; |
7 | 7 | import de.peeeq.wurstscript.ast.*; |
8 | 8 | import de.peeeq.wurstscript.attributes.names.FuncLink; |
| 9 | +import de.peeeq.wurstscript.attributes.names.NameResolution; |
9 | 10 | import de.peeeq.wurstscript.attributes.names.Visibility; |
10 | 11 | import de.peeeq.wurstscript.types.*; |
11 | 12 | import de.peeeq.wurstscript.utils.Utils; |
|
18 | 19 | import java.util.function.Predicate; |
19 | 20 | import java.util.stream.Collectors; |
20 | 21 |
|
| 22 | +import static de.peeeq.wurstscript.attributes.AttrPossibleFunctionSignatures.*; |
| 23 | +import static de.peeeq.wurstscript.attributes.names.NameResolution.lookupMemberFuncs; |
| 24 | + |
21 | 25 |
|
22 | 26 | /** |
23 | 27 | * this attribute find the variable definition for every variable reference |
@@ -65,18 +69,102 @@ public static FuncLink calculate(final ExprFuncRef node) { |
65 | 69 | } |
66 | 70 |
|
67 | 71 | public static @Nullable FuncLink calculate(final ExprMemberMethod node) { |
| 72 | + WurstType recvT = node.getLeft().attrTyp(); |
| 73 | + var raw = NameResolution.lookupMemberFuncs(node, recvT, node.getFuncName(), /*showErrors=*/false); |
68 | 74 |
|
69 | | - Expr left = node.getLeft(); |
70 | | - WurstType leftType = left.attrTyp(); |
71 | | - String funcName = node.getFuncName(); |
| 75 | + java.util.ArrayList<FuncLink> visible = new java.util.ArrayList<>(raw.size()); |
| 76 | + java.util.ArrayList<FuncLink> hidden = new java.util.ArrayList<>(raw.size()); |
| 77 | + for (var f : raw) { |
| 78 | + if (isVisible(f)) visible.add(f); else hidden.add(f); |
| 79 | + } |
72 | 80 |
|
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); |
| 81 | + if (!raw.isEmpty() && visible.isEmpty()) { |
| 82 | + // Keep the classic diagnostic the tests look for: |
| 83 | + node.addError("The method " + node.getFuncName() + " is not visible here."); |
| 84 | + return null; // don’t leak a def to downstream passes/codegen |
76 | 85 | } |
77 | | - return result; |
| 86 | + |
| 87 | + java.util.List<FuncLink> methods = new java.util.ArrayList<>(); |
| 88 | + java.util.List<FuncLink> exts = new java.util.ArrayList<>(); |
| 89 | + for (var f : visible) { |
| 90 | + if (isExtension(f)) exts.add(f); else methods.add(f); |
| 91 | + } |
| 92 | + |
| 93 | + if (!exts.isEmpty()) { |
| 94 | + exts = keepMostSpecificReceivers(exts, FuncLink::getReceiverType, node); |
| 95 | + } |
| 96 | + |
| 97 | + java.util.ArrayList<FuncLink> cands = new java.util.ArrayList<>(methods.size() + exts.size()); |
| 98 | + cands.addAll(methods); |
| 99 | + cands.addAll(exts); |
| 100 | + |
| 101 | + var argTypes = AttrFuncDef.argumentTypesPre(node); |
| 102 | + |
| 103 | + // Pass 1: exact matches |
| 104 | + java.util.ArrayList<FuncLink> exactLinks = new java.util.ArrayList<>(); |
| 105 | + java.util.ArrayList<FunctionSignature> exactSigs = new java.util.ArrayList<>(); |
| 106 | + for (var f : cands) { |
| 107 | + var sig = FunctionSignature.fromNameLink(f); |
| 108 | + var m = sig.matchAgainstArgs(argTypes, node); |
| 109 | + if (m != null) { |
| 110 | + exactLinks.add(f); |
| 111 | + exactSigs.add(m); |
| 112 | + } |
| 113 | + } |
| 114 | + if (!exactLinks.isEmpty()) { |
| 115 | + // methods vs others |
| 116 | + java.util.ArrayList<Integer> methodIdxs = new java.util.ArrayList<>(); |
| 117 | + for (int i = 0; i < exactLinks.size(); i++) { |
| 118 | + if (!isExtension(exactLinks.get(i))) methodIdxs.add(i); |
| 119 | + } |
| 120 | + if (methodIdxs.size() > 1) { |
| 121 | + // filter method candidates by most specific receiver |
| 122 | + java.util.ArrayList<FunctionSignature> methSigs = new java.util.ArrayList<>(); |
| 123 | + for (int i : methodIdxs) methSigs.add(exactSigs.get(i)); |
| 124 | + methSigs = (java.util.ArrayList<FunctionSignature>) keepMostSpecificReceivers( |
| 125 | + methSigs, FunctionSignature::getReceiverType, node |
| 126 | + ); |
| 127 | + // pick the first of the survivors |
| 128 | + var chosenSig = methSigs.get(0); |
| 129 | + // find corresponding link |
| 130 | + for (int i = 0; i < exactSigs.size(); i++) { |
| 131 | + if (exactSigs.get(i) == chosenSig) { |
| 132 | + return exactLinks.get(i).withTypeArgBinding(node, chosenSig.getMapping()); |
| 133 | + } |
| 134 | + } |
| 135 | + } else if (methodIdxs.size() == 1) { |
| 136 | + int i = methodIdxs.get(0); |
| 137 | + return exactLinks.get(i).withTypeArgBinding(node, exactSigs.get(i).getMapping()); |
| 138 | + } else { |
| 139 | + // no methods, only extensions exact → pick first (they were narrowed already) |
| 140 | + return exactLinks.get(0).withTypeArgBinding(node, exactSigs.get(0).getMapping()); |
| 141 | + } |
| 142 | + } |
| 143 | + |
| 144 | + // Pass 2: best-effort (unchanged) |
| 145 | + int bestBad = Integer.MAX_VALUE; |
| 146 | + FuncLink best = null; |
| 147 | + FunctionSignature bestSig = null; |
| 148 | + for (var f : cands) { |
| 149 | + var sig = FunctionSignature.fromNameLink(f); |
| 150 | + var r = sig.tryMatchAgainstArgs(argTypes, node.getArgs(), node); |
| 151 | + if (r.getBadness() < bestBad) { |
| 152 | + bestBad = r.getBadness(); |
| 153 | + best = f; |
| 154 | + bestSig = r.getSig(); |
| 155 | + } |
| 156 | + } |
| 157 | + return best == null ? null : best.withTypeArgBinding(node, bestSig.getMapping()); |
| 158 | + } |
| 159 | + |
| 160 | + |
| 161 | + |
| 162 | + public static @Nullable FunctionDefinition calculateDef(final ExprMemberMethod node) { |
| 163 | + var fl = node.attrFuncLink(); |
| 164 | + return fl == null ? null : fl.getDef(); |
78 | 165 | } |
79 | 166 |
|
| 167 | + |
80 | 168 | public static @Nullable FuncLink calculate(final ExprFunctionCall node) { |
81 | 169 | FuncLink result = searchFunction(node.getFuncName(), node, argumentTypes(node)); |
82 | 170 |
|
|
0 commit comments