• 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
18 
19 import com.android.tools.metalava.model.AnnotationItem
20 import com.android.tools.metalava.model.FieldItem
21 import com.android.tools.metalava.model.Item
22 import com.android.tools.metalava.model.MethodItem
23 import com.android.tools.metalava.model.ParameterItem
24 import com.android.tools.metalava.model.SUPPORT_TYPE_USE_ANNOTATIONS
25 import com.android.tools.metalava.model.TypeItem
26 
27 /**
28  * Performs null migration analysis, looking at previous API signature
29  * files and new signature files, and replacing new @Nullable and @NonNull
30  * annotations with @RecentlyNullable and @RecentlyNonNull.
31  *
32  * TODO: Enforce compatibility across type use annotations, e.g.
33  * changing parameter value from
34  *    {@code @NonNull List<@Nullable String>}
35  * to
36  *    {@code @NonNull List<@NonNull String>}
37  * is forbidden.
38  */
39 class NullnessMigration : ComparisonVisitor(visitAddedItemsRecursively = true) {
comparenull40     override fun compare(old: Item, new: Item) {
41         if (hasNullnessInformation(new) && !hasNullnessInformation(old)) {
42             new.markRecent()
43         }
44     }
45 
46     // Note: We don't override added(new: Item) to mark newly added methods as newly
47     // having nullness annotations: those APIs are themselves new, so there's no reason
48     // to mark the nullness contract as migration (warning- rather than error-severity)
49 
comparenull50     override fun compare(old: MethodItem, new: MethodItem) {
51         @Suppress("ConstantConditionIf")
52         if (SUPPORT_TYPE_USE_ANNOTATIONS) {
53             val newType = new.returnType() ?: return
54             val oldType = old.returnType() ?: return
55             checkType(oldType, newType)
56         }
57     }
58 
comparenull59     override fun compare(old: FieldItem, new: FieldItem) {
60         @Suppress("ConstantConditionIf")
61         if (SUPPORT_TYPE_USE_ANNOTATIONS) {
62             val newType = new.type()
63             val oldType = old.type()
64             checkType(oldType, newType)
65         }
66     }
67 
comparenull68     override fun compare(old: ParameterItem, new: ParameterItem) {
69         @Suppress("ConstantConditionIf")
70         if (SUPPORT_TYPE_USE_ANNOTATIONS) {
71             val newType = new.type()
72             val oldType = old.type()
73             checkType(oldType, newType)
74         }
75     }
76 
hasNullnessInformationnull77     private fun hasNullnessInformation(type: TypeItem): Boolean {
78         @Suppress("ConstantConditionIf")
79         return if (SUPPORT_TYPE_USE_ANNOTATIONS) {
80             val typeString = type.toTypeString(outerAnnotations = false, innerAnnotations = true)
81             typeString.contains(".Nullable") || typeString.contains(".NonNull")
82         } else {
83             false
84         }
85     }
86 
checkTypenull87     private fun checkType(old: TypeItem, new: TypeItem) {
88         if (hasNullnessInformation(new)) {
89             assert(SUPPORT_TYPE_USE_ANNOTATIONS)
90             if (old.toTypeString(outerAnnotations = false, innerAnnotations = true) !=
91                 new.toTypeString(outerAnnotations = false, innerAnnotations = true)
92             ) {
93                 new.markRecent()
94             }
95         }
96     }
97 
98     companion object {
hasNullnessInformationnull99         fun hasNullnessInformation(item: Item): Boolean {
100             return isNullable(item) || isNonNull(item)
101         }
102 
findNullnessAnnotationnull103         fun findNullnessAnnotation(item: Item): AnnotationItem? {
104             return item.modifiers.annotations().firstOrNull { it.isNullnessAnnotation() }
105         }
106 
isNullablenull107         fun isNullable(item: Item): Boolean {
108             return item.modifiers.annotations().any { it.isNullable() }
109         }
110 
isNonNullnull111         private fun isNonNull(item: Item): Boolean {
112             return item.modifiers.annotations().any { it.isNonNull() }
113         }
114     }
115 }
116