• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.tools.metalava.model.psi
18 
19 import com.android.tools.metalava.model.ConstructorItem
20 import com.android.tools.metalava.model.DefaultModifierList.Companion.PACKAGE_PRIVATE
21 import com.android.tools.metalava.model.MethodItem
22 import com.intellij.psi.JavaPsiFacade
23 import com.intellij.psi.PsiClass
24 import com.intellij.psi.PsiElement
25 import com.intellij.psi.PsiExpressionStatement
26 import com.intellij.psi.PsiKeyword
27 import com.intellij.psi.PsiMethod
28 import com.intellij.psi.PsiMethodCallExpression
29 import com.intellij.psi.PsiWhiteSpace
30 import org.jetbrains.kotlin.psi.KtClassOrObject
31 import org.jetbrains.kotlin.psi.KtPrimaryConstructor
32 import org.jetbrains.uast.UMethod
33 
34 class PsiConstructorItem(
35     codebase: PsiBasedCodebase,
36     psiMethod: PsiMethod,
37     containingClass: PsiClassItem,
38     name: String,
39     modifiers: PsiModifierItem,
40     documentation: String,
41     parameters: List<PsiParameterItem>,
42     returnType: PsiTypeItem,
43     val implicitConstructor: Boolean = false,
44     override val isPrimary: Boolean = false
45 ) :
46     PsiMethodItem(
47         codebase = codebase,
48         modifiers = modifiers,
49         documentation = documentation,
50         psiMethod = psiMethod,
51         containingClass = containingClass,
52         name = name,
53         returnType = returnType,
54         parameters = parameters
55     ),
56     ConstructorItem {
57 
58     init {
59         if (implicitConstructor) {
60             setThrowsTypes(emptyList())
61         }
62     }
63 
isImplicitConstructornull64     override fun isImplicitConstructor(): Boolean = implicitConstructor
65     override fun isConstructor(): Boolean = true
66     override var superConstructor: ConstructorItem? = null
67     override fun isCloned(): Boolean = false
68 
69     private var _superMethods: List<MethodItem>? = null
70     override fun superMethods(): List<MethodItem> {
71         if (_superMethods == null) {
72             val result = mutableListOf<MethodItem>()
73             psiMethod.findSuperMethods().mapTo(result) { codebase.findMethod(it) }
74 
75             if (result.isEmpty() && isConstructor() && containingClass().superClass() != null) {
76                 // Try a little harder; psi findSuperMethod doesn't seem to find super constructors in
77                 // some cases, but maybe we can find it by resolving actual super() calls!
78                 // TODO: Port to UAST
79                 var curr: PsiElement? = psiMethod.body?.firstBodyElement
80                 while (curr != null && curr is PsiWhiteSpace) {
81                     curr = curr.nextSibling
82                 }
83                 if (curr is PsiExpressionStatement && curr.expression is PsiMethodCallExpression &&
84                     curr.expression.firstChild?.lastChild is PsiKeyword &&
85                     curr.expression.firstChild?.lastChild?.text == "super"
86                 ) {
87                     val resolved = (curr.expression as PsiMethodCallExpression).resolveMethod()
88                     if (resolved is PsiMethod) {
89                         val superConstructor = codebase.findMethod(resolved)
90                         result.add(superConstructor)
91                     }
92                 }
93             }
94             _superMethods = result
95         }
96 
97         return _superMethods!!
98     }
99 
psinull100     override fun psi(): PsiElement? {
101         // If no PSI element, is this a synthetic/implicit constructor? If so
102         // grab the parent class' PSI element instead for file/location purposes
103         if (implicitConstructor && element.containingFile?.virtualFile == null) {
104             return containingClass().psi()
105         }
106 
107         return element
108     }
109 
110     companion object {
createnull111         fun create(
112             codebase: PsiBasedCodebase,
113             containingClass: PsiClassItem,
114             psiMethod: PsiMethod
115         ): PsiConstructorItem {
116             assert(psiMethod.isConstructor)
117             val name = psiMethod.name
118             val commentText = javadoc(psiMethod)
119             val modifiers = modifiers(codebase, psiMethod, commentText)
120             val parameters = parameterList(codebase, psiMethod)
121             val constructor = PsiConstructorItem(
122                 codebase = codebase,
123                 psiMethod = psiMethod,
124                 containingClass = containingClass,
125                 name = name,
126                 documentation = commentText,
127                 modifiers = modifiers,
128                 parameters = parameters,
129                 returnType = codebase.getType(containingClass.psiClass),
130                 implicitConstructor = false,
131                 isPrimary = (psiMethod as? UMethod)?.isPrimaryConstructor ?: false
132             )
133             constructor.modifiers.setOwner(constructor)
134             return constructor
135         }
136 
createDefaultConstructornull137         fun createDefaultConstructor(
138             codebase: PsiBasedCodebase,
139             containingClass: PsiClassItem,
140             psiClass: PsiClass
141         ): PsiConstructorItem {
142             val name = psiClass.name!!
143 
144             val factory = JavaPsiFacade.getInstance(psiClass.project).elementFactory
145             val psiMethod = factory.createConstructor(name, psiClass)
146             val modifiers = PsiModifierItem(codebase, PACKAGE_PRIVATE, null)
147             modifiers.setVisibilityLevel(containingClass.modifiers.getVisibilityLevel())
148 
149             val item = PsiConstructorItem(
150                 codebase = codebase,
151                 psiMethod = psiMethod,
152                 containingClass = containingClass,
153                 name = name,
154                 documentation = "",
155                 modifiers = modifiers,
156                 parameters = emptyList(),
157                 returnType = codebase.getType(psiClass),
158                 implicitConstructor = true
159             )
160             modifiers.setOwner(item)
161             return item
162         }
163 
164         private val UMethod.isPrimaryConstructor: Boolean
165             get() = sourcePsi is KtPrimaryConstructor || sourcePsi is KtClassOrObject
166     }
167 }
168