constructor reference: don't ignore constructor parameters during method reference...
[idea/community.git] / java / java-analysis-impl / src / com / intellij / codeInspection / apiUsage / ApiUsageUastVisitor.kt
1 // 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.
2 package com.intellij.codeInspection.apiUsage
3
4 import com.intellij.psi.*
5 import com.intellij.uast.UastVisitorAdapter
6 import org.jetbrains.uast.*
7 import org.jetbrains.uast.visitor.AbstractUastNonRecursiveVisitor
8
9 /**
10  * Non-recursive UAST visitor that detects usages of APIs in source code of UAST-supporting languages
11  * and reports them via [ApiUsageProcessor] interface.
12  */
13 class ApiUsageUastVisitor(private val apiUsageProcessor: ApiUsageProcessor) : AbstractUastNonRecursiveVisitor() {
14
15   companion object {
16     @JvmStatic
17     fun createPsiElementVisitor(apiUsageProcessor: ApiUsageProcessor): PsiElementVisitor =
18       UastVisitorAdapter(ApiUsageUastVisitor(apiUsageProcessor), true)
19   }
20
21   override fun visitSimpleNameReferenceExpression(node: USimpleNameReferenceExpression): Boolean {
22     if (maybeProcessReferenceInsideImportStatement(node)) {
23       return true
24     }
25     if (maybeProcessJavaModuleReference(node)) {
26       return true
27     }
28     if (isMethodReferenceOfCallExpression(node)
29         || isNewArrayClassReference(node)
30         || isMethodReferenceOfCallableReferenceExpression(node)
31         || isSelectorOfQualifiedReference(node)
32     ) {
33       return true
34     }
35     val resolved = node.resolve()
36     if (resolved is PsiMethod) {
37       if (isClassReferenceInConstructorInvocation(node) || isClassReferenceInKotlinSuperClassConstructor(node)) {
38         /*
39           Suppose a code:
40           ```
41              object : SomeClass(42) { }
42
43              or
44
45              new SomeClass(42)
46           ```
47           with USimpleNameReferenceExpression pointing to `SomeClass`.
48
49           We want ApiUsageProcessor to notice two events: 1) reference to `SomeClass` and 2) reference to `SomeClass(int)` constructor.
50
51           But Kotlin UAST resolves this simple reference to the PSI constructor of the class SomeClass.
52           So we resolve it manually to the class because the constructor will be handled separately
53           in "visitObjectLiteralExpression" or "visitCallExpression".
54         */
55         val resolvedClass = resolved.containingClass
56         if (resolvedClass != null) {
57           apiUsageProcessor.processReference(node, resolvedClass, null)
58         }
59         return true
60       }
61     }
62     if (resolved is PsiModifierListOwner) {
63       apiUsageProcessor.processReference(node, resolved, null)
64       return true
65     }
66     if (resolved == null) {
67       /*
68        * KT-30522 UAST for Kotlin: reference to annotation parameter resolves to null.
69        */
70       val psiReferences = node.sourcePsi?.references.orEmpty()
71       for (psiReference in psiReferences) {
72         val target = psiReference.resolve()?.toUElement()?.javaPsi as? PsiAnnotationMethod
73         if (target != null) {
74           apiUsageProcessor.processReference(node, target, null)
75           return true
76         }
77       }
78     }
79     return true
80   }
81
82   override fun visitQualifiedReferenceExpression(node: UQualifiedReferenceExpression): Boolean {
83     if (maybeProcessReferenceInsideImportStatement(node)) {
84       return true
85     }
86     if (node.sourcePsi is PsiMethodCallExpression || node.selector is UCallExpression) {
87       //UAST for Java produces UQualifiedReferenceExpression for both PsiMethodCallExpression and PsiReferenceExpression inside it
88       //UAST for Kotlin produces UQualifiedReferenceExpression with UCallExpression as selector
89       return true
90     }
91     val resolved = node.resolve()
92     if (resolved is PsiModifierListOwner) {
93       apiUsageProcessor.processReference(node.selector, resolved, node.receiver)
94     }
95     return true
96   }
97
98   private fun isKotlin(node: UElement): Boolean {
99     val sourcePsi = node.sourcePsi ?: return false
100     return sourcePsi.language.id.contains("kotlin", true)
101   }
102
103   override fun visitCallableReferenceExpression(node: UCallableReferenceExpression): Boolean {
104     /*
105      * KT-31181: Kotlin UAST: UCallableReferenceExpression.referenceNameElement is always null.
106      */
107     fun workaroundKotlinGetReferenceNameElement(node: UCallableReferenceExpression): UElement? {
108       if (isKotlin(node)) {
109         val sourcePsi = node.sourcePsi
110         if (sourcePsi != null) {
111           val children = sourcePsi.children
112           if (children.size == 2) {
113             return children[1].toUElement()
114           }
115         }
116       }
117       return null
118     }
119
120     val resolve = node.resolve()
121     if (resolve is PsiModifierListOwner) {
122       val sourceNode = node.referenceNameElement ?: workaroundKotlinGetReferenceNameElement(node) ?: node
123       apiUsageProcessor.processReference(sourceNode, resolve, node.qualifierExpression)
124     }
125     return true
126   }
127
128   override fun visitCallExpression(node: UCallExpression): Boolean {
129     if (node.sourcePsi is PsiExpressionStatement) {
130       //UAST for Java generates UCallExpression for PsiExpressionStatement and PsiMethodCallExpression inside it.
131       return true
132     }
133
134     val psiMethod = node.resolve()
135     val sourceNode = node.methodIdentifier ?: node.classReference?.referenceNameElement ?: node.classReference ?: node
136     if (psiMethod != null) {
137       val containingClass = psiMethod.containingClass
138       if (psiMethod.isConstructor) {
139         if (containingClass != null) {
140           apiUsageProcessor.processConstructorInvocation(sourceNode, containingClass, psiMethod, null)
141         }
142       }
143       else {
144         apiUsageProcessor.processReference(sourceNode, psiMethod, node.receiver)
145       }
146       return true
147     }
148
149     if (node.methodName == "super" && node.valueArgumentCount == 0) {
150       //Java does not resolve constructor for subclass constructor's "super()" statement
151       // if the superclass has the default constructor, which is not declared in source code and lacks PsiMethod.
152       val superClass = node.getContainingUClass()?.javaPsi?.superClass ?: return true
153       apiUsageProcessor.processConstructorInvocation(sourceNode, superClass, null, null)
154       return true
155     }
156
157     val classReference = node.classReference
158     if (classReference != null) {
159       val resolvedClass = classReference.resolve() as? PsiClass
160       if (resolvedClass != null) {
161         if (node.kind == UastCallKind.CONSTRUCTOR_CALL) {
162           val emptyConstructor = resolvedClass.constructors.find { it.parameterList.isEmpty }
163           apiUsageProcessor.processConstructorInvocation(sourceNode, resolvedClass, emptyConstructor, null)
164         }
165         else {
166           apiUsageProcessor.processReference(sourceNode, resolvedClass, node.receiver)
167         }
168       }
169       return true
170     }
171     return true
172   }
173
174   override fun visitObjectLiteralExpression(node: UObjectLiteralExpression): Boolean {
175     val psiMethod = node.resolve()
176     val sourceNode = node.methodIdentifier
177                      ?: node.classReference?.referenceNameElement
178                      ?: node.classReference
179                      ?: node.declaration.uastSuperTypes.firstOrNull()
180                      ?: node
181     if (psiMethod != null) {
182       val containingClass = psiMethod.containingClass
183       if (psiMethod.isConstructor) {
184         if (containingClass != null) {
185           apiUsageProcessor.processConstructorInvocation(sourceNode, containingClass, psiMethod, node.declaration)
186         }
187       }
188     }
189     else {
190       maybeProcessImplicitConstructorInvocationAtSubclassDeclaration(sourceNode, node.declaration)
191     }
192     return true
193   }
194
195   override fun visitElement(node: UElement): Boolean {
196     if (node is UNamedExpression) {
197       //IDEA-209279: UAstVisitor lacks a hook for UNamedExpression
198       //KT-30522: Kotlin does not generate UNamedExpression for annotation's parameters.
199       processNamedExpression(node)
200       return true
201     }
202     return super.visitElement(node)
203   }
204
205   override fun visitClass(node: UClass): Boolean {
206     val uastAnchor = node.uastAnchor
207     if (uastAnchor == null || node is UAnonymousClass || node.javaPsi is PsiTypeParameter) {
208       return true
209     }
210     maybeProcessImplicitConstructorInvocationAtSubclassDeclaration(uastAnchor, node)
211     return true
212   }
213
214   override fun visitMethod(node: UMethod): Boolean {
215     if (node.isConstructor) {
216       checkImplicitCallOfSuperEmptyConstructor(node)
217     }
218     else {
219       checkMethodOverriding(node)
220     }
221     return true
222   }
223
224   private fun maybeProcessJavaModuleReference(node: UElement): Boolean {
225     val sourcePsi = node.sourcePsi
226     val psiParent = sourcePsi?.parent
227     if (sourcePsi is PsiIdentifier && psiParent is PsiJavaModuleReferenceElement && sourcePsi == psiParent.lastChild) {
228       val reference = psiParent.reference
229       val target = reference?.resolve()
230       if (target != null) {
231         apiUsageProcessor.processJavaModuleReference(reference, target)
232       }
233       return true
234     }
235     return false
236   }
237
238   private fun maybeProcessReferenceInsideImportStatement(node: UReferenceExpression): Boolean {
239     if (isInsideImportStatement(node)) {
240       if (isKotlin(node)) {
241         /*
242         UAST for Kotlin 1.3.30 import statements have bugs.
243
244         KT-30546: some references resolve to nulls.
245         KT-30957: simple references for members resolve incorrectly to class declaration, not to the member declaration
246
247         Therefore, we have to fallback to base PSI for Kotlin references.
248          */
249         val resolved = node.sourcePsi?.reference?.resolve()
250         val target = (resolved?.toUElement()?.javaPsi ?: resolved) as? PsiModifierListOwner
251         if (target != null) {
252           apiUsageProcessor.processImportReference(node, target)
253         }
254       }
255       else {
256         val resolved = node.resolve() as? PsiModifierListOwner
257         if (resolved != null) {
258           apiUsageProcessor.processImportReference(node.referenceNameElement ?: node, resolved)
259         }
260       }
261       return true
262     }
263     return false
264   }
265
266   private fun isInsideImportStatement(node: UElement) = node.sourcePsi.findContaining(UImportStatement::class.java) != null
267
268   private fun maybeProcessImplicitConstructorInvocationAtSubclassDeclaration(sourceNode: UElement, subclassDeclaration: UClass) {
269     val instantiatedClass = subclassDeclaration.javaPsi.superClass ?: return
270     val subclassHasExplicitConstructor = subclassDeclaration.methods.any { it.isConstructor }
271     val emptyConstructor = instantiatedClass.constructors.find { it.parameterList.isEmpty }
272     if (subclassDeclaration is UAnonymousClass || !subclassHasExplicitConstructor) {
273       apiUsageProcessor.processConstructorInvocation(sourceNode, instantiatedClass, emptyConstructor, subclassDeclaration)
274     }
275   }
276
277   private fun processNamedExpression(node: UNamedExpression) {
278     val sourcePsi = node.sourcePsi
279     val annotationMethod = sourcePsi?.reference?.resolve() as? PsiAnnotationMethod
280     if (annotationMethod != null) {
281       val sourceNode = (sourcePsi as? PsiNameValuePair)?.nameIdentifier?.toUElement() ?: node
282       apiUsageProcessor.processReference(sourceNode, annotationMethod, null)
283     }
284   }
285
286   private fun checkImplicitCallOfSuperEmptyConstructor(constructor: UMethod) {
287     val containingUClass = constructor.getContainingUClass() ?: return
288     val superClass = containingUClass.javaPsi.superClass ?: return
289     val uastBody = constructor.uastBody
290     val uastAnchor = constructor.uastAnchor
291     if (uastAnchor != null && isImplicitCallOfSuperConstructorFromSubclassConstructorBody(uastBody)) {
292       val emptyConstructor = superClass.constructors.find { it.parameterList.isEmpty }
293       apiUsageProcessor.processConstructorInvocation(uastAnchor, superClass, emptyConstructor, null)
294     }
295   }
296
297   private fun isImplicitCallOfSuperConstructorFromSubclassConstructorBody(constructorBody: UExpression?): Boolean {
298     if (constructorBody == null || constructorBody is UBlockExpression && constructorBody.expressions.isEmpty()) {
299       //Empty constructor body => implicit super() call.
300       return true
301     }
302     val firstExpression = (constructorBody as? UBlockExpression)?.expressions?.firstOrNull() ?: constructorBody
303     if (firstExpression !is UCallExpression) {
304       //First expression is not super() => the super() is implicit.
305       return true
306     }
307     return firstExpression.methodName != "super"
308   }
309
310   private fun checkMethodOverriding(node: UMethod) {
311     val method = node.javaPsi
312     val superMethods = method.findSuperMethods(true)
313     for (superMethod in superMethods) {
314       apiUsageProcessor.processMethodOverriding(node, superMethod)
315     }
316   }
317
318   /**
319    * UAST for Kotlin generates UAST tree with "UnknownKotlinExpression (CONSTRUCTOR_CALLEE)" for the following expressions:
320    * 1) an object literal expression: `object : BaseClass() { ... }`
321    * 2) a super class constructor invocation `class Derived : BaseClass(42) { ... }`
322    *
323    *
324    * ```
325    * UObjectLiteralExpression
326    *     UnknownKotlinExpression (CONSTRUCTOR_CALLEE)
327    *         UTypeReferenceExpression (BaseClass)
328    *             USimpleNameReferenceExpression (BaseClass)
329    * ```
330    *
331    * and
332    *
333    * ```
334    * UCallExpression (kind = CONSTRUCTOR_CALL)
335    *     UnknownKotlinExpression (CONSTRUCTOR_CALLEE)
336    *         UTypeReferenceExpression (BaseClass)
337    *             USimpleNameReferenceExpression (BaseClass)
338    * ```
339    *
340    * This method checks if the given simple reference points to the `BaseClass` part,
341    * which is treated by Kotlin UAST as a reference to `BaseClass'` constructor, not to the `BaseClass` itself.
342    */
343   private fun isClassReferenceInKotlinSuperClassConstructor(expression: USimpleNameReferenceExpression): Boolean {
344     val parent1 = expression.uastParent
345     val parent2 = parent1?.uastParent
346     val parent3 = parent2?.uastParent
347     return parent1 is UTypeReferenceExpression
348            && parent2 != null && parent2.asLogString().contains("CONSTRUCTOR_CALLEE")
349            && (parent3 is UObjectLiteralExpression || parent3 is UCallExpression && parent3.kind == UastCallKind.CONSTRUCTOR_CALL)
350   }
351
352   private fun isSelectorOfQualifiedReference(expression: USimpleNameReferenceExpression): Boolean {
353     val qualifiedReference = expression.uastParent as? UQualifiedReferenceExpression ?: return false
354     return haveSameSourceElement(expression, qualifiedReference.selector)
355   }
356
357   private fun isNewArrayClassReference(simpleReference: USimpleNameReferenceExpression): Boolean {
358     val callExpression = simpleReference.uastParent as? UCallExpression ?: return false
359     return callExpression.kind == UastCallKind.NEW_ARRAY_WITH_DIMENSIONS
360   }
361
362   private fun isClassReferenceInConstructorInvocation(simpleReference: USimpleNameReferenceExpression): Boolean {
363     val callExpression = simpleReference.uastParent as? UCallExpression ?: return false
364     if (callExpression.kind != UastCallKind.CONSTRUCTOR_CALL) {
365       return false
366     }
367     val classReferenceNameElement = callExpression.classReference?.referenceNameElement
368     if (classReferenceNameElement != null) {
369       return haveSameSourceElement(classReferenceNameElement, simpleReference.referenceNameElement)
370     }
371     return callExpression.classReference?.resolvedName == simpleReference.resolvedName
372   }
373
374   private fun isMethodReferenceOfCallExpression(expression: USimpleNameReferenceExpression): Boolean {
375     val callExpression = expression.uastParent as? UCallExpression ?: return false
376     if (callExpression.kind != UastCallKind.METHOD_CALL) {
377       return false
378     }
379     val expressionNameElement = expression.referenceNameElement
380     if (expressionNameElement == null && expression.identifier == "super") {
381       //UAST for Java returns null for "referenceNameElement" of "super()" statement : IDEA-210418
382       return true
383     }
384     val methodIdentifier = callExpression.methodIdentifier
385     return methodIdentifier != null && haveSameSourceElement(expressionNameElement, methodIdentifier)
386   }
387
388   private fun isMethodReferenceOfCallableReferenceExpression(expression: USimpleNameReferenceExpression): Boolean {
389     val callableReferenceExpression = expression.uastParent as? UCallableReferenceExpression ?: return false
390     if (haveSameSourceElement(callableReferenceExpression.referenceNameElement, expression)) {
391       return true
392     }
393     return expression.identifier == callableReferenceExpression.callableName
394   }
395
396   private fun haveSameSourceElement(element1: UElement?, element2: UElement?): Boolean {
397     if (element1 == null || element2 == null) return false
398     val sourcePsi1 = element1.sourcePsi
399     return sourcePsi1 != null && sourcePsi1 == element2.sourcePsi
400   }
401 }