• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
<lambda>null2  * 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.ApiVariantSelectors
20 import com.android.tools.metalava.model.BaseModifierList
21 import com.android.tools.metalava.model.ClassItem
22 import com.android.tools.metalava.model.ClassTypeItem
23 import com.android.tools.metalava.model.ExceptionTypeItem
24 import com.android.tools.metalava.model.ItemDocumentation
25 import com.android.tools.metalava.model.ItemDocumentationFactory
26 import com.android.tools.metalava.model.TypeParameterList
27 import com.android.tools.metalava.model.VisibilityLevel
28 import com.android.tools.metalava.model.createImmutableModifiers
29 import com.android.tools.metalava.model.item.DefaultConstructorItem
30 import com.android.tools.metalava.model.item.ParameterItemsFactory
31 import com.android.tools.metalava.model.psi.PsiCallableItem.Companion.parameterList
32 import com.android.tools.metalava.model.psi.PsiCallableItem.Companion.throwsTypes
33 import com.android.tools.metalava.reporter.FileLocation
34 import com.intellij.psi.JavaPsiFacade
35 import com.intellij.psi.PsiClass
36 import com.intellij.psi.PsiMethod
37 import com.intellij.psi.PsiParameter
38 import org.jetbrains.kotlin.lexer.KtTokens
39 import org.jetbrains.kotlin.psi.KtClassOrObject
40 import org.jetbrains.kotlin.psi.KtConstructor
41 import org.jetbrains.kotlin.psi.KtPrimaryConstructor
42 import org.jetbrains.uast.UMethod
43 
44 internal class PsiConstructorItem
45 private constructor(
46     override val codebase: PsiBasedCodebase,
47     override val psiMethod: PsiMethod,
48     fileLocation: FileLocation = PsiFileLocation(psiMethod),
49     containingClass: ClassItem,
50     name: String,
51     modifiers: BaseModifierList,
52     documentationFactory: ItemDocumentationFactory,
53     parameterItemsFactory: ParameterItemsFactory,
54     returnType: ClassTypeItem,
55     typeParameterList: TypeParameterList,
56     throwsTypes: List<ExceptionTypeItem>,
57     implicitConstructor: Boolean = false,
58     isPrimary: Boolean = false
59 ) :
60     DefaultConstructorItem(
61         codebase = codebase,
62         fileLocation = fileLocation,
63         itemLanguage = psiMethod.itemLanguage,
64         modifiers = modifiers,
65         documentationFactory = documentationFactory,
66         variantSelectorsFactory = ApiVariantSelectors.MUTABLE_FACTORY,
67         name = name,
68         containingClass = containingClass,
69         typeParameterList = typeParameterList,
70         returnType = returnType,
71         parameterItemsFactory = parameterItemsFactory,
72         throwsTypes = throwsTypes,
73         callableBodyFactory = { PsiCallableBody(it as PsiCallableItem) },
74         implicitConstructor = implicitConstructor,
75         isPrimary = isPrimary,
76     ),
77     PsiCallableItem {
78 
79     companion object {
createnull80         internal fun create(
81             codebase: PsiBasedCodebase,
82             containingClass: ClassItem,
83             psiMethod: PsiMethod,
84             enclosingClassTypeItemFactory: PsiTypeItemFactory,
85             psiParameters: List<PsiParameter> = psiMethod.psiParameters,
86         ): PsiConstructorItem {
87             assert(psiMethod.isConstructor)
88             val name = psiMethod.name
89             val modifiers = PsiModifierItem.create(codebase, psiMethod)
90 
91             // After KT-13495, "all constructors of `sealed` classes now have `protected`
92             // visibility by default," and (S|U)LC follows that (hence the same in UAST).
93             // However, that change was made to allow more flexible class hierarchy and
94             // nesting. If they're compiled to JVM bytecode, sealed class's ctor is still
95             // technically `private` to block instantiation from outside class hierarchy.
96             // Another synthetic constructor, along with an internal ctor marker, is added
97             // for subclasses of a sealed class. Therefore, from Metalava's perspective,
98             // it is not necessary to track such semantically protected ctor. Here we force
99             // set the visibility to `private` back to ignore it during signature writing.
100             if (containingClass.modifiers.isSealed()) {
101                 modifiers.setVisibilityLevel(VisibilityLevel.PRIVATE)
102             }
103 
104             // Create the TypeParameterList for this before wrapping any of the other types used by
105             // it as they may reference a type parameter in the list.
106             val (typeParameterList, constructorTypeItemFactory) =
107                 PsiTypeParameterList.create(
108                     codebase,
109                     enclosingClassTypeItemFactory,
110                     "constructor $name",
111                     psiMethod
112                 )
113             val constructor =
114                 PsiConstructorItem(
115                     codebase = codebase,
116                     psiMethod = psiMethod,
117                     containingClass = containingClass,
118                     name = name,
119                     modifiers = modifiers,
120                     documentationFactory = PsiItemDocumentation.factory(psiMethod, codebase),
121                     parameterItemsFactory = { containingCallable ->
122                         parameterList(
123                             codebase,
124                             psiMethod,
125                             containingCallable as PsiCallableItem,
126                             constructorTypeItemFactory,
127                             psiParameters,
128                         )
129                     },
130                     returnType = containingClass.type(),
131                     typeParameterList = typeParameterList,
132                     throwsTypes = throwsTypes(psiMethod, constructorTypeItemFactory),
133                     implicitConstructor = false,
134                     isPrimary = (psiMethod as? UMethod)?.isPrimaryConstructor ?: false,
135                 )
136 
137             // Undo setting of constructors with value class types to private (b/395472914).
138             // Constructors that use value class types are effectively private to java callers, but
139             // they can be public in source to kotlin callers, so we want to track them.
140             if (
141                 constructor.modifiers.isPrivate() &&
142                     constructor.parameters().any { (it.type() as PsiTypeItem).isValueClassType() }
143             ) {
144                 (psiMethod.sourceElement as? KtConstructor<*>)?.let { sourcePsi ->
145                     if (!sourcePsi.hasModifier(KtTokens.PRIVATE_KEYWORD)) {
146                         constructor.mutateModifiers {
147                             val correctedVisibility =
148                                 when {
149                                     sourcePsi.hasModifier(KtTokens.PROTECTED_KEYWORD) ->
150                                         VisibilityLevel.PROTECTED
151                                     sourcePsi.hasModifier(KtTokens.INTERNAL_KEYWORD) ->
152                                         VisibilityLevel.INTERNAL
153                                     else -> VisibilityLevel.PUBLIC
154                                 }
155                             setVisibilityLevel(correctedVisibility)
156                         }
157                     }
158                 }
159             }
160 
161             return constructor
162         }
163 
createDefaultConstructornull164         fun createDefaultConstructor(
165             codebase: PsiBasedCodebase,
166             containingClass: ClassItem,
167             psiClass: PsiClass,
168             visibilityLevel: VisibilityLevel,
169         ): PsiConstructorItem {
170             val name = psiClass.name!!
171 
172             val factory = JavaPsiFacade.getInstance(psiClass.project).elementFactory
173             val psiMethod = factory.createConstructor(name, psiClass)
174             val modifiers = createImmutableModifiers(visibilityLevel)
175 
176             val item =
177                 PsiConstructorItem(
178                     codebase = codebase,
179                     psiMethod = psiMethod,
180                     // Use the location of the containing class for the implicit default
181                     // constructor.
182                     fileLocation = containingClass.fileLocation,
183                     containingClass = containingClass,
184                     name = name,
185                     modifiers = modifiers,
186                     documentationFactory = ItemDocumentation.NONE_FACTORY,
187                     parameterItemsFactory = { emptyList() },
188                     returnType = containingClass.type(),
189                     typeParameterList = TypeParameterList.NONE,
190                     throwsTypes = emptyList(),
191                     implicitConstructor = true,
192                 )
193             return item
194         }
195 
196         /**
197          * Whether the [UMethod] is the primary constructor of a Kotlin class. A primary constructor
198          * is declared in the class header, and all other constructors must delegate to it (see
199          * https://kotlinlang.org/docs/classes.html#constructors).
200          */
201         internal val UMethod.isPrimaryConstructor: Boolean
202             get() = sourcePsi is KtPrimaryConstructor || sourcePsi is KtClassOrObject
203     }
204 }
205