|
3 | 3 | import java.util.Arrays; |
4 | 4 | import java.util.List; |
5 | 5 | import java.util.Optional; |
| 6 | +import java.util.stream.Collectors; |
| 7 | +import java.util.stream.Stream; |
| 8 | + |
6 | 9 | import liquidjava.errors.ErrorEmitter; |
7 | 10 | import liquidjava.errors.ErrorHandler; |
8 | 11 | import liquidjava.processor.context.Context; |
|
18 | 21 | import spoon.reflect.declaration.CtField; |
19 | 22 | import spoon.reflect.declaration.CtInterface; |
20 | 23 | import spoon.reflect.declaration.CtMethod; |
| 24 | +import spoon.reflect.declaration.CtParameter; |
21 | 25 | import spoon.reflect.declaration.CtType; |
22 | | -import spoon.reflect.declaration.CtTypeParameter; |
23 | 26 | import spoon.reflect.factory.Factory; |
24 | 27 | import spoon.reflect.reference.CtTypeReference; |
25 | 28 |
|
@@ -78,11 +81,38 @@ public <R> void visitCtMethod(CtMethod<R> method) { |
78 | 81 | if (errorEmitter.foundError()) |
79 | 82 | return; |
80 | 83 |
|
81 | | - if (!methodExists(method)) { |
82 | | - ErrorHandler.printCustomError(method, |
83 | | - "Could not find method '" + method.getSignature() + "' in class '" + prefix + "'", errorEmitter); |
| 84 | + CtType<?> targetType = factory.Type().createReference(prefix).getTypeDeclaration(); |
| 85 | + if (targetType == null || !(targetType instanceof CtClass)) |
84 | 86 | return; |
| 87 | + |
| 88 | + boolean isConstructor = method.getSimpleName().equals(targetType.getSimpleName()); |
| 89 | + if (isConstructor) { |
| 90 | + if (!constructorExists(targetType, method)) { |
| 91 | + ErrorHandler.printCustomError(method, |
| 92 | + String.format("Could not find constructor '%s' for '%s'", method.getSignature(), prefix), |
| 93 | + errorEmitter); |
| 94 | + return; |
| 95 | + } |
| 96 | + } else { |
| 97 | + if (!methodExists(targetType, method)) { |
| 98 | + String matchingNames = targetType.getMethods().stream() |
| 99 | + .filter(m -> m.getSimpleName().equals(method.getSimpleName())) |
| 100 | + .map(m -> String.format("%s %s", m.getType().getSimpleName(), m.getSignature())) |
| 101 | + .collect(Collectors.joining("\n ")); |
| 102 | + |
| 103 | + StringBuilder sb = new StringBuilder(); |
| 104 | + sb.append(String.format("Could not find method '%s %s' for '%s'", method.getType().getSimpleName(), |
| 105 | + method.getSignature(), prefix)); |
| 106 | + |
| 107 | + if (!matchingNames.isEmpty()) { |
| 108 | + sb.append("\nAvailable overloads:\n "); |
| 109 | + sb.append(matchingNames); |
| 110 | + } |
| 111 | + ErrorHandler.printCustomError(method, sb.toString(), errorEmitter); |
| 112 | + return; |
| 113 | + } |
85 | 114 | } |
| 115 | + |
86 | 116 | MethodsFunctionsChecker mfc = new MethodsFunctionsChecker(this); |
87 | 117 | try { |
88 | 118 | mfc.getMethodRefinements(method, prefix); |
@@ -138,17 +168,42 @@ private boolean classExists(String className) { |
138 | 168 | return factory.Type().createReference(className).getTypeDeclaration() != null; |
139 | 169 | } |
140 | 170 |
|
141 | | - private boolean methodExists(CtMethod<?> method) { |
142 | | - CtType<?> targetType = factory.Type().createReference(prefix).getTypeDeclaration(); |
| 171 | + private boolean methodExists(CtType<?> targetType, CtMethod<?> method) { |
| 172 | + // find method with matching signature |
| 173 | + return targetType.getMethods().stream().filter(m -> m.getSimpleName().equals(method.getSimpleName())) |
| 174 | + .anyMatch(m -> parametersMatch(m.getParameters(), method.getParameters()) |
| 175 | + && typesMatch(m.getType(), method.getType())); |
| 176 | + } |
| 177 | + |
| 178 | + private boolean constructorExists(CtType<?> targetType, CtMethod<?> method) { |
| 179 | + // find constructor with matching signature |
| 180 | + CtClass<?> targetClass = (CtClass<?>) targetType; |
| 181 | + return targetClass.getConstructors().stream() |
| 182 | + .anyMatch(c -> parametersMatch(c.getParameters(), method.getParameters())); |
| 183 | + } |
| 184 | + |
| 185 | + private boolean typesMatch(CtTypeReference<?> type1, CtTypeReference<?> type2) { |
| 186 | + if (type1 == null && type2 == null) |
| 187 | + return true; |
| 188 | + |
| 189 | + if (type1 == null || type2 == null) |
| 190 | + return false; |
| 191 | + |
| 192 | + return type1.getQualifiedName().equals(type2.getQualifiedName()); |
| 193 | + } |
| 194 | + |
| 195 | + private boolean parametersMatch(List<?> targetParams, List<?> refinementParams) { |
| 196 | + if (targetParams.size() != refinementParams.size()) |
| 197 | + return false; |
143 | 198 |
|
144 | | - // find a method with matching name and parameter count |
145 | | - boolean methodFound = targetType.getMethods().stream() |
146 | | - .anyMatch(m -> m.getSimpleName().equals(method.getSimpleName()) |
147 | | - && m.getParameters().size() == method.getParameters().size()); |
| 199 | + for (int i = 0; i < targetParams.size(); i++) { |
| 200 | + CtParameter<?> targetParam = (CtParameter<?>) targetParams.get(i); |
| 201 | + CtParameter<?> refinementParam = (CtParameter<?>) refinementParams.get(i); |
| 202 | + if (targetParam == null || refinementParam == null) |
| 203 | + return false; |
148 | 204 |
|
149 | | - if (!methodFound) { |
150 | | - // check if constructor method |
151 | | - return method.getSimpleName().equals(targetType.getSimpleName()); |
| 205 | + if (!typesMatch(targetParam.getType(), refinementParam.getType())) |
| 206 | + return false; |
152 | 207 | } |
153 | 208 | return true; |
154 | 209 | } |
|
0 commit comments