• 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.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