|
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.*; |
21 | 23 | import static de.peeeq.wurstscript.attributes.names.NameResolution.lookupMemberFuncs; |
22 | 24 |
|
23 | 25 |
|
@@ -66,55 +68,97 @@ public static FuncLink calculate(final ExprFuncRef node) { |
66 | 68 | return getExtensionFunction(node.getLeft(), node.getRight(), node.getOp()); |
67 | 69 | } |
68 | 70 |
|
69 | | - public static de.peeeq.wurstscript.attributes.names.FuncLink calculate(final ExprMemberMethod node) { |
70 | | - // collect candidates |
| 71 | + public static @Nullable FuncLink calculate(final ExprMemberMethod node) { |
71 | 72 | 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); |
| 73 | + var raw = NameResolution.lookupMemberFuncs(node, recvT, node.getFuncName(), /*showErrors=*/false); |
75 | 74 |
|
76 | | - if (cands.isEmpty()) return null; |
| 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 | + } |
77 | 80 |
|
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; |
| 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 |
| 85 | + } |
83 | 86 |
|
84 | | - // Pass 1: exact matches only |
| 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<>(); |
85 | 106 | 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; |
| 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); |
93 | 112 | } |
94 | 113 | } |
95 | | - |
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(); |
| 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 | + } |
106 | 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()); |
107 | 141 | } |
108 | 142 | } |
109 | 143 |
|
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; |
| 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()); |
116 | 158 | } |
117 | 159 |
|
| 160 | + |
| 161 | + |
118 | 162 | public static @Nullable FunctionDefinition calculateDef(final ExprMemberMethod node) { |
119 | 163 | var fl = node.attrFuncLink(); |
120 | 164 | return fl == null ? null : fl.getDef(); |
|
0 commit comments