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