1 /*
2 * Copyright (C) 2023 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
18
19 /**
20 * Encapsulates information that metalava needs to know about a specific annotation type.
21 *
22 * Instances of [AnnotationInfo] will be shared across [AnnotationItem]s that have the same
23 * qualified name and (where applicable) the same attributes. That will allow the information in
24 * [AnnotationInfo] to be computed once and then reused whenever needed.
25 */
26 interface AnnotationInfo {
27
28 /** The applicable targets for this annotation */
29 val targets: Set<AnnotationTarget>
30
31 /**
32 * Determines whether the annotation is nullability related.
33 *
34 * If this is null then the annotation is not a nullability annotation, otherwise this
35 * determines whether it is nullable or non-null.
36 */
37 val typeNullability: TypeNullability?
38
39 /**
40 * Determines whether this annotation affects whether the annotated item is shown or hidden and
41 * if so how.
42 */
43 val showability: Showability
44
45 val suppressCompatibility: Boolean
46 }
47
48 /** Compute the [TypeNullability], if any, for the annotation with [qualifiedName]. */
computeTypeNullabilitynull49 internal fun computeTypeNullability(qualifiedName: String): TypeNullability? =
50 when {
51 isNullableAnnotation(qualifiedName) -> TypeNullability.NULLABLE
52 isNonNullAnnotation(qualifiedName) -> TypeNullability.NONNULL
53 else -> null
54 }
55
56 /**
57 * The set of possible effects on whether an `Item` is part of an API.
58 *
59 * They are in order from the lowest priority to the highest priority, see [highestPriority].
60 */
61 enum class ShowOrHide(private val show: Boolean?) {
62 /** No effect either way. */
63 NO_EFFECT(show = null),
64
65 /** Hide an item from the API. */
66 HIDE(show = false),
67
68 /** Show an item as part of the API. */
69 SHOW(show = true),
70
71 /**
72 * Revert an unstable API.
73 *
74 * The effect of reverting an unstable API depends on what the previously released API contains
75 * but in the case when the item is new and does not exist in the previously released API
76 * reverting requires hiding the API. As the items being hidden could have show annotations
77 * (which override hide annotations) then in order for the item to be hidden then this needs to
78 * come after [SHOW].
79 */
80 REVERT_UNSTABLE_API(show = null) {
81 /**
82 * If the [revertItem] is not null and `emit = true`, i.e. is for the API surface currently
83 * being generated, then reverting will still show this item.
84 */
shownull85 override fun show(revertItem: SelectableItem?): Boolean {
86 return revertItem != null && revertItem.emit
87 }
88
89 /** If the [revertItem] is null then reverting will hide this item. */
hidenull90 override fun hide(revertItem: SelectableItem?): Boolean {
91 return revertItem == null
92 }
93 },
94 ;
95
96 /**
97 * Return true if this shows an `Item` as part of the API.
98 *
99 * @param revertItem the optional [Item] in the previously released API to which this will be
100 * reverted. This is only set for, and only has an effect on, [REVERT_UNSTABLE_API], see
101 * [REVERT_UNSTABLE_API.show] for details.
102 */
shownull103 open fun show(revertItem: SelectableItem?): Boolean = show == true
104
105 /**
106 * Return true if this hides an `Item` from the API.
107 *
108 * @param revertItem the optional [Item] in the previously released API to which this will be
109 * reverted. This is only set for, and only has an effect on, [REVERT_UNSTABLE_API], see
110 * [REVERT_UNSTABLE_API.show] for details.
111 */
112 open fun hide(revertItem: SelectableItem?): Boolean = show == false
113
114 /** Return the highest priority between this and another [ShowOrHide]. */
115 fun highestPriority(other: ShowOrHide): ShowOrHide = maxOf(this, other)
116 }
117
118 /**
119 * Determines how an annotation will affect whether [SelectableItem]s annotated with it are part of
120 * the API or not and also determines whether a [SelectableItem] is part of the API or not.
121 */
122 data class Showability(
123 /**
124 * Determines whether an API [SelectableItem] is shown as part of the API or hidden from the
125 * API.
126 *
127 * If [ShowOrHide.show] is `true` then the annotated [SelectableItem] will be shown as part of
128 * the API. That is the case for annotations that match `--show-annotation`, or
129 * `--show-single-annotation`, but not `--show-for-stub-purposes-annotation`.
130 *
131 * If [ShowOrHide.hide] is `true` then the annotated [SelectableItem] will NOT be shown as part
132 * of the API. That is the case for annotations that match `--hide-annotation`.
133 *
134 * If neither of the above is then this has no effect on whether an annotated [SelectableItem]
135 * will be shown or not, that decision will be determined by its container's
136 * [Showability.recursive] setting.
137 */
138 private val show: ShowOrHide,
139
140 /**
141 * Determines whether the contents of an API [Item] is shown as part of the API or hidden from
142 * the API.
143 *
144 * If [ShowOrHide.show] is `true` then the contents of the annotated [Item] will be included in
145 * the API unless overridden by a closer annotation. That is the case for annotations that match
146 * `--show-annotation`, but not `--show-single-annotation`, or
147 * `--show-for-stub-purposes-annotation`.
148 *
149 * If [ShowOrHide.hide] is `true` then the contents of the annotated [Item] will be included in
150 * the API unless overridden by a closer annotation. That is the case for annotations that match
151 * `--hide-annotation`.
152 */
153 private val recursive: ShowOrHide,
154
155 /**
156 * Determines whether an API [Item] ands its contents is considered to be part of the base API
157 * and so must be included in the stubs but not the signature files.
158 *
159 * If [ShowOrHide.show] is `true` then the API [Item] ands its contents are considered to be
160 * part of the base API. That is the case for annotations that match
161 * `--show-for-stub-purposes-annotation` but not `--show-annotation`, or
162 * `--show-single-annotation`.
163 */
164 private val forStubsOnly: ShowOrHide,
165
166 /** The item to which this item should be reverted. Null if no such item exists. */
167 val revertItem: SelectableItem? = null,
168 ) {
169 /**
170 * Check whether the annotated item should be considered part of the API or not.
171 *
172 * Returns `true` if the item is annotated with a `--show-annotation`,
173 * `--show-single-annotation`, or `--show-for-stub-purposes-annotation`.
174 */
175 fun show() = show.show(revertItem) || forStubsOnly.show(revertItem)
176
177 /**
178 * Check whether the annotated item should only be considered part of the API when generating
179 * stubs.
180 *
181 * Returns `true` if the item is annotated with a `--show-for-stub-purposes-annotation`. Such
182 * items will be part of an API surface that the API being generated extends.
183 */
184 fun showForStubsOnly() = forStubsOnly.show(revertItem)
185
186 /**
187 * Check whether the annotations on this item affect nested `Item`s.
188 *
189 * Returns `true` if they do, `false` if they do not affect nested `Item`s.
190 */
191 fun showRecursive() = recursive.show(revertItem) || forStubsOnly.show(revertItem)
192
193 /**
194 * Check whether the annotations on this item only affect the current `Item`.
195 *
196 * Returns `true` if they do, `false` if they can also affect nested `Item`s.
197 */
198 fun showNonRecursive() =
199 show.show(revertItem) && !recursive.show(revertItem) && !forStubsOnly.show(revertItem)
200
201 /**
202 * Check whether the annotated item should be hidden from the API.
203 *
204 * Returns `true` if the annotation matches an `--hide-annotation`.
205 */
206 fun hide() = show.hide(revertItem)
207
208 /**
209 * Check whether the annotated item is part of an unstable API that needs to be reverted.
210 *
211 * Returns `true` if the annotation matches `--hide-annotation android.annotation.FlaggedApi` or
212 * if this is on an item then when the item is annotated with such an annotation or is a method
213 * that overrides such an item or is contained within a class that is annotated with such an
214 * annotation.
215 */
216 fun revertUnstableApi() = show == ShowOrHide.REVERT_UNSTABLE_API
217
218 /** Combine this with [other] to produce a combination [Showability]. */
219 fun combineWith(other: Showability): Showability {
220 // Show wins over not showing.
221 val newShow = show.highestPriority(other.show)
222
223 // Recursive wins over not recursive.
224 val newRecursive = recursive.highestPriority(other.recursive)
225
226 // For everything wins over only for stubs.
227 val forStubsOnly =
228 if (newShow.show(revertItem)) {
229 ShowOrHide.NO_EFFECT
230 } else {
231 forStubsOnly.highestPriority(other.forStubsOnly)
232 }
233
234 return Showability(newShow, newRecursive, forStubsOnly)
235 }
236
237 companion object {
238 /** The annotation does not affect whether an annotated item is shown. */
239 val NO_EFFECT =
240 Showability(
241 show = ShowOrHide.NO_EFFECT,
242 recursive = ShowOrHide.NO_EFFECT,
243 forStubsOnly = ShowOrHide.NO_EFFECT
244 )
245 }
246 }
247