1 package com.github.javaparser.symbolsolver.resolution.naming; 2 3 import com.github.javaparser.ast.ImportDeclaration; 4 import com.github.javaparser.ast.Node; 5 import com.github.javaparser.ast.PackageDeclaration; 6 import com.github.javaparser.ast.body.*; 7 import com.github.javaparser.ast.expr.*; 8 import com.github.javaparser.ast.modules.*; 9 import com.github.javaparser.ast.stmt.ExplicitConstructorInvocationStmt; 10 11 import com.github.javaparser.ast.stmt.TryStmt; 12 import com.github.javaparser.ast.type.ClassOrInterfaceType; 13 import com.github.javaparser.ast.type.TypeParameter; 14 import com.github.javaparser.resolution.UnsolvedSymbolException; 15 import com.github.javaparser.resolution.declarations.ResolvedReferenceTypeDeclaration; 16 import com.github.javaparser.resolution.declarations.ResolvedTypeDeclaration; 17 import com.github.javaparser.symbolsolver.core.resolution.Context; 18 import com.github.javaparser.symbolsolver.javaparsermodel.JavaParserFactory; 19 import com.github.javaparser.symbolsolver.model.resolution.SymbolReference; 20 import com.github.javaparser.symbolsolver.model.resolution.TypeSolver; 21 import com.github.javaparser.ast.stmt.ReturnStmt; 22 23 /** 24 * NameLogic contains a set of static methods to implement the abstraction of a "Name" as defined 25 * in Chapter 6 of the JLS. This code could be moved to an interface or base class in a successive version of 26 * JavaParser. 27 */ 28 public class NameLogic { 29 30 /** 31 * Is the given node a non-qualified name? 32 * 33 * @throws IllegalArgumentException if the node is not a name 34 */ isSimpleName(Node node)35 public static boolean isSimpleName(Node node) { 36 return !isQualifiedName(node); 37 } 38 39 /** 40 * Is the given node a qualified name? 41 * 42 * @throws IllegalArgumentException if the node is not a name 43 */ isQualifiedName(Node node)44 public static boolean isQualifiedName(Node node) { 45 if (!isAName(node)) { 46 throw new IllegalArgumentException(); 47 } 48 return nameAsString(node).contains("."); 49 } 50 51 /** 52 * Does the Node represent a Name? 53 * <p> 54 * Note that while most specific AST classes either always represent names or never represent names 55 * there are exceptions as the FieldAccessExpr 56 */ isAName(Node node)57 public static boolean isAName(Node node) { 58 if (node instanceof FieldAccessExpr) { 59 FieldAccessExpr fieldAccessExpr = (FieldAccessExpr) node; 60 return isAName(fieldAccessExpr.getScope()); 61 } else { 62 return node instanceof SimpleName || 63 node instanceof Name || 64 node instanceof ClassOrInterfaceType || 65 node instanceof NameExpr; 66 } 67 } 68 getQualifier(Node node)69 private static Node getQualifier(Node node) { 70 if (node instanceof FieldAccessExpr) { 71 FieldAccessExpr fieldAccessExpr = (FieldAccessExpr) node; 72 return fieldAccessExpr.getScope(); 73 } 74 throw new UnsupportedOperationException(node.getClass().getCanonicalName()); 75 } 76 getRightMostName(Node node)77 private static Node getRightMostName(Node node) { 78 if (node instanceof FieldAccessExpr) { 79 FieldAccessExpr fieldAccessExpr = (FieldAccessExpr) node; 80 return fieldAccessExpr.getName(); 81 } 82 throw new UnsupportedOperationException(node.getClass().getCanonicalName()); 83 } 84 85 /** 86 * What is the Role of the given name? Does it represent a Declaration or a Reference? 87 * <p> 88 * This classification is purely syntactical, i.e., it does not require symbol resolution. For this reason in the 89 * future this could be moved to the core module of JavaParser. 90 */ classifyRole(Node name)91 public static NameRole classifyRole(Node name) { 92 if (!isAName(name)) { 93 throw new IllegalArgumentException("The given node is not a name"); 94 } 95 if (!name.getParentNode().isPresent()) { 96 throw new IllegalArgumentException("We cannot understand the role of a name if it has no parent"); 97 } 98 if (whenParentIs(Name.class, name, (p, c) -> p.getQualifier().isPresent() && p.getQualifier().get() == c)) { 99 return classifyRole(name.getParentNode().get()); 100 } 101 if (whenParentIs(PackageDeclaration.class, name, (p, c) -> p.getName() == c)) { 102 return NameRole.DECLARATION; 103 } 104 if (whenParentIs(ImportDeclaration.class, name, (p, c) -> p.getName() == c)) { 105 return NameRole.REFERENCE; 106 } 107 if (whenParentIs(MarkerAnnotationExpr.class, name, (p, c) -> p.getName() == c)) { 108 return NameRole.REFERENCE; 109 } 110 if (whenParentIs(ClassOrInterfaceDeclaration.class, name, (p, c) -> p.getName() == c)) { 111 return NameRole.DECLARATION; 112 } 113 if (whenParentIs(ClassOrInterfaceDeclaration.class, name, (p, c) -> p.getExtendedTypes().contains(c) 114 || p.getImplementedTypes().contains(c))) { 115 return NameRole.REFERENCE; 116 } 117 if (whenParentIs(ClassOrInterfaceType.class, name, (p, c) -> p.getName() == c)) { 118 return NameRole.REFERENCE; 119 } 120 if (whenParentIs(VariableDeclarator.class, name, (p, c) -> p.getName() == c)) { 121 return NameRole.DECLARATION; 122 } 123 if (whenParentIs(NameExpr.class, name, (p, c) -> p.getName() == c)) { 124 return NameRole.REFERENCE; 125 } 126 if (whenParentIs(FieldAccessExpr.class, name, (p, c) -> p.getName() == c)) { 127 return NameRole.REFERENCE; 128 } 129 if (whenParentIs(MethodDeclaration.class, name, (p, c) -> p.getName() == c)) { 130 return NameRole.DECLARATION; 131 } 132 if (whenParentIs(Parameter.class, name, (p, c) -> p.getName() == c)) { 133 return NameRole.DECLARATION; 134 } 135 if (whenParentIs(MethodCallExpr.class, name, (p, c) -> p.getName() == c)) { 136 return NameRole.REFERENCE; 137 } 138 if (whenParentIs(ConstructorDeclaration.class, name, (p, c) -> p.getName() == c)) { 139 return NameRole.REFERENCE; 140 } 141 if (whenParentIs(AnnotationDeclaration.class, name, (p, c) -> p.getName() == c)) { 142 return NameRole.DECLARATION; 143 } 144 if (whenParentIs(AnnotationMemberDeclaration.class, name, (p, c) -> p.getName() == c)) { 145 return NameRole.DECLARATION; 146 } 147 if (whenParentIs(AnnotationMemberDeclaration.class, name, (p, c) -> p.getType() == c)) { 148 return NameRole.REFERENCE; 149 } 150 if (whenParentIs(MethodDeclaration.class, name, (p, c) -> p.getName() == c)) { 151 return NameRole.DECLARATION; 152 } 153 if (whenParentIs(MethodDeclaration.class, name, (p, c) -> p.getType() == c || p.getThrownExceptions().contains(c))) { 154 return NameRole.REFERENCE; 155 } 156 if (whenParentIs(Parameter.class, name, (p, c) -> p.getName() == c)) { 157 return NameRole.DECLARATION; 158 } 159 if (whenParentIs(Parameter.class, name, (p, c) -> p.getType() == c)) { 160 return NameRole.REFERENCE; 161 } 162 if (whenParentIs(ReceiverParameter.class, name, (p, c) -> p.getType() == c)) { 163 return NameRole.REFERENCE; 164 } 165 if (whenParentIs(MethodCallExpr.class, name, (p, c) -> p.getName() == c || 166 (p.getTypeArguments().isPresent() && p.getTypeArguments().get().contains(c)) || 167 (p.getScope().isPresent() && p.getScope().get() == c))) { 168 return NameRole.REFERENCE; 169 } 170 if (whenParentIs(ConstructorDeclaration.class, name, (p, c) -> p.getName() == c || p.getThrownExceptions().contains(c))) { 171 return NameRole.REFERENCE; 172 } 173 if (whenParentIs(TypeParameter.class, name, (p, c) -> p.getName() == c)) { 174 return NameRole.DECLARATION; 175 } 176 if (whenParentIs(EnumDeclaration.class, name, (p, c) -> p.getName() == c)) { 177 return NameRole.DECLARATION; 178 } 179 if (whenParentIs(EnumConstantDeclaration.class, name, (p, c) -> p.getName() == c)) { 180 return NameRole.DECLARATION; 181 } 182 if (whenParentIs(FieldAccessExpr.class, name, (p, c) -> p.getName() == c || p.getScope() == c)) { 183 return NameRole.REFERENCE; 184 } 185 if (whenParentIs(ObjectCreationExpr.class, name, (p, c) -> p.getType() == c)) { 186 return NameRole.REFERENCE; 187 } 188 if (whenParentIs(ReturnStmt.class, name, (p, c) -> p.getExpression().isPresent() && p.getExpression().get() == c)) { 189 return NameRole.REFERENCE; 190 } 191 if (whenParentIs(ModuleDeclaration.class, name, (p, c) -> p.getName() == c)) { 192 return NameRole.DECLARATION; 193 } 194 if (whenParentIs(ModuleRequiresDirective.class, name, (p, c) -> p.getName() == c)) { 195 return NameRole.REFERENCE; 196 } 197 if (whenParentIs(ModuleExportsDirective.class, name, (p, c) -> p.getName() == c)) { 198 return NameRole.REFERENCE; 199 } 200 if (whenParentIs(ModuleExportsDirective.class, name, (p, c) -> p.getModuleNames().contains(c))) { 201 return NameRole.REFERENCE; 202 } 203 if (whenParentIs(ModuleOpensDirective.class, name, (p, c) -> p.getName() == c)) { 204 return NameRole.REFERENCE; 205 } 206 if (whenParentIs(ModuleOpensDirective.class, name, (p, c) -> p.getModuleNames().contains(c))) { 207 return NameRole.REFERENCE; 208 } 209 if (whenParentIs(ModuleUsesDirective.class, name, (p, c) -> p.getName() == c)) { 210 return NameRole.REFERENCE; 211 } 212 if (whenParentIs(ModuleProvidesDirective.class, name, (p, c) -> p.getName() == c)) { 213 return NameRole.REFERENCE; 214 } 215 if (whenParentIs(ClassExpr.class, name, (p, c) -> p.getType() == c)) { 216 return NameRole.REFERENCE; 217 } 218 if (whenParentIs(ThisExpr.class, name, (p, c) -> p.getTypeName().isPresent() && p.getTypeName().get() == c)) { 219 return NameRole.REFERENCE; 220 } 221 if (whenParentIs(SuperExpr.class, name, (p, c) -> p.getTypeName().isPresent() && p.getTypeName().get() == c)) { 222 return NameRole.REFERENCE; 223 } 224 if (whenParentIs(VariableDeclarator.class, name, (p, c) -> p.getName() == c)) { 225 return NameRole.DECLARATION; 226 } 227 if (whenParentIs(VariableDeclarator.class, name, (p, c) -> p.getType() == c)) { 228 return NameRole.REFERENCE; 229 } 230 if (whenParentIs(ArrayCreationExpr.class, name, (p, c) -> p.getElementType() == c)) { 231 return NameRole.REFERENCE; 232 } 233 if (whenParentIs(CastExpr.class, name, (p, c) -> p.getType() == c)) { 234 return NameRole.REFERENCE; 235 } 236 if (whenParentIs(InstanceOfExpr.class, name, (p, c) -> p.getType() == c)) { 237 return NameRole.REFERENCE; 238 } 239 if (whenParentIs(TypeExpr.class, name, (p, c) -> p.getType() == c)) { 240 return NameRole.REFERENCE; 241 } 242 if (whenParentIs(ArrayAccessExpr.class, name, (p, c) -> p.getName() == c)) { 243 return NameRole.REFERENCE; 244 } 245 if (whenParentIs(UnaryExpr.class, name, (p, c) -> p.getExpression() == c)) { 246 return NameRole.REFERENCE; 247 } 248 if (whenParentIs(AssignExpr.class, name, (p, c) -> p.getTarget() == c || p.getValue() == c)) { 249 return NameRole.REFERENCE; 250 } 251 if (whenParentIs(TryStmt.class, name, (p, c) -> p.getResources().contains(c))) { 252 return NameRole.REFERENCE; 253 } 254 if (whenParentIs(VariableDeclarator.class, name, (p, c) -> p.getName() == c)) { 255 return NameRole.DECLARATION; 256 } 257 if (whenParentIs(VariableDeclarator.class, name, (p, c) -> p.getType() == c)) { 258 return NameRole.REFERENCE; 259 } 260 if (whenParentIs(VariableDeclarator.class, name, (p, c) -> p.getInitializer().isPresent() && p.getInitializer().get() == c)) { 261 return NameRole.REFERENCE; 262 } 263 if (whenParentIs(MemberValuePair.class, name, (p, c) -> p.getValue() == c)) { 264 return NameRole.REFERENCE; 265 } 266 if (whenParentIs(MemberValuePair.class, name, (p, c) -> p.getName() == c)) { 267 return NameRole.DECLARATION; 268 } 269 if (whenParentIs(ExplicitConstructorInvocationStmt.class, name, (p, c) -> 270 (p.getExpression().isPresent() && p.getExpression().get() == c) || 271 (p.getTypeArguments().isPresent() && p.getTypeArguments().get().contains(c)))) { 272 return NameRole.REFERENCE; 273 } 274 if (whenParentIs(ObjectCreationExpr.class, name, (p, c) -> p.getType() == c || 275 (p.getScope().isPresent() && p.getScope().get() == c))) { 276 return NameRole.REFERENCE; 277 } 278 if (name.getParentNode().isPresent() && NameLogic.isAName(name.getParentNode().get())) { 279 return classifyRole(name.getParentNode().get()); 280 } 281 throw new UnsupportedOperationException("Unable to classify role of name contained in " + name.getParentNode().get().getClass().getSimpleName()); 282 } 283 classifyReference(Node name, TypeSolver typeSolver)284 public static NameCategory classifyReference(Node name, TypeSolver typeSolver) { 285 if (!name.getParentNode().isPresent()) { 286 throw new IllegalArgumentException("We cannot understand the category of a name if it has no parent"); 287 } 288 if (classifyRole(name) != NameRole.REFERENCE) { 289 throw new IllegalArgumentException("This method can be used only to classify names used as references"); 290 } 291 292 // JLS 6.5 293 // First, context causes a name syntactically to fall into one of seven categories: ModuleName, PackageName, 294 // TypeName, ExpressionName, MethodName, PackageOrTypeName, or AmbiguousName. 295 296 NameCategory first = syntacticClassificationAccordingToContext(name); 297 298 // Second, a name that is initially classified by its context as an AmbiguousName or as a PackageOrTypeName is 299 // then reclassified to be a PackageName, TypeName, or ExpressionName. 300 if (first.isNeedingDisambiguation()) { 301 NameCategory second = reclassificationOfContextuallyAmbiguousNames(name, first, typeSolver); 302 assert !second.isNeedingDisambiguation(); 303 return second; 304 } else { 305 return first; 306 } 307 } 308 309 /** 310 * JLS 6.5.2. Reclassification of Contextually Ambiguous Names 311 */ reclassificationOfContextuallyAmbiguousNames(Node name, NameCategory ambiguousCategory, TypeSolver typeSolver)312 private static NameCategory reclassificationOfContextuallyAmbiguousNames(Node name, NameCategory ambiguousCategory, 313 TypeSolver typeSolver) { 314 if (!ambiguousCategory.isNeedingDisambiguation()) { 315 throw new IllegalArgumentException("The Name Category is not ambiguous: " + ambiguousCategory); 316 } 317 if (ambiguousCategory == NameCategory.AMBIGUOUS_NAME && isSimpleName(name)) { 318 return reclassificationOfContextuallyAmbiguousSimpleAmbiguousName(name, typeSolver); 319 } 320 if (ambiguousCategory == NameCategory.AMBIGUOUS_NAME && isQualifiedName(name)) { 321 return reclassificationOfContextuallyAmbiguousQualifiedAmbiguousName(name, typeSolver); 322 } 323 if (ambiguousCategory == NameCategory.PACKAGE_OR_TYPE_NAME) { 324 return reclassificationOfContextuallyAmbiguosPackageOrTypeName(name, typeSolver); 325 } 326 throw new UnsupportedOperationException("I do not know how to handle this semantic reclassification of ambiguous name categories"); 327 } 328 reclassificationOfContextuallyAmbiguosPackageOrTypeName(Node name, TypeSolver typeSolver)329 private static NameCategory reclassificationOfContextuallyAmbiguosPackageOrTypeName(Node name, TypeSolver typeSolver) { 330 // 6.5.4.1. Simple PackageOrTypeNames 331 // 332 // If the PackageOrTypeName, Q, is a valid TypeIdentifier and occurs in the scope of a type named Q, then the 333 // PackageOrTypeName is reclassified as a TypeName. 334 // 335 // Otherwise, the PackageOrTypeName is reclassified as a PackageName. The meaning of the PackageOrTypeName is 336 // the meaning of the reclassified name. 337 338 if (isSimpleName(name)) { 339 if (JavaParserFactory.getContext(name, typeSolver).solveType(nameAsString(name)).isSolved()) { 340 return NameCategory.TYPE_NAME; 341 } else { 342 return NameCategory.PACKAGE_NAME; 343 } 344 } 345 346 // 6.5.4.2. Qualified PackageOrTypeNames 347 // 348 // Given a qualified PackageOrTypeName of the form Q.Id, if Id is a valid TypeIdentifier and the type or package 349 // denoted by Q has a member type named Id, then the qualified PackageOrTypeName name is reclassified as a 350 // TypeName. 351 // 352 // Otherwise, it is reclassified as a PackageName. The meaning of the qualified PackageOrTypeName is the meaning 353 // of the reclassified name. 354 355 if (isQualifiedName(name)) { 356 if (JavaParserFactory.getContext(name, typeSolver).solveType(nameAsString(name)).isSolved()) { 357 return NameCategory.TYPE_NAME; 358 } else { 359 return NameCategory.PACKAGE_NAME; 360 } 361 } 362 363 throw new UnsupportedOperationException("This is unexpected: the name is neither simple or qualified"); 364 } 365 reclassificationOfContextuallyAmbiguousQualifiedAmbiguousName(Node nameNode, TypeSolver typeSolver)366 private static NameCategory reclassificationOfContextuallyAmbiguousQualifiedAmbiguousName(Node nameNode, 367 TypeSolver typeSolver) { 368 // If the AmbiguousName is a qualified name, consisting of a name, a ".", and an Identifier, then the name to 369 // the left of the "." is first reclassified, for it is itself an AmbiguousName. There is then a choice: 370 371 Node leftName = NameLogic.getQualifier(nameNode); 372 String rightName = NameLogic.nameAsString(NameLogic.getRightMostName(nameNode)); 373 NameCategory leftNameCategory = classifyReference(leftName, typeSolver); 374 375 // * If the name to the left of the "." is reclassified as a PackageName, then: 376 // 377 // * If the Identifier is a valid TypeIdentifier, and there is a package whose name is the name to the left 378 // of the ".", and that package contains a declaration of a type whose name is the same as the Identifier, 379 // then this AmbiguousName is reclassified as a TypeName. 380 // 381 // * Otherwise, this AmbiguousName is reclassified as a PackageName. A later step determines whether or not 382 // a package of that name actually exists. 383 384 if (leftNameCategory == NameCategory.PACKAGE_NAME) { 385 if (typeSolver.hasType(nameAsString(nameNode))) { 386 return NameCategory.TYPE_NAME; 387 } else { 388 return NameCategory.PACKAGE_NAME; 389 } 390 } 391 392 // * If the name to the left of the "." is reclassified as a TypeName, then: 393 // 394 // * If the Identifier is the name of a method or field of the type denoted by TypeName, then this 395 // AmbiguousName is reclassified as an ExpressionName. 396 // 397 // * Otherwise, if the Identifier is a valid TypeIdentifier and is the name of a member type of the type 398 // denoted by TypeName, then this AmbiguousName is reclassified as a TypeName. 399 // 400 // * Otherwise, a compile-time error occurs. 401 402 if (leftNameCategory == NameCategory.TYPE_NAME) { 403 SymbolReference<ResolvedTypeDeclaration> scopeTypeRef = JavaParserFactory.getContext(leftName, typeSolver) 404 .solveType(NameLogic.nameAsString(leftName)); 405 if (scopeTypeRef.isSolved()) { 406 ResolvedTypeDeclaration scopeType = scopeTypeRef.getCorrespondingDeclaration(); 407 if (scopeType instanceof ResolvedReferenceTypeDeclaration) { 408 ResolvedReferenceTypeDeclaration scopeRefType = scopeType.asReferenceType(); 409 if (scopeRefType.getAllMethods().stream().anyMatch(m -> m.getName().equals(rightName))) { 410 return NameCategory.EXPRESSION_NAME; 411 } 412 if (scopeRefType.getAllFields().stream().anyMatch(f -> f.isStatic() && f.getName().equals(rightName))) { 413 return NameCategory.EXPRESSION_NAME; 414 } 415 if (scopeRefType.hasInternalType(rightName)) { 416 return NameCategory.TYPE_NAME; 417 } 418 return NameCategory.COMPILATION_ERROR; 419 } else { 420 throw new UnsupportedOperationException("The name is a type but it has been resolved to something that is not a reference type"); 421 } 422 } else { 423 throw new UnsolvedSymbolException("Unable to solve context type: " + NameLogic.nameAsString(leftName)); 424 } 425 } 426 427 // * If the name to the left of the "." is reclassified as an ExpressionName, then this AmbiguousName is 428 // reclassified as an ExpressionName. A later step determines whether or not a member with the name Identifier 429 // actually exists. 430 431 if (leftNameCategory == NameCategory.EXPRESSION_NAME) { 432 return NameCategory.EXPRESSION_NAME; 433 } 434 435 throw new UnsupportedOperationException("I do not know how to handle this semantic reclassification of ambiguous name categories"); 436 } 437 reclassificationOfContextuallyAmbiguousSimpleAmbiguousName(Node nameNode, TypeSolver typeSolver)438 private static NameCategory reclassificationOfContextuallyAmbiguousSimpleAmbiguousName(Node nameNode, 439 TypeSolver typeSolver) { 440 // If the AmbiguousName is a simple name, consisting of a single Identifier: 441 // 442 // * If the Identifier appears within the scope (§6.3) of a local variable declaration (§14.4) or parameter 443 // declaration (§8.4.1, §8.8.1, §14.20) or field declaration (§8.3) with that name, then the AmbiguousName is 444 // reclassified as an ExpressionName. 445 446 String name = nameAsString(nameNode); 447 Context context = JavaParserFactory.getContext(nameNode, typeSolver); 448 if (context.localVariableDeclarationInScope(name).isPresent()) { 449 return NameCategory.EXPRESSION_NAME; 450 } 451 if (context.parameterDeclarationInScope(name).isPresent()) { 452 return NameCategory.EXPRESSION_NAME; 453 } 454 if (context.fieldDeclarationInScope(name).isPresent()) { 455 return NameCategory.EXPRESSION_NAME; 456 } 457 458 // * Otherwise, if a field of that name is declared in the compilation unit (§7.3) containing the Identifier by 459 // a single-static-import declaration (§7.5.3), or by a static-import-on-demand declaration (§7.5.4) then the 460 // AmbiguousName is reclassified as an ExpressionName. 461 // 462 // * Otherwise, if the Identifier is a valid TypeIdentifier and appears within the scope (§6.3) of a top level 463 // class (§8 (Classes)) or interface type declaration (§9 (Interfaces)), a local class declaration (§14.3) or 464 // member type declaration (§8.5, §9.5) with that name, then the AmbiguousName is reclassified as a TypeName. 465 // 466 // * Otherwise, if the Identifier is a valid TypeIdentifier and a type of that name is declared in the 467 // compilation unit (§7.3) containing the Identifier, either by a single-type-import declaration (§7.5.1), or 468 // by a type-import-on-demand declaration (§7.5.2), or by a single-static-import declaration (§7.5.3), or by 469 // a static-import-on-demand declaration (§7.5.4), then the AmbiguousName is reclassified as a TypeName. 470 // 471 // Otherwise, the AmbiguousName is reclassified as a PackageName. A later step determines whether or not a 472 // package of that name actually exists. 473 474 return NameCategory.PACKAGE_NAME; 475 } 476 477 /** 478 * See JLS 6.5.1 Syntactic Classification of a Name According to Context. 479 * <p> 480 * Most users do not want to call directly this method but call classifyReference instead. 481 */ syntacticClassificationAccordingToContext(Node name)482 public static NameCategory syntacticClassificationAccordingToContext(Node name) { 483 484 if (name.getParentNode().isPresent()) { 485 Node parent = name.getParentNode().get(); 486 if (isAName(parent) && nameAsString(name).equals(nameAsString(parent))) { 487 return syntacticClassificationAccordingToContext(parent); 488 } 489 } 490 491 if (isSyntacticallyATypeName(name)) { 492 return NameCategory.TYPE_NAME; 493 } 494 if (isSyntacticallyAnExpressionName(name)) { 495 return NameCategory.EXPRESSION_NAME; 496 } 497 if (isSyntacticallyAMethodName(name)) { 498 return NameCategory.METHOD_NAME; 499 } 500 if (isSyntacticallyAPackageOrTypeName(name)) { 501 return NameCategory.PACKAGE_OR_TYPE_NAME; 502 } 503 if (isSyntacticallyAAmbiguousName(name)) { 504 return NameCategory.AMBIGUOUS_NAME; 505 } 506 if (isSyntacticallyAModuleName(name)) { 507 return NameCategory.MODULE_NAME; 508 } 509 if (isSyntacticallyAPackageName(name)) { 510 return NameCategory.PACKAGE_NAME; 511 } 512 513 if (name instanceof NameExpr) { 514 return NameCategory.EXPRESSION_NAME; 515 } 516 if (name instanceof FieldAccessExpr) { 517 return NameCategory.EXPRESSION_NAME; 518 } 519 if (name instanceof ClassOrInterfaceType) { 520 return NameCategory.TYPE_NAME; 521 } 522 if (name.getParentNode().isPresent() && name.getParentNode().get() instanceof ClassOrInterfaceType) { 523 return NameCategory.TYPE_NAME; 524 } 525 if (name.getParentNode().isPresent() && name.getParentNode().get() instanceof FieldAccessExpr) { 526 return NameCategory.EXPRESSION_NAME; 527 } 528 529 throw new UnsupportedOperationException("Unable to classify category of name contained in " 530 + name.getParentNode().get().getClass().getSimpleName() + ". See " + name + " at " + name.getRange()); 531 } 532 isSyntacticallyAAmbiguousName(Node name)533 private static boolean isSyntacticallyAAmbiguousName(Node name) { 534 // A name is syntactically classified as an AmbiguousName in these contexts: 535 // 536 // 1. To the left of the "." in a qualified ExpressionName 537 538 if (whenParentIs(FieldAccessExpr.class, name, (p, c) -> p.getScope() == c)) { 539 return true; 540 } 541 542 // 2. To the left of the rightmost . that occurs before the "(" in a method invocation expression 543 544 if (whenParentIs(MethodCallExpr.class, name, (p, c) -> p.getScope().isPresent() && p.getScope().get() == c)) { 545 return true; 546 } 547 548 // 3. To the left of the "." in a qualified AmbiguousName 549 // 550 // 4. In the default value clause of an annotation type element declaration (§9.6.2) 551 // 552 // 5. To the right of an "=" in an an element-value pair (§9.7.1) 553 554 if (whenParentIs(MemberValuePair.class, name, (p, c) -> p.getValue() == c)) { 555 return true; 556 } 557 558 // 6. To the left of :: in a method reference expression (§15.13) 559 return false; 560 } 561 isSyntacticallyAPackageOrTypeName(Node name)562 private static boolean isSyntacticallyAPackageOrTypeName(Node name) { 563 // A name is syntactically classified as a PackageOrTypeName in these contexts: 564 // 565 // 1. To the left of the "." in a qualified TypeName 566 567 if (whenParentIs(ClassOrInterfaceType.class, name, (p, c) -> p.getScope().isPresent() && p.getScope().get() == c && (isSyntacticallyATypeName(p) || isSyntacticallyAPackageOrTypeName(p)))) { 568 return true; 569 } 570 571 // 2. In a type-import-on-demand declaration (§7.5.2) 572 573 if (whenParentIs(ImportDeclaration.class, name, (p, c) -> 574 !p.isStatic() && p.isAsterisk() && p.getName() == name)) { 575 return true; 576 } 577 578 return false; 579 } 580 isSyntacticallyAMethodName(Node name)581 private static boolean isSyntacticallyAMethodName(Node name) { 582 // A name is syntactically classified as a MethodName in this context: 583 // 584 // 1. Before the "(" in a method invocation expression (§15.12) 585 586 if (whenParentIs(MethodCallExpr.class, name, (p, c) -> p.getName() == c)) { 587 return true; 588 } 589 590 return false; 591 } 592 isSyntacticallyAModuleName(Node name)593 private static boolean isSyntacticallyAModuleName(Node name) { 594 // A name is syntactically classified as a ModuleName in these contexts: 595 // 596 // 1. In a requires directive in a module declaration (§7.7.1) 597 598 if (whenParentIs(ModuleRequiresDirective.class, name, (p, c) -> p.getName() == name)) { 599 return true; 600 } 601 602 // 2. To the right of to in an exports or opens directive in a module declaration (§7.7.2) 603 604 if (whenParentIs(ModuleExportsDirective.class, name, (p, c) -> p.getModuleNames().contains(name))) { 605 return true; 606 } 607 if (whenParentIs(ModuleOpensDirective.class, name, (p, c) -> p.getModuleNames().contains(name))) { 608 return true; 609 } 610 611 return false; 612 } 613 isSyntacticallyAPackageName(Node name)614 private static boolean isSyntacticallyAPackageName(Node name) { 615 // A name is syntactically classified as a PackageName in these contexts: 616 // 617 // 1. To the right of exports or opens in a module declaration 618 if (whenParentIs(ModuleExportsDirective.class, name, (p, c) -> p.getName() == name)) { 619 return true; 620 } 621 if (whenParentIs(ModuleOpensDirective.class, name, (p, c) -> p.getName() == name)) { 622 return true; 623 } 624 // 2. To the left of the "." in a qualified PackageName 625 if (whenParentIs(Name.class, name, (p, c) -> p.getQualifier().isPresent() 626 && p.getQualifier().get() == name 627 && isSyntacticallyAPackageName(p))) { 628 return true; 629 } 630 return false; 631 } 632 isSyntacticallyATypeName(Node name)633 private static boolean isSyntacticallyATypeName(Node name) { 634 // A name is syntactically classified as a TypeName in these contexts: 635 // 636 // The first eleven non-generic contexts (§6.1): 637 // 638 // 1. In a uses or provides directive in a module declaration (§7.7.1) 639 640 if (whenParentIs(ModuleUsesDirective.class, name, (p, c) -> p.getName() == c)) { 641 return true; 642 } 643 if (whenParentIs(ModuleProvidesDirective.class, name, (p, c) -> p.getName() == c)) { 644 return true; 645 } 646 647 // 2. In a single-type-import declaration (§7.5.1) 648 649 if (whenParentIs(ImportDeclaration.class, name, (p, c) -> 650 !p.isStatic() && !p.isAsterisk() && p.getName() == name)) { 651 return true; 652 } 653 654 // 3. To the left of the . in a single-static-import declaration (§7.5.3) 655 656 if (whenParentIs(Name.class, name, (largerName, c) -> 657 whenParentIs(ImportDeclaration.class, largerName, (importDecl, c2) -> 658 importDecl.isStatic() && !importDecl.isAsterisk() && importDecl.getName() == c2) 659 )) { 660 return true; 661 } 662 if (whenParentIs(ImportDeclaration.class, name, (importDecl, c2) -> 663 importDecl.isStatic() && !importDecl.isAsterisk() && importDecl.getName() == c2)) { 664 return true; 665 } 666 667 // 4. To the left of the . in a static-import-on-demand declaration (§7.5.4) 668 669 if (whenParentIs(ImportDeclaration.class, name, (p, c) -> 670 p.isStatic() && p.isAsterisk() && p.getName() == name)) { 671 return true; 672 } 673 674 // 5. To the left of the ( in a constructor declaration (§8.8) 675 676 if (whenParentIs(ConstructorDeclaration.class, name, (p, c) -> p.getName() == name)) { 677 return true; 678 } 679 680 // 6. After the @ sign in an annotation (§9.7) 681 682 if (whenParentIs(AnnotationExpr.class, name, (p, c) -> p.getName() == name)) { 683 return true; 684 } 685 686 // 7. To the left of .class in a class literal (§15.8.2) 687 688 if (whenParentIs(ClassExpr.class, name, (p, c) -> p.getType() == c)) { 689 return true; 690 } 691 692 // 8. To the left of .this in a qualified this expression (§15.8.4) 693 694 if (whenParentIs(ThisExpr.class, name, (ne, c2) -> 695 ne.getTypeName().isPresent() && ne.getTypeName().get() == c2)) { 696 return true; 697 } 698 699 // 9. To the left of .super in a qualified superclass field access expression (§15.11.2) 700 701 if (whenParentIs(SuperExpr.class, name, (ne, c2) -> 702 ne.getTypeName().isPresent() && ne.getTypeName().get() == c2)) { 703 return true; 704 } 705 706 // 10. To the left of .Identifier or .super.Identifier in a qualified method invocation expression (§15.12) 707 // 708 // 11. To the left of .super:: in a method reference expression (§15.13) 709 // 710 // As the Identifier or dotted Identifier sequence that constitutes any ReferenceType (including a 711 // ReferenceType to the left of the brackets in an array type, or to the left of the < in a parameterized type, 712 // or in a non-wildcard type argument of a parameterized type, or in an extends or super clause of a wildcard 713 // type argument of a parameterized type) in the 16 contexts where types are used (§4.11): 714 // 715 // 1. In an extends or implements clause of a class declaration (§8.1.4, §8.1.5, §8.5, §9.5) 716 // 2. In an extends clause of an interface declaration (§9.1.3) 717 718 if (whenParentIs(ClassOrInterfaceDeclaration.class, name, (p, c) -> 719 p.getExtendedTypes().contains(c) || p.getImplementedTypes().contains(c))) { 720 return true; 721 } 722 723 // 3. The return type of a method (§8.4, §9.4) (including the type of an element of an annotation type (§9.6.1)) 724 725 if (whenParentIs(MethodDeclaration.class, name, (p, c) -> 726 p.getType() == c)) { 727 return true; 728 } 729 if (whenParentIs(AnnotationMemberDeclaration.class, name, (p, c) -> 730 p.getType() == c)) { 731 return true; 732 } 733 734 // 4. In the throws clause of a method or constructor (§8.4.6, §8.8.5, §9.4) 735 736 if (whenParentIs(MethodDeclaration.class, name, (p, c) -> 737 p.getThrownExceptions().contains(c))) { 738 return true; 739 } 740 if (whenParentIs(ConstructorDeclaration.class, name, (p, c) -> 741 p.getThrownExceptions().contains(c))) { 742 return true; 743 } 744 745 // 5. In an extends clause of a type parameter declaration of a generic class, interface, method, or 746 // constructor (§8.1.2, §9.1.2, §8.4.4, §8.8.4) 747 // 748 // 6. The type in a field declaration of a class or interface (§8.3, §9.3) 749 750 if (whenParentIs(VariableDeclarator.class, name, (p1, c1) -> 751 p1.getType() == c1 && whenParentIs(FieldDeclaration.class, p1, (p2, c2) -> 752 p2.getVariables().contains(c2)))) { 753 return true; 754 } 755 756 // 7. The type in a formal parameter declaration of a method, constructor, or lambda expression 757 // (§8.4.1, §8.8.1, §9.4, §15.27.1) 758 759 if (whenParentIs(Parameter.class, name, (p, c) -> 760 p.getType() == c)) { 761 return true; 762 } 763 764 // 8. The type of the receiver parameter of a method (§8.4.1) 765 766 if (whenParentIs(ReceiverParameter.class, name, (p, c) -> 767 p.getType() == c)) { 768 return true; 769 } 770 771 // 9. The type in a local variable declaration (§14.4, §14.14.1, §14.14.2, §14.20.3) 772 773 if (whenParentIs(VariableDeclarator.class, name, (p1, c1) -> 774 p1.getType() == c1 && whenParentIs(VariableDeclarationExpr.class, p1, (p2, c2) -> 775 p2.getVariables().contains(c2)))) { 776 return true; 777 } 778 779 // 10. A type in an exception parameter declaration (§14.20) 780 // 781 // 11. In an explicit type argument list to an explicit constructor invocation statement or class instance 782 // creation expression or method invocation expression (§8.8.7.1, §15.9, §15.12) 783 784 if (whenParentIs(ClassOrInterfaceType.class, name, (p, c) -> 785 p.getTypeArguments().isPresent() && p.getTypeArguments().get().contains(c))) { 786 return true; 787 } 788 if (whenParentIs(MethodCallExpr.class, name, (p, c) -> 789 p.getTypeArguments().isPresent() && p.getTypeArguments().get().contains(c))) { 790 return true; 791 } 792 793 // 12. In an unqualified class instance creation expression, either as the class type to be instantiated (§15.9) 794 // or as the direct superclass or direct superinterface of an anonymous class to be instantiated (§15.9.5) 795 796 if (whenParentIs(ObjectCreationExpr.class, name, (p, c) -> 797 p.getType() == c)) { 798 return true; 799 } 800 801 // 13. The element type in an array creation expression (§15.10.1) 802 803 if (whenParentIs(ArrayCreationExpr.class, name, (p, c) -> 804 p.getElementType() == c)) { 805 return true; 806 } 807 808 // 14. The type in the cast operator of a cast expression (§15.16) 809 810 if (whenParentIs(CastExpr.class, name, (p, c) -> 811 p.getType() == c)) { 812 return true; 813 } 814 815 // 15. The type that follows the instanceof relational operator (§15.20.2) 816 817 if (whenParentIs(InstanceOfExpr.class, name, (p, c) -> 818 p.getType() == c)) { 819 return true; 820 } 821 822 // 16. In a method reference expression (§15.13), as the reference type to search for a member method or as the class type or array type to construct. 823 824 if (whenParentIs(TypeExpr.class, name, (p1, c1) -> 825 p1.getType() == c1 && whenParentIs(MethodReferenceExpr.class, p1, (p2, c2) -> 826 p2.getScope() == c2) 827 )) { 828 return true; 829 } 830 831 // The extraction of a TypeName from the identifiers of a ReferenceType in the 16 contexts above is intended to 832 // apply recursively to all sub-terms of the ReferenceType, such as its element type and any type arguments. 833 // 834 // For example, suppose a field declaration uses the type p.q.Foo[]. The brackets of the array type are ignored, 835 // and the term p.q.Foo is extracted as a dotted sequence of Identifiers to the left of the brackets in an array 836 // type, and classified as a TypeName. A later step determines which of p, q, and Foo is a type name or a 837 // package name. 838 // 839 // As another example, suppose a cast operator uses the type p.q.Foo<? extends String>. The term p.q.Foo is 840 // again extracted as a dotted sequence of Identifier terms, this time to the left of the < in a parameterized 841 // type, and classified as a TypeName. The term String is extracted as an Identifier in an extends clause of a 842 // wildcard type argument of a parameterized type, and classified as a TypeName. 843 return false; 844 } 845 isSyntacticallyAnExpressionName(Node name)846 private static boolean isSyntacticallyAnExpressionName(Node name) { 847 // A name is syntactically classified as an ExpressionName in these contexts: 848 // 849 // 1. As the qualifying expression in a qualified superclass constructor invocation (§8.8.7.1) 850 851 if (whenParentIs(NameExpr.class, name, (nameExpr, c) -> 852 nameExpr.getName() == c && whenParentIs(ExplicitConstructorInvocationStmt.class, nameExpr, (ne, c2) -> 853 ne.getExpression().isPresent() && ne.getExpression().get() == c2) 854 )) { 855 return true; 856 } 857 if (whenParentIs(ExplicitConstructorInvocationStmt.class, name, (ne, c2) -> 858 ne.getExpression().isPresent() && ne.getExpression().get() == c2)) { 859 return true; 860 } 861 862 // 2. As the qualifying expression in a qualified class instance creation expression (§15.9) 863 864 if (whenParentIs(NameExpr.class, name, (nameExpr, c) -> 865 nameExpr.getName() == c && whenParentIs(ObjectCreationExpr.class, nameExpr, (ne, c2) -> 866 ne.getScope().isPresent() && ne.getScope().get() == c2) 867 )) { 868 return true; 869 } 870 if (whenParentIs(ObjectCreationExpr.class, name, (ne, c2) -> 871 ne.getScope().isPresent() && ne.getScope().get() == c2)) { 872 return true; 873 } 874 875 // 3. As the array reference expression in an array access expression (§15.10.3) 876 877 if (whenParentIs(NameExpr.class, name, (nameExpr, c) -> 878 nameExpr.getName() == c && whenParentIs(ArrayAccessExpr.class, nameExpr, (ne, c2) -> 879 ne.getName() == c2) 880 )) { 881 return true; 882 } 883 if (whenParentIs(ArrayAccessExpr.class, name, (ne, c2) -> 884 ne.getName() == c2)) { 885 return true; 886 } 887 888 // 4. As a PostfixExpression (§15.14) 889 890 if (whenParentIs(NameExpr.class, name, (nameExpr, c) -> 891 nameExpr.getName() == c && whenParentIs(UnaryExpr.class, nameExpr, (ne, c2) -> 892 ne.getExpression() == c2 && ne.isPostfix()) 893 )) { 894 return true; 895 } 896 if (whenParentIs(UnaryExpr.class, name, (ne, c2) -> 897 ne.getExpression() == c2 && ne.isPostfix())) { 898 return true; 899 } 900 901 // 5. As the left-hand operand of an assignment operator (§15.26) 902 903 if (whenParentIs(NameExpr.class, name, (nameExpr, c) -> 904 nameExpr.getName() == c && whenParentIs(AssignExpr.class, nameExpr, (ne, c2) -> 905 ne.getTarget() == c2) 906 )) { 907 return true; 908 } 909 if (whenParentIs(AssignExpr.class, name, (ne, c2) -> 910 ne.getTarget() == c2)) { 911 return true; 912 } 913 914 // 6. As a VariableAccess in a try-with-resources statement (§14.20.3) 915 916 if (whenParentIs(NameExpr.class, name, (nameExpr, c) -> 917 nameExpr.getName() == c && whenParentIs(TryStmt.class, nameExpr, (ne, c2) -> 918 ne.getResources().contains(c2)) 919 )) { 920 return true; 921 } 922 if (whenParentIs(NameExpr.class, name, (p1 /*NameExpr*/, c1 /*SimpleName*/) -> 923 p1.getName() == c1 && whenParentIs(VariableDeclarator.class, p1, (p2, c2) -> 924 p2.getInitializer().isPresent() && p2.getInitializer().get() == c2 && whenParentIs(VariableDeclarationExpr.class, p2, (p3, c3) -> 925 p3.getVariables().contains(c3) && whenParentIs(TryStmt.class, p3, (p4, c4) -> 926 p4.getResources().contains(c4) 927 ) 928 )) 929 )) { 930 return true; 931 } 932 if (whenParentIs(TryStmt.class, name, (ne, c2) -> 933 ne.getResources().contains(c2))) { 934 return true; 935 } 936 if (whenParentIs(VariableDeclarator.class, name, (p2, c2) -> 937 p2.getInitializer().isPresent() && p2.getInitializer().get() == c2 && whenParentIs(VariableDeclarationExpr.class, p2, (p3, c3) -> 938 p3.getVariables().contains(c3) && whenParentIs(TryStmt.class, p3, (p4, c4) -> 939 p4.getResources().contains(c4) 940 ) 941 ))) { 942 return true; 943 } 944 945 return false; 946 } 947 948 /** 949 * Return the string representation of the name 950 */ nameAsString(Node name)951 public static String nameAsString(Node name) { 952 if (!isAName(name)) { 953 throw new IllegalArgumentException("A name was expected"); 954 } 955 if (name instanceof Name) { 956 return ((Name) name).asString(); 957 } else if (name instanceof SimpleName) { 958 return ((SimpleName) name).getIdentifier(); 959 } else if (name instanceof ClassOrInterfaceType) { 960 return ((ClassOrInterfaceType) name).asString(); 961 } else if (name instanceof FieldAccessExpr) { 962 FieldAccessExpr fieldAccessExpr = (FieldAccessExpr) name; 963 if (isAName(fieldAccessExpr.getScope())) { 964 return nameAsString(fieldAccessExpr.getScope()) + "." + nameAsString(fieldAccessExpr.getName()); 965 } else { 966 throw new IllegalArgumentException(); 967 } 968 } else if (name instanceof NameExpr) { 969 return ((NameExpr) name).getNameAsString(); 970 } else { 971 throw new UnsupportedOperationException("Unknown type of name found: " + name + " (" 972 + name.getClass().getCanonicalName() + ")"); 973 } 974 } 975 976 private interface PredicateOnParentAndChild<P extends Node, C extends Node> { isSatisfied(P parent, C child)977 boolean isSatisfied(P parent, C child); 978 } 979 whenParentIs(Class<P> parentClass, C child)980 private static <P extends Node, C extends Node> boolean whenParentIs(Class<P> parentClass, C child) { 981 return whenParentIs(parentClass, child, (p, c) -> true); 982 } 983 whenParentIs( Class<P> parentClass, C child, PredicateOnParentAndChild<P, C> predicate)984 private static <P extends Node, C extends Node> boolean whenParentIs( 985 Class<P> parentClass, 986 C child, 987 PredicateOnParentAndChild<P, C> predicate) { 988 if (child.getParentNode().isPresent()) { 989 Node parent = child.getParentNode().get(); 990 return parentClass.isInstance(parent) && predicate.isSatisfied(parentClass.cast(parent), child); 991 } else { 992 return false; 993 } 994 } 995 996 } 997