diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/apiUsage/ApiUsageDetector.java b/java/java-analysis-impl/src/com/intellij/codeInspection/apiUsage/ApiUsageDetector.java
deleted file mode 100644
index 19ee2d2d9211..000000000000
--- a/java/java-analysis-impl/src/com/intellij/codeInspection/apiUsage/ApiUsageDetector.java
+++ /dev/null
@@ -1,95 +0,0 @@
-// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
-package com.intellij.codeInspection.apiUsage;
-
-import com.intellij.psi.*;
-import org.jetbrains.annotations.NotNull;
-
-/**
- * Interface containing all types of API usages events emitted by {@link ApiUsageVisitorBase}.
- *
- * The most common event is {@link #processReference(PsiReference)},
- * which reports a reference to a class, method, field, or any other API.
- * Implicit usages of APIs, which are not present in source code by any reference, are handled by remaining methods.
- */
-interface ApiUsageDetector {
-
- /**
- * Checks whether references from this PSI element to other PSI elements must be processed.
- *
- * For example, it may be overridden to ignore references of elements residing in import statements.
- */
- default boolean shouldProcessReferences(@NotNull PsiElement element) {
- return true;
- }
-
- /**
- * Reference to API is found.
- */
- void processReference(@NotNull PsiReference reference);
-
- /**
- * Invocation of a constructor is found in {@code new} expression.
- * {@code instantiatedClass} is a reference to a class being instantiated.
- *
- * When anonymous class is instantiated, the {@code instantiatedClass} is the base class.
- */
- default void processConstructorInvocation(@NotNull PsiJavaCodeReferenceElement instantiatedClass, @NotNull PsiMethod constructor) {
- processReference(instantiatedClass);
- }
-
- /**
- * Invocation of the default constructor of a class is found in a {@code new} expression.
- *
{@code
- * class Test {
- * //The default implicit constructor
- * }
- *
- * void foo() {
- * Object o = new Test(); //The default constructor invocation
- * }}
- * {@code instantiatedClass} is a reference to a class being instantiated.
- *
- * When anonymous class is instantiated, the {@code instantiatedClass} is the base class.
- */
- default void processDefaultConstructorInvocation(@NotNull PsiJavaCodeReferenceElement instantiatedClass) {}
-
- /**
- * Invocation of the empty constructor of a super class is found in the default constructor of a subclass, which is not an anonymous class.
- *
- * {@code
- * class Subclass extends Super {
- *
- * }}
- *
- */
- default void processEmptyConstructorOfSuperClassImplicitInvocationAtSubclassDeclaration(
- @NotNull PsiClass subclass,
- @NotNull PsiClass superClass
- ) { }
-
- /**
- * Implicit invocation of an empty constructor of a super class is found in a constructor of a subclass.
- * The empty constructor is either a constructor with no arguments, or the default constructor.
- *
- *
- * {@code
- * class Subclass extends Super {
- * Subclass() {
- *
- * }
- * }}
- *
- */
- default void processEmptyConstructorOfSuperClassImplicitInvocationAtSubclassConstructor(
- @NotNull PsiClass superClass,
- @NotNull PsiMethod subclassConstructor
- ) {}
-
- /**
- * Method of a super class {@code overriddenMethod} is overridden by a subclass' {@code method}.
- *
- * The super class is {@code overriddenMethod.containingClass}, and the subclass is {@code method.containingClass}.
- */
- default void processMethodOverriding(@NotNull PsiMethod method, @NotNull PsiMethod overriddenMethod) {}
-
-}
diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/apiUsage/ApiUsageProcessor.java b/java/java-analysis-impl/src/com/intellij/codeInspection/apiUsage/ApiUsageProcessor.java
new file mode 100644
index 000000000000..5d64e85f68ff
--- /dev/null
+++ b/java/java-analysis-impl/src/com/intellij/codeInspection/apiUsage/ApiUsageProcessor.java
@@ -0,0 +1,68 @@
+// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
+package com.intellij.codeInspection.apiUsage;
+
+import com.intellij.psi.*;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.uast.UClass;
+import org.jetbrains.uast.UElement;
+import org.jetbrains.uast.UExpression;
+import org.jetbrains.uast.UMethod;
+
+/**
+ * Processes usages of APIs in source code of UAST-supporting languages, which are detected by {@link ApiUsageUastVisitor}.
+ */
+@ApiStatus.Experimental
+public interface ApiUsageProcessor {
+
+ /**
+ * Process reference to a class, method (not constructor), field or any other API element found in source code.
+ *
+ * @param sourceNode can be used to get actual PSI element to highlight in inspections via {@code sourceNode.sourcePsi}
+ * @param target resolved API element
+ * @param qualifier is optionally a qualified expression of the reference.
+ */
+ default void processReference(@NotNull UElement sourceNode, @NotNull PsiModifierListOwner target, @Nullable UExpression qualifier) { }
+
+ /**
+ * Process reference to an imported API element.
+ *
+ * @param sourceNode can be used to get actual PSI element to highlight in inspections via {@code sourceNode.sourcePsi}
+ * @param target resolved API element being imported
+ */
+ default void processImportReference(@NotNull UElement sourceNode, @NotNull PsiModifierListOwner target) { }
+
+ /**
+ * Process constructor invocation of a class.
+ * The invoked constructor may be the default constructor, which is not declared in source code.
+ * The constructor invocation may be implicit: in declaration of a subclass with no constructors defined,
+ * in declaration of an anonymous class, as implicit {@code super()} invocation in subclass' constructor, etc.
+ *
+ * @param sourceNode can be used to get actual PSI element to highlight in inspections via {@code sourceNode.sourcePsi}
+ * @param instantiatedClass class being instantiated
+ * @param constructor PSI constructor defined in source code, or {@code null} if the default constructor is being invoked.
+ * @param subclassDeclaration declaration of a subclass or anonymous subclass where the constructor invocation's happens,
+ * or {@code null} if the constructor is being invoked explicitly.
+ */
+ default void processConstructorInvocation(@NotNull UElement sourceNode,
+ @NotNull PsiClass instantiatedClass,
+ @Nullable PsiMethod constructor,
+ @Nullable UClass subclassDeclaration) {}
+
+ /**
+ * Process overriding of a super class' method.
+ *
+ * @param method method that overrides the parent's method. {@code method.uastAnchor.sourcePsi} can be be used to highlight name declaration.
+ * @param overriddenMethod super class' method being overridden
+ */
+ default void processMethodOverriding(@NotNull UMethod method, @NotNull PsiMethod overriddenMethod) {}
+
+ /**
+ * Process reference to a Java module found in {@code module-info.java} file.
+ *
+ * @param javaModuleReference Java module reference
+ * @param target resolved Java module
+ */
+ default void processJavaModuleReference(@NotNull PsiJavaModuleReference javaModuleReference, @NotNull PsiJavaModule target) {}
+}
diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/apiUsage/ApiUsageUastVisitor.kt b/java/java-analysis-impl/src/com/intellij/codeInspection/apiUsage/ApiUsageUastVisitor.kt
new file mode 100644
index 000000000000..66ba2c5a65b3
--- /dev/null
+++ b/java/java-analysis-impl/src/com/intellij/codeInspection/apiUsage/ApiUsageUastVisitor.kt
@@ -0,0 +1,323 @@
+// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
+package com.intellij.codeInspection.apiUsage
+
+import com.intellij.psi.*
+import org.jetbrains.uast.*
+import org.jetbrains.uast.visitor.AbstractUastNonRecursiveVisitor
+
+/**
+ * Non-recursive UAST visitor that detects usages of APIs in source code of UAST-supporting languages
+ * and reports them via [ApiUsageProcessor] interface.
+ */
+class ApiUsageUastVisitor(private val apiUsageProcessor: ApiUsageProcessor) : AbstractUastNonRecursiveVisitor() {
+
+ override fun visitSimpleNameReferenceExpression(node: USimpleNameReferenceExpression): Boolean {
+ if (maybeProcessReferenceInsideImportStatement(node)) {
+ return true
+ }
+ if (maybeProcessJavaModuleReference(node)) {
+ return true
+ }
+ if (isMethodReferenceOfCallExpression(node)
+ || isSelectorOfQualifiedReference(node)
+ || isKotlinConstructorCalleeInObjectDeclarationReference(node)
+ ) {
+ return true
+ }
+ val resolved = node.resolve()
+ if (resolved is PsiModifierListOwner) {
+ apiUsageProcessor.processReference(node, resolved, null)
+ return true
+ }
+ if (resolved == null) {
+ /*
+ * KT-30522 UAST for Kotlin: reference to annotation parameter resolves to null.
+ */
+ val psiReferences = node.sourcePsi?.references.orEmpty()
+ for (psiReference in psiReferences) {
+ val target = psiReference.resolve()?.toUElement()?.javaPsi as? PsiAnnotationMethod
+ if (target != null) {
+ apiUsageProcessor.processReference(node, target, null)
+ return true
+ }
+ }
+ }
+ return true
+ }
+
+ override fun visitQualifiedReferenceExpression(node: UQualifiedReferenceExpression): Boolean {
+ if (maybeProcessReferenceInsideImportStatement(node)) {
+ return true
+ }
+ if (node.sourcePsi is PsiMethodCallExpression || node.selector is UCallExpression) {
+ //UAST for Java produces UQualifiedReferenceExpression for both PsiMethodCallExpression and PsiReferenceExpression inside it
+ //UAST for Kotlin produces UQualifiedReferenceExpression with UCallExpression as selector
+ return true
+ }
+ val uastParent = node.uastParent
+ if (uastParent is UCallExpression && uastParent.kind == UastCallKind.CONSTRUCTOR_CALL) {
+ //Constructor call will be handled in visitCallExpression().
+ return true
+ }
+ val resolved = node.resolve()
+ if (resolved is PsiMember) {
+ apiUsageProcessor.processReference(node.selector, resolved, node.receiver)
+ }
+ return true
+ }
+
+ override fun visitCallableReferenceExpression(node: UCallableReferenceExpression): Boolean {
+ val resolve = node.resolve()
+ if (resolve is PsiMember) {
+ val sourceNode = node.referenceNameElement ?: node
+ apiUsageProcessor.processReference(sourceNode, resolve, node.qualifierExpression)
+ }
+ return true
+ }
+
+ override fun visitCallExpression(node: UCallExpression): Boolean {
+ if (node.sourcePsi is PsiExpressionStatement) {
+ //UAST for Java generates UCallExpression for PsiExpressionStatement and PsiMethodCallExpression inside it.
+ return true
+ }
+
+ val psiMethod = node.resolve()
+ val sourceNode = node.methodIdentifier ?: node.classReference?.referenceNameElement ?: node.classReference ?: node
+ if (psiMethod != null) {
+ val containingClass = psiMethod.containingClass
+ if (psiMethod.isConstructor) {
+ if (containingClass != null) {
+ apiUsageProcessor.processConstructorInvocation(sourceNode, containingClass, psiMethod, null)
+ }
+ }
+ else {
+ apiUsageProcessor.processReference(sourceNode, psiMethod, node.receiver)
+ }
+ return true
+ }
+
+ if (node.methodName == "super" && node.valueArgumentCount == 0) {
+ //Java does not resolve constructor for subclass constructor's "super()" statement
+ // if the superclass has the default constructor, which is not declared in source code and lacks PsiMethod.
+ val superClass = node.getContainingUClass()?.javaPsi?.superClass ?: return true
+ apiUsageProcessor.processConstructorInvocation(sourceNode, superClass, null, null)
+ return true
+ }
+
+ val classReference = node.classReference
+ if (classReference != null) {
+ val resolvedClass = classReference.resolve() as? PsiClass
+ if (resolvedClass != null) {
+ if (node.kind == UastCallKind.CONSTRUCTOR_CALL) {
+ val emptyConstructor = resolvedClass.constructors.find { it.parameterList.isEmpty }
+ apiUsageProcessor.processConstructorInvocation(sourceNode, resolvedClass, emptyConstructor, null)
+ }
+ else {
+ apiUsageProcessor.processReference(sourceNode, resolvedClass, node.receiver)
+ }
+ }
+ return true
+ }
+ return true
+ }
+
+ override fun visitObjectLiteralExpression(node: UObjectLiteralExpression): Boolean {
+ val psiMethod = node.resolve()
+ val sourceNode = node.methodIdentifier ?: node.classReference ?: node.declaration.uastSuperTypes.firstOrNull() ?: node
+ if (psiMethod != null) {
+ val containingClass = psiMethod.containingClass
+ if (psiMethod.isConstructor) {
+ if (containingClass != null) {
+ apiUsageProcessor.processConstructorInvocation(sourceNode, containingClass, psiMethod, node.declaration)
+ }
+ }
+ else {
+ apiUsageProcessor.processReference(sourceNode, psiMethod, node.receiver)
+ }
+ }
+ else {
+ maybeProcessImplicitConstructorInvocationAtSubclassDeclaration(sourceNode, node.declaration)
+ }
+ return true
+ }
+
+ override fun visitElement(node: UElement): Boolean {
+ if (node is UNamedExpression) {
+ //IDEA-209279: UAstVisitor lacks a hook for UNamedExpression
+ //KT-30522: Kotlin does not generate UNamedExpression for annotation's parameters.
+ processNamedExpression(node)
+ return true
+ }
+ return super.visitElement(node)
+ }
+
+ override fun visitClass(node: UClass): Boolean {
+ val uastAnchor = node.uastAnchor
+ if (uastAnchor == null || node is UAnonymousClass || node.javaPsi is PsiTypeParameter) {
+ return true
+ }
+ maybeProcessImplicitConstructorInvocationAtSubclassDeclaration(uastAnchor, node)
+ return true
+ }
+
+ override fun visitMethod(node: UMethod): Boolean {
+ if (node.isConstructor) {
+ checkImplicitCallOfSuperEmptyConstructor(node)
+ }
+ else {
+ checkMethodOverriding(node)
+ }
+ return true
+ }
+
+ private fun maybeProcessJavaModuleReference(node: UElement): Boolean {
+ val sourcePsi = node.sourcePsi
+ val psiParent = sourcePsi?.parent
+ if (sourcePsi is PsiIdentifier && psiParent is PsiJavaModuleReferenceElement && sourcePsi == psiParent.lastChild) {
+ val reference = psiParent.reference
+ val target = reference?.resolve()
+ if (target != null) {
+ apiUsageProcessor.processJavaModuleReference(reference, target)
+ }
+ return true
+ }
+ return false
+ }
+
+ private fun maybeProcessReferenceInsideImportStatement(node: UReferenceExpression): Boolean {
+ if (isInsideImportStatement(node)) {
+ fun isKotlin(node: UElement): Boolean {
+ val sourcePsi = node.sourcePsi ?: return false
+ return sourcePsi.language.id.contains("kotlin", true)
+ }
+
+ if (isKotlin(node)) {
+ /*
+ UAST for Kotlin 1.3.30 import statements have bugs.
+
+ KT-30546: some references resolve to nulls.
+ KT-30957: simple references for members resolve incorrectly to class declaration, not to the member declaration
+
+ Therefore, we have to fallback to base PSI for Kotlin references.
+ */
+ val resolved = node.sourcePsi?.reference?.resolve()
+ val target = (resolved?.toUElement()?.javaPsi ?: resolved) as? PsiModifierListOwner
+ if (target != null) {
+ apiUsageProcessor.processImportReference(node, target)
+ }
+ }
+ else {
+ val resolved = node.resolve() as? PsiModifierListOwner
+ if (resolved != null) {
+ apiUsageProcessor.processImportReference(node.referenceNameElement ?: node, resolved)
+ }
+ }
+ return true
+ }
+ return false
+ }
+
+ private fun isInsideImportStatement(node: UElement) =
+ node.skipParentOfType(true, UQualifiedReferenceExpression::class.java) is UImportStatement
+
+ private fun maybeProcessImplicitConstructorInvocationAtSubclassDeclaration(sourceNode: UElement, subclassDeclaration: UClass) {
+ val hasExplicitConstructor = subclassDeclaration.methods.any { it.isConstructor }
+ if (!hasExplicitConstructor) {
+ val instantiatedClass = subclassDeclaration.javaPsi.superClass ?: return
+ val constructor = instantiatedClass.constructors.find { it.parameterList.isEmpty }
+ apiUsageProcessor.processConstructorInvocation(sourceNode, instantiatedClass, constructor, subclassDeclaration)
+ }
+ }
+
+ private fun processNamedExpression(node: UNamedExpression) {
+ val sourcePsi = node.sourcePsi
+ val annotationMethod = sourcePsi?.reference?.resolve() as? PsiMember
+ if (annotationMethod != null) {
+ val sourceNode = (sourcePsi as? PsiNameValuePair)?.nameIdentifier?.toUElement() ?: node
+ apiUsageProcessor.processReference(sourceNode, annotationMethod, null)
+ }
+ }
+
+ private fun checkImplicitCallOfSuperEmptyConstructor(constructor: UMethod) {
+ val containingUClass = constructor.getContainingUClass() ?: return
+ val superClass = containingUClass.javaPsi.superClass ?: return
+ val uastBody = constructor.uastBody
+ val uastAnchor = constructor.uastAnchor
+ if (uastAnchor != null && isImplicitCallOfSuperConstructorFromSubclassConstructorBody(uastBody)) {
+ val emptyConstructor = superClass.constructors.find { it.parameterList.isEmpty }
+ apiUsageProcessor.processConstructorInvocation(uastAnchor, superClass, emptyConstructor, null)
+ }
+ }
+
+ private fun isImplicitCallOfSuperConstructorFromSubclassConstructorBody(constructorBody: UExpression?): Boolean {
+ if (constructorBody == null || constructorBody is UBlockExpression && constructorBody.expressions.isEmpty()) {
+ //Empty constructor body => implicit super() call.
+ return true
+ }
+ val firstExpression = (constructorBody as? UBlockExpression)?.expressions?.firstOrNull() ?: constructorBody
+ if (firstExpression !is UCallExpression) {
+ //First expression is not super() => the super() is implicit.
+ return true
+ }
+ return firstExpression.methodName != "super"
+ }
+
+ private fun checkMethodOverriding(node: UMethod) {
+ val method = node.javaPsi
+ val superMethods = method.findSuperMethods(true)
+ for (superMethod in superMethods) {
+ apiUsageProcessor.processMethodOverriding(node, superMethod)
+ }
+ }
+
+ /**
+ * UAST for Kotlin generates UAST tree with UnknownKotlinExpression element, for expression "object : BaseClass() { ... }".
+ *
+ * ```
+ * UObjectLiteralExpression
+ * UnknownKotlinExpression (CONSTRUCTOR_CALLEE)
+ * UTypeReferenceExpression (BaseClass)
+ * USimpleNameReferenceExpression (BaseClass)
+ * ```
+ *
+ * This method checks that [expression] is a simple reference from the super class' constructor invocation.
+ * If so, we have to ignore its processing in "visitSimpleNameReferenceExpression" because it will be processed in "visitObjectLiteralExpression".
+ */
+ private fun isKotlinConstructorCalleeInObjectDeclarationReference(expression: USimpleNameReferenceExpression): Boolean {
+ val parent1 = expression.uastParent
+ val parent2 = parent1?.uastParent
+ val parent3 = parent2?.uastParent
+ return parent3 is UObjectLiteralExpression
+ && parent1 is UTypeReferenceExpression
+ && parent2.asLogString().contains("CONSTRUCTOR_CALLEE")
+ }
+
+ private fun isSelectorOfQualifiedReference(expression: USimpleNameReferenceExpression): Boolean {
+ val qualifiedReference = expression.uastParent as? UQualifiedReferenceExpression ?: return false
+ return haveSameSourceElement(expression, qualifiedReference.selector)
+ }
+
+ private fun isMethodReferenceOfCallExpression(expression: USimpleNameReferenceExpression): Boolean {
+ val callExpression = expression.uastParent as? UCallExpression ?: return false
+ val expressionNameElement = expression.referenceNameElement
+ if (expressionNameElement == null && expression.identifier == "super") {
+ //UAST for Java returns null for "referenceNameElement" of "super()" statement : IDEA-210418
+ return true
+ }
+ val methodIdentifier = callExpression.methodIdentifier
+ val classReference = callExpression.classReference
+
+ return if (methodIdentifier != null) {
+ haveSameSourceElement(expressionNameElement, methodIdentifier)
+ }
+ else {
+ haveSameSourceElement(expressionNameElement, classReference?.referenceNameElement ?: classReference)
+ }
+ }
+
+ private fun haveSameSourceElement(element1: UElement?, element2: UElement?): Boolean {
+ if (element1 == null || element2 == null) return false
+ val sourcePsi1 = element1.sourcePsi
+ return sourcePsi1 != null && sourcePsi1 == element2.sourcePsi
+ }
+}
diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/apiUsage/ApiUsageVisitorBase.kt b/java/java-analysis-impl/src/com/intellij/codeInspection/apiUsage/ApiUsageVisitorBase.kt
deleted file mode 100644
index a8d04f5d28a0..000000000000
--- a/java/java-analysis-impl/src/com/intellij/codeInspection/apiUsage/ApiUsageVisitorBase.kt
+++ /dev/null
@@ -1,115 +0,0 @@
-// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
-package com.intellij.codeInspection.apiUsage
-
-import com.intellij.psi.*
-import com.intellij.psi.impl.source.tree.LeafPsiElement
-import com.intellij.psi.infos.MethodCandidateInfo
-
-/**
- * Non-recursive PSI visitor that detects usages of APIs and reports them via [ApiUsageDetector] interface.
- * This visitor is mainly designed for Java, but may basically work with any language, including Kotlin.
- * Inheritors should provide at least implementation of [processReference].
- */
-abstract class ApiUsageVisitorBase : PsiElementVisitor(), ApiUsageDetector {
-
- final override fun visitElement(element: PsiElement) {
- super.visitElement(element)
- if (element is PsiLanguageInjectionHost || element is LeafPsiElement) {
- //Better performance.
- return
- }
-
- when (element) {
- is PsiClass -> visitClass(element)
- is PsiMethod -> visitMethod(element)
- is PsiNewExpression -> visitNewExpression(element)
- else -> processReferences(element)
- }
- }
-
- private fun processReferences(element: PsiElement) {
- if (shouldProcessReferences(element)) {
- for (reference in element.references) {
- processReference(reference)
- }
- }
- }
-
- private fun visitClass(aClass: PsiClass) {
- if (aClass is PsiTypeParameter || aClass is PsiAnonymousClass) return
- if (aClass.constructors.isEmpty()) {
- val superClass = aClass.superClass ?: return
- val superConstructors = superClass.constructors
- if (superConstructors.isEmpty() || superConstructors.any { it.parameterList.isEmpty }) {
- processEmptyConstructorOfSuperClassImplicitInvocationAtSubclassDeclaration(aClass, superClass)
- }
- }
- }
-
- private fun visitMethod(method: PsiMethod) {
- if (method.isConstructor) {
- checkCallToSuperEmptyConstructor(method)
- }
- else {
- checkMethodOverriding(method)
- }
- }
-
- private fun visitNewExpression(expression: PsiNewExpression) {
- var classType = expression.type as? PsiClassType ?: return
- val argumentList = expression.argumentList ?: return
- val classReference = expression.classOrAnonymousClassReference ?: return
-
- var typeResult = classType.resolveGenerics()
- var aClass = typeResult.element ?: return
- if (aClass is PsiAnonymousClass) {
- classType = aClass.baseClassType
- typeResult = classType.resolveGenerics()
- aClass = typeResult.element ?: return
- }
-
- if (aClass.constructors.isEmpty()) {
- processDefaultConstructorInvocation(classReference)
- } else {
- val results = JavaPsiFacade
- .getInstance(expression.project)
- .resolveHelper
- .multiResolveConstructor(classType, argumentList, argumentList)
- val result = results.singleOrNull() as? MethodCandidateInfo ?: return
- val constructor = result.element
- processConstructorInvocation(classReference, constructor)
- }
- }
-
- private fun checkCallToSuperEmptyConstructor(constructor: PsiMethod) {
- val superClass = constructor.containingClass?.superClass ?: return
- val statements = constructor.body?.statements ?: return
- if (isCallToEmptySuper(statements)) {
- processEmptyConstructorOfSuperClassImplicitInvocationAtSubclassConstructor(superClass, constructor)
- }
- }
-
- private fun isCallToEmptySuper(statements: Array): Boolean {
- if (statements.isEmpty()) {
- //Implicit call to super()
- return true
- }
- val expressionStatement = statements.first() as? PsiExpressionStatement
- val methodCallExpression = expressionStatement?.expression as? PsiMethodCallExpression
- val methodExpression = methodCallExpression?.methodExpression
- if (methodExpression == null) {
- //First statement is not "this" nor "super" => there is implicit super().
- return true
- }
- return methodExpression.text == "super" && methodCallExpression.argumentList.isEmpty
- }
-
- private fun checkMethodOverriding(method: PsiMethod) {
- val superSignatures = method.findSuperMethodSignaturesIncludingStatic(true)
- for (superSignature in superSignatures) {
- val superMethod = superSignature.method
- processMethodOverriding(method, superMethod)
- }
- }
-
-}
diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/deprecation/DeprecatedApiUsageProcessor.java b/java/java-analysis-impl/src/com/intellij/codeInspection/deprecation/DeprecatedApiUsageProcessor.java
new file mode 100644
index 000000000000..f71b183b3f1e
--- /dev/null
+++ b/java/java-analysis-impl/src/com/intellij/codeInspection/deprecation/DeprecatedApiUsageProcessor.java
@@ -0,0 +1,141 @@
+// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
+package com.intellij.codeInspection.deprecation;
+
+import com.intellij.codeInsight.ExternalAnnotationsManager;
+import com.intellij.codeInsight.daemon.JavaErrorMessages;
+import com.intellij.codeInspection.ProblemHighlightType;
+import com.intellij.codeInspection.ProblemsHolder;
+import com.intellij.codeInspection.apiUsage.ApiUsageProcessor;
+import com.intellij.lang.annotation.HighlightSeverity;
+import com.intellij.psi.*;
+import com.intellij.psi.impl.PsiImplUtil;
+import com.intellij.util.containers.ContainerUtil;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.uast.*;
+
+import java.util.List;
+
+import static com.intellij.codeInspection.deprecation.DeprecationInspectionBase.*;
+
+/**
+ * PSI visitor that detects usages of deprecated APIs, which are deprecated
+ * with {@code @Deprecated, @ScheduledForRemoval} annotations or {@code @deprecated} Javadoc tag.
+ */
+public final class DeprecatedApiUsageProcessor implements ApiUsageProcessor {
+ private final ProblemsHolder myHolder;
+ private final boolean myIgnoreInsideDeprecated;
+ private final boolean myIgnoreAbstractDeprecatedOverrides;
+ private final boolean myIgnoreImportStatements;
+ private final boolean myIgnoreMethodsOfDeprecated;
+ private final boolean myIgnoreInSameOutermostClass;
+ private final boolean myForRemoval;
+ private final ProblemHighlightType myHighlightType;
+
+ public DeprecatedApiUsageProcessor(@NotNull ProblemsHolder holder,
+ boolean ignoreInsideDeprecated,
+ boolean ignoreAbstractDeprecatedOverrides,
+ boolean ignoreImportStatements,
+ boolean ignoreMethodsOfDeprecated,
+ boolean ignoreInSameOutermostClass,
+ boolean forRemoval,
+ @Nullable HighlightSeverity severity) {
+ myHolder = holder;
+ myIgnoreInsideDeprecated = ignoreInsideDeprecated;
+ myIgnoreAbstractDeprecatedOverrides = ignoreAbstractDeprecatedOverrides;
+ myIgnoreImportStatements = ignoreImportStatements;
+ myIgnoreMethodsOfDeprecated = ignoreMethodsOfDeprecated;
+ myIgnoreInSameOutermostClass = ignoreInSameOutermostClass;
+ myForRemoval = forRemoval;
+ myHighlightType = forRemoval && severity == HighlightSeverity.ERROR
+ ? ProblemHighlightType.LIKE_MARKED_FOR_REMOVAL
+ : ProblemHighlightType.LIKE_DEPRECATED;
+ }
+
+ @Override
+ public void processReference(@NotNull UElement sourceNode, @NotNull PsiModifierListOwner target, @Nullable UExpression qualifier) {
+ checkTargetDeprecated(sourceNode, target);
+ }
+
+ @Override
+ public void processImportReference(@NotNull UElement sourceNode, @NotNull PsiModifierListOwner target) {
+ checkTargetDeprecated(sourceNode, target);
+ }
+
+ private void checkTargetDeprecated(@NotNull UElement sourceNode, @NotNull PsiModifierListOwner target) {
+ PsiElement elementToHighlight = sourceNode.getSourcePsi();
+ if (elementToHighlight != null) {
+ checkTargetDeprecated(elementToHighlight, target);
+ }
+ }
+
+ private void checkTargetDeprecated(@NotNull PsiElement elementToHighlight, @NotNull PsiModifierListOwner target) {
+ checkDeprecated(target, elementToHighlight, null, myIgnoreInsideDeprecated, myIgnoreImportStatements,
+ myIgnoreMethodsOfDeprecated, myIgnoreInSameOutermostClass, myHolder, myForRemoval, myHighlightType);
+ }
+
+ @Override
+ public void processConstructorInvocation(@NotNull UElement sourceNode,
+ @NotNull PsiClass instantiatedClass,
+ @Nullable PsiMethod constructor,
+ @Nullable UClass subclassDeclaration) {
+ if (constructor != null) {
+ if (PsiImplUtil.isDeprecated(constructor) && myForRemoval == isForRemovalAttributeSet(constructor)) {
+ checkTargetDeprecated(sourceNode, constructor);
+ return;
+ }
+ }
+
+ if (isDefaultConstructorDeprecated(instantiatedClass)) {
+ PsiElement elementToHighlight = sourceNode.getSourcePsi();
+ if (elementToHighlight == null) {
+ return;
+ }
+ String description = JavaErrorMessages.message(myForRemoval
+ ? "marked.for.removal.default.constructor"
+ : "deprecated.default.constructor",
+ instantiatedClass.getQualifiedName());
+ myHolder.registerProblem(elementToHighlight, getDescription(description, myForRemoval, myHighlightType), myHighlightType);
+ }
+ else {
+ checkTargetDeprecated(sourceNode, instantiatedClass);
+ }
+ }
+
+ @Override
+ public void processMethodOverriding(@NotNull UMethod method, @NotNull PsiMethod overriddenMethod) {
+ PsiClass aClass = overriddenMethod.getContainingClass();
+ if (aClass == null) return;
+
+ PsiElement methodNameElement = UElementKt.getSourcePsiElement(method.getUastAnchor());
+ if (methodNameElement == null) return;
+
+ //Do not show deprecated warning for class implementing deprecated methods
+ if (myIgnoreAbstractDeprecatedOverrides && !aClass.isDeprecated() && overriddenMethod.hasModifierProperty(PsiModifier.ABSTRACT)) {
+ return;
+ }
+
+ if (overriddenMethod.isDeprecated() && myForRemoval == isForRemovalAttributeSet(overriddenMethod)) {
+ String description = JavaErrorMessages.message(myForRemoval ? "overrides.marked.for.removal.method" : "overrides.deprecated.method",
+ getPresentableName(aClass));
+ myHolder.registerProblem(methodNameElement, getDescription(description, myForRemoval, myHighlightType), myHighlightType);
+ }
+ }
+
+ @Override
+ public void processJavaModuleReference(@NotNull PsiJavaModuleReference javaModuleReference, @NotNull PsiJavaModule target) {
+ checkTargetDeprecated(javaModuleReference.getElement(), target);
+ }
+
+ /**
+ * The default constructor of a class can be externally annotated (IDEA-200832).
+ */
+ private boolean isDefaultConstructorDeprecated(@NotNull PsiClass aClass) {
+ List externalDeprecated = ExternalAnnotationsManager
+ .getInstance(aClass.getProject())
+ .findDefaultConstructorExternalAnnotations(aClass, CommonClassNames.JAVA_LANG_DEPRECATED);
+
+ return externalDeprecated != null &&
+ ContainerUtil.exists(externalDeprecated, annotation -> isForRemovalAttributeSet(annotation) == myForRemoval);
+ }
+}
\ No newline at end of file
diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/deprecation/DeprecationElementVisitor.java b/java/java-analysis-impl/src/com/intellij/codeInspection/deprecation/DeprecationElementVisitor.java
deleted file mode 100644
index 4e051d6e5066..000000000000
--- a/java/java-analysis-impl/src/com/intellij/codeInspection/deprecation/DeprecationElementVisitor.java
+++ /dev/null
@@ -1,169 +0,0 @@
-// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
-package com.intellij.codeInspection.deprecation;
-
-import com.intellij.codeInsight.ExternalAnnotationsManager;
-import com.intellij.codeInsight.daemon.JavaErrorMessages;
-import com.intellij.codeInspection.ProblemHighlightType;
-import com.intellij.codeInspection.ProblemsHolder;
-import com.intellij.codeInspection.apiUsage.ApiUsageVisitorBase;
-import com.intellij.lang.annotation.HighlightSeverity;
-import com.intellij.psi.*;
-import com.intellij.util.containers.ContainerUtil;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-import java.util.Arrays;
-import java.util.List;
-
-import static com.intellij.codeInspection.deprecation.DeprecationInspectionBase.*;
-
-/**
- * PSI visitor that detects usages of deprecated APIs, which are deprecated
- * with {@code @Deprecated, @ScheduledForRemoval} annotations or {@code @deprecated} Javadoc tag.
- */
-public class DeprecationElementVisitor extends ApiUsageVisitorBase {
- private final ProblemsHolder myHolder;
- private final boolean myIgnoreInsideDeprecated;
- private final boolean myIgnoreAbstractDeprecatedOverrides;
- private final boolean myIgnoreImportStatements;
- private final boolean myIgnoreMethodsOfDeprecated;
- private final boolean myIgnoreInSameOutermostClass;
- private final boolean myForRemoval;
- private final ProblemHighlightType myHighlightType;
-
- DeprecationElementVisitor(@NotNull ProblemsHolder holder,
- boolean ignoreInsideDeprecated,
- boolean ignoreAbstractDeprecatedOverrides,
- boolean ignoreImportStatements,
- boolean ignoreMethodsOfDeprecated,
- boolean ignoreInSameOutermostClass,
- boolean forRemoval,
- @Nullable HighlightSeverity severity) {
- myHolder = holder;
- myIgnoreInsideDeprecated = ignoreInsideDeprecated;
- myIgnoreAbstractDeprecatedOverrides = ignoreAbstractDeprecatedOverrides;
- myIgnoreImportStatements = ignoreImportStatements;
- myIgnoreMethodsOfDeprecated = ignoreMethodsOfDeprecated;
- myIgnoreInSameOutermostClass = ignoreInSameOutermostClass;
- myForRemoval = forRemoval;
- myHighlightType = forRemoval && severity == HighlightSeverity.ERROR
- ? ProblemHighlightType.LIKE_MARKED_FOR_REMOVAL
- : ProblemHighlightType.LIKE_DEPRECATED;
- }
-
- @Override
- public void processReference(@NotNull PsiReference reference) {
- if (reference instanceof ResolvingHint && !((ResolvingHint)reference).canResolveTo(PsiModifierListOwner.class)) {
- return;
- }
- PsiElement resolved = reference.resolve();
- if (resolved != null) {
- PsiElement elementToHighlight = getElementToHighlight(reference);
- checkDeprecated(resolved, elementToHighlight, null, myIgnoreInsideDeprecated, myIgnoreImportStatements,
- myIgnoreMethodsOfDeprecated, myIgnoreInSameOutermostClass, myHolder, myForRemoval, myHighlightType);
- }
- }
-
- @NotNull
- private static PsiElement getElementToHighlight(@NotNull PsiReference reference) {
- if (reference instanceof PsiJavaCodeReferenceElement) {
- PsiElement referenceNameElement = ((PsiJavaCodeReferenceElement)reference).getReferenceNameElement();
- if (referenceNameElement != null) {
- return referenceNameElement;
- }
- }
- return reference.getElement();
- }
-
- @Override
- public void processConstructorInvocation(@NotNull PsiJavaCodeReferenceElement instantiatedClass, @NotNull PsiMethod constructor) {
- checkDeprecated(constructor, instantiatedClass, null, myIgnoreInsideDeprecated, myIgnoreImportStatements,
- true, myIgnoreInSameOutermostClass, myHolder, myForRemoval, myHighlightType);
- }
-
- @Override
- public void processDefaultConstructorInvocation(@NotNull PsiJavaCodeReferenceElement instantiatedClass) {
- PsiElement createdClass = instantiatedClass.resolve();
- if (createdClass instanceof PsiClass && hasEmptyDeprecatedConstructor((PsiClass) createdClass, myForRemoval)) {
- registerDefaultConstructorProblem((PsiClass) createdClass, instantiatedClass);
- }
- }
-
- @Override
- public void processMethodOverriding(@NotNull PsiMethod method, @NotNull PsiMethod overriddenMethod) {
- PsiClass aClass = overriddenMethod.getContainingClass();
- if (aClass == null) return;
-
- PsiIdentifier nameIdentifier = method.getNameIdentifier();
- if (nameIdentifier == null) return;
-
- //Do not show deprecated warning for class implementing deprecated methods
- if (myIgnoreAbstractDeprecatedOverrides && !aClass.isDeprecated() && overriddenMethod.hasModifierProperty(PsiModifier.ABSTRACT)) {
- return;
- }
-
- if (overriddenMethod.isDeprecated() && myForRemoval == isForRemovalAttributeSet(overriddenMethod)) {
- String description = JavaErrorMessages.message(myForRemoval ? "overrides.marked.for.removal.method" : "overrides.deprecated.method",
- getPresentableName(aClass));
- myHolder.registerProblem(nameIdentifier, getDescription(description, myForRemoval, myHighlightType), myHighlightType);
- }
- }
-
- @Override
- public void processEmptyConstructorOfSuperClassImplicitInvocationAtSubclassConstructor(@NotNull PsiClass superClass,
- @NotNull PsiMethod subclassConstructor) {
- if (hasEmptyDeprecatedConstructor(superClass, myForRemoval)) {
- PsiIdentifier nameIdentifier = subclassConstructor.getNameIdentifier();
- if (nameIdentifier != null) {
- registerDefaultConstructorProblem(superClass, nameIdentifier);
- }
- }
- }
-
- @Override
- public void processEmptyConstructorOfSuperClassImplicitInvocationAtSubclassDeclaration(@NotNull PsiClass subclass,
- @NotNull PsiClass superClass) {
- if (hasEmptyDeprecatedConstructor(superClass, myForRemoval)) {
- final boolean isAnonymous = subclass instanceof PsiAnonymousClass;
- if (isAnonymous) {
- final PsiExpressionList argumentList = ((PsiAnonymousClass)subclass).getArgumentList();
- if (argumentList != null && !argumentList.isEmpty()) return;
- }
- PsiElement elementToHighlight = isAnonymous ? ((PsiAnonymousClass) subclass).getBaseClassReference() : subclass.getNameIdentifier();
- if (elementToHighlight != null) {
- registerDefaultConstructorProblem(superClass, elementToHighlight);
- }
- }
- }
-
- private static boolean hasEmptyDeprecatedConstructor(@NotNull PsiClass aClass, boolean forRemoval) {
- PsiMethod[] constructors = aClass.getConstructors();
- if (constructors.length == 0) {
- /*
- The default constructor of a class can be externally annotated (IDEA-200832).
- There cannot be inferred annotations for a default constructor,
- so here is no need to check all annotations returned
- by `AnnotationUtil.findAnnotations()`, but only external ones.
- */
- List externalDeprecated = ExternalAnnotationsManager
- .getInstance(aClass.getProject())
- .findDefaultConstructorExternalAnnotations(aClass, CommonClassNames.JAVA_LANG_DEPRECATED);
-
- return externalDeprecated != null
- && !externalDeprecated.isEmpty()
- && ContainerUtil.exists(externalDeprecated, annotation -> isForRemovalAttributeSet(annotation) == forRemoval);
- }
- return Arrays.stream(constructors)
- .anyMatch(constructor -> constructor.getParameterList().isEmpty() &&
- constructor.isDeprecated() &&
- forRemoval == isForRemovalAttributeSet(constructor));
- }
-
- private void registerDefaultConstructorProblem(@NotNull PsiClass constructorOwner,
- @NotNull PsiElement elementToHighlight) {
- String description =
- JavaErrorMessages.message(myForRemoval ? "marked.for.removal.default.constructor" : "deprecated.default.constructor",
- constructorOwner.getQualifiedName());
- myHolder.registerProblem(elementToHighlight, getDescription(description, myForRemoval, myHighlightType), myHighlightType);
- }
-}
\ No newline at end of file
diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/deprecation/DeprecationInspection.java b/java/java-analysis-impl/src/com/intellij/codeInspection/deprecation/DeprecationInspection.java
index a5a942c19472..b0f154d14dff 100644
--- a/java/java-analysis-impl/src/com/intellij/codeInspection/deprecation/DeprecationInspection.java
+++ b/java/java-analysis-impl/src/com/intellij/codeInspection/deprecation/DeprecationInspection.java
@@ -3,8 +3,10 @@ package com.intellij.codeInspection.deprecation;
import com.intellij.codeInspection.DeprecationUtil;
import com.intellij.codeInspection.ProblemsHolder;
+import com.intellij.codeInspection.apiUsage.ApiUsageUastVisitor;
import com.intellij.codeInspection.ui.MultipleCheckboxOptionsPanel;
import com.intellij.psi.PsiElementVisitor;
+import com.intellij.uast.UastVisitorAdapter;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
@@ -26,9 +28,15 @@ public class DeprecationInspection extends DeprecationInspectionBase {
@Override
@NotNull
public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, boolean isOnTheFly) {
- return new DeprecationElementVisitor(holder, IGNORE_INSIDE_DEPRECATED, IGNORE_ABSTRACT_DEPRECATED_OVERRIDES,
- IGNORE_IMPORT_STATEMENTS, IGNORE_METHODS_OF_DEPRECATED,
- IGNORE_IN_SAME_OUTERMOST_CLASS, false, null);
+ return new UastVisitorAdapter(
+ new ApiUsageUastVisitor(
+ new DeprecatedApiUsageProcessor(holder, IGNORE_INSIDE_DEPRECATED, IGNORE_ABSTRACT_DEPRECATED_OVERRIDES,
+ IGNORE_IMPORT_STATEMENTS, IGNORE_METHODS_OF_DEPRECATED,
+ IGNORE_IN_SAME_OUTERMOST_CLASS, false, null)
+ )
+ ,
+ true
+ );
}
@Override
diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/deprecation/DeprecationInspectionBase.java b/java/java-analysis-impl/src/com/intellij/codeInspection/deprecation/DeprecationInspectionBase.java
index 6a5c6fd45b99..93a3b7c04ece 100644
--- a/java/java-analysis-impl/src/com/intellij/codeInspection/deprecation/DeprecationInspectionBase.java
+++ b/java/java-analysis-impl/src/com/intellij/codeInspection/deprecation/DeprecationInspectionBase.java
@@ -38,7 +38,7 @@ public abstract class DeprecationInspectionBase extends LocalInspectionTool {
return true;
}
- public static void checkDeprecated(@NotNull PsiElement element,
+ public static void checkDeprecated(@NotNull PsiModifierListOwner element,
@NotNull PsiElement elementToHighlight,
@Nullable TextRange rangeInElement,
boolean ignoreInsideDeprecated,
@@ -48,13 +48,8 @@ public abstract class DeprecationInspectionBase extends LocalInspectionTool {
@NotNull ProblemsHolder holder,
boolean forRemoval,
@NotNull ProblemHighlightType highlightType) {
- if (!(element instanceof PsiModifierListOwner)) {
- return;
- }
-
- PsiModifierListOwner modifierListOwner = (PsiModifierListOwner)element;
- if (PsiImplUtil.isDeprecated(modifierListOwner)) {
- if (forRemoval != isForRemovalAttributeSet(modifierListOwner)) {
+ if (PsiImplUtil.isDeprecated(element)) {
+ if (forRemoval != isForRemovalAttributeSet(element)) {
return;
}
}
@@ -108,7 +103,7 @@ public abstract class DeprecationInspectionBase extends LocalInspectionTool {
}
}
if (refElement instanceof PsiField) {
- PsiReferenceExpression referenceExpression = ObjectUtils.tryCast(elementToHighlight.getParent(), PsiReferenceExpression.class);
+ PsiReferenceExpression referenceExpression = getFieldReferenceExpression(elementToHighlight);
if (referenceExpression != null) {
PsiField replacement = findReplacementInJavaDoc((PsiField)refElement, referenceExpression);
if (replacement != null) {
@@ -269,11 +264,22 @@ public abstract class DeprecationInspectionBase extends LocalInspectionTool {
return PsiUtil.resolveClassInType(expression.getType());
}
+ @Nullable
+ private static PsiReferenceExpression getFieldReferenceExpression(@NotNull PsiElement element) {
+ if (element instanceof PsiReferenceExpression) {
+ return (PsiReferenceExpression) element;
+ }
+ return ObjectUtils.tryCast(element.getParent(), PsiReferenceExpression.class);
+ }
+
@Nullable
private static PsiMethodCallExpression getMethodCall(@NotNull PsiElement element) {
- if (!(element instanceof PsiIdentifier)) return null;
- PsiElement parent = element.getParent();
- if (!(parent instanceof PsiReferenceExpression)) return null;
- return ObjectUtils.tryCast(parent.getParent(), PsiMethodCallExpression.class);
+ if (element instanceof PsiReferenceExpression) {
+ return ObjectUtils.tryCast(element.getParent(), PsiMethodCallExpression.class);
+ }
+ if (element instanceof PsiIdentifier) {
+ return getMethodCall(element.getParent());
+ }
+ return null;
}
}
diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/deprecation/MarkedForRemovalInspection.java b/java/java-analysis-impl/src/com/intellij/codeInspection/deprecation/MarkedForRemovalInspection.java
index 38f073e8e872..700f6f5b0451 100644
--- a/java/java-analysis-impl/src/com/intellij/codeInspection/deprecation/MarkedForRemovalInspection.java
+++ b/java/java-analysis-impl/src/com/intellij/codeInspection/deprecation/MarkedForRemovalInspection.java
@@ -6,6 +6,7 @@ import com.intellij.codeInsight.daemon.HighlightDisplayKey;
import com.intellij.codeInspection.DeprecationUtil;
import com.intellij.codeInspection.InspectionProfile;
import com.intellij.codeInspection.ProblemsHolder;
+import com.intellij.codeInspection.apiUsage.ApiUsageUastVisitor;
import com.intellij.codeInspection.ui.MultipleCheckboxOptionsPanel;
import com.intellij.lang.annotation.HighlightSeverity;
import com.intellij.pom.java.LanguageLevel;
@@ -13,6 +14,7 @@ import com.intellij.profile.codeInspection.InspectionProjectProfileManager;
import com.intellij.psi.PsiElementVisitor;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiJavaFile;
+import com.intellij.uast.UastVisitorAdapter;
import org.intellij.lang.annotations.Pattern;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
@@ -27,7 +29,12 @@ public class MarkedForRemovalInspection extends DeprecationInspectionBase {
PsiFile file = holder.getFile();
if (file instanceof PsiJavaFile && ((PsiJavaFile)file).getLanguageLevel().isAtLeast(LanguageLevel.JDK_1_9)) {
HighlightSeverity severity = getCurrentSeverity(file);
- return new DeprecationElementVisitor(holder, false, false, false, false, IGNORE_IN_SAME_OUTERMOST_CLASS, true, severity);
+ return new UastVisitorAdapter(
+ new ApiUsageUastVisitor(
+ new DeprecatedApiUsageProcessor(holder, false, false, false, false, IGNORE_IN_SAME_OUTERMOST_CLASS, true, severity)
+ ),
+ true
+ );
}
return PsiElementVisitor.EMPTY_VISITOR;
}
diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/xml/DeprecatedClassUsageInspection.java b/java/java-analysis-impl/src/com/intellij/codeInspection/xml/DeprecatedClassUsageInspection.java
index 084fe75d0b2b..f8055707ee0a 100644
--- a/java/java-analysis-impl/src/com/intellij/codeInspection/xml/DeprecatedClassUsageInspection.java
+++ b/java/java-analysis-impl/src/com/intellij/codeInspection/xml/DeprecatedClassUsageInspection.java
@@ -43,8 +43,8 @@ public class DeprecatedClassUsageInspection extends XmlSuppressableInspectionToo
PsiReference last = ArrayUtil.getLastElement(references);
if (last != null && (!(last instanceof ResolvingHint) || ((ResolvingHint)last).canResolveTo(PsiDocCommentOwner.class))) {
PsiElement resolved = last.resolve();
- if (resolved != null) {
- DeprecationInspectionBase.checkDeprecated(resolved, psiElement, last.getRangeInElement(), false, false, true, false,
+ if (resolved instanceof PsiModifierListOwner) {
+ DeprecationInspectionBase.checkDeprecated((PsiModifierListOwner)resolved, psiElement, last.getRangeInElement(), false, false, true, false,
holder, false, ProblemHighlightType.LIKE_DEPRECATED);
}
}
diff --git a/java/java-tests/testData/inspection/deprecation/deprecatedDefaultConstructorInSuper/expected.xml b/java/java-tests/testData/inspection/deprecation/deprecatedDefaultConstructorInSuper/expected.xml
index dd14b20ddb1b..04616138a1e8 100644
--- a/java/java-tests/testData/inspection/deprecation/deprecatedDefaultConstructorInSuper/expected.xml
+++ b/java/java-tests/testData/inspection/deprecation/deprecatedDefaultConstructorInSuper/expected.xml
@@ -3,7 +3,7 @@
Test.java
5
- Default constructor in C is deprecated
+ 'C()' is deprecated
Test.java
diff --git a/java/java-tests/testData/inspection/deprecation/deprecatedDefaultConstructorInSuperNotCalled/expected.xml b/java/java-tests/testData/inspection/deprecation/deprecatedDefaultConstructorInSuperNotCalled/expected.xml
index fbfc5a0037f4..e5483e38400f 100644
--- a/java/java-tests/testData/inspection/deprecation/deprecatedDefaultConstructorInSuperNotCalled/expected.xml
+++ b/java/java-tests/testData/inspection/deprecation/deprecatedDefaultConstructorInSuperNotCalled/expected.xml
@@ -3,7 +3,7 @@
Test.java
6
- Default constructor in C is deprecated
+ 'C()' is deprecated
diff --git a/java/java-tests/testData/inspection/deprecation/externallyDeprecatedDefaultConstructor/expected.xml b/java/java-tests/testData/inspection/deprecation/externallyDeprecatedDefaultConstructor/expected.xml
index 66dbc21a8224..58c57d3e4be2 100644
--- a/java/java-tests/testData/inspection/deprecation/externallyDeprecatedDefaultConstructor/expected.xml
+++ b/java/java-tests/testData/inspection/deprecation/externallyDeprecatedDefaultConstructor/expected.xml
@@ -12,7 +12,7 @@
Test.java
- 16
+ 17
Default constructor in 'a.A' is deprecated
diff --git a/java/java-tests/testData/inspection/deprecation/externallyDeprecatedDefaultConstructor/src/Test.java b/java/java-tests/testData/inspection/deprecation/externallyDeprecatedDefaultConstructor/src/Test.java
index e20149695856..c41d55ae4813 100644
--- a/java/java-tests/testData/inspection/deprecation/externallyDeprecatedDefaultConstructor/src/Test.java
+++ b/java/java-tests/testData/inspection/deprecation/externallyDeprecatedDefaultConstructor/src/Test.java
@@ -12,8 +12,8 @@ class Test2 {
}
class Test3 extends A {
- //Here "Default constructor in A is deprecated" warning must be reported.
public Test3() {
+ //Here "Default constructor in A is deprecated" warning must be reported.
super();
}
}
\ No newline at end of file
diff --git a/java/java-tests/testData/inspection/forRemoval/DefaultConstructorInSuper.java b/java/java-tests/testData/inspection/forRemoval/DefaultConstructorInSuper.java
index d754c7b080f9..320a18b1240c 100644
--- a/java/java-tests/testData/inspection/forRemoval/DefaultConstructorInSuper.java
+++ b/java/java-tests/testData/inspection/forRemoval/DefaultConstructorInSuper.java
@@ -2,7 +2,7 @@ class C {
@Deprecated(forRemoval=true) C() { }
}
-class D extends C {
+class D extends C {
}
class P {
diff --git a/jvm/jvm-analysis-impl/src/com/intellij/codeInspection/AnnotatedApiUsageProcessor.java b/jvm/jvm-analysis-impl/src/com/intellij/codeInspection/AnnotatedApiUsageProcessor.java
new file mode 100644
index 000000000000..7c6c108ec09f
--- /dev/null
+++ b/jvm/jvm-analysis-impl/src/com/intellij/codeInspection/AnnotatedApiUsageProcessor.java
@@ -0,0 +1,20 @@
+// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
+package com.intellij.codeInspection;
+
+import com.intellij.psi.PsiAnnotation;
+import com.intellij.psi.PsiModifierListOwner;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.uast.UElement;
+
+import java.util.List;
+
+/**
+ * Processor of usages of APIs marked with specified annotations, which are detected by {@link AnnotatedElementInspectionBase}.
+ */
+public interface AnnotatedApiUsageProcessor {
+ void processAnnotatedTarget(
+ @NotNull UElement sourceNode,
+ @NotNull PsiModifierListOwner annotatedTarget,
+ @NotNull List annotations
+ );
+}
diff --git a/jvm/jvm-analysis-impl/src/com/intellij/codeInspection/AnnotatedElementInspectionBase.java b/jvm/jvm-analysis-impl/src/com/intellij/codeInspection/AnnotatedElementInspectionBase.java
index 9fab49fcb36b..7eba9cb29460 100644
--- a/jvm/jvm-analysis-impl/src/com/intellij/codeInspection/AnnotatedElementInspectionBase.java
+++ b/jvm/jvm-analysis-impl/src/com/intellij/codeInspection/AnnotatedElementInspectionBase.java
@@ -2,6 +2,9 @@
package com.intellij.codeInspection;
import com.intellij.analysis.JvmAnalysisBundle;
+import com.intellij.codeInsight.AnnotationUtil;
+import com.intellij.codeInspection.apiUsage.ApiUsageProcessor;
+import com.intellij.codeInspection.apiUsage.ApiUsageUastVisitor;
import com.intellij.codeInspection.deprecation.DeprecationInspectionBase;
import com.intellij.codeInspection.ui.SingleCheckboxOptionsPanel;
import com.intellij.openapi.application.ApplicationManager;
@@ -11,8 +14,12 @@ import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.util.PsiUtilCore;
+import com.intellij.uast.UastVisitorAdapter;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
+import org.jetbrains.uast.UClass;
+import org.jetbrains.uast.UElement;
+import org.jetbrains.uast.UExpression;
import javax.swing.*;
import java.util.List;
@@ -27,15 +34,6 @@ public abstract class AnnotatedElementInspectionBase extends LocalInspectionTool
@NotNull
protected abstract List getAnnotations();
- protected abstract void createProblem(@NotNull PsiReference reference,
- @NotNull PsiModifierListOwner annotatedTarget,
- @NotNull List annotations,
- @NotNull ProblemsHolder holder);
-
- protected boolean shouldProcessElement(@NotNull PsiModifierListOwner element) {
- return isLibraryElement(element);
- }
-
@NotNull
@Override
public JPanel createOptionsPanel() {
@@ -43,6 +41,9 @@ public abstract class AnnotatedElementInspectionBase extends LocalInspectionTool
JvmAnalysisBundle.message("jvm.inspections.unstable.api.usage.ignore.inside.imports"), this, "myIgnoreInsideImports");
}
+ @NotNull
+ protected abstract AnnotatedApiUsageProcessor buildAnnotatedApiUsageProcessor(@NotNull ProblemsHolder holder);
+
@NotNull
@Override
public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly) {
@@ -50,16 +51,11 @@ public abstract class AnnotatedElementInspectionBase extends LocalInspectionTool
return PsiElementVisitor.EMPTY_VISITOR;
}
- return new AnnotatedElementVisitorBase(myIgnoreInsideImports, getAnnotations()) {
- @Override
- public void processAnnotatedTarget(@NotNull PsiReference reference,
- @NotNull PsiModifierListOwner annotatedTarget,
- @NotNull List annotations) {
- if (AnnotatedElementInspectionBase.this.shouldProcessElement(annotatedTarget)) {
- createProblem(reference, annotatedTarget, annotations, holder);
- }
- }
- };
+ AnnotatedApiUsageProcessor annotatedApiProcessor = buildAnnotatedApiUsageProcessor(holder);
+ AnnotatedApiUsageProcessorBridge processorBridge = new AnnotatedApiUsageProcessorBridge(
+ myIgnoreInsideImports, getAnnotations(), annotatedApiProcessor
+ );
+ return new UastVisitorAdapter(new ApiUsageUastVisitor(processorBridge), true);
}
private boolean isApplicable(@Nullable PsiFile file, @Nullable Project project) {
@@ -82,11 +78,56 @@ public abstract class AnnotatedElementInspectionBase extends LocalInspectionTool
return DeprecationInspectionBase.getPresentableName(psiElement);
}
- private static boolean isLibraryElement(@NotNull PsiElement element) {
+ protected static boolean isLibraryElement(@NotNull PsiElement element) {
if (ApplicationManager.getApplication().isUnitTestMode()) {
return true;
}
VirtualFile containingVirtualFile = PsiUtilCore.getVirtualFile(element);
return containingVirtualFile != null && ProjectFileIndex.getInstance(element.getProject()).isInLibraryClasses(containingVirtualFile);
}
+
+ private static final class AnnotatedApiUsageProcessorBridge implements ApiUsageProcessor {
+ private final boolean myIgnoreInsideImports;
+ private final List myAnnotations;
+ private final AnnotatedApiUsageProcessor myAnnotatedApiProcessor;
+
+ private AnnotatedApiUsageProcessorBridge(boolean ignoreInsideImports,
+ @NotNull List annotations,
+ @NotNull AnnotatedApiUsageProcessor annotatedApiProcessor) {
+ myIgnoreInsideImports = ignoreInsideImports;
+ myAnnotations = annotations;
+ myAnnotatedApiProcessor = annotatedApiProcessor;
+ }
+
+ @Override
+ public void processImportReference(@NotNull UElement sourceNode, @NotNull PsiModifierListOwner target) {
+ if (!myIgnoreInsideImports) {
+ maybeProcessAnnotatedTarget(sourceNode, target);
+ }
+ }
+
+ @Override
+ public void processReference(@NotNull UElement sourceNode, @NotNull PsiModifierListOwner target, @Nullable UExpression qualifier) {
+ maybeProcessAnnotatedTarget(sourceNode, target);
+ }
+
+ @Override
+ public void processConstructorInvocation(@NotNull UElement sourceNode,
+ @NotNull PsiClass instantiatedClass,
+ @Nullable PsiMethod constructor,
+ @Nullable UClass subclassDeclaration) {
+ if (constructor == null || !maybeProcessAnnotatedTarget(sourceNode, constructor)) {
+ maybeProcessAnnotatedTarget(sourceNode, instantiatedClass);
+ }
+ }
+
+ private boolean maybeProcessAnnotatedTarget(@NotNull UElement sourceNode, @NotNull PsiModifierListOwner target) {
+ List annotations = AnnotationUtil.findAllAnnotations(target, myAnnotations, false);
+ if (annotations.isEmpty()) {
+ return false;
+ }
+ myAnnotatedApiProcessor.processAnnotatedTarget(sourceNode, target, annotations);
+ return true;
+ }
+ }
}
\ No newline at end of file
diff --git a/jvm/jvm-analysis-impl/src/com/intellij/codeInspection/AnnotatedElementVisitorBase.java b/jvm/jvm-analysis-impl/src/com/intellij/codeInspection/AnnotatedElementVisitorBase.java
deleted file mode 100644
index e90be2689586..000000000000
--- a/jvm/jvm-analysis-impl/src/com/intellij/codeInspection/AnnotatedElementVisitorBase.java
+++ /dev/null
@@ -1,74 +0,0 @@
-// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
-package com.intellij.codeInspection;
-
-import com.intellij.codeInsight.AnnotationUtil;
-import com.intellij.codeInspection.apiUsage.ApiUsageVisitorBase;
-import com.intellij.psi.*;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-import org.jetbrains.uast.UImportStatement;
-import org.jetbrains.uast.UastContextKt;
-
-import java.util.List;
-
-/**
- * PSI visitor for any language that detects references to API annotated with one of {@code annotations}.
- */
-public abstract class AnnotatedElementVisitorBase extends ApiUsageVisitorBase {
- private final boolean myIgnoreInsideImports;
- private final List myAnnotations;
-
- public AnnotatedElementVisitorBase(boolean ignoreInsideImports, @NotNull List annotations) {
- myIgnoreInsideImports = ignoreInsideImports;
- myAnnotations = annotations;
- }
-
- /**
- * Reference {@code reference} is found in source code, which resolves to {@code annotatedTarget} that has the
- * following {@code annotations}, which are subset of sought-for annotations passed to constructor.
- */
- public abstract void processAnnotatedTarget(@NotNull PsiReference reference,
- @NotNull PsiModifierListOwner annotatedTarget,
- @NotNull List annotations);
-
- @Override
- public boolean shouldProcessReferences(@NotNull PsiElement element) {
- return !myIgnoreInsideImports || !isInsideImportStatement(element);
- }
-
- private static boolean isInsideImportStatement(@NotNull PsiElement element) {
- return UastContextKt.getUastParentOfType(element, UImportStatement.class) != null;
- }
-
- @Override
- public void processReference(@NotNull PsiReference reference) {
- PsiModifierListOwner annotationsOwner = resolveModifierListOwner(reference);
- if (annotationsOwner != null) {
- List annotations = AnnotationUtil.findAllAnnotations(annotationsOwner, myAnnotations, false);
- if (!annotations.isEmpty()) {
- processAnnotatedTarget(reference, annotationsOwner, annotations);
- }
- }
- }
-
- @Override
- public void processConstructorInvocation(@NotNull PsiJavaCodeReferenceElement instantiatedClass, @NotNull PsiMethod constructor) {
- List annotations = AnnotationUtil.findAllAnnotations(constructor, myAnnotations, false);
- if (!annotations.isEmpty()) {
- processAnnotatedTarget(instantiatedClass, constructor, annotations);
- }
- }
-
- @Nullable
- private static PsiModifierListOwner resolveModifierListOwner(@NotNull PsiReference reference) {
- if (reference instanceof ResolvingHint && !((ResolvingHint)reference).canResolveTo(PsiModifierListOwner.class)) {
- return null;
- }
-
- PsiElement resolvedElement = reference.resolve();
- if (resolvedElement instanceof PsiModifierListOwner) {
- return (PsiModifierListOwner)resolvedElement;
- }
- return null;
- }
-}
diff --git a/jvm/jvm-analysis-impl/src/com/intellij/codeInspection/ScheduledForRemovalInspection.java b/jvm/jvm-analysis-impl/src/com/intellij/codeInspection/ScheduledForRemovalInspection.java
index bb28cf1c892d..061ad63fe04e 100644
--- a/jvm/jvm-analysis-impl/src/com/intellij/codeInspection/ScheduledForRemovalInspection.java
+++ b/jvm/jvm-analysis-impl/src/com/intellij/codeInspection/ScheduledForRemovalInspection.java
@@ -4,11 +4,12 @@ package com.intellij.codeInspection;
import com.intellij.analysis.JvmAnalysisBundle;
import com.intellij.codeInsight.AnnotationUtil;
import com.intellij.psi.PsiAnnotation;
+import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiModifierListOwner;
-import com.intellij.psi.PsiReference;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
+import org.jetbrains.uast.UElement;
import java.util.Collections;
import java.util.List;
@@ -24,23 +25,35 @@ public class ScheduledForRemovalInspection extends AnnotatedElementInspectionBas
return Collections.singletonList(ANNOTATION_NAME);
}
+ @NotNull
@Override
- protected void createProblem(@NotNull PsiReference reference,
- @NotNull PsiModifierListOwner annotatedTarget,
- @NotNull List annotations,
- @NotNull ProblemsHolder holder) {
- //TODO determine highlight severity like in MarkedForRemovalInspection?
- PsiAnnotation scheduledForRemoval = ContainerUtil.find(annotations, psiAnnotation -> psiAnnotation.hasQualifiedName(ANNOTATION_NAME));
- if (scheduledForRemoval != null) {
- String inVersion = AnnotationUtil.getDeclaredStringAttributeValue(scheduledForRemoval, "inVersion");
- String targetText = getPresentableText(annotatedTarget);
- String message;
- if (inVersion == null || inVersion.isEmpty()) {
- message = JvmAnalysisBundle.message("jvm.inspections.scheduled.for.removal.description.no.version", targetText);
- } else {
- message = JvmAnalysisBundle.message("jvm.inspections.scheduled.for.removal.description.with.version", targetText, inVersion);
+ protected AnnotatedApiUsageProcessor buildAnnotatedApiUsageProcessor(@NotNull ProblemsHolder holder) {
+ return new AnnotatedApiUsageProcessor() {
+ @Override
+ public void processAnnotatedTarget(@NotNull UElement sourceNode,
+ @NotNull PsiModifierListOwner annotatedTarget,
+ @NotNull List annotations) {
+ if (!AnnotatedElementInspectionBase.isLibraryElement(annotatedTarget)) {
+ return;
+ }
+ //TODO determine highlight severity like in MarkedForRemovalInspection?
+ PsiElement elementToHighlight = sourceNode.getSourcePsi();
+ PsiAnnotation scheduledForRemoval = ContainerUtil.find(
+ annotations, psiAnnotation -> psiAnnotation.hasQualifiedName(ANNOTATION_NAME)
+ );
+ if (elementToHighlight != null && scheduledForRemoval != null) {
+ String inVersion = AnnotationUtil.getDeclaredStringAttributeValue(scheduledForRemoval, "inVersion");
+ String targetText = getPresentableText(annotatedTarget);
+ String message;
+ if (inVersion == null || inVersion.isEmpty()) {
+ message = JvmAnalysisBundle.message("jvm.inspections.scheduled.for.removal.description.no.version", targetText);
+ }
+ else {
+ message = JvmAnalysisBundle.message("jvm.inspections.scheduled.for.removal.description.with.version", targetText, inVersion);
+ }
+ holder.registerProblem(elementToHighlight, message, ProblemHighlightType.LIKE_MARKED_FOR_REMOVAL);
+ }
}
- holder.registerProblem(reference, message, ProblemHighlightType.LIKE_MARKED_FOR_REMOVAL);
- }
+ };
}
}
diff --git a/jvm/jvm-analysis-impl/src/com/intellij/codeInspection/UnstableApiUsageInspection.java b/jvm/jvm-analysis-impl/src/com/intellij/codeInspection/UnstableApiUsageInspection.java
index e3c7091ceed7..054cfadba732 100644
--- a/jvm/jvm-analysis-impl/src/com/intellij/codeInspection/UnstableApiUsageInspection.java
+++ b/jvm/jvm-analysis-impl/src/com/intellij/codeInspection/UnstableApiUsageInspection.java
@@ -4,10 +4,11 @@ package com.intellij.codeInspection;
import com.intellij.analysis.JvmAnalysisBundle;
import com.intellij.codeInspection.util.SpecialAnnotationsUtil;
import com.intellij.psi.PsiAnnotation;
+import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiModifierListOwner;
-import com.intellij.psi.PsiReference;
import com.siyeh.ig.ui.ExternalizableStringSet;
import org.jetbrains.annotations.NotNull;
+import org.jetbrains.uast.UElement;
import javax.swing.*;
import java.awt.*;
@@ -34,13 +35,24 @@ public class UnstableApiUsageInspection extends AnnotatedElementInspectionBase {
return unstableApiAnnotations;
}
+ @NotNull
@Override
- protected void createProblem(@NotNull PsiReference reference,
- @NotNull PsiModifierListOwner annotatedTarget,
- @NotNull List annotations,
- @NotNull ProblemsHolder holder) {
- String message = JvmAnalysisBundle.message("jvm.inspections.unstable.api.usage.description", getPresentableName(annotatedTarget));
- holder.registerProblem(reference, message, ProblemHighlightType.GENERIC_ERROR_OR_WARNING);
+ protected AnnotatedApiUsageProcessor buildAnnotatedApiUsageProcessor(@NotNull ProblemsHolder holder) {
+ return new AnnotatedApiUsageProcessor() {
+ @Override
+ public void processAnnotatedTarget(@NotNull UElement sourceNode,
+ @NotNull PsiModifierListOwner annotatedTarget,
+ @NotNull List annotations) {
+ if (!AnnotatedElementInspectionBase.isLibraryElement(annotatedTarget)) {
+ return;
+ }
+ String message = JvmAnalysisBundle.message("jvm.inspections.unstable.api.usage.description", getPresentableName(annotatedTarget));
+ PsiElement elementToHighlight = sourceNode.getSourcePsi();
+ if (elementToHighlight != null) {
+ holder.registerProblem(elementToHighlight, message, ProblemHighlightType.GENERIC_ERROR_OR_WARNING);
+ }
+ }
+ };
}
@NotNull
diff --git a/jvm/jvm-analysis-java-tests/testData/codeInspection/scheduledForRemoval/ScheduledForRemovalElementsIgnoreImportsTest.java b/jvm/jvm-analysis-java-tests/testData/codeInspection/scheduledForRemoval/ScheduledForRemovalElementsIgnoreImportsTest.java
index f930ad6ec6a9..c2f578822d45 100644
--- a/jvm/jvm-analysis-java-tests/testData/codeInspection/scheduledForRemoval/ScheduledForRemovalElementsIgnoreImportsTest.java
+++ b/jvm/jvm-analysis-java-tests/testData/codeInspection/scheduledForRemoval/ScheduledForRemovalElementsIgnoreImportsTest.java
@@ -35,7 +35,7 @@ public class ScheduledForRemovalElementsIgnoreImportsTest {
s = AnnotatedClass.ANNOTATED_CONSTANT_IN_ANNOTATED_CLASS;
AnnotatedClass.staticAnnotatedMethodInAnnotatedClass();
- AnnotatedClass annotatedClassInstanceViaAnnotatedConstructor = new AnnotatedClass("");
+ AnnotatedClass annotatedClassInstanceViaAnnotatedConstructor = new AnnotatedClass("");
s = annotatedClassInstanceViaAnnotatedConstructor.annotatedFieldInAnnotatedClass;
annotatedClassInstanceViaAnnotatedConstructor.annotatedMethodInAnnotatedClass();
s = ANNOTATED_CONSTANT_IN_ANNOTATED_CLASS;
diff --git a/jvm/jvm-analysis-java-tests/testData/codeInspection/scheduledForRemoval/ScheduledForRemovalElementsTest.java b/jvm/jvm-analysis-java-tests/testData/codeInspection/scheduledForRemoval/ScheduledForRemovalElementsTest.java
index 4c1166828841..c3f1dd6ec717 100644
--- a/jvm/jvm-analysis-java-tests/testData/codeInspection/scheduledForRemoval/ScheduledForRemovalElementsTest.java
+++ b/jvm/jvm-analysis-java-tests/testData/codeInspection/scheduledForRemoval/ScheduledForRemovalElementsTest.java
@@ -35,7 +35,7 @@ public class ScheduledForRemovalElementsTest {
s = AnnotatedClass.ANNOTATED_CONSTANT_IN_ANNOTATED_CLASS;
AnnotatedClass.staticAnnotatedMethodInAnnotatedClass();
- AnnotatedClass annotatedClassInstanceViaAnnotatedConstructor = new AnnotatedClass("");
+ AnnotatedClass annotatedClassInstanceViaAnnotatedConstructor = new AnnotatedClass("");
s = annotatedClassInstanceViaAnnotatedConstructor.annotatedFieldInAnnotatedClass;
annotatedClassInstanceViaAnnotatedConstructor.annotatedMethodInAnnotatedClass();
s = ANNOTATED_CONSTANT_IN_ANNOTATED_CLASS;
diff --git a/jvm/jvm-analysis-java-tests/testData/codeInspection/unstableApiUsage/UnstableElementsIgnoreImportsTest.java b/jvm/jvm-analysis-java-tests/testData/codeInspection/unstableApiUsage/UnstableElementsIgnoreImportsTest.java
index ee0110bd8843..853f2f10f427 100644
--- a/jvm/jvm-analysis-java-tests/testData/codeInspection/unstableApiUsage/UnstableElementsIgnoreImportsTest.java
+++ b/jvm/jvm-analysis-java-tests/testData/codeInspection/unstableApiUsage/UnstableElementsIgnoreImportsTest.java
@@ -35,7 +35,7 @@ public class UnstableElementsIgnoreImportsTest {
s = AnnotatedClass.ANNOTATED_CONSTANT_IN_ANNOTATED_CLASS;
AnnotatedClass.staticAnnotatedMethodInAnnotatedClass();
- AnnotatedClass annotatedClassInstanceViaAnnotatedConstructor = new AnnotatedClass("");
+ AnnotatedClass annotatedClassInstanceViaAnnotatedConstructor = new AnnotatedClass("");
s = annotatedClassInstanceViaAnnotatedConstructor.annotatedFieldInAnnotatedClass;
annotatedClassInstanceViaAnnotatedConstructor.annotatedMethodInAnnotatedClass();
s = ANNOTATED_CONSTANT_IN_ANNOTATED_CLASS;
diff --git a/jvm/jvm-analysis-java-tests/testData/codeInspection/unstableApiUsage/UnstableElementsTest.java b/jvm/jvm-analysis-java-tests/testData/codeInspection/unstableApiUsage/UnstableElementsTest.java
index 68fe87ad8811..7717fe31ad2f 100644
--- a/jvm/jvm-analysis-java-tests/testData/codeInspection/unstableApiUsage/UnstableElementsTest.java
+++ b/jvm/jvm-analysis-java-tests/testData/codeInspection/unstableApiUsage/UnstableElementsTest.java
@@ -35,7 +35,7 @@ public class UnstableElementsTest {
s = AnnotatedClass.ANNOTATED_CONSTANT_IN_ANNOTATED_CLASS;
AnnotatedClass.staticAnnotatedMethodInAnnotatedClass();
- AnnotatedClass annotatedClassInstanceViaAnnotatedConstructor = new AnnotatedClass("");
+ AnnotatedClass annotatedClassInstanceViaAnnotatedConstructor = new AnnotatedClass("");
s = annotatedClassInstanceViaAnnotatedConstructor.annotatedFieldInAnnotatedClass;
annotatedClassInstanceViaAnnotatedConstructor.annotatedMethodInAnnotatedClass();
s = ANNOTATED_CONSTANT_IN_ANNOTATED_CLASS;
diff --git a/jvm/jvm-analysis-kotlin-tests/testData/codeInspection/scheduledForRemoval/ScheduledForRemovalElementsIgnoreImportsTest.kt b/jvm/jvm-analysis-kotlin-tests/testData/codeInspection/scheduledForRemoval/ScheduledForRemovalElementsIgnoreImportsTest.kt
index 4eb9f40c7f12..0e78da694b4e 100644
--- a/jvm/jvm-analysis-kotlin-tests/testData/codeInspection/scheduledForRemoval/ScheduledForRemovalElementsIgnoreImportsTest.kt
+++ b/jvm/jvm-analysis-kotlin-tests/testData/codeInspection/scheduledForRemoval/ScheduledForRemovalElementsIgnoreImportsTest.kt
@@ -27,7 +27,7 @@ class ScheduledForRemovalElementsIgnoreImportsTest {
fun test() {
var s = AnnotatedClass.NON_ANNOTATED_CONSTANT_IN_ANNOTATED_CLASS
AnnotatedClass.staticNonAnnotatedMethodInAnnotatedClass()
- val annotatedClassInstanceViaNonAnnotatedConstructor : AnnotatedClass = AnnotatedClass()
+ val annotatedClassInstanceViaNonAnnotatedConstructor : AnnotatedClass = AnnotatedClass()
s = annotatedClassInstanceViaNonAnnotatedConstructor.nonAnnotatedFieldInAnnotatedClass
annotatedClassInstanceViaNonAnnotatedConstructor.nonAnnotatedMethodInAnnotatedClass()
s = NON_ANNOTATED_CONSTANT_IN_ANNOTATED_CLASS
diff --git a/jvm/jvm-analysis-kotlin-tests/testData/codeInspection/scheduledForRemoval/ScheduledForRemovalElementsTest.kt b/jvm/jvm-analysis-kotlin-tests/testData/codeInspection/scheduledForRemoval/ScheduledForRemovalElementsTest.kt
index a683e322c619..2236a9c1ef21 100644
--- a/jvm/jvm-analysis-kotlin-tests/testData/codeInspection/scheduledForRemoval/ScheduledForRemovalElementsTest.kt
+++ b/jvm/jvm-analysis-kotlin-tests/testData/codeInspection/scheduledForRemoval/ScheduledForRemovalElementsTest.kt
@@ -27,7 +27,7 @@ class ScheduledForRemovalElementsTest {
fun test() {
var s = AnnotatedClass.NON_ANNOTATED_CONSTANT_IN_ANNOTATED_CLASS
AnnotatedClass.staticNonAnnotatedMethodInAnnotatedClass()
- val annotatedClassInstanceViaNonAnnotatedConstructor : AnnotatedClass = AnnotatedClass()
+ val annotatedClassInstanceViaNonAnnotatedConstructor : AnnotatedClass = AnnotatedClass()
s = annotatedClassInstanceViaNonAnnotatedConstructor.nonAnnotatedFieldInAnnotatedClass
annotatedClassInstanceViaNonAnnotatedConstructor.nonAnnotatedMethodInAnnotatedClass()
s = NON_ANNOTATED_CONSTANT_IN_ANNOTATED_CLASS
diff --git a/jvm/jvm-analysis-kotlin-tests/testData/codeInspection/suspiciousPackagePrivateAccess/src/AccessingPackagePrivateMembers.kt b/jvm/jvm-analysis-kotlin-tests/testData/codeInspection/suspiciousPackagePrivateAccess/src/AccessingPackagePrivateMembers.kt
index 2ce2dddbd4b8..0ab132fb5588 100644
--- a/jvm/jvm-analysis-kotlin-tests/testData/codeInspection/suspiciousPackagePrivateAccess/src/AccessingPackagePrivateMembers.kt
+++ b/jvm/jvm-analysis-kotlin-tests/testData/codeInspection/suspiciousPackagePrivateAccess/src/AccessingPackagePrivateMembers.kt
@@ -8,17 +8,17 @@ import xxx.StaticMembers.*
*/
@Suppress("UNUSED_VARIABLE")
class AccessingPackagePrivateMembers {
- private val property = PackagePrivateClass()
+ private val property = PackagePrivateClass()
fun main() {
- PackagePrivateClass()
+ PackagePrivateClass()
var variable: PackagePrivateClass
val sam = PackagePrivateInterface { "" }
val aClass: PublicClass = PublicClass(1);
val aClass2: PublicClassWithDefaultConstructor = PublicClassWithDefaultConstructor();
- PublicClass()
- PublicClass(true)
+ PublicClass()
+ PublicClass(true)
System.out.println(aClass.publicField)
System.out.println(aClass.packagePrivateField)
@@ -26,17 +26,17 @@ class AccessingPackagePrivateMembers {
System.out.println(PublicClass.PACKAGE_PRIVATE_STATIC_FIELD)
aClass.publicMethod()
- aClass.packagePrivateMethod()
+ aClass.packagePrivateMethod()
System.out.println(IMPORTED_FIELD)
importedMethod()
- InnerClasses.PackagePrivateInnerClass()
- InnerClasses.PackagePrivateInnerClassWithConstructor()
- InnerClasses.ClassWithPackagePrivateConstructor()
+ InnerClasses.PackagePrivateInnerClass()
+ InnerClasses.PackagePrivateInnerClassWithConstructor()
+ InnerClasses.ClassWithPackagePrivateConstructor()
}
companion object {
- private val staticProperty = PackagePrivateClass()
+ private val staticProperty = PackagePrivateClass()
}
}
\ No newline at end of file
diff --git a/jvm/jvm-analysis-kotlin-tests/testData/codeInspection/suspiciousPackagePrivateAccess/src/AccessingProtectedMembers.kt b/jvm/jvm-analysis-kotlin-tests/testData/codeInspection/suspiciousPackagePrivateAccess/src/AccessingProtectedMembers.kt
index e910cc31e1cc..5fb0bcf48d25 100644
--- a/jvm/jvm-analysis-kotlin-tests/testData/codeInspection/suspiciousPackagePrivateAccess/src/AccessingProtectedMembers.kt
+++ b/jvm/jvm-analysis-kotlin-tests/testData/codeInspection/suspiciousPackagePrivateAccess/src/AccessingProtectedMembers.kt
@@ -3,10 +3,10 @@ package xxx
class AccessingProtectedMembersNotFromSubclass {
fun foo() {
val aClass: ProtectedMembers = ProtectedMembers()
- aClass.method()
- ProtectedMembers.staticMethod()
- ProtectedConstructors()
- ProtectedConstructors(1)
+ aClass.method()
+ ProtectedMembers.staticMethod()
+ ProtectedConstructors()
+ ProtectedConstructors(1)
}
}
@@ -18,7 +18,7 @@ class AccessingProtectedMembersFromSubclass : ProtectedMembers() {
ProtectedMembers.staticMethod()
val aClass = ProtectedMembers()
- aClass.method()
+ aClass.method()
val myInstance = AccessingProtectedMembersFromSubclass()
myInstance.method()
diff --git a/jvm/jvm-analysis-kotlin-tests/testData/codeInspection/unstableApiUsage/UnstableElementsIgnoreImportsTest.kt b/jvm/jvm-analysis-kotlin-tests/testData/codeInspection/unstableApiUsage/UnstableElementsIgnoreImportsTest.kt
index b93fc7e1f0a3..197d59f7ff88 100644
--- a/jvm/jvm-analysis-kotlin-tests/testData/codeInspection/unstableApiUsage/UnstableElementsIgnoreImportsTest.kt
+++ b/jvm/jvm-analysis-kotlin-tests/testData/codeInspection/unstableApiUsage/UnstableElementsIgnoreImportsTest.kt
@@ -27,7 +27,7 @@ class UnstableElementsIgnoreImportsTest {
fun test() {
var s = AnnotatedClass.NON_ANNOTATED_CONSTANT_IN_ANNOTATED_CLASS
AnnotatedClass.staticNonAnnotatedMethodInAnnotatedClass()
- val annotatedClassInstanceViaNonAnnotatedConstructor : AnnotatedClass = AnnotatedClass()
+ val annotatedClassInstanceViaNonAnnotatedConstructor : AnnotatedClass = AnnotatedClass()
s = annotatedClassInstanceViaNonAnnotatedConstructor.nonAnnotatedFieldInAnnotatedClass
annotatedClassInstanceViaNonAnnotatedConstructor.nonAnnotatedMethodInAnnotatedClass()
s = NON_ANNOTATED_CONSTANT_IN_ANNOTATED_CLASS
diff --git a/jvm/jvm-analysis-kotlin-tests/testData/codeInspection/unstableApiUsage/UnstableElementsTest.kt b/jvm/jvm-analysis-kotlin-tests/testData/codeInspection/unstableApiUsage/UnstableElementsTest.kt
index 8b35661b6252..4d76dd9ee079 100644
--- a/jvm/jvm-analysis-kotlin-tests/testData/codeInspection/unstableApiUsage/UnstableElementsTest.kt
+++ b/jvm/jvm-analysis-kotlin-tests/testData/codeInspection/unstableApiUsage/UnstableElementsTest.kt
@@ -27,7 +27,7 @@ class UnstableElementsTest {
fun test() {
var s = AnnotatedClass.NON_ANNOTATED_CONSTANT_IN_ANNOTATED_CLASS
AnnotatedClass.staticNonAnnotatedMethodInAnnotatedClass()
- val annotatedClassInstanceViaNonAnnotatedConstructor : AnnotatedClass = AnnotatedClass()
+ val annotatedClassInstanceViaNonAnnotatedConstructor : AnnotatedClass = AnnotatedClass()
s = annotatedClassInstanceViaNonAnnotatedConstructor.nonAnnotatedFieldInAnnotatedClass
annotatedClassInstanceViaNonAnnotatedConstructor.nonAnnotatedMethodInAnnotatedClass()
s = NON_ANNOTATED_CONSTANT_IN_ANNOTATED_CLASS
diff --git a/plugins/InspectionGadgets/src/com/siyeh/ig/dependency/SuspiciousPackagePrivateAccessInspection.java b/plugins/InspectionGadgets/src/com/siyeh/ig/dependency/SuspiciousPackagePrivateAccessInspection.java
index eb2d24de5a7e..1fc9b8db25b5 100644
--- a/plugins/InspectionGadgets/src/com/siyeh/ig/dependency/SuspiciousPackagePrivateAccessInspection.java
+++ b/plugins/InspectionGadgets/src/com/siyeh/ig/dependency/SuspiciousPackagePrivateAccessInspection.java
@@ -3,6 +3,8 @@ package com.siyeh.ig.dependency;
import com.intellij.codeInsight.intention.IntentionAction;
import com.intellij.codeInspection.*;
+import com.intellij.codeInspection.apiUsage.ApiUsageProcessor;
+import com.intellij.codeInspection.apiUsage.ApiUsageUastVisitor;
import com.intellij.codeInspection.ex.InspectionProfileImpl;
import com.intellij.lang.jvm.JvmModifier;
import com.intellij.lang.jvm.actions.JvmElementActionFactories;
@@ -25,6 +27,7 @@ import com.intellij.ui.components.JBLabel;
import com.intellij.ui.components.JBScrollPane;
import com.intellij.ui.components.JBTextArea;
import com.intellij.util.ArrayUtil;
+import com.intellij.util.ObjectUtils;
import com.intellij.util.xmlb.annotations.Property;
import com.intellij.util.xmlb.annotations.Tag;
import com.intellij.util.xmlb.annotations.XCollection;
@@ -34,7 +37,6 @@ import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.uast.*;
-import org.jetbrains.uast.visitor.AbstractUastNonRecursiveVisitor;
import javax.swing.*;
import javax.swing.event.DocumentEvent;
@@ -60,150 +62,98 @@ public class SuspiciousPackagePrivateAccessInspection extends AbstractBaseUastLo
@NotNull
@Override
public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly) {
- return new UastVisitorAdapter(new AbstractUastNonRecursiveVisitor() {
- @Override
- public boolean visitQualifiedReferenceExpression(@NotNull UQualifiedReferenceExpression node) {
- UExpression receiver = node.getReceiver();
- if (node.getSourcePsi() instanceof PsiMethodCallExpression) {
- //JavaUastLanguagePlugin produces UQualifiedReferenceExpression for the both PsiMethodCallExpression and PsiReferenceExpression inside it, so we need to ignore them
- return true;
- }
- PsiElement resolved = node.resolve();
- if (resolved instanceof PsiMember) {
- checkAccess(node.getSelector(), (PsiMember)resolved, getAccessObjectType(receiver));
- }
- return true;
- }
-
- @Override
- public boolean visitSimpleNameReferenceExpression(@NotNull USimpleNameReferenceExpression node) {
- UElement uastParent = node.getUastParent();
- //we should skip 'checkAccess' here if node is part of UQualifiedReferenceExpression or UCallExpression node,
- // otherwise the same problem will be reported twice
- if (!isSelectorOfQualifiedReference(node) && !isReferenceToConstructorOrQualifiedMethodReference(node, uastParent)) {
- PsiElement resolved = node.resolve();
- if (resolved instanceof PsiMember) {
- checkAccess(node, (PsiMember)resolved, null);
- }
- }
- return true;
- }
-
- private boolean isReferenceToConstructorOrQualifiedMethodReference(@NotNull USimpleNameReferenceExpression node,
- @Nullable UElement uastParent) {
- return uastParent instanceof UCallExpression && isMethodReferenceOfCallExpression(node, (UCallExpression)uastParent)
- && (((UCallExpression)uastParent).getKind() == UastCallKind.CONSTRUCTOR_CALL || isSelectorOfQualifiedReference((UExpression)uastParent));
- }
-
- private boolean isSelectorOfQualifiedReference(@Nullable UExpression expression) {
- if (expression == null) return false;
- UElement parent = expression.getUastParent();
- return parent instanceof UQualifiedReferenceExpression
- && referToSameSourceElement(expression, ((UQualifiedReferenceExpression)parent).getSelector());
- }
-
- private boolean isMethodReferenceOfCallExpression(@NotNull USimpleNameReferenceExpression expression, @NotNull UCallExpression parent) {
- UElement methodIdentifier = parent.getMethodIdentifier();
- UReferenceExpression classReference = parent.getClassReference();
- if (methodIdentifier == null && classReference != null) {
- methodIdentifier = classReference.getReferenceNameElement();
- }
- return referToSameSourceElement(expression.getReferenceNameElement(), methodIdentifier);
- }
-
- private boolean referToSameSourceElement(@Nullable UElement element1, @Nullable UElement element2) {
- if (element1 == null || element2 == null) return false;
- PsiElement sourcePsi1 = element1.getSourcePsi();
- return sourcePsi1 != null && sourcePsi1.equals(element2.getSourcePsi());
- }
-
- @Override
- public boolean visitCallableReferenceExpression(@NotNull UCallableReferenceExpression node) {
- PsiElement resolve = node.resolve();
- if (resolve instanceof PsiMember) {
- PsiMember member = (PsiMember)resolve;
- UElement sourceNode = getReferenceNameElement(node);
- if (sourceNode != null) {
- checkAccess(sourceNode, member, getAccessObjectType(node.getQualifierExpression()));
- }
- }
- return true;
- }
-
- @Override
- public boolean visitCallExpression(@NotNull UCallExpression node) {
- //regular method calls are handled by visitSimpleNameReferenceExpression or visitQualifiedReferenceExpression, but we need to handle
- // constructor calls in a special way because they may refer to classes
- if (!isSelectorOfQualifiedReference(node) && node.getKind() == UastCallKind.CONSTRUCTOR_CALL) {
- PsiMethod resolved = node.resolve();
- if (resolved != null) {
- checkAccess(node, resolved, null);
- }
- else {
- UReferenceExpression classReference = node.getClassReference();
- PsiElement resolvedClass = classReference != null ? classReference.resolve() : null;
- if (resolvedClass instanceof PsiClass) {
- checkAccess(node, (PsiClass)resolvedClass, null);
- }
- }
- }
- return true;
- }
-
- private void checkAccess(@NotNull UElement sourceNode, @NotNull PsiMember target, @Nullable PsiClass accessObjectType) {
- if (target.hasModifier(JvmModifier.PACKAGE_LOCAL)) {
- checkPackageLocalAccess(sourceNode, target, "package-private");
- }
- else if (target.hasModifier(JvmModifier.PROTECTED) && !canAccessProtectedMember(sourceNode, target, accessObjectType)) {
- checkPackageLocalAccess(sourceNode, target, "protected and used not through a subclass here");
- }
- }
-
- private void checkPackageLocalAccess(@NotNull UElement sourceNode, PsiMember targetElement, final String accessType) {
- PsiElement sourcePsi = sourceNode.getSourcePsi();
- if (sourcePsi != null) {
- Module targetModule = ModuleUtilCore.findModuleForPsiElement(targetElement);
- Module sourceModule = ModuleUtilCore.findModuleForPsiElement(sourcePsi);
- if (isPackageLocalAccessSuspicious(sourceModule, targetModule) && PsiTreeUtil.getParentOfType(sourcePsi, PsiComment.class) == null) {
- List fixes =
- JvmElementActionFactories.createModifierActions(targetElement, MemberRequestsKt.modifierRequest(JvmModifier.PUBLIC, true));
- String elementDescription = StringUtil.removeHtmlTags(StringUtil.capitalize(RefactoringUIUtil.getDescription(targetElement, true)));
- LocalQuickFix[] quickFixes = IntentionWrapper.wrapToQuickFixes(fixes.toArray(IntentionAction.EMPTY_ARRAY), targetElement.getContainingFile());
- holder.registerProblem(sourcePsi, elementDescription + " is " + accessType + ", but declared in a different module '"
- + targetModule.getName() + "'",
- ArrayUtil.append(quickFixes, new MarkModulesAsLoadedTogetherFix(sourceModule.getName(), targetModule.getName())));
- }
- }
- }
- }, true);
+ return new UastVisitorAdapter(new ApiUsageUastVisitor(
+ new SuspiciousApiUsageProcessor(holder, myModuleSetByModuleName.getValue())
+ ), true);
}
- @Nullable
- private static UElement getReferenceNameElement(UCallableReferenceExpression node) {
- PsiElement psi = node.getSourcePsi();
- if (psi instanceof PsiReferenceExpression) {
- PsiElement nameElement = ((PsiReferenceExpression)psi).getReferenceNameElement();
- if (nameElement != null) {
- return UastContextKt.toUElement(nameElement);
+ private static class SuspiciousApiUsageProcessor implements ApiUsageProcessor {
+
+ private final ProblemsHolder myProblemsHolder;
+ private final Map myModuleNameToModulesSet;
+
+ private SuspiciousApiUsageProcessor(ProblemsHolder problemsHolder, Map moduleNameToModulesSet) {
+ myProblemsHolder = problemsHolder;
+ myModuleNameToModulesSet = moduleNameToModulesSet;
+ }
+
+ @Override
+ public void processReference(@NotNull UElement sourceNode, @NotNull PsiModifierListOwner target, @Nullable UExpression qualifier) {
+ PsiClass accessObjectType = getAccessObjectType(qualifier);
+ if (target instanceof PsiMember) {
+ checkAccess(sourceNode, (PsiMember)target, accessObjectType);
}
}
- return node;
- }
- @Nullable
- private static PsiClass getAccessObjectType(@Nullable UExpression receiver) {
- if (receiver == null || receiver instanceof UThisExpression || receiver instanceof USuperExpression) {
+ @Override
+ public void processConstructorInvocation(@NotNull UElement sourceNode,
+ @NotNull PsiClass instantiatedClass,
+ @Nullable PsiMethod constructor,
+ @Nullable UClass subclassDeclaration) {
+ if (subclassDeclaration == null) {
+ if (constructor != null) {
+ checkAccess(sourceNode, constructor, null);
+ }
+ else {
+ checkAccess(sourceNode, instantiatedClass, null);
+ }
+ }
+ }
+
+ @Nullable
+ private static PsiClass getAccessObjectType(@Nullable UExpression qualifier) {
+ if (qualifier == null || qualifier instanceof UThisExpression || qualifier instanceof USuperExpression) {
+ return null;
+ }
+
+ PsiType type = qualifier.getExpressionType();
+ if (type instanceof PsiClassType) {
+ return ((PsiClassType)type).resolve();
+ }
+ if (qualifier instanceof UReferenceExpression) {
+ return ObjectUtils.tryCast(((UReferenceExpression)qualifier).resolve(), PsiClass.class);
+ }
return null;
}
- PsiType type = receiver.getExpressionType();
- if (type != null) {
- if (!(type instanceof PsiClassType)) return null;
- return ((PsiClassType)type).resolve();
+ private void checkAccess(@NotNull UElement sourceNode, @NotNull PsiMember target, @Nullable PsiClass accessObjectType) {
+ if (target.hasModifier(JvmModifier.PACKAGE_LOCAL)) {
+ checkPackageLocalAccess(sourceNode, target, "package-private");
+ }
+ else if (target.hasModifier(JvmModifier.PROTECTED) && !canAccessProtectedMember(sourceNode, target, accessObjectType)) {
+ checkPackageLocalAccess(sourceNode, target, "protected and used not through a subclass here");
+ }
}
- else {
- PsiElement element = ((UReferenceExpression)receiver).resolve();
- return element instanceof PsiClass ? (PsiClass)element : null;
+
+ private void checkPackageLocalAccess(@NotNull UElement sourceNode, PsiMember targetElement, final String accessType) {
+ PsiElement sourcePsi = sourceNode.getSourcePsi();
+ if (sourcePsi != null) {
+ Module targetModule = ModuleUtilCore.findModuleForPsiElement(targetElement);
+ Module sourceModule = ModuleUtilCore.findModuleForPsiElement(sourcePsi);
+ if (isPackageLocalAccessSuspicious(sourceModule, targetModule) &&
+ PsiTreeUtil.getParentOfType(sourcePsi, PsiComment.class) == null) {
+ List fixes =
+ JvmElementActionFactories.createModifierActions(targetElement, MemberRequestsKt.modifierRequest(JvmModifier.PUBLIC, true));
+ String elementDescription =
+ StringUtil.removeHtmlTags(StringUtil.capitalize(RefactoringUIUtil.getDescription(targetElement, true)));
+ LocalQuickFix[] quickFixes =
+ IntentionWrapper.wrapToQuickFixes(fixes.toArray(IntentionAction.EMPTY_ARRAY), targetElement.getContainingFile());
+ myProblemsHolder.registerProblem(sourcePsi, elementDescription + " is " + accessType + ", but declared in a different module '"
+ + targetModule.getName() + "'",
+ ArrayUtil.append(quickFixes,
+ new MarkModulesAsLoadedTogetherFix(sourceModule.getName(),
+ targetModule.getName())));
+ }
+ }
+ }
+
+ private boolean isPackageLocalAccessSuspicious(Module sourceModule, Module targetModule) {
+ if (targetModule == null || sourceModule == null || targetModule.equals(sourceModule)) {
+ return false;
+ }
+ ModulesSet sourceGroup = myModuleNameToModulesSet.get(sourceModule.getName());
+ ModulesSet targetGroup = myModuleNameToModulesSet.get(targetModule.getName());
+ return sourceGroup == null || sourceGroup != targetGroup;
}
}
@@ -250,15 +200,6 @@ public class SuspiciousPackagePrivateAccessInspection extends AbstractBaseUastLo
return UastUtils.findContaining(sourcePsi, UClass.class);
}
- private boolean isPackageLocalAccessSuspicious(Module sourceModule, Module targetModule) {
- if (targetModule == null || sourceModule == null || targetModule.equals(sourceModule)) {
- return false;
- }
- ModulesSet sourceGroup = myModuleSetByModuleName.getValue().get(sourceModule.getName());
- ModulesSet targetGroup = myModuleSetByModuleName.getValue().get(targetModule.getName());
- return sourceGroup == null || sourceGroup != targetGroup;
- }
-
@Tag("modules-set")
public static class ModulesSet {
@XCollection(elementName = "module", valueAttributeName = "name")
diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/dependency/suspicious_package_private_access/src/AccessingPackagePrivateMembers.java b/plugins/InspectionGadgets/test/com/siyeh/igtest/dependency/suspicious_package_private_access/src/AccessingPackagePrivateMembers.java
index 5c609bca49dd..5fc7666648df 100644
--- a/plugins/InspectionGadgets/test/com/siyeh/igtest/dependency/suspicious_package_private_access/src/AccessingPackagePrivateMembers.java
+++ b/plugins/InspectionGadgets/test/com/siyeh/igtest/dependency/suspicious_package_private_access/src/AccessingPackagePrivateMembers.java
@@ -7,23 +7,23 @@ import static xxx.StaticMembers.*;
* @see PublicClass#packagePrivateField
*/
public class AccessingPackagePrivateMembers {
- static Object staticField = new PackagePrivateClass();
- Object field = new PackagePrivateClass();
+ static Object staticField = new PackagePrivateClass();
+ Object field = new PackagePrivateClass();
{
- new PackagePrivateClass();
+ new PackagePrivateClass();
}
static {
- new PackagePrivateClass();
+ new PackagePrivateClass();
}
public void main() {
- new PackagePrivateClass();
+ new PackagePrivateClass();
PackagePrivateClass variable;
PublicClass aClass = new PublicClass(1);
PublicClassWithDefaultConstructor aClass2 = new PublicClassWithDefaultConstructor();
- new PublicClass();
- new PublicClass(true);
+ new PublicClass();
+ new PublicClass(true);
System.out.println(aClass.publicField);
System.out.println(aClass.packagePrivateField);
@@ -37,8 +37,8 @@ public class AccessingPackagePrivateMembers {
System.out.println(IMPORTED_FIELD);
importedMethod();
- new InnerClasses.PackagePrivateInnerClass();
- new InnerClasses.PackagePrivateInnerClassWithConstructor();
- new InnerClasses.ClassWithPackagePrivateConstructor();
+ new InnerClasses.PackagePrivateInnerClass();
+ new InnerClasses.PackagePrivateInnerClassWithConstructor();
+ new InnerClasses.ClassWithPackagePrivateConstructor();
}
}
\ No newline at end of file
diff --git a/plugins/InspectionGadgets/test/com/siyeh/igtest/dependency/suspicious_package_private_access/src/AccessingProtectedMembers.java b/plugins/InspectionGadgets/test/com/siyeh/igtest/dependency/suspicious_package_private_access/src/AccessingProtectedMembers.java
index 1950836066f6..db8d6a6863aa 100644
--- a/plugins/InspectionGadgets/test/com/siyeh/igtest/dependency/suspicious_package_private_access/src/AccessingProtectedMembers.java
+++ b/plugins/InspectionGadgets/test/com/siyeh/igtest/dependency/suspicious_package_private_access/src/AccessingProtectedMembers.java
@@ -5,8 +5,8 @@ class AccessingProtectedMembersNotFromSubclass {
ProtectedMembers aClass = new ProtectedMembers();
aClass.method();
ProtectedMembers.staticMethod();
- new ProtectedConstructors();
- new ProtectedConstructors(1);
+ new ProtectedConstructors();
+ new ProtectedConstructors(1);
new ProtectedConstructors() {};
new ProtectedConstructors(1) {};
}
diff --git a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/dependency/SuspiciousPackagePrivateAccessInspectionTestCase.java b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/dependency/SuspiciousPackagePrivateAccessInspectionTestCase.java
index 53b9164c761c..f2babd23d719 100644
--- a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/dependency/SuspiciousPackagePrivateAccessInspectionTestCase.java
+++ b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/dependency/SuspiciousPackagePrivateAccessInspectionTestCase.java
@@ -20,7 +20,7 @@ import org.jetbrains.jps.model.java.JavaSourceRootType;
import java.io.File;
import java.io.IOException;
-public class SuspiciousPackagePrivateAccessInspectionTestCase extends LightInspectionTestCase {
+public abstract class SuspiciousPackagePrivateAccessInspectionTestCase extends LightInspectionTestCase {
private final ProjectWithDepModuleDescriptor myProjectDescriptor = new ProjectWithDepModuleDescriptor(LanguageLevel.HIGHEST);
private final String myExtension;
diff --git a/plugins/devkit/devkit-core/src/inspections/missingApi/MissingRecentApiInspection.kt b/plugins/devkit/devkit-core/src/inspections/missingApi/MissingRecentApiInspection.kt
index e8d7952be0b0..b08980fd3837 100644
--- a/plugins/devkit/devkit-core/src/inspections/missingApi/MissingRecentApiInspection.kt
+++ b/plugins/devkit/devkit-core/src/inspections/missingApi/MissingRecentApiInspection.kt
@@ -5,11 +5,13 @@ import com.intellij.codeInspection.InspectionProfileEntry
import com.intellij.codeInspection.LocalInspectionTool
import com.intellij.codeInspection.ProblemHighlightType
import com.intellij.codeInspection.ProblemsHolder
+import com.intellij.codeInspection.apiUsage.ApiUsageUastVisitor
import com.intellij.openapi.module.Module
import com.intellij.openapi.module.ModuleUtil
import com.intellij.openapi.util.BuildNumber
import com.intellij.psi.PsiElementVisitor
import com.intellij.psi.xml.XmlFile
+import com.intellij.uast.UastVisitorAdapter
import com.intellij.ui.components.JBLabel
import com.intellij.util.ui.FormBuilder
import org.jetbrains.idea.devkit.actions.DevkitActionsUtil
@@ -70,7 +72,12 @@ class MissingRecentApiInspection : LocalInspectionTool() {
if (targetedSinceUntilRanges.isEmpty()) {
return PsiElementVisitor.EMPTY_VISITOR
}
- return MissingRecentApiVisitor(holder, ProblemHighlightType.GENERIC_ERROR_OR_WARNING, targetedSinceUntilRanges)
+ return UastVisitorAdapter(
+ ApiUsageUastVisitor(
+ MissingRecentApiUsageProcessor(holder, ProblemHighlightType.GENERIC_ERROR_OR_WARNING, targetedSinceUntilRanges)
+ ),
+ true
+ )
}
override fun createOptionsPanel(): JComponent {
diff --git a/plugins/devkit/devkit-core/src/inspections/missingApi/MissingRecentApiUsageProcessor.kt b/plugins/devkit/devkit-core/src/inspections/missingApi/MissingRecentApiUsageProcessor.kt
new file mode 100644
index 000000000000..6754a8066194
--- /dev/null
+++ b/plugins/devkit/devkit-core/src/inspections/missingApi/MissingRecentApiUsageProcessor.kt
@@ -0,0 +1,139 @@
+// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
+package org.jetbrains.idea.devkit.inspections.missingApi
+
+import com.intellij.codeInsight.AnnotationUtil
+import com.intellij.codeInsight.ExternalAnnotationsManager
+import com.intellij.codeInsight.daemon.impl.analysis.HighlightMessageUtil
+import com.intellij.codeInspection.ProblemHighlightType
+import com.intellij.codeInspection.ProblemsHolder
+import com.intellij.codeInspection.apiUsage.ApiUsageProcessor
+import com.intellij.openapi.util.BuildNumber
+import com.intellij.psi.*
+import com.intellij.psi.util.PsiUtil
+import org.jetbrains.annotations.ApiStatus
+import org.jetbrains.idea.devkit.DevKitBundle
+import org.jetbrains.uast.*
+
+/**
+ * PSI visitor containing implementation of [MissingRecentApiInspection],
+ * which reports usages of APIs that are not available in old IDE builds matching the
+ * "since" constraint of the plugin.
+ */
+class MissingRecentApiUsageProcessor(
+ private val holder: ProblemsHolder,
+ private val highlightType: ProblemHighlightType,
+ private val targetedSinceUntilRanges: List
+) : ApiUsageProcessor {
+
+ companion object {
+ val AVAILABLE_SINCE_ANNOTATION: String = ApiStatus.AvailableSince::class.java.canonicalName
+ }
+
+ override fun processReference(sourceNode: UElement, target: PsiModifierListOwner, qualifier: UExpression?) {
+ val elementToHighlight = sourceNode.sourcePsi ?: return
+ checkApiIsRecent(target, elementToHighlight)
+ }
+
+ override fun processConstructorInvocation(
+ sourceNode: UElement,
+ instantiatedClass: PsiClass,
+ constructor: PsiMethod?,
+ subclassDeclaration: UClass?
+ ) {
+ val elementToHighlight = sourceNode.sourcePsi ?: return
+ if (constructor != null) {
+ checkApiIsRecent(constructor, elementToHighlight)
+ }
+ else {
+ val availableSince = findEmptyConstructorAnnotations(instantiatedClass)?.getAvailableSinceBuildNumber() ?: return
+ val brokenRanges = targetedSinceUntilRanges.filter { it.someBuildsAreNotCovered(availableSince) }
+ if (brokenRanges.isNotEmpty()) {
+ registerDefaultConstructorProblem(instantiatedClass, elementToHighlight, availableSince, brokenRanges)
+ }
+ }
+ }
+
+ override fun processMethodOverriding(method: UMethod, overriddenMethod: PsiMethod) {
+ val availableSince = overriddenMethod.getApiSinceBuildNumber() ?: return
+ val brokenRanges = targetedSinceUntilRanges.filter { it.someBuildsAreNotCovered(availableSince) }
+ if (brokenRanges.isNotEmpty()) {
+ val aClass = overriddenMethod.containingClass ?: return
+ val methodNameElement = method.uastAnchor.sourcePsiElement ?: return
+ val description = DevKitBundle.message(
+ "inspections.api.overrides.method.available.only.since",
+ aClass.getPresentableName(),
+ availableSince.asString(),
+ brokenRanges.joinToString { it.asString() }
+ )
+ holder.registerProblem(methodNameElement, description, highlightType)
+ }
+ }
+
+ private fun registerDefaultConstructorProblem(
+ constructorOwner: PsiClass,
+ elementToHighlight: PsiElement,
+ apiSinceBuildNumber: BuildNumber,
+ brokenRanges: List
+ ) {
+ val description = DevKitBundle.message(
+ "inspections.api.constructor.only.since",
+ constructorOwner.qualifiedName,
+ apiSinceBuildNumber.asString(),
+ brokenRanges.joinToString { it.asString() }
+ )
+ holder.registerProblem(elementToHighlight, description, highlightType)
+ }
+
+ private fun findEmptyConstructorAnnotations(psiClass: PsiClass): List? {
+ // Default constructor of a class, which is not represented in PSI, can be externally annotated (IDEA-200832).
+ return ExternalAnnotationsManager.getInstance(psiClass.project)
+ .findDefaultConstructorExternalAnnotations(psiClass, AVAILABLE_SINCE_ANNOTATION)
+ }
+
+ private fun checkApiIsRecent(modifierListOwner: PsiModifierListOwner, elementToHighlight: PsiElement) {
+ val presentableName = modifierListOwner.getPresentableName()
+ val availableSince = modifierListOwner.getApiSinceBuildNumber() ?: return
+ val brokenRanges = targetedSinceUntilRanges.filter { it.someBuildsAreNotCovered(availableSince) }
+ if (brokenRanges.isNotEmpty()) {
+ val description = DevKitBundle.message(
+ "inspections.api.available.only.since",
+ presentableName,
+ availableSince.asString(),
+ brokenRanges.joinToString { it.asString() }
+ )
+ holder.registerProblem(elementToHighlight, description, highlightType)
+ }
+ }
+
+ /**
+ * Returns the first build number when `this` API element was added.
+ */
+ private fun PsiModifierListOwner.getApiSinceBuildNumber(): BuildNumber? {
+ val externalAnnotations = AnnotationUtil.findAllAnnotations(this, listOf(AVAILABLE_SINCE_ANNOTATION), false)
+ if (externalAnnotations.isEmpty()) return null
+ return externalAnnotations.getAvailableSinceBuildNumber()
+ }
+
+ private fun List.getAvailableSinceBuildNumber(): BuildNumber? =
+ asSequence()
+ .mapNotNull { annotation ->
+ AnnotationUtil.getDeclaredStringAttributeValue(annotation, "value")?.let {
+ BuildNumber.fromStringOrNull(it)
+ }
+ }
+ .min()
+
+ private fun SinceUntilRange.someBuildsAreNotCovered(apiSinceBuildNumber: BuildNumber) =
+ sinceBuild == null || sinceBuild < apiSinceBuildNumber
+
+ private fun PsiElement.getPresentableName(): String? {
+ //Annotation attribute methods don't have parameters.
+ return if (this is PsiMethod && PsiUtil.isAnnotationMethod(this)) {
+ name
+ }
+ else {
+ HighlightMessageUtil.getSymbolName(this)
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/plugins/devkit/devkit-core/src/inspections/missingApi/MissingRecentApiVisitor.kt b/plugins/devkit/devkit-core/src/inspections/missingApi/MissingRecentApiVisitor.kt
deleted file mode 100644
index 5428d812ea93..000000000000
--- a/plugins/devkit/devkit-core/src/inspections/missingApi/MissingRecentApiVisitor.kt
+++ /dev/null
@@ -1,202 +0,0 @@
-// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
-package org.jetbrains.idea.devkit.inspections.missingApi
-
-import com.intellij.codeInsight.AnnotationUtil
-import com.intellij.codeInsight.ExternalAnnotationsManager
-import com.intellij.codeInsight.daemon.impl.analysis.HighlightMessageUtil
-import com.intellij.codeInspection.ProblemHighlightType
-import com.intellij.codeInspection.ProblemsHolder
-import com.intellij.codeInspection.apiUsage.ApiUsageVisitorBase
-import com.intellij.openapi.util.BuildNumber
-import com.intellij.psi.*
-import com.intellij.psi.util.PsiUtil
-import org.jetbrains.annotations.ApiStatus
-import org.jetbrains.idea.devkit.DevKitBundle
-import org.jetbrains.uast.UImportStatement
-import org.jetbrains.uast.getUastParentOfType
-
-/**
- * PSI visitor containing implementation of [MissingRecentApiInspection],
- * which reports usages of APIs that are not available in old IDE builds matching the
- * "since" constraint of the plugin.
- */
-class MissingRecentApiVisitor(
- private val holder: ProblemsHolder,
- private val highlightType: ProblemHighlightType,
- private val targetedSinceUntilRanges: List
-) : ApiUsageVisitorBase() {
-
- companion object {
- val AVAILABLE_SINCE_ANNOTATION: String = ApiStatus.AvailableSince::class.java.canonicalName
- }
-
- override fun shouldProcessReferences(element: PsiElement) = !element.isInsideImportStatement()
-
- private fun PsiElement.isInsideImportStatement() = getUastParentOfType() != null
-
- override fun processReference(reference: PsiReference) {
- if (reference is ResolvingHint && !(reference as ResolvingHint).canResolveTo(PsiModifierListOwner::class.java)) {
- return
- }
- val resolved = reference.resolve()
- if (resolved != null) {
- val elementToHighlight = getElementToHighlight(reference)
- checkMissingApi(resolved, elementToHighlight)
- }
- }
-
- private fun getElementToHighlight(reference: PsiReference): PsiElement {
- if (reference is PsiJavaCodeReferenceElement) {
- val referenceNameElement = reference.referenceNameElement
- if (referenceNameElement != null) {
- return referenceNameElement
- }
- }
- return reference.element
- }
-
- override fun processConstructorInvocation(instantiatedClass: PsiJavaCodeReferenceElement, constructor: PsiMethod) {
- checkMissingApi(constructor, instantiatedClass)
- }
-
- override fun processDefaultConstructorInvocation(instantiatedClass: PsiJavaCodeReferenceElement) {
- val createdClass = instantiatedClass.resolve() as? PsiClass ?: return
- checkClassDefaultConstructorApi(createdClass, instantiatedClass)
- }
-
- private fun checkClassDefaultConstructorApi(psiClass: PsiClass, elementToHighlight: PsiElement) {
- val availableSince = findEmptyConstructorAnnotations(psiClass)?.getAvailableSinceBuildNumber() ?: return
- val brokenRanges = targetedSinceUntilRanges.filter { it.someBuildsAreNotCovered(availableSince) }
- if (brokenRanges.isNotEmpty()) {
- registerDefaultConstructorProblem(psiClass, elementToHighlight, availableSince, brokenRanges)
- }
- }
-
- override fun processEmptyConstructorOfSuperClassImplicitInvocationAtSubclassDeclaration(
- subclass: PsiClass,
- superClass: PsiClass
- ) {
- val availableSince = findEmptyConstructorAnnotations(superClass)?.getAvailableSinceBuildNumber() ?: return
- val brokenRanges = targetedSinceUntilRanges.filter { it.someBuildsAreNotCovered(availableSince) }
- if (brokenRanges.isNotEmpty()) {
- val asAnonymous = subclass as? PsiAnonymousClass
- if (asAnonymous != null) {
- val argumentList = asAnonymous.argumentList
- if (argumentList != null && !argumentList.isEmpty) return
- }
- val elementToHighlight = asAnonymous?.baseClassReference ?: subclass.nameIdentifier
- if (elementToHighlight != null) {
- registerDefaultConstructorProblem(superClass, elementToHighlight, availableSince, brokenRanges)
- }
- }
- }
-
- override fun processEmptyConstructorOfSuperClassImplicitInvocationAtSubclassConstructor(
- superClass: PsiClass,
- subclassConstructor: PsiMethod
- ) {
- val nameIdentifier = subclassConstructor.nameIdentifier ?: return
- checkClassDefaultConstructorApi(superClass, nameIdentifier)
- }
-
- override fun processMethodOverriding(method: PsiMethod, overriddenMethod: PsiMethod) {
- val availableSince = overriddenMethod.getApiSinceBuildNumber() ?: return
- val brokenRanges = targetedSinceUntilRanges.filter { it.someBuildsAreNotCovered(availableSince) }
- if (brokenRanges.isNotEmpty()) {
- val aClass = overriddenMethod.containingClass ?: return
- val nameIdentifier = method.nameIdentifier ?: return
- val description = DevKitBundle.message(
- "inspections.api.overrides.method.available.only.since",
- aClass.getPresentableName(),
- availableSince.asString(),
- brokenRanges.joinToString { it.asString() }
- )
- holder.registerProblem(nameIdentifier, description, highlightType)
- }
- }
-
- private fun registerDefaultConstructorProblem(
- constructorOwner: PsiClass,
- elementToHighlight: PsiElement,
- apiSinceBuildNumber: BuildNumber,
- brokenRanges: List
- ) {
- val description = DevKitBundle.message(
- "inspections.api.constructor.only.since",
- constructorOwner.qualifiedName,
- apiSinceBuildNumber.asString(),
- brokenRanges.joinToString { it.asString() }
- )
- holder.registerProblem(elementToHighlight, description, highlightType)
- }
-
- private fun findEmptyConstructorAnnotations(psiClass: PsiClass): List? {
- val constructors = psiClass.constructors
- if (constructors.isEmpty()) {
- /*
- Default constructor of a class, which is not present in source code,
- can be externally annotated (IDEA-200832).
- */
- return ExternalAnnotationsManager.getInstance(psiClass.project)
- .findDefaultConstructorExternalAnnotations(psiClass, AVAILABLE_SINCE_ANNOTATION)
- } else {
- val emptyConstructor = constructors.find { it.parameterList.isEmpty }
- if (emptyConstructor != null) {
- return AnnotationUtil.findAllAnnotations(emptyConstructor, listOf(AVAILABLE_SINCE_ANNOTATION), false)
- }
- return null
- }
- }
-
- /**
- * Checks if the API element [refElement] is annotated
- * with [org.jetbrains.annotations.ApiStatus.AvailableSince].
- * If so, it checks plugin's [since, until] compatibility range
- * and registers a problem if the API was first introduced later
- * than plugin's `since` build.
- */
- private fun checkMissingApi(refElement: PsiElement, elementToHighlight: PsiElement) {
- if (refElement !is PsiModifierListOwner) return
-
- val availableSince = refElement.getApiSinceBuildNumber() ?: return
- val brokenRanges = targetedSinceUntilRanges.filter { it.someBuildsAreNotCovered(availableSince) }
- if (brokenRanges.isNotEmpty()) {
- val description = DevKitBundle.message(
- "inspections.api.available.only.since",
- refElement.getPresentableName(),
- availableSince.asString(),
- brokenRanges.joinToString { it.asString() }
- )
- holder.registerProblem(elementToHighlight, description, highlightType)
- }
- }
-
- /**
- * Returns the first build number when `this` API element was added.
- */
- private fun PsiModifierListOwner.getApiSinceBuildNumber(): BuildNumber? {
- val externalAnnotations = AnnotationUtil.findAllAnnotations(this, listOf(AVAILABLE_SINCE_ANNOTATION), false)
- if (externalAnnotations.isEmpty()) return null
- return externalAnnotations.getAvailableSinceBuildNumber()
- }
-
- private fun List.getAvailableSinceBuildNumber(): BuildNumber? =
- asSequence()
- .mapNotNull { annotation ->
- AnnotationUtil.getDeclaredStringAttributeValue(annotation, "value")?.let {
- BuildNumber.fromStringOrNull(it)
- }
- }
- .min()
-
- private fun SinceUntilRange.someBuildsAreNotCovered(apiSinceBuildNumber: BuildNumber) =
- sinceBuild == null || sinceBuild < apiSinceBuildNumber
-
- private fun PsiElement.getPresentableName(): String? {
- //Annotation attribute methods don't have parameters.
- return if (this is PsiMethod && PsiUtil.isAnnotationMethod(this)) {
- name
- } else HighlightMessageUtil.getSymbolName(this)
- }
-
-}
\ No newline at end of file
diff --git a/plugins/devkit/devkit-java-tests/testData/inspections/missingApi/library/RecentInterface.java b/plugins/devkit/devkit-java-tests/testData/inspections/missingApi/library/RecentInterface.java
new file mode 100644
index 000000000000..24d6bad9d6e8
--- /dev/null
+++ b/plugins/devkit/devkit-java-tests/testData/inspections/missingApi/library/RecentInterface.java
@@ -0,0 +1,4 @@
+package library;
+
+public interface RecentInterface {
+}
\ No newline at end of file
diff --git a/plugins/devkit/devkit-java-tests/testData/inspections/missingApi/library/RecentKotlinInterface.kt b/plugins/devkit/devkit-java-tests/testData/inspections/missingApi/library/RecentKotlinInterface.kt
new file mode 100644
index 000000000000..6a0182781d82
--- /dev/null
+++ b/plugins/devkit/devkit-java-tests/testData/inspections/missingApi/library/RecentKotlinInterface.kt
@@ -0,0 +1,4 @@
+package library
+
+interface RecentKotlinInterface {
+}
\ No newline at end of file
diff --git a/plugins/devkit/devkit-java-tests/testData/inspections/missingApi/library/RecentSamInterface.java b/plugins/devkit/devkit-java-tests/testData/inspections/missingApi/library/RecentSamInterface.java
new file mode 100644
index 000000000000..b99f4daad6ab
--- /dev/null
+++ b/plugins/devkit/devkit-java-tests/testData/inspections/missingApi/library/RecentSamInterface.java
@@ -0,0 +1,5 @@
+package library;
+
+public interface RecentSamInterface {
+ int singleMethod();
+}
\ No newline at end of file
diff --git a/plugins/devkit/devkit-java-tests/testData/inspections/missingApi/plugin/missingApiUsages.java b/plugins/devkit/devkit-java-tests/testData/inspections/missingApi/plugin/missingApiUsages.java
index 45ffa454aafc..885aa43338e0 100644
--- a/plugins/devkit/devkit-java-tests/testData/inspections/missingApi/plugin/missingApiUsages.java
+++ b/plugins/devkit/devkit-java-tests/testData/inspections/missingApi/plugin/missingApiUsages.java
@@ -1,5 +1,6 @@
-package client;
+package plugin;
+import java.util.function.Consumer;
import java.util.function.Supplier;
import library.RecentClass;
@@ -25,6 +26,22 @@ class A {
public RecentClass r = null;
public RecentKotlinClass r2 = null;
+ public void parameters(
+ RecentClass rc,
+ RecentKotlinClass rkc
+ ) {
+ }
+
+ public void array() {
+ RecentClass[] a1 = new RecentClass[0];
+ RecentKotlinClass[] a2 = new RecentKotlinClass[0];
+ }
+
+ public void classAccess() {
+ Class<RecentClass> o = RecentClass.class;
+ Class<RecentKotlinClass> o2 = RecentKotlinClass.class;
+ }
+
public void m1(OldClass oc, OldKotlinClass okc) {
String s = oc.recentField;
String s2 = okc.recentField;
@@ -66,14 +83,18 @@ class A {
}
});
}
+
+ public void m8() {
+ Consumer methodReference = OldClass::recentMethod;
+ }
}
-class B extends OldClass {
- //implicit call to default "recent" constructor available in source code.
+class B extends OldClass {
+ //implicit call to empty "recent" constructor available in source code.
}
-class BK extends OldKotlinClass {
- //implicit call to default "recent" constructor available in source code.
+class BK extends OldKotlinClass {
+ //implicit call to empty "recent" constructor available in source code.
}
class C extends OldClass {
@@ -104,7 +125,7 @@ class DK extends OldKotlinClassWithDefaultConstructor {
+class DK extends OldKotlinClassWithDefaultConstructor {
//implicit call to default "recent" constructor that is NOT available in source code.
}
@@ -112,16 +133,16 @@ class E() {}
- public E(int x) {
- super();
+ public E(int x) {
+ super();
}
}
//Class with constructors delegating to default "recent" constructor.
class EK extends OldKotlinClassWithDefaultConstructor {
- public EK() {}
+ public EK() {}
- public EK(int x) {
+ public EK(int x) {
super();
}
}
diff --git a/plugins/devkit/devkit-java-tests/testData/inspections/missingApi/plugin/missingApiUsages.kt b/plugins/devkit/devkit-java-tests/testData/inspections/missingApi/plugin/missingApiUsages.kt
index 921a7167d2af..e3a7f9b59c20 100644
--- a/plugins/devkit/devkit-java-tests/testData/inspections/missingApi/plugin/missingApiUsages.kt
+++ b/plugins/devkit/devkit-java-tests/testData/inspections/missingApi/plugin/missingApiUsages.kt
@@ -3,55 +3,124 @@ package client
import java.util.function.Supplier
import library.RecentClass
+import library.RecentKotlinClass
+
+import library.RecentInterface
+import library.RecentKotlinInterface
+
+import library.RecentSamInterface
+
import library.OldClass
+import library.OldKotlinClass
+
import library.OldClassWithDefaultConstructor
+import library.OldKotlinClassWithDefaultConstructor
import library.RecentAnnotation
+import library.RecentKotlinAnnotation
+
import library.OldAnnotation
+import library.OldKotlinAnnotation
import library.RecentClass.*
+import library.*
class A {
var r: RecentClass? = null
+ var kr: RecentKotlinClass? = null
- fun m1(oc: OldClass): Any {
- return oc.recentField
+ @Suppress("UNUSED_PARAMETER")
+ fun parameters(
+ rc: RecentClass,
+ krc: RecentKotlinClass
+ ) {
}
- fun m2(oc: OldClass) {
+ fun arrays() {
+ arrayOfNulls<RecentClass>(0)
+ arrayOfNulls<RecentKotlinClass>(0)
+ }
+
+ fun classAccess() {
+ RecentClass::class.java
+ RecentKotlinClass::class.java
+ }
+
+ fun m1(oc: OldClass, okc: OldKotlinClass) {
+ oc.recentField
+ okc.recentField
+ }
+
+ fun m2(oc: OldClass, okc: OldKotlinClass) {
oc.recentMethod()
+ okc.recentMethod()
}
fun m3() {
OldClass.recentStaticMethod()
+ OldKotlinClass.recentStaticMethod()
}
- fun m4(): Any {
- return OldClass("")
+ fun m4() {
+ OldClass("")
+ OldKotlinClass("")
}
- fun m5(): Any {
- //anonymous class
- return object : OldClass() {
+ fun anonymousClasses() {
+ object : OldClass(), RecentInterface {
+ }
+ object : OldKotlinClass(), RecentKotlinInterface {
}
}
- fun m6(): Any {
- return Supplier { OldClass() }
+ fun m6() {
+ Supplier { OldClass() }
+ Supplier { OldKotlinClass() }
}
+
+ fun topLevelFunction(s: String) {
+ recentTopLevelFunction()
+ s.recentExtensionFunction()
+ s.recentInlineExtensionFunction { "" }
+ }
+
+ fun singleAbstractMethod() {
+ RecentSamInterface { 42 }
+ }
+}
+
+class Overrider : OldClass(42) {
+ //overrides "recent" method.
+ override fun recentMethod() { }
+}
+
+class KOverrider : OldKotlinClass(42) {
+ //overrides "recent" method.
+ override fun recentMethod() { }
}
class B {
@RecentAnnotation
- fun m1() {
+ fun markedWithAnnotation() {
+ }
+
+ @RecentKotlinAnnotation
+ fun markedWithKotlinAnnotation() {
}
@OldAnnotation(
oldParam = 0,
recentParam = 1
)
- fun m2() {
+ fun recentAnnotationParam() {
+ }
+
+ @OldKotlinAnnotation(
+ oldParam = 0,
+ recentParam = 1
+ )
+ fun recentKotlinAnnotationParam() {
}
}
\ No newline at end of file
diff --git a/plugins/devkit/devkit-java-tests/testData/inspections/missingApi/since-2.0/library/annotations.xml b/plugins/devkit/devkit-java-tests/testData/inspections/missingApi/since-2.0/library/annotations.xml
index 4fd3a329b7fe..c7488239601f 100644
--- a/plugins/devkit/devkit-java-tests/testData/inspections/missingApi/since-2.0/library/annotations.xml
+++ b/plugins/devkit/devkit-java-tests/testData/inspections/missingApi/since-2.0/library/annotations.xml
@@ -5,6 +5,16 @@
+ -
+
+
+
+
+ -
+
+
+
+
-
@@ -52,6 +62,11 @@
+ -
+
+
+
+
-
@@ -87,6 +102,11 @@
+ -
+
+
+
+
-
diff --git a/plugins/devkit/devkit-kotlin-tests/testSrc/org/jetbrains/idea/devkit/kotlin/inspections/missingApi/MissingRecentApiInspectionTestBase.kt b/plugins/devkit/devkit-kotlin-tests/testSrc/org/jetbrains/idea/devkit/kotlin/inspections/missingApi/MissingRecentApiInspectionTestBase.kt
index 5f2a5432775b..49776f4e963d 100644
--- a/plugins/devkit/devkit-kotlin-tests/testSrc/org/jetbrains/idea/devkit/kotlin/inspections/missingApi/MissingRecentApiInspectionTestBase.kt
+++ b/plugins/devkit/devkit-kotlin-tests/testSrc/org/jetbrains/idea/devkit/kotlin/inspections/missingApi/MissingRecentApiInspectionTestBase.kt
@@ -10,7 +10,7 @@ import com.intellij.testFramework.TestDataPath
import org.jetbrains.idea.devkit.DevkitJavaTestsUtil
import org.jetbrains.idea.devkit.inspections.PluginModuleTestCase
import org.jetbrains.idea.devkit.inspections.missingApi.MissingRecentApiInspection
-import org.jetbrains.idea.devkit.inspections.missingApi.MissingRecentApiVisitor
+import org.jetbrains.idea.devkit.inspections.missingApi.MissingRecentApiUsageProcessor
import org.jetbrains.idea.devkit.kotlin.inspections.missingApi.project.PluginProjectWithIdeaJdkDescriptor
import org.jetbrains.idea.devkit.kotlin.inspections.missingApi.project.PluginProjectWithIdeaLibraryDescriptor
@@ -60,12 +60,15 @@ abstract class MissingRecentApiInspectionTestBase : PluginModuleTestCase() {
private fun configureLibraryFiles() {
myFixture.configureByFiles(
"library/RecentClass.java",
+ "library/RecentInterface.java",
+ "library/RecentSamInterface.java",
"library/RecentAnnotation.java",
"library/OldClass.java",
"library/OldClassWithDefaultConstructor.java",
"library/OldAnnotation.java",
"library/RecentKotlinClass.kt",
+ "library/RecentKotlinInterface.kt",
"library/RecentKotlinUtils.kt",
"library/RecentKotlinAnnotation.kt",
@@ -90,7 +93,7 @@ abstract class MissingRecentApiInspectionTestBase : PluginModuleTestCase() {
private fun assertAnnotationsFoundForClass(className: String) {
val psiClass = JavaPsiFacade.getInstance(project).findClass(className, GlobalSearchScope.allScope(project))!!
- val annotations = AnnotationUtil.findAllAnnotations(psiClass, listOf(MissingRecentApiVisitor.AVAILABLE_SINCE_ANNOTATION), false)
+ val annotations = AnnotationUtil.findAllAnnotations(psiClass, listOf(MissingRecentApiUsageProcessor.AVAILABLE_SINCE_ANNOTATION), false)
assertNotEmpty(annotations)
}