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