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