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.ClassKind
23 import com.android.tools.metalava.model.FieldItem
24 import com.android.tools.metalava.model.ItemDocumentationFactory
25 import com.android.tools.metalava.model.PropertyItem
26 import com.android.tools.metalava.model.TypeItem
27 import com.android.tools.metalava.model.TypeNullability
28 import com.android.tools.metalava.model.VisibilityLevel
29 import com.android.tools.metalava.model.isNonNullAnnotation
30 import com.android.tools.metalava.model.item.DefaultFieldItem
31 import com.android.tools.metalava.model.item.FieldValue
32 import com.intellij.psi.PsiCallExpression
33 import com.intellij.psi.PsiClassType
34 import com.intellij.psi.PsiEnumConstant
35 import com.intellij.psi.PsiField
36 import com.intellij.psi.PsiModifierListOwner
37 import com.intellij.psi.PsiPrimitiveType
38 import com.intellij.psi.PsiReference
39 import com.intellij.psi.impl.JavaConstantExpressionEvaluator
40
41 internal class PsiFieldItem(
42 override val codebase: PsiBasedCodebase,
43 private val psiField: PsiField,
44 modifiers: BaseModifierList,
45 documentationFactory: ItemDocumentationFactory,
46 name: String,
47 containingClass: ClassItem,
48 type: TypeItem,
49 private val isEnumConstant: Boolean,
50 override val legacyFieldValue: FieldValue?,
51 ) :
52 DefaultFieldItem(
53 codebase = codebase,
54 fileLocation = PsiFileLocation(psiField),
55 itemLanguage = psiField.itemLanguage,
56 modifiers = modifiers,
57 documentationFactory = documentationFactory,
58 variantSelectorsFactory = ApiVariantSelectors.MUTABLE_FACTORY,
59 name = name,
60 containingClass = containingClass,
61 type = type,
62 isEnumConstant = isEnumConstant,
63 legacyFieldValue = legacyFieldValue,
64 ),
65 FieldItem,
66 PsiItem {
67
68 override fun psi(): PsiField = psiField
69
70 override var property: PropertyItem? = null
71
72 override fun duplicate(targetContainingClass: ClassItem) =
73 create(
74 codebase,
75 targetContainingClass,
76 psiField,
77 codebase.globalTypeItemFactory.from(targetContainingClass),
78 )
79 .also { duplicated -> duplicated.inheritedFrom = containingClass() }
80
81 companion object {
82 internal fun create(
83 codebase: PsiBasedCodebase,
84 containingClass: ClassItem,
85 psiField: PsiField,
86 enclosingClassTypeItemFactory: PsiTypeItemFactory,
87 ): PsiFieldItem {
88 val name = psiField.name
89 val modifiers = PsiModifierItem.create(codebase, psiField)
90
91 if (containingClass.classKind == ClassKind.INTERFACE) {
92 // All interface fields are implicitly public and static.
93 modifiers.setVisibilityLevel(VisibilityLevel.PUBLIC)
94 modifiers.setStatic(true)
95 }
96
97 val isEnumConstant = psiField is PsiEnumConstant
98
99 // Wrap the PsiField in a PsiFieldValue that can provide the field's initial value.
100 val fieldValue = PsiFieldValue(psiField)
101
102 // Create a type for the field, taking into account the modifiers, whether it is an
103 // enum constant and whether the field's initial value is non-null.
104 val fieldType =
105 enclosingClassTypeItemFactory.getFieldType(
106 underlyingType = PsiTypeInfo(psiField.type, psiField),
107 itemAnnotations = modifiers.annotations(),
108 isEnumConstant = isEnumConstant,
109 isFinal = modifiers.isFinal(),
110 isInitialValueNonNull = {
111 // The initial value is non-null if the field initializer is a method that
112 // is annotated as being non-null so would produce a non-null value, or the
113 // value is a literal which is not null.
114 psiField.isFieldInitializerNonNull() ||
115 fieldValue.initialValue(false) != null
116 },
117 )
118
119 return PsiFieldItem(
120 codebase = codebase,
121 psiField = psiField,
122 documentationFactory = PsiItemDocumentation.factory(psiField, codebase),
123 modifiers = modifiers,
124 name = name,
125 containingClass = containingClass,
126 type = fieldType,
127 isEnumConstant = isEnumConstant,
128 legacyFieldValue = fieldValue
129 )
130 }
131 }
132 }
133
134 /**
135 * Check to see whether the [PsiField] on which this is called has an initializer whose
136 * [TypeNullability] is known to be [TypeNullability.NONNULL].
137 */
PsiFieldnull138 private fun PsiField.isFieldInitializerNonNull(): Boolean {
139 // If we're looking at a final field, look on the right hand side of the field to the
140 // field initialization. If that right hand side for example represents a method call,
141 // and the method we're calling is annotated with @NonNull, then the field (since it is
142 // final) will always be @NonNull as well.
143 val resolved =
144 when (val initializer = initializer) {
145 is PsiReference -> {
146 initializer.resolve()
147 }
148 is PsiCallExpression -> {
149 initializer.resolveMethod()
150 }
151 else -> null
152 }
153 ?: return false
154
155 return resolved is PsiModifierListOwner &&
156 resolved.annotations.any { isNonNullAnnotation(it.qualifiedName ?: "") }
157 }
158
159 /**
160 * Wrapper around a [PsiField] that will provide access to the initial value of the field, if
161 * available, or `null` otherwise.
162 */
163 class PsiFieldValue(private val psiField: PsiField) : FieldValue {
164
initialValuenull165 override fun initialValue(requireConstant: Boolean): Any? {
166 val constant = psiField.computeConstantValue()
167 // Offset [ClsFieldImpl#computeConstantValue] for [TYPE] field in boxed primitive types.
168 // Those fields hold [Class] object, but the constant value should not be of [PsiType].
169 if (
170 constant is PsiPrimitiveType &&
171 psiField.name == "TYPE" &&
172 (psiField.type as? PsiClassType)?.computeQualifiedName() == "java.lang.Class"
173 ) {
174 return null
175 }
176 if (constant != null) {
177 return constant
178 }
179
180 return if (!requireConstant) {
181 val initializer = psiField.initializer ?: return null
182 JavaConstantExpressionEvaluator.computeConstantExpression(initializer, false)
183 } else {
184 null
185 }
186 }
187 }
188