1 /******************************************************************************* 2 * Copyright (c) 2000, 2019 IBM Corporation and others. 3 * 4 * This program and the accompanying materials 5 * are made available under the terms of the Eclipse Public License 2.0 6 * which accompanies this distribution, and is available at 7 * https://www.eclipse.org/legal/epl-2.0/ 8 * 9 * SPDX-License-Identifier: EPL-2.0 10 * 11 * Contributors: 12 * IBM Corporation - initial API and implementation 13 *******************************************************************************/ 14 package org.eclipse.jdt.core.manipulation; 15 16 import java.util.Collection; 17 import java.util.Iterator; 18 import java.util.List; 19 20 import org.eclipse.jface.text.Region; 21 22 import org.eclipse.jdt.core.IJavaProject; 23 import org.eclipse.jdt.core.dom.AST; 24 import org.eclipse.jdt.core.dom.ASTNode; 25 import org.eclipse.jdt.core.dom.AnnotatableType; 26 import org.eclipse.jdt.core.dom.ClassInstanceCreation; 27 import org.eclipse.jdt.core.dom.CompilationUnit; 28 import org.eclipse.jdt.core.dom.ContinueStatement; 29 import org.eclipse.jdt.core.dom.CreationReference; 30 import org.eclipse.jdt.core.dom.Expression; 31 import org.eclipse.jdt.core.dom.ExpressionMethodReference; 32 import org.eclipse.jdt.core.dom.FieldAccess; 33 import org.eclipse.jdt.core.dom.IBinding; 34 import org.eclipse.jdt.core.dom.IMethodBinding; 35 import org.eclipse.jdt.core.dom.ITypeBinding; 36 import org.eclipse.jdt.core.dom.IVariableBinding; 37 import org.eclipse.jdt.core.dom.ImportDeclaration; 38 import org.eclipse.jdt.core.dom.LabeledStatement; 39 import org.eclipse.jdt.core.dom.MarkerAnnotation; 40 import org.eclipse.jdt.core.dom.MemberRef; 41 import org.eclipse.jdt.core.dom.MethodDeclaration; 42 import org.eclipse.jdt.core.dom.MethodInvocation; 43 import org.eclipse.jdt.core.dom.MethodRef; 44 import org.eclipse.jdt.core.dom.MethodRefParameter; 45 import org.eclipse.jdt.core.dom.Modifier; 46 import org.eclipse.jdt.core.dom.Name; 47 import org.eclipse.jdt.core.dom.NameQualifiedType; 48 import org.eclipse.jdt.core.dom.NormalAnnotation; 49 import org.eclipse.jdt.core.dom.PackageDeclaration; 50 import org.eclipse.jdt.core.dom.ProvidesDirective; 51 import org.eclipse.jdt.core.dom.QualifiedName; 52 import org.eclipse.jdt.core.dom.QualifiedType; 53 import org.eclipse.jdt.core.dom.SimpleName; 54 import org.eclipse.jdt.core.dom.SimpleType; 55 import org.eclipse.jdt.core.dom.SingleMemberAnnotation; 56 import org.eclipse.jdt.core.dom.SuperConstructorInvocation; 57 import org.eclipse.jdt.core.dom.SuperFieldAccess; 58 import org.eclipse.jdt.core.dom.SuperMethodReference; 59 import org.eclipse.jdt.core.dom.TagElement; 60 import org.eclipse.jdt.core.dom.ThisExpression; 61 import org.eclipse.jdt.core.dom.TypeDeclaration; 62 import org.eclipse.jdt.core.dom.TypeMethodReference; 63 import org.eclipse.jdt.core.dom.UsesDirective; 64 import org.eclipse.jdt.core.dom.YieldStatement; 65 66 import org.eclipse.jdt.internal.corext.dom.GenericVisitor; 67 import org.eclipse.jdt.internal.corext.dom.ScopeAnalyzer; 68 import org.eclipse.jdt.internal.corext.util.JavaModelUtil; 69 70 import org.eclipse.jdt.internal.ui.util.ASTHelper; 71 72 73 /** 74 * @since 1.10 75 */ 76 public class ImportReferencesCollector extends GenericVisitor { 77 78 /** 79 * Collect import statements from an AST node. 80 * 81 * @param node The AST node 82 * @param project The Java project 83 * @param rangeLimit The range within the source file 84 * @param resultingTypeImports The collected import references 85 * @param resultingStaticImports The collected static imports 86 */ collect(ASTNode node, IJavaProject project, Region rangeLimit, Collection<SimpleName> resultingTypeImports, Collection<SimpleName> resultingStaticImports)87 public static void collect(ASTNode node, IJavaProject project, Region rangeLimit, Collection<SimpleName> resultingTypeImports, Collection<SimpleName> resultingStaticImports) { 88 collect(node, project, rangeLimit, false, resultingTypeImports, resultingStaticImports); 89 } 90 91 /** 92 * Collect import statements from an AST node. 93 * 94 * @param node The AST node 95 * @param project The Java project 96 * @param rangeLimit The range within the source file 97 * @param skipMethodBodies If set, do not visit method bodies 98 * @param resultingTypeImports The collected import references 99 * @param resultingStaticImports The collected static imports 100 */ collect(ASTNode node, IJavaProject project, Region rangeLimit, boolean skipMethodBodies, Collection<SimpleName> resultingTypeImports, Collection<SimpleName> resultingStaticImports)101 public static void collect(ASTNode node, IJavaProject project, Region rangeLimit, boolean skipMethodBodies, Collection<SimpleName> resultingTypeImports, Collection<SimpleName> resultingStaticImports) { 102 ASTNode root= node.getRoot(); 103 CompilationUnit astRoot= root instanceof CompilationUnit ? (CompilationUnit) root : null; 104 node.accept(new ImportReferencesCollector(project, astRoot, rangeLimit, skipMethodBodies, resultingTypeImports, resultingStaticImports)); 105 } 106 107 private CompilationUnit fASTRoot; 108 private Region fSubRange; 109 private Collection<SimpleName> fTypeImports; 110 private Collection<SimpleName> fStaticImports; 111 private boolean fSkipMethodBodies; 112 ImportReferencesCollector(IJavaProject project, CompilationUnit astRoot, Region rangeLimit, boolean skipMethodBodies, Collection<SimpleName> resultingTypeImports, Collection<SimpleName> resultingStaticImports)113 private ImportReferencesCollector(IJavaProject project, CompilationUnit astRoot, Region rangeLimit, boolean skipMethodBodies, Collection<SimpleName> resultingTypeImports, Collection<SimpleName> resultingStaticImports) { 114 super(processJavadocComments(astRoot)); 115 fTypeImports= resultingTypeImports; 116 fStaticImports= resultingStaticImports; 117 fSubRange= rangeLimit; 118 if (project == null || !JavaModelUtil.is50OrHigher(project)) { 119 fStaticImports= null; // do not collect 120 } 121 fASTRoot= astRoot; // can be null 122 fSkipMethodBodies= skipMethodBodies; 123 } 124 processJavadocComments(CompilationUnit astRoot)125 private static boolean processJavadocComments(CompilationUnit astRoot) { 126 // don't visit Javadoc for 'package-info' (bug 216432) 127 if (astRoot != null && astRoot.getTypeRoot() != null) { 128 return !JavaModelUtil.PACKAGE_INFO_JAVA.equals(astRoot.getTypeRoot().getElementName()); 129 } 130 return true; 131 } 132 isAffected(ASTNode node)133 private boolean isAffected(ASTNode node) { 134 if (fSubRange == null) { 135 return true; 136 } 137 int nodeStart= node.getStartPosition(); 138 int offset= fSubRange.getOffset(); 139 return nodeStart + node.getLength() > offset && offset + fSubRange.getLength() > nodeStart; 140 } 141 142 addReference(SimpleName name)143 private void addReference(SimpleName name) { 144 if (isAffected(name)) { 145 fTypeImports.add(name); 146 } 147 } 148 typeRefFound(Name node)149 private void typeRefFound(Name node) { 150 if (node != null) { 151 while (node.isQualifiedName()) { 152 node= ((QualifiedName) node).getQualifier(); 153 } 154 addReference((SimpleName) node); 155 } 156 } 157 possibleTypeRefFound(Name node)158 private void possibleTypeRefFound(Name node) { 159 while (node.isQualifiedName()) { 160 node= ((QualifiedName) node).getQualifier(); 161 } 162 IBinding binding= node.resolveBinding(); 163 if (binding == null || binding.getKind() == IBinding.TYPE) { 164 // if the binding is null, we cannot determine if 165 // we have a type binding or not, so we will assume 166 // we do. 167 addReference((SimpleName) node); 168 } 169 } 170 possibleStaticImportFound(Name name)171 private void possibleStaticImportFound(Name name) { 172 if (fStaticImports == null || fASTRoot == null) { 173 return; 174 } 175 176 while (name.isQualifiedName()) { 177 name= ((QualifiedName) name).getQualifier(); 178 } 179 if (!isAffected(name)) { 180 return; 181 } 182 183 IBinding binding= name.resolveBinding(); 184 SimpleName simpleName= (SimpleName)name; 185 if (binding == null) { 186 // This may be a currently unresolvable reference to a static member. 187 fStaticImports.add(simpleName); 188 } else if (binding instanceof ITypeBinding || !Modifier.isStatic(binding.getModifiers()) || simpleName.isDeclaration()) { 189 return; 190 } else if (binding instanceof IVariableBinding) { 191 IVariableBinding varBinding= (IVariableBinding) binding; 192 if (varBinding.isField()) { 193 varBinding= varBinding.getVariableDeclaration(); 194 ITypeBinding declaringClass= varBinding.getDeclaringClass(); 195 if (declaringClass != null && !declaringClass.isLocal()) { 196 if (new ScopeAnalyzer(fASTRoot).isDeclaredInScope(varBinding, simpleName, ScopeAnalyzer.VARIABLES | ScopeAnalyzer.CHECK_VISIBILITY)) 197 return; 198 fStaticImports.add(simpleName); 199 } 200 } 201 } else if (binding instanceof IMethodBinding) { 202 IMethodBinding methodBinding= ((IMethodBinding) binding).getMethodDeclaration(); 203 ITypeBinding declaringClass= methodBinding.getDeclaringClass(); 204 if (declaringClass != null && !declaringClass.isLocal()) { 205 if (new ScopeAnalyzer(fASTRoot).isDeclaredInScope(methodBinding, simpleName, ScopeAnalyzer.METHODS | ScopeAnalyzer.CHECK_VISIBILITY)) 206 return; 207 fStaticImports.add(simpleName); 208 } 209 } 210 211 } 212 doVisitChildren(List<? extends ASTNode> elements)213 private void doVisitChildren(List<? extends ASTNode> elements) { 214 int nElements= elements.size(); 215 for (int i= 0; i < nElements; i++) { 216 ((ASTNode) elements.get(i)).accept(this); 217 } 218 } 219 doVisitNode(ASTNode node)220 private void doVisitNode(ASTNode node) { 221 if (node != null) { 222 node.accept(this); 223 } 224 } 225 226 @Override visitNode(ASTNode node)227 protected boolean visitNode(ASTNode node) { 228 return isAffected(node); 229 } 230 231 /* 232 * @see ASTVisitor#visit(SimpleType) 233 */ 234 @Override visit(SimpleType node)235 public boolean visit(SimpleType node) { 236 if (node.getAST().apiLevel() < AST.JLS10 || !node.isVar()) { 237 typeRefFound(node.getName()); 238 } 239 visitAnnotations(node); 240 return false; 241 } 242 243 /* 244 * @see ASTVisitor#visit(NameQualifiedType) 245 */ 246 @Override visit(NameQualifiedType node)247 public boolean visit(NameQualifiedType node) { 248 possibleTypeRefFound(node.getQualifier()); 249 visitAnnotations(node); 250 return false; 251 } 252 253 /* 254 * @see ASTVisitor#visit(QualifiedType) 255 */ 256 @Override visit(QualifiedType node)257 public boolean visit(QualifiedType node) { 258 doVisitNode(node.getQualifier()); 259 visitAnnotations(node); 260 return false; 261 } 262 visitAnnotations(AnnotatableType node)263 private void visitAnnotations(AnnotatableType node) { 264 if (node.getAST().apiLevel() >= AST.JLS8) { 265 doVisitChildren(node.annotations()); 266 } 267 } 268 269 /* 270 * @see ASTVisitor#visit(QualifiedName) 271 */ 272 @Override visit(QualifiedName node)273 public boolean visit(QualifiedName node) { 274 possibleTypeRefFound(node); // possible ref 275 possibleStaticImportFound(node); 276 return false; 277 } 278 279 /* 280 * @see ASTVisitor#visit(ImportDeclaration) 281 */ 282 @Override visit(ImportDeclaration node)283 public boolean visit(ImportDeclaration node) { 284 return false; 285 } 286 287 /* 288 * @see ASTVisitor#visit(PackageDeclaration) 289 */ 290 @Override visit(PackageDeclaration node)291 public boolean visit(PackageDeclaration node) { 292 doVisitNode(node.getJavadoc()); 293 doVisitChildren(node.annotations()); 294 return false; 295 } 296 297 @Override visit(LabeledStatement node)298 public boolean visit(LabeledStatement node) { 299 doVisitNode(node.getBody()); 300 return false; 301 } 302 303 @Override visit(ContinueStatement node)304 public boolean visit(ContinueStatement node) { 305 return false; 306 } 307 308 @Override visit(YieldStatement node)309 public boolean visit(YieldStatement node) { 310 if (ASTHelper.isYieldNodeSupportedInAST(node.getAST())) { 311 evalQualifyingExpression(node.getExpression(), null); 312 } 313 return false; 314 } 315 316 /* 317 * @see ASTVisitor#visit(ThisExpression) 318 */ 319 @Override visit(ThisExpression node)320 public boolean visit(ThisExpression node) { 321 typeRefFound(node.getQualifier()); 322 return false; 323 } 324 325 @Override visit(SuperFieldAccess node)326 public boolean visit(SuperFieldAccess node) { 327 typeRefFound(node.getQualifier()); 328 return false; 329 } 330 evalQualifyingExpression(Expression expr, Name selector)331 private void evalQualifyingExpression(Expression expr, Name selector) { 332 if (expr != null) { 333 if (expr instanceof Name) { 334 Name name= (Name) expr; 335 possibleTypeRefFound(name); 336 possibleStaticImportFound(name); 337 } else { 338 expr.accept(this); 339 } 340 } else if (selector != null) { 341 possibleStaticImportFound(selector); 342 } 343 } 344 345 /* 346 * @see ASTVisitor#visit(ClassInstanceCreation) 347 */ 348 @Override visit(ClassInstanceCreation node)349 public boolean visit(ClassInstanceCreation node) { 350 doVisitChildren(node.typeArguments()); 351 doVisitNode(node.getType()); 352 evalQualifyingExpression(node.getExpression(), null); 353 if (node.getAnonymousClassDeclaration() != null) { 354 node.getAnonymousClassDeclaration().accept(this); 355 } 356 doVisitChildren(node.arguments()); 357 return false; 358 } 359 360 /* 361 * @see ASTVisitor#endVisit(MethodInvocation) 362 */ 363 @Override visit(MethodInvocation node)364 public boolean visit(MethodInvocation node) { 365 evalQualifyingExpression(node.getExpression(), node.getName()); 366 doVisitChildren(node.typeArguments()); 367 doVisitChildren(node.arguments()); 368 return false; 369 } 370 371 @Override visit(CreationReference node)372 public boolean visit(CreationReference node) { 373 doVisitNode(node.getType()); 374 doVisitChildren(node.typeArguments()); 375 return false; 376 } 377 378 @Override visit(ExpressionMethodReference node)379 public boolean visit(ExpressionMethodReference node) { 380 evalQualifyingExpression(node.getExpression(), node.getName()); 381 doVisitChildren(node.typeArguments()); 382 return false; 383 } 384 385 @Override visit(SuperMethodReference node)386 public boolean visit(SuperMethodReference node) { 387 doVisitNode(node.getQualifier()); 388 doVisitChildren(node.typeArguments()); 389 return false; 390 } 391 392 @Override visit(TypeMethodReference node)393 public boolean visit(TypeMethodReference node) { 394 doVisitNode(node.getType()); 395 doVisitChildren(node.typeArguments()); 396 return false; 397 } 398 399 @Override visit(UsesDirective node)400 public boolean visit(UsesDirective node) { 401 possibleTypeRefFound(node.getName()); 402 return false; 403 } 404 405 @Override visit(ProvidesDirective node)406 public boolean visit(ProvidesDirective node) { 407 possibleTypeRefFound(node.getName()); 408 for (Object impl : node.implementations()) { 409 possibleTypeRefFound((Name) impl); 410 } 411 return false; 412 } 413 414 /* 415 * @see ASTVisitor#visit(SuperConstructorInvocation) 416 */ 417 @Override visit(SuperConstructorInvocation node)418 public boolean visit(SuperConstructorInvocation node) { 419 if (!isAffected(node)) { 420 return false; 421 } 422 423 evalQualifyingExpression(node.getExpression(), null); 424 doVisitChildren(node.typeArguments()); 425 doVisitChildren(node.arguments()); 426 return false; 427 } 428 429 /* 430 * @see ASTVisitor#visit(FieldAccess) 431 */ 432 @Override visit(FieldAccess node)433 public boolean visit(FieldAccess node) { 434 evalQualifyingExpression(node.getExpression(), node.getName()); 435 return false; 436 } 437 438 /* 439 * @see ASTVisitor#visit(SimpleName) 440 */ 441 @Override visit(SimpleName node)442 public boolean visit(SimpleName node) { 443 // if the call gets here, it can only be a variable reference 444 possibleStaticImportFound(node); 445 return false; 446 } 447 448 @Override visit(MarkerAnnotation node)449 public boolean visit(MarkerAnnotation node) { 450 typeRefFound(node.getTypeName()); 451 return false; 452 } 453 454 @Override visit(NormalAnnotation node)455 public boolean visit(NormalAnnotation node) { 456 typeRefFound(node.getTypeName()); 457 doVisitChildren(node.values()); 458 return false; 459 } 460 461 @Override visit(SingleMemberAnnotation node)462 public boolean visit(SingleMemberAnnotation node) { 463 typeRefFound(node.getTypeName()); 464 doVisitNode(node.getValue()); 465 return false; 466 } 467 468 /* 469 * @see ASTVisitor#visit(TypeDeclaration) 470 */ 471 @Override visit(TypeDeclaration node)472 public boolean visit(TypeDeclaration node) { 473 if (!isAffected(node)) { 474 return false; 475 } 476 return true; 477 } 478 479 /* 480 * @see ASTVisitor#visit(MethodDeclaration) 481 */ 482 @Override visit(MethodDeclaration node)483 public boolean visit(MethodDeclaration node) { 484 if (!isAffected(node)) { 485 return false; 486 } 487 doVisitNode(node.getJavadoc()); 488 489 doVisitChildren(node.modifiers()); 490 doVisitChildren(node.typeParameters()); 491 492 if (!node.isConstructor()) { 493 doVisitNode(node.getReturnType2()); 494 } 495 // name not visited 496 497 int apiLevel= node.getAST().apiLevel(); 498 if (apiLevel >= AST.JLS8) { 499 doVisitNode(node.getReceiverType()); 500 } 501 // receiverQualifier not visited: 502 // Enclosing class names cannot be shadowed by an import (qualification is always redundant). 503 doVisitChildren(node.parameters()); 504 if (apiLevel >= AST.JLS8) { 505 doVisitChildren(node.extraDimensions()); 506 doVisitChildren(node.thrownExceptionTypes()); 507 } else { 508 Iterator<Name> iter= getThrownExceptions(node).iterator(); 509 while (iter.hasNext()) { 510 typeRefFound(iter.next()); 511 } 512 } 513 if (!fSkipMethodBodies) { 514 doVisitNode(node.getBody()); 515 } 516 return false; 517 } 518 519 /** 520 * @param decl method declaration 521 * @return thrown exception names 522 * @deprecated to avoid deprecation warnings 523 */ 524 @Deprecated getThrownExceptions(MethodDeclaration decl)525 private static List<Name> getThrownExceptions(MethodDeclaration decl) { 526 return decl.thrownExceptions(); 527 } 528 529 @Override visit(TagElement node)530 public boolean visit(TagElement node) { 531 String tagName= node.getTagName(); 532 List<? extends ASTNode> list= node.fragments(); 533 int idx= 0; 534 if (tagName != null && !list.isEmpty()) { 535 Object first= list.get(0); 536 if (first instanceof Name) { 537 if ("@throws".equals(tagName) || "@exception".equals(tagName)) { //$NON-NLS-1$//$NON-NLS-2$ 538 typeRefFound((Name) first); 539 } else if ("@see".equals(tagName) || "@link".equals(tagName) || "@linkplain".equals(tagName)) { //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$ 540 Name name= (Name) first; 541 possibleTypeRefFound(name); 542 } 543 idx++; 544 } 545 } 546 for (int i= idx; i < list.size(); i++) { 547 doVisitNode(list.get(i)); 548 } 549 return false; 550 } 551 552 @Override visit(MemberRef node)553 public boolean visit(MemberRef node) { 554 Name qualifier= node.getQualifier(); 555 if (qualifier != null) { 556 typeRefFound(qualifier); 557 } 558 return false; 559 } 560 561 @Override visit(MethodRef node)562 public boolean visit(MethodRef node) { 563 Name qualifier= node.getQualifier(); 564 if (qualifier != null) { 565 typeRefFound(qualifier); 566 } 567 List<MethodRefParameter> list= node.parameters(); 568 if (list != null) { 569 doVisitChildren(list); // visit MethodRefParameter with Type 570 } 571 return false; 572 } 573 574 @Override visit(MethodRefParameter node)575 public boolean visit(MethodRefParameter node) { 576 doVisitNode(node.getType()); 577 return false; 578 } 579 } 580