1 /* 2 * Copyright 2016 Federico Tomassetti 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.github.javaparser.symbolsolver.javaparsermodel; 18 19 import com.github.javaparser.ast.CompilationUnit; 20 import com.github.javaparser.ast.DataKey; 21 import com.github.javaparser.ast.Node; 22 import com.github.javaparser.ast.NodeList; 23 import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration; 24 import com.github.javaparser.ast.body.EnumDeclaration; 25 import com.github.javaparser.ast.body.TypeDeclaration; 26 import com.github.javaparser.ast.body.VariableDeclarator; 27 import com.github.javaparser.ast.expr.*; 28 import com.github.javaparser.ast.stmt.ExplicitConstructorInvocationStmt; 29 import com.github.javaparser.ast.type.*; 30 import com.github.javaparser.resolution.MethodUsage; 31 import com.github.javaparser.resolution.UnsolvedSymbolException; 32 import com.github.javaparser.resolution.declarations.*; 33 import com.github.javaparser.resolution.types.*; 34 import com.github.javaparser.symbolsolver.core.resolution.Context; 35 import com.github.javaparser.symbolsolver.javaparsermodel.contexts.FieldAccessContext; 36 import com.github.javaparser.symbolsolver.javaparsermodel.declarations.JavaParserAnonymousClassDeclaration; 37 import com.github.javaparser.symbolsolver.javaparsermodel.declarations.JavaParserEnumDeclaration; 38 import com.github.javaparser.symbolsolver.javaparsermodel.declarations.JavaParserTypeVariableDeclaration; 39 import com.github.javaparser.symbolsolver.model.resolution.SymbolReference; 40 import com.github.javaparser.symbolsolver.model.resolution.TypeSolver; 41 import com.github.javaparser.symbolsolver.model.typesystem.ReferenceTypeImpl; 42 import com.github.javaparser.symbolsolver.reflectionmodel.ReflectionClassDeclaration; 43 import com.github.javaparser.symbolsolver.resolution.ConstructorResolutionLogic; 44 import com.github.javaparser.symbolsolver.resolution.SymbolSolver; 45 import com.github.javaparser.utils.Log; 46 47 import java.util.*; 48 import java.util.stream.Collectors; 49 50 import static com.github.javaparser.symbolsolver.javaparser.Navigator.requireParentNode; 51 52 /** 53 * Class to be used by final users to solve symbols for JavaParser ASTs. 54 * 55 * @author Federico Tomassetti 56 */ 57 public class JavaParserFacade { 58 59 private static final DataKey<ResolvedType> TYPE_WITH_LAMBDAS_RESOLVED = new DataKey<ResolvedType>() { 60 }; 61 private static final DataKey<ResolvedType> TYPE_WITHOUT_LAMBDAS_RESOLVED = new DataKey<ResolvedType>() { 62 }; 63 64 private static final Map<TypeSolver, JavaParserFacade> instances = new WeakHashMap<>(); 65 private final TypeSolver typeSolver; 66 private final TypeExtractor typeExtractor; 67 private final SymbolSolver symbolSolver; 68 JavaParserFacade(TypeSolver typeSolver)69 private JavaParserFacade(TypeSolver typeSolver) { 70 this.typeSolver = typeSolver.getRoot(); 71 this.symbolSolver = new SymbolSolver(typeSolver); 72 this.typeExtractor = new TypeExtractor(typeSolver, this); 73 } 74 getTypeSolver()75 public TypeSolver getTypeSolver() { 76 return typeSolver; 77 } 78 getSymbolSolver()79 public SymbolSolver getSymbolSolver() { 80 return symbolSolver; 81 } 82 get(TypeSolver typeSolver)83 public static JavaParserFacade get(TypeSolver typeSolver) { 84 return instances.computeIfAbsent(typeSolver, JavaParserFacade::new); 85 } 86 87 /** 88 * This method is used to clear internal caches for the sake of releasing memory. 89 */ clearInstances()90 public static void clearInstances() { 91 instances.clear(); 92 } 93 solveGenericTypes(ResolvedType type, Context context)94 protected static ResolvedType solveGenericTypes(ResolvedType type, Context context) { 95 if (type.isTypeVariable()) { 96 return context.solveGenericType(type.describe()).orElse(type); 97 } 98 if (type.isWildcard()) { 99 if (type.asWildcard().isExtends() || type.asWildcard().isSuper()) { 100 ResolvedWildcard wildcardUsage = type.asWildcard(); 101 ResolvedType boundResolved = solveGenericTypes(wildcardUsage.getBoundedType(), context); 102 if (wildcardUsage.isExtends()) { 103 return ResolvedWildcard.extendsBound(boundResolved); 104 } else { 105 return ResolvedWildcard.superBound(boundResolved); 106 } 107 } 108 } 109 return type; 110 } 111 solve(NameExpr nameExpr)112 public SymbolReference<? extends ResolvedValueDeclaration> solve(NameExpr nameExpr) { 113 return symbolSolver.solveSymbol(nameExpr.getName().getId(), nameExpr); 114 } 115 solve(SimpleName nameExpr)116 public SymbolReference<? extends ResolvedValueDeclaration> solve(SimpleName nameExpr) { 117 return symbolSolver.solveSymbol(nameExpr.getId(), nameExpr); 118 } 119 solve(Expression expr)120 public SymbolReference<? extends ResolvedValueDeclaration> solve(Expression expr) { 121 return expr.toNameExpr().map(this::solve).orElseThrow(() -> new IllegalArgumentException(expr.getClass().getCanonicalName())); 122 } 123 solve(MethodCallExpr methodCallExpr)124 public SymbolReference<ResolvedMethodDeclaration> solve(MethodCallExpr methodCallExpr) { 125 return solve(methodCallExpr, true); 126 } 127 solve(ObjectCreationExpr objectCreationExpr)128 public SymbolReference<ResolvedConstructorDeclaration> solve(ObjectCreationExpr objectCreationExpr) { 129 return solve(objectCreationExpr, true); 130 } 131 solve(ExplicitConstructorInvocationStmt explicitConstructorInvocationStmt)132 public SymbolReference<ResolvedConstructorDeclaration> solve(ExplicitConstructorInvocationStmt explicitConstructorInvocationStmt) { 133 return solve(explicitConstructorInvocationStmt, true); 134 } 135 solve(ExplicitConstructorInvocationStmt explicitConstructorInvocationStmt, boolean solveLambdas)136 public SymbolReference<ResolvedConstructorDeclaration> solve(ExplicitConstructorInvocationStmt explicitConstructorInvocationStmt, boolean solveLambdas) { 137 List<ResolvedType> argumentTypes = new LinkedList<>(); 138 List<LambdaArgumentTypePlaceholder> placeholders = new LinkedList<>(); 139 140 solveArguments(explicitConstructorInvocationStmt, explicitConstructorInvocationStmt.getArguments(), solveLambdas, argumentTypes, placeholders); 141 142 Optional<ClassOrInterfaceDeclaration> optAncestor = explicitConstructorInvocationStmt.findAncestor(ClassOrInterfaceDeclaration.class); 143 if (!optAncestor.isPresent()) { 144 return SymbolReference.unsolved(ResolvedConstructorDeclaration.class); 145 } 146 ClassOrInterfaceDeclaration classNode = optAncestor.get(); 147 ResolvedTypeDeclaration typeDecl = null; 148 if (!explicitConstructorInvocationStmt.isThis()) { 149 ResolvedType classDecl = JavaParserFacade.get(typeSolver).convert(classNode.getExtendedTypes(0), classNode); 150 if (classDecl.isReferenceType()) { 151 typeDecl = classDecl.asReferenceType().getTypeDeclaration(); 152 } 153 } else { 154 SymbolReference<ResolvedTypeDeclaration> sr = JavaParserFactory.getContext(classNode, typeSolver).solveType(classNode.getNameAsString()); 155 if (sr.isSolved()) { 156 typeDecl = sr.getCorrespondingDeclaration(); 157 } 158 } 159 if (typeDecl == null) { 160 return SymbolReference.unsolved(ResolvedConstructorDeclaration.class); 161 } 162 SymbolReference<ResolvedConstructorDeclaration> res = ConstructorResolutionLogic.findMostApplicable(((ResolvedClassDeclaration) typeDecl).getConstructors(), argumentTypes, typeSolver); 163 for (LambdaArgumentTypePlaceholder placeholder : placeholders) { 164 placeholder.setMethod(res); 165 } 166 return res; 167 } 168 solve(ThisExpr node)169 public SymbolReference<ResolvedTypeDeclaration> solve(ThisExpr node) { 170 // If 'this' is prefixed by a class eg. MyClass.this 171 if (node.getTypeName().isPresent()) { 172 // Get the class name 173 String className = node.getTypeName().get().asString(); 174 // Attempt to resolve using a typeSolver 175 SymbolReference<ResolvedReferenceTypeDeclaration> clazz = typeSolver.tryToSolveType(className); 176 if (clazz.isSolved()) { 177 return SymbolReference.solved(clazz.getCorrespondingDeclaration()); 178 } 179 // Attempt to resolve locally in Compilation unit 180 Optional<CompilationUnit> cu = node.findAncestor(CompilationUnit.class); 181 if (cu.isPresent()) { 182 Optional<ClassOrInterfaceDeclaration> classByName = cu.get().getClassByName(className); 183 if (classByName.isPresent()) { 184 return SymbolReference.solved(getTypeDeclaration(classByName.get())); 185 } 186 } 187 } 188 return SymbolReference.solved(getTypeDeclaration(findContainingTypeDeclOrObjectCreationExpr(node))); 189 } 190 191 /** 192 * Given a constructor call find out to which constructor declaration it corresponds. 193 */ solve(ObjectCreationExpr objectCreationExpr, boolean solveLambdas)194 public SymbolReference<ResolvedConstructorDeclaration> solve(ObjectCreationExpr objectCreationExpr, boolean solveLambdas) { 195 List<ResolvedType> argumentTypes = new LinkedList<>(); 196 List<LambdaArgumentTypePlaceholder> placeholders = new LinkedList<>(); 197 198 solveArguments(objectCreationExpr, objectCreationExpr.getArguments(), solveLambdas, argumentTypes, placeholders); 199 200 ResolvedType classDecl = JavaParserFacade.get(typeSolver).convert(objectCreationExpr.getType(), objectCreationExpr); 201 if (!classDecl.isReferenceType()) { 202 return SymbolReference.unsolved(ResolvedConstructorDeclaration.class); 203 } 204 SymbolReference<ResolvedConstructorDeclaration> res = ConstructorResolutionLogic.findMostApplicable(classDecl.asReferenceType().getTypeDeclaration().getConstructors(), argumentTypes, typeSolver); 205 for (LambdaArgumentTypePlaceholder placeholder : placeholders) { 206 placeholder.setMethod(res); 207 } 208 return res; 209 } 210 solveArguments(Node node, NodeList<Expression> args, boolean solveLambdas, List<ResolvedType> argumentTypes, List<LambdaArgumentTypePlaceholder> placeholders)211 private void solveArguments(Node node, NodeList<Expression> args, boolean solveLambdas, List<ResolvedType> argumentTypes, 212 List<LambdaArgumentTypePlaceholder> placeholders) { 213 int i = 0; 214 for (Expression parameterValue : args) { 215 if (parameterValue instanceof LambdaExpr || parameterValue instanceof MethodReferenceExpr) { 216 LambdaArgumentTypePlaceholder placeholder = new LambdaArgumentTypePlaceholder(i); 217 argumentTypes.add(placeholder); 218 placeholders.add(placeholder); 219 } else { 220 try { 221 argumentTypes.add(JavaParserFacade.get(typeSolver).getType(parameterValue, solveLambdas)); 222 } catch (com.github.javaparser.resolution.UnsolvedSymbolException e) { 223 throw e; 224 } catch (Exception e) { 225 throw new RuntimeException(String.format("Unable to calculate the type of a parameter of a method call. Method call: %s, Parameter: %s", 226 node, parameterValue), e); 227 } 228 } 229 i++; 230 } 231 } 232 233 /** 234 * Given a method call find out to which method declaration it corresponds. 235 */ solve(MethodCallExpr methodCallExpr, boolean solveLambdas)236 public SymbolReference<ResolvedMethodDeclaration> solve(MethodCallExpr methodCallExpr, boolean solveLambdas) { 237 List<ResolvedType> argumentTypes = new LinkedList<>(); 238 List<LambdaArgumentTypePlaceholder> placeholders = new LinkedList<>(); 239 240 solveArguments(methodCallExpr, methodCallExpr.getArguments(), solveLambdas, argumentTypes, placeholders); 241 242 SymbolReference<ResolvedMethodDeclaration> res = JavaParserFactory.getContext(methodCallExpr, typeSolver).solveMethod(methodCallExpr.getName().getId(), argumentTypes, false); 243 for (LambdaArgumentTypePlaceholder placeholder : placeholders) { 244 placeholder.setMethod(res); 245 } 246 return res; 247 } 248 solve(AnnotationExpr annotationExpr)249 public SymbolReference<ResolvedAnnotationDeclaration> solve(AnnotationExpr annotationExpr) { 250 Context context = JavaParserFactory.getContext(annotationExpr, typeSolver); 251 SymbolReference<ResolvedTypeDeclaration> typeDeclarationSymbolReference = context.solveType(annotationExpr.getNameAsString()); 252 if (typeDeclarationSymbolReference.isSolved()) { 253 ResolvedAnnotationDeclaration annotationDeclaration = (ResolvedAnnotationDeclaration) typeDeclarationSymbolReference.getCorrespondingDeclaration(); 254 return SymbolReference.solved(annotationDeclaration); 255 } else { 256 return SymbolReference.unsolved(ResolvedAnnotationDeclaration.class); 257 } 258 } 259 solve(FieldAccessExpr fieldAccessExpr)260 public SymbolReference<ResolvedValueDeclaration> solve(FieldAccessExpr fieldAccessExpr) { 261 return ((FieldAccessContext) JavaParserFactory.getContext(fieldAccessExpr, typeSolver)).solveField(fieldAccessExpr.getName().getId()); 262 } 263 264 /** 265 * Get the type associated with the node. 266 * <p> 267 * This method was originally intended to get the type of a value: any value has a type. 268 * <p> 269 * For example: 270 * <pre> 271 * int foo(int a) { 272 * return a; // when getType is invoked on "a" it returns the type "int" 273 * } 274 * </pre> 275 * <p> 276 * Now, users started using also of names of types itself, which do not have a type. 277 * <p> 278 * For example: 279 * <pre> 280 * class A { 281 * int foo(int a) { 282 * return A.someStaticField; // when getType is invoked on "A", which represents a class, it returns 283 * // the type "A" itself while it used to throw UnsolvedSymbolException 284 * } 285 * </pre> 286 * <p> 287 * To accomodate this usage and avoid confusion this method return 288 * the type itself when used on the name of type. 289 */ getType(Node node)290 public ResolvedType getType(Node node) { 291 try { 292 return getType(node, true); 293 } catch (UnsolvedSymbolException e) { 294 if (node instanceof NameExpr) { 295 NameExpr nameExpr = (NameExpr) node; 296 SymbolReference<ResolvedTypeDeclaration> typeDeclaration = JavaParserFactory.getContext(node, typeSolver) 297 .solveType(nameExpr.getNameAsString()); 298 if (typeDeclaration.isSolved() && typeDeclaration.getCorrespondingDeclaration() instanceof ResolvedReferenceTypeDeclaration) { 299 ResolvedReferenceTypeDeclaration resolvedReferenceTypeDeclaration = (ResolvedReferenceTypeDeclaration) typeDeclaration.getCorrespondingDeclaration(); 300 return ReferenceTypeImpl.undeterminedParameters(resolvedReferenceTypeDeclaration, typeSolver); 301 } 302 } 303 throw e; 304 } 305 } 306 getType(Node node, boolean solveLambdas)307 public ResolvedType getType(Node node, boolean solveLambdas) { 308 if (solveLambdas) { 309 if (!node.containsData(TYPE_WITH_LAMBDAS_RESOLVED)) { 310 ResolvedType res = getTypeConcrete(node, solveLambdas); 311 312 node.setData(TYPE_WITH_LAMBDAS_RESOLVED, res); 313 314 boolean secondPassNecessary = false; 315 if (node instanceof MethodCallExpr) { 316 MethodCallExpr methodCallExpr = (MethodCallExpr) node; 317 for (Node arg : methodCallExpr.getArguments()) { 318 if (!arg.containsData(TYPE_WITH_LAMBDAS_RESOLVED)) { 319 getType(arg, true); 320 secondPassNecessary = true; 321 } 322 } 323 } 324 if (secondPassNecessary) { 325 node.removeData(TYPE_WITH_LAMBDAS_RESOLVED); 326 ResolvedType type = getType(node, true); 327 node.setData(TYPE_WITH_LAMBDAS_RESOLVED, type); 328 329 } 330 Log.trace("getType on %s -> %s" ,()-> node, ()-> res); 331 } 332 return node.getData(TYPE_WITH_LAMBDAS_RESOLVED); 333 } else { 334 Optional<ResolvedType> res = find(TYPE_WITH_LAMBDAS_RESOLVED, node); 335 if (res.isPresent()) { 336 return res.get(); 337 } 338 res = find(TYPE_WITHOUT_LAMBDAS_RESOLVED, node); 339 if (!res.isPresent()) { 340 ResolvedType resType = getTypeConcrete(node, solveLambdas); 341 node.setData(TYPE_WITHOUT_LAMBDAS_RESOLVED, resType); 342 Optional<ResolvedType> finalRes = res; 343 Log.trace("getType on %s (no solveLambdas) -> %s", ()-> node, ()-> finalRes); 344 return resType; 345 } 346 return res.get(); 347 } 348 } 349 find(DataKey<ResolvedType> dataKey, Node node)350 private Optional<ResolvedType> find(DataKey<ResolvedType> dataKey, Node node) { 351 if (node.containsData(dataKey)) { 352 return Optional.of(node.getData(dataKey)); 353 } 354 return Optional.empty(); 355 } 356 toMethodUsage(MethodReferenceExpr methodReferenceExpr)357 protected MethodUsage toMethodUsage(MethodReferenceExpr methodReferenceExpr) { 358 if (!(methodReferenceExpr.getScope() instanceof TypeExpr)) { 359 throw new UnsupportedOperationException(); 360 } 361 TypeExpr typeExpr = (TypeExpr) methodReferenceExpr.getScope(); 362 if (!(typeExpr.getType() instanceof com.github.javaparser.ast.type.ClassOrInterfaceType)) { 363 throw new UnsupportedOperationException(typeExpr.getType().getClass().getCanonicalName()); 364 } 365 ClassOrInterfaceType classOrInterfaceType = (ClassOrInterfaceType) typeExpr.getType(); 366 SymbolReference<ResolvedTypeDeclaration> typeDeclarationSymbolReference = JavaParserFactory.getContext(classOrInterfaceType, typeSolver).solveType(classOrInterfaceType.getName().getId()); 367 if (!typeDeclarationSymbolReference.isSolved()) { 368 throw new UnsupportedOperationException(); 369 } 370 List<MethodUsage> methodUsages = ((ResolvedReferenceTypeDeclaration) typeDeclarationSymbolReference.getCorrespondingDeclaration()).getAllMethods().stream().filter(it -> it.getName().equals(methodReferenceExpr.getIdentifier())).collect(Collectors.toList()); 371 switch (methodUsages.size()) { 372 case 0: 373 throw new UnsupportedOperationException(); 374 case 1: 375 return methodUsages.get(0); 376 default: 377 throw new UnsupportedOperationException(); 378 } 379 } 380 getBinaryTypeConcrete(Node left, Node right, boolean solveLambdas, BinaryExpr.Operator operator)381 protected ResolvedType getBinaryTypeConcrete(Node left, Node right, boolean solveLambdas, BinaryExpr.Operator operator) { 382 ResolvedType leftType = getTypeConcrete(left, solveLambdas); 383 ResolvedType rightType = getTypeConcrete(right, solveLambdas); 384 385 // JLS 15.18.1. String Concatenation Operator + 386 // If only one operand expression is of type String, then string conversion (§5.1.11) is performed on the other 387 // operand to produce a string at run time. 388 // 389 // The result of string concatenation is a reference to a String object that is the concatenation of the two 390 // operand strings. The characters of the left-hand operand precede the characters of the right-hand operand in 391 // the newly created string. 392 393 if (operator == BinaryExpr.Operator.PLUS) { 394 boolean isLeftString = leftType.isReferenceType() && leftType.asReferenceType() 395 .getQualifiedName().equals(String.class.getCanonicalName()); 396 boolean isRightString = rightType.isReferenceType() && rightType.asReferenceType() 397 .getQualifiedName().equals(String.class.getCanonicalName()); 398 if (isLeftString || isRightString) { 399 return isLeftString ? leftType : rightType; 400 } 401 } 402 403 // JLS 5.6.2. Binary Numeric Promotion 404 // 405 // Widening primitive conversion (§5.1.2) is applied to convert either or both operands as specified by the 406 // following rules: 407 // 408 // * If either operand is of type double, the other is converted to double. 409 // * Otherwise, if either operand is of type float, the other is converted to float. 410 // * Otherwise, if either operand is of type long, the other is converted to long. 411 // * Otherwise, both operands are converted to type int. 412 413 boolean isLeftNumeric = leftType.isPrimitive() && leftType.asPrimitive().isNumeric(); 414 boolean isRightNumeric = rightType.isPrimitive() && rightType.asPrimitive().isNumeric(); 415 416 if (isLeftNumeric && isRightNumeric) { 417 if (leftType.asPrimitive().equals(ResolvedPrimitiveType.DOUBLE) 418 || rightType.asPrimitive().equals(ResolvedPrimitiveType.DOUBLE)) { 419 return ResolvedPrimitiveType.DOUBLE; 420 } 421 422 if (leftType.asPrimitive().equals(ResolvedPrimitiveType.FLOAT) 423 || rightType.asPrimitive().equals(ResolvedPrimitiveType.FLOAT)) { 424 return ResolvedPrimitiveType.FLOAT; 425 } 426 427 if (leftType.asPrimitive().equals(ResolvedPrimitiveType.LONG) 428 || rightType.asPrimitive().equals(ResolvedPrimitiveType.LONG)) { 429 return ResolvedPrimitiveType.LONG; 430 } 431 432 return ResolvedPrimitiveType.INT; 433 } 434 435 if (rightType.isAssignableBy(leftType)) { 436 return rightType; 437 } 438 return leftType; 439 } 440 441 442 /** 443 * Should return more like a TypeApplication: a TypeDeclaration and possible typeParametersValues or array 444 * modifiers. 445 */ getTypeConcrete(Node node, boolean solveLambdas)446 private ResolvedType getTypeConcrete(Node node, boolean solveLambdas) { 447 if (node == null) throw new IllegalArgumentException(); 448 return node.accept(typeExtractor, solveLambdas); 449 } 450 findContainingTypeDecl(Node node)451 protected com.github.javaparser.ast.body.TypeDeclaration<?> findContainingTypeDecl(Node node) { 452 if (node instanceof ClassOrInterfaceDeclaration) { 453 return (ClassOrInterfaceDeclaration) node; 454 } 455 if (node instanceof EnumDeclaration) { 456 return (EnumDeclaration) node; 457 } 458 return findContainingTypeDecl(requireParentNode(node)); 459 460 } 461 findContainingTypeDeclOrObjectCreationExpr(Node node)462 protected Node findContainingTypeDeclOrObjectCreationExpr(Node node) { 463 if (node instanceof ClassOrInterfaceDeclaration) { 464 return node; 465 } 466 if (node instanceof EnumDeclaration) { 467 return node; 468 } 469 Node parent = requireParentNode(node); 470 if (parent instanceof ObjectCreationExpr && !((ObjectCreationExpr) parent).getArguments().contains(node)) { 471 return parent; 472 } 473 return findContainingTypeDeclOrObjectCreationExpr(parent); 474 } 475 convertToUsageVariableType(VariableDeclarator var)476 public ResolvedType convertToUsageVariableType(VariableDeclarator var) { 477 return get(typeSolver).convertToUsage(var.getType(), var); 478 } 479 convertToUsage(com.github.javaparser.ast.type.Type type, Node context)480 public ResolvedType convertToUsage(com.github.javaparser.ast.type.Type type, Node context) { 481 if (type.isUnknownType()) { 482 throw new IllegalArgumentException("Inferred lambda parameter type"); 483 } 484 return convertToUsage(type, JavaParserFactory.getContext(context, typeSolver)); 485 } 486 convertToUsage(com.github.javaparser.ast.type.Type type)487 public ResolvedType convertToUsage(com.github.javaparser.ast.type.Type type) { 488 return convertToUsage(type, type); 489 } 490 491 // This is an hack around an issue in JavaParser qName(ClassOrInterfaceType classOrInterfaceType)492 private String qName(ClassOrInterfaceType classOrInterfaceType) { 493 String name = classOrInterfaceType.getName().getId(); 494 if (classOrInterfaceType.getScope().isPresent()) { 495 return qName(classOrInterfaceType.getScope().get()) + "." + name; 496 } 497 return name; 498 } 499 convertToUsage(com.github.javaparser.ast.type.Type type, Context context)500 protected ResolvedType convertToUsage(com.github.javaparser.ast.type.Type type, Context context) { 501 if (context == null) { 502 throw new NullPointerException("Context should not be null"); 503 } 504 if (type instanceof ClassOrInterfaceType) { 505 ClassOrInterfaceType classOrInterfaceType = (ClassOrInterfaceType) type; 506 String name = qName(classOrInterfaceType); 507 SymbolReference<ResolvedTypeDeclaration> ref = context.solveType(name); 508 if (!ref.isSolved()) { 509 throw new UnsolvedSymbolException(name); 510 } 511 ResolvedTypeDeclaration typeDeclaration = ref.getCorrespondingDeclaration(); 512 List<ResolvedType> typeParameters = Collections.emptyList(); 513 if (classOrInterfaceType.getTypeArguments().isPresent()) { 514 typeParameters = classOrInterfaceType.getTypeArguments().get().stream().map((pt) -> convertToUsage(pt, context)).collect(Collectors.toList()); 515 } 516 if (typeDeclaration.isTypeParameter()) { 517 if (typeDeclaration instanceof ResolvedTypeParameterDeclaration) { 518 return new ResolvedTypeVariable((ResolvedTypeParameterDeclaration) typeDeclaration); 519 } else { 520 JavaParserTypeVariableDeclaration javaParserTypeVariableDeclaration = (JavaParserTypeVariableDeclaration) typeDeclaration; 521 return new ResolvedTypeVariable(javaParserTypeVariableDeclaration.asTypeParameter()); 522 } 523 } else { 524 return new ReferenceTypeImpl((ResolvedReferenceTypeDeclaration) typeDeclaration, typeParameters, typeSolver); 525 } 526 } else if (type instanceof com.github.javaparser.ast.type.PrimitiveType) { 527 return ResolvedPrimitiveType.byName(((com.github.javaparser.ast.type.PrimitiveType) type).getType().name()); 528 } else if (type instanceof WildcardType) { 529 WildcardType wildcardType = (WildcardType) type; 530 if (wildcardType.getExtendedType().isPresent() && !wildcardType.getSuperType().isPresent()) { 531 return ResolvedWildcard.extendsBound(convertToUsage(wildcardType.getExtendedType().get(), context)); // removed (ReferenceTypeImpl) 532 } else if (!wildcardType.getExtendedType().isPresent() && wildcardType.getSuperType().isPresent()) { 533 return ResolvedWildcard.superBound(convertToUsage(wildcardType.getSuperType().get(), context)); // removed (ReferenceTypeImpl) 534 } else if (!wildcardType.getExtendedType().isPresent() && !wildcardType.getSuperType().isPresent()) { 535 return ResolvedWildcard.UNBOUNDED; 536 } else { 537 throw new UnsupportedOperationException(wildcardType.toString()); 538 } 539 } else if (type instanceof com.github.javaparser.ast.type.VoidType) { 540 return ResolvedVoidType.INSTANCE; 541 } else if (type instanceof com.github.javaparser.ast.type.ArrayType) { 542 com.github.javaparser.ast.type.ArrayType jpArrayType = (com.github.javaparser.ast.type.ArrayType) type; 543 return new ResolvedArrayType(convertToUsage(jpArrayType.getComponentType(), context)); 544 } else if (type instanceof UnionType) { 545 UnionType unionType = (UnionType) type; 546 return new ResolvedUnionType(unionType.getElements().stream().map(el -> convertToUsage(el, context)).collect(Collectors.toList())); 547 } else if (type instanceof VarType) { 548 Node parent = type.getParentNode().get(); 549 if (!(parent instanceof VariableDeclarator)) { 550 throw new IllegalStateException("Trying to resolve a `var` which is not in a variable declaration."); 551 } 552 final VariableDeclarator variableDeclarator = (VariableDeclarator) parent; 553 return variableDeclarator.getInitializer() 554 .map(Expression::calculateResolvedType) 555 .orElseThrow(() -> new IllegalStateException("Cannot resolve `var` which has no initializer.")); 556 } else { 557 throw new UnsupportedOperationException(type.getClass().getCanonicalName()); 558 } 559 } 560 561 convert(Type type, Node node)562 public ResolvedType convert(Type type, Node node) { 563 return convert(type, JavaParserFactory.getContext(node, typeSolver)); 564 } 565 convert(com.github.javaparser.ast.type.Type type, Context context)566 public ResolvedType convert(com.github.javaparser.ast.type.Type type, Context context) { 567 return convertToUsage(type, context); 568 } 569 solveMethodAsUsage(MethodCallExpr call)570 public MethodUsage solveMethodAsUsage(MethodCallExpr call) { 571 List<ResolvedType> params = new ArrayList<>(); 572 if (call.getArguments() != null) { 573 for (Expression param : call.getArguments()) { 574 //getTypeConcrete(Node node, boolean solveLambdas) 575 try { 576 params.add(getType(param, false)); 577 } catch (Exception e) { 578 throw new RuntimeException(String.format("Error calculating the type of parameter %s of method call %s", param, call), e); 579 } 580 //params.add(getTypeConcrete(param, false)); 581 } 582 } 583 Context context = JavaParserFactory.getContext(call, typeSolver); 584 Optional<MethodUsage> methodUsage = context.solveMethodAsUsage(call.getName().getId(), params); 585 if (!methodUsage.isPresent()) { 586 throw new RuntimeException("Method '" + call.getName() + "' cannot be resolved in context " 587 + call + " (line: " + call.getRange().map(r -> "" + r.begin.line).orElse("??") + ") " + context + ". Parameter types: " + params); 588 } 589 return methodUsage.get(); 590 } 591 getTypeDeclaration(Node node)592 public ResolvedReferenceTypeDeclaration getTypeDeclaration(Node node) { 593 if (node instanceof TypeDeclaration) { 594 return getTypeDeclaration((TypeDeclaration) node); 595 } else if (node instanceof ObjectCreationExpr) { 596 return new JavaParserAnonymousClassDeclaration((ObjectCreationExpr) node, typeSolver); 597 } else { 598 throw new IllegalArgumentException(); 599 } 600 } 601 getTypeDeclaration(ClassOrInterfaceDeclaration classOrInterfaceDeclaration)602 public ResolvedReferenceTypeDeclaration getTypeDeclaration(ClassOrInterfaceDeclaration classOrInterfaceDeclaration) { 603 return JavaParserFactory.toTypeDeclaration(classOrInterfaceDeclaration, typeSolver); 604 } 605 606 /** 607 * "this" inserted in the given point, which type would have? 608 */ getTypeOfThisIn(Node node)609 public ResolvedType getTypeOfThisIn(Node node) { 610 // TODO consider static methods 611 if (node instanceof ClassOrInterfaceDeclaration) { 612 return new ReferenceTypeImpl(getTypeDeclaration((ClassOrInterfaceDeclaration) node), typeSolver); 613 } else if (node instanceof EnumDeclaration) { 614 JavaParserEnumDeclaration enumDeclaration = new JavaParserEnumDeclaration((EnumDeclaration) node, typeSolver); 615 return new ReferenceTypeImpl(enumDeclaration, typeSolver); 616 } else if (node instanceof ObjectCreationExpr && ((ObjectCreationExpr) node).getAnonymousClassBody().isPresent()) { 617 JavaParserAnonymousClassDeclaration anonymousDeclaration = new JavaParserAnonymousClassDeclaration((ObjectCreationExpr) node, typeSolver); 618 return new ReferenceTypeImpl(anonymousDeclaration, typeSolver); 619 } 620 return getTypeOfThisIn(requireParentNode(node)); 621 } 622 getTypeDeclaration(com.github.javaparser.ast.body.TypeDeclaration<?> typeDeclaration)623 public ResolvedReferenceTypeDeclaration getTypeDeclaration(com.github.javaparser.ast.body.TypeDeclaration<?> typeDeclaration) { 624 return JavaParserFactory.toTypeDeclaration(typeDeclaration, typeSolver); 625 } 626 classToResolvedType(Class<?> clazz)627 public ResolvedType classToResolvedType(Class<?> clazz) { 628 if (clazz.isPrimitive()) { 629 return ResolvedPrimitiveType.byName(clazz.getName()); 630 } 631 return new ReferenceTypeImpl(new ReflectionClassDeclaration(clazz, typeSolver), typeSolver); 632 } 633 } 634