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.ANDROID_DEPRECATED_FOR_SDK
20 import com.android.tools.metalava.model.AnnotationItem
21 import com.android.tools.metalava.model.DefaultModifierList
22 import com.android.tools.metalava.model.DefaultModifierList.Companion.ABSTRACT
23 import com.android.tools.metalava.model.DefaultModifierList.Companion.ACTUAL
24 import com.android.tools.metalava.model.DefaultModifierList.Companion.COMPANION
25 import com.android.tools.metalava.model.DefaultModifierList.Companion.CONST
26 import com.android.tools.metalava.model.DefaultModifierList.Companion.DATA
27 import com.android.tools.metalava.model.DefaultModifierList.Companion.DEFAULT
28 import com.android.tools.metalava.model.DefaultModifierList.Companion.EXPECT
29 import com.android.tools.metalava.model.DefaultModifierList.Companion.FINAL
30 import com.android.tools.metalava.model.DefaultModifierList.Companion.FUN
31 import com.android.tools.metalava.model.DefaultModifierList.Companion.INFIX
32 import com.android.tools.metalava.model.DefaultModifierList.Companion.INLINE
33 import com.android.tools.metalava.model.DefaultModifierList.Companion.INTERNAL
34 import com.android.tools.metalava.model.DefaultModifierList.Companion.NATIVE
35 import com.android.tools.metalava.model.DefaultModifierList.Companion.OPERATOR
36 import com.android.tools.metalava.model.DefaultModifierList.Companion.PACKAGE_PRIVATE
37 import com.android.tools.metalava.model.DefaultModifierList.Companion.PRIVATE
38 import com.android.tools.metalava.model.DefaultModifierList.Companion.PROTECTED
39 import com.android.tools.metalava.model.DefaultModifierList.Companion.PUBLIC
40 import com.android.tools.metalava.model.DefaultModifierList.Companion.SEALED
41 import com.android.tools.metalava.model.DefaultModifierList.Companion.STATIC
42 import com.android.tools.metalava.model.DefaultModifierList.Companion.STRICT_FP
43 import com.android.tools.metalava.model.DefaultModifierList.Companion.SUSPEND
44 import com.android.tools.metalava.model.DefaultModifierList.Companion.SYNCHRONIZED
45 import com.android.tools.metalava.model.DefaultModifierList.Companion.TRANSIENT
46 import com.android.tools.metalava.model.DefaultModifierList.Companion.VALUE
47 import com.android.tools.metalava.model.DefaultModifierList.Companion.VARARG
48 import com.android.tools.metalava.model.DefaultModifierList.Companion.VISIBILITY_MASK
49 import com.android.tools.metalava.model.DefaultModifierList.Companion.VOLATILE
50 import com.android.tools.metalava.model.JAVA_LANG_ANNOTATION_TARGET
51 import com.android.tools.metalava.model.JAVA_LANG_TYPE_USE_TARGET
52 import com.android.tools.metalava.model.hasAnnotation
53 import com.android.tools.metalava.model.isNullnessAnnotation
54 import com.android.tools.metalava.model.psi.KotlinTypeInfo.Companion.isInheritedGenericType
55 import com.intellij.psi.PsiAnnotation
56 import com.intellij.psi.PsiAnnotationMemberValue
57 import com.intellij.psi.PsiArrayInitializerMemberValue
58 import com.intellij.psi.PsiDocCommentOwner
59 import com.intellij.psi.PsiField
60 import com.intellij.psi.PsiMethod
61 import com.intellij.psi.PsiModifier
62 import com.intellij.psi.PsiModifierList
63 import com.intellij.psi.PsiModifierListOwner
64 import com.intellij.psi.PsiParameter
65 import com.intellij.psi.PsiPrimitiveType
66 import com.intellij.psi.PsiReferenceExpression
67 import com.intellij.psi.impl.light.LightModifierList
68 import org.jetbrains.annotations.NotNull
69 import org.jetbrains.annotations.Nullable
70 import org.jetbrains.kotlin.analysis.api.analyze
71 import org.jetbrains.kotlin.analysis.api.symbols.markers.KtSymbolWithVisibility
72 import org.jetbrains.kotlin.asJava.elements.KtLightElement
73 import org.jetbrains.kotlin.descriptors.Visibilities
74 import org.jetbrains.kotlin.lexer.KtTokens
75 import org.jetbrains.kotlin.psi.KtAnnotated
76 import org.jetbrains.kotlin.psi.KtDeclaration
77 import org.jetbrains.kotlin.psi.KtElement
78 import org.jetbrains.kotlin.psi.KtModifierList
79 import org.jetbrains.kotlin.psi.KtModifierListOwner
80 import org.jetbrains.kotlin.psi.KtNamedFunction
81 import org.jetbrains.kotlin.psi.KtPropertyAccessor
82 import org.jetbrains.kotlin.psi.psiUtil.hasActualModifier
83 import org.jetbrains.kotlin.psi.psiUtil.hasExpectModifier
84 import org.jetbrains.kotlin.psi.psiUtil.hasFunModifier
85 import org.jetbrains.kotlin.psi.psiUtil.hasSuspendModifier
86 import org.jetbrains.kotlin.psi.psiUtil.hasValueModifier
87 import org.jetbrains.kotlin.psi.psiUtil.visibilityModifier
88 import org.jetbrains.uast.UAnnotated
89 import org.jetbrains.uast.UAnnotation
90 import org.jetbrains.uast.UElement
91 import org.jetbrains.uast.UMethod
92 import org.jetbrains.uast.UVariable
93 import org.jetbrains.uast.kotlin.KotlinUMethodWithFakeLightDelegateBase
94 
95 internal object PsiModifierItem {
96     fun create(
97         codebase: PsiBasedCodebase,
98         element: PsiModifierListOwner,
99         documentation: String? = null,
100     ): DefaultModifierList {
101         val modifiers =
102             if (element is UAnnotated) {
103                 create(codebase, element, element)
104             } else {
105                 create(codebase, element)
106             }
107 
108         // Sometimes Psi/Kotlin interoperation goes a little awry and adds nullability annotations
109         // that it should not, so this removes them.
110         if (shouldRemoveNullnessAnnotations(element, modifiers)) {
111             modifiers.removeAnnotations { it.isNullnessAnnotation() }
112         }
113 
114         val docDeprecated =
115             if (codebase.allowReadingComments) {
116                 documentation?.contains("@deprecated") == true ||
117                     // Check for @Deprecated annotation
118                     ((element as? PsiDocCommentOwner)?.isDeprecated == true)
119             } else {
120                 false
121             }
122         if (
123             docDeprecated ||
124                 hasDeprecatedAnnotation(modifiers) ||
125                 // Check for @Deprecated on sourcePsi
126                 isDeprecatedFromSourcePsi(element)
127         ) {
128             modifiers.setDeprecated(true)
129         }
130 
131         return modifiers
132     }
133 
134     /** Determine whether nullness annotations need removing from [modifiers]. */
135     private fun shouldRemoveNullnessAnnotations(
136         element: PsiModifierListOwner,
137         modifiers: DefaultModifierList,
138     ): Boolean {
139         // Kotlin varargs are not nullable but can sometimes and up with an @Nullable annotation
140         // added to the [PsiParameter] so remove it from the modifiers. Only Kotlin varargs have a
141         // `vararg` modifier.
142         if (modifiers.isVarArg()) {
143             return true
144         }
145 
146         // Although https://youtrack.jetbrains.com/issue/KTIJ-19087 has been fixed there still
147         // seems to be an issue with reified type parameters causing nullability annotations
148         // being added to the parameter even when the use site does not require
149         // them. So, this removes them.
150         val kotlinTypeInfo = KotlinTypeInfo.fromContext(element)
151         if (
152             kotlinTypeInfo.analysisSession != null &&
153                 kotlinTypeInfo.ktType != null &&
154                 kotlinTypeInfo.analysisSession.isInheritedGenericType(kotlinTypeInfo.ktType)
155         ) {
156             return true
157         }
158 
159         return false
160     }
161 
162     private fun hasDeprecatedAnnotation(modifiers: DefaultModifierList) =
163         modifiers.hasAnnotation {
164             it.qualifiedName?.let { qualifiedName ->
165                 qualifiedName == "Deprecated" ||
166                     qualifiedName.endsWith(".Deprecated") ||
167                     // DeprecatedForSdk that do not apply to this API surface have been filtered
168                     // out so if any are left then treat it as a standard Deprecated annotation.
169                     qualifiedName == ANDROID_DEPRECATED_FOR_SDK
170             }
171                 ?: false
172         }
173 
174     private fun isDeprecatedFromSourcePsi(element: PsiModifierListOwner): Boolean {
175         if (element is UMethod) {
176             // NB: we can't use sourcePsi.annotationEntries directly due to
177             // annotation use-site targets. The given `javaPsi` as a light element,
178             // which spans regular functions, property accessors, etc., is already
179             // built with targeted annotations. Even KotlinUMethod is using LC annotations.
180             return element.javaPsi.isDeprecated
181         }
182         return ((element as? UElement)?.sourcePsi as? KtAnnotated)?.annotationEntries?.any {
183             it.shortName?.toString() == "Deprecated"
184         }
185             ?: false
186     }
187 
188     private fun computeFlag(element: PsiModifierListOwner, modifierList: PsiModifierList): Int {
189         var flags = 0
190         if (modifierList.hasModifierProperty(PsiModifier.STATIC)) {
191             flags = flags or STATIC
192         }
193         if (modifierList.hasModifierProperty(PsiModifier.ABSTRACT)) {
194             flags = flags or ABSTRACT
195         }
196         if (modifierList.hasModifierProperty(PsiModifier.FINAL)) {
197             flags = flags or FINAL
198         }
199         if (modifierList.hasModifierProperty(PsiModifier.NATIVE)) {
200             flags = flags or NATIVE
201         }
202         if (modifierList.hasModifierProperty(PsiModifier.SYNCHRONIZED)) {
203             flags = flags or SYNCHRONIZED
204         }
205         if (modifierList.hasModifierProperty(PsiModifier.STRICTFP)) {
206             flags = flags or STRICT_FP
207         }
208         if (modifierList.hasModifierProperty(PsiModifier.TRANSIENT)) {
209             flags = flags or TRANSIENT
210         }
211         if (modifierList.hasModifierProperty(PsiModifier.VOLATILE)) {
212             flags = flags or VOLATILE
213         }
214         if (modifierList.hasModifierProperty(PsiModifier.DEFAULT)) {
215             flags = flags or DEFAULT
216         }
217 
218         // Look for special Kotlin keywords
219         var ktModifierList: KtModifierList? = null
220         val sourcePsi = (element as? UElement)?.sourcePsi
221         when (modifierList) {
222             is KtLightElement<*, *> -> {
223                 ktModifierList = modifierList.kotlinOrigin as? KtModifierList
224             }
225             is LightModifierList -> {
226                 if (sourcePsi is KtModifierListOwner) {
227                     ktModifierList = sourcePsi.modifierList
228                 }
229             }
230         }
231         var visibilityFlags =
232             when {
233                 modifierList.hasModifierProperty(PsiModifier.PUBLIC) -> PUBLIC
234                 modifierList.hasModifierProperty(PsiModifier.PROTECTED) -> PROTECTED
235                 modifierList.hasModifierProperty(PsiModifier.PRIVATE) -> PRIVATE
236                 ktModifierList != null ->
237                     when {
238                         ktModifierList.hasModifier(KtTokens.PRIVATE_KEYWORD) -> PRIVATE
239                         ktModifierList.hasModifier(KtTokens.PROTECTED_KEYWORD) -> PROTECTED
240                         ktModifierList.hasModifier(KtTokens.INTERNAL_KEYWORD) -> INTERNAL
241                         else -> PUBLIC
242                     }
243                 // UAST workaround: fake light method for inline/hidden function may not have a
244                 // concrete modifier list, but overrides `hasModifierProperty` to mimic
245                 // modifiers.
246                 element is KotlinUMethodWithFakeLightDelegateBase<*> ->
247                     when {
248                         element.hasModifierProperty(PsiModifier.PUBLIC) -> PUBLIC
249                         element.hasModifierProperty(PsiModifier.PROTECTED) -> PROTECTED
250                         element.hasModifierProperty(PsiModifier.PRIVATE) -> PRIVATE
251                         else -> PUBLIC
252                     }
253                 else -> PACKAGE_PRIVATE
254             }
255         if (ktModifierList != null) {
256             if (ktModifierList.hasModifier(KtTokens.INTERNAL_KEYWORD)) {
257                 // Reset visibilityFlags to INTERNAL if the internal modifier is explicitly
258                 // present on the element
259                 visibilityFlags = INTERNAL
260             } else if (
261                 ktModifierList.hasModifier(KtTokens.OVERRIDE_KEYWORD) &&
262                     ktModifierList.visibilityModifier() == null &&
263                     sourcePsi is KtElement
264             ) {
265                 // Reset visibilityFlags to INTERNAL if the element has no explicit visibility
266                 // modifier, but overrides an internal declaration. Adapted from
267                 // org.jetbrains.kotlin.asJava.classes.UltraLightMembersCreator.isInternal
268                 analyze(sourcePsi) {
269                     val symbol = (sourcePsi as? KtDeclaration)?.getSymbol()
270                     val visibility = (symbol as? KtSymbolWithVisibility)?.visibility
271                     if (visibility == Visibilities.Internal) {
272                         visibilityFlags = INTERNAL
273                     }
274                 }
275             }
276             if (ktModifierList.hasModifier(KtTokens.VARARG_KEYWORD)) {
277                 flags = flags or VARARG
278             }
279             if (ktModifierList.hasModifier(KtTokens.SEALED_KEYWORD)) {
280                 flags = flags or SEALED
281             }
282             if (ktModifierList.hasModifier(KtTokens.INFIX_KEYWORD)) {
283                 flags = flags or INFIX
284             }
285             if (ktModifierList.hasModifier(KtTokens.CONST_KEYWORD)) {
286                 flags = flags or CONST
287             }
288             if (ktModifierList.hasModifier(KtTokens.OPERATOR_KEYWORD)) {
289                 flags = flags or OPERATOR
290             }
291             if (ktModifierList.hasModifier(KtTokens.INLINE_KEYWORD)) {
292                 flags = flags or INLINE
293 
294                 // Workaround for b/117565118:
295                 val func = sourcePsi as? KtNamedFunction
296                 if (
297                     func != null &&
298                         (func.typeParameterList?.text ?: "").contains(
299                             KtTokens.REIFIED_KEYWORD.value
300                         ) &&
301                         !ktModifierList.hasModifier(KtTokens.PRIVATE_KEYWORD) &&
302                         !ktModifierList.hasModifier(KtTokens.INTERNAL_KEYWORD)
303                 ) {
304                     // Switch back from private to public
305                     visibilityFlags = PUBLIC
306                 }
307             }
308             if (ktModifierList.hasValueModifier()) {
309                 flags = flags or VALUE
310             }
311             if (ktModifierList.hasSuspendModifier()) {
312                 flags = flags or SUSPEND
313             }
314             if (ktModifierList.hasModifier(KtTokens.COMPANION_KEYWORD)) {
315                 flags = flags or COMPANION
316             }
317             if (ktModifierList.hasFunModifier()) {
318                 flags = flags or FUN
319             }
320             if (ktModifierList.hasModifier(KtTokens.DATA_KEYWORD)) {
321                 flags = flags or DATA
322             }
323             if (ktModifierList.hasExpectModifier()) {
324                 flags = flags or EXPECT
325             }
326             if (ktModifierList.hasActualModifier()) {
327                 flags = flags or ACTUAL
328             }
329         }
330         // Methods that are property accessors inherit visibility from the source element
331         if (element is UMethod && (element.sourceElement is KtPropertyAccessor)) {
332             val sourceElement = element.sourceElement
333             if (sourceElement is KtModifierListOwner) {
334                 val sourceModifierList = sourceElement.modifierList
335                 if (sourceModifierList != null) {
336                     if (sourceModifierList.hasModifier(KtTokens.INTERNAL_KEYWORD)) {
337                         visibilityFlags = INTERNAL
338                     }
339                 }
340             }
341         }
342 
343         // Merge in the visibility flags.
344         flags = flags or visibilityFlags
345 
346         return flags
347     }
348 
349     /**
350      * Returns a list of the targets this annotation is defined to apply to, as qualified names
351      * (e.g. "java.lang.annotation.ElementType.TYPE_USE").
352      *
353      * If the annotation can't be resolved or does not use `@Target`, returns an empty list.
354      */
355     private fun PsiAnnotation.targets(): List<String> {
356         return resolveAnnotationType()
357             ?.annotations
358             ?.firstOrNull { it.hasQualifiedName(JAVA_LANG_ANNOTATION_TARGET) }
359             ?.findAttributeValue("value")
360             ?.targets()
361             ?: emptyList()
362     }
363 
364     /**
365      * Returns the element types listed in the annotation value, if the value is a direct reference
366      * or an array of direct references.
367      */
368     private fun PsiAnnotationMemberValue.targets(): List<String> {
369         return when (this) {
370             is PsiReferenceExpression -> listOf(qualifiedName)
371             is PsiArrayInitializerMemberValue ->
372                 initializers.mapNotNull { (it as? PsiReferenceExpression)?.qualifiedName }
373             else -> emptyList()
374         }
375     }
376 
377     /**
378      * Annotations which are only `TYPE_USE` should only apply to types, not the owning item of the
379      * type. However, psi incorrectly applies these items to both their types and owning items.
380      *
381      * If an annotation targets both `TYPE_USE` and other use sites, it should be applied to both
382      * types and owning items of the type if the owning item is one of the targeted use sites. See
383      * https://docs.oracle.com/javase/specs/jls/se11/html/jls-9.html#jls-9.7.4 for more detail.
384      *
385      * To work around psi incorrectly applying exclusively `TYPE_USE` annotations to non-type items,
386      * this filters all annotations which should apply to types but not the [forOwner] item.
387      */
388     private fun List<PsiAnnotation>.filterIncorrectTypeUseAnnotations(
389         forOwner: PsiModifierListOwner
390     ): List<PsiAnnotation> {
391         val expectedTarget =
392             when (forOwner) {
393                 is PsiMethod -> "java.lang.annotation.ElementType.METHOD"
394                 is PsiParameter -> "java.lang.annotation.ElementType.PARAMETER"
395                 is PsiField -> "java.lang.annotation.ElementType.FIELD"
396                 else -> return this
397             }
398 
399         return filter { annotation ->
400             val applicableTargets = annotation.targets()
401             // If the annotation is not type use, it has been correctly applied to the item.
402             !applicableTargets.contains(JAVA_LANG_TYPE_USE_TARGET) ||
403                 // If the annotation has the item type as a target, it should be applied here.
404                 applicableTargets.contains(expectedTarget) ||
405                 // For now, leave in nullness annotations until they are specially handled.
406                 isNullnessAnnotation(annotation.qualifiedName.orEmpty())
407         }
408     }
409 
410     private fun create(
411         codebase: PsiBasedCodebase,
412         element: PsiModifierListOwner
413     ): DefaultModifierList {
414         var flags =
415             element.modifierList?.let { modifierList -> computeFlag(element, modifierList) }
416                 ?: PACKAGE_PRIVATE
417 
418         val psiAnnotations = element.annotations
419         return if (psiAnnotations.isEmpty()) {
420             DefaultModifierList(codebase, flags)
421         } else {
422             val annotations: MutableList<AnnotationItem> =
423                 // psi sometimes returns duplicate annotations, using distinct() to counter
424                 // that.
425                 psiAnnotations
426                     .distinct()
427                     // Remove any type-use annotations that psi incorrectly applied to the item.
428                     .filterIncorrectTypeUseAnnotations(element)
429                     .map {
430                         val qualifiedName = it.qualifiedName
431                         // Consider also supporting
432                         // com.android.internal.annotations.VisibleForTesting?
433                         if (qualifiedName == ANDROIDX_VISIBLE_FOR_TESTING) {
434                             val otherwise = it.findAttributeValue(ATTR_OTHERWISE)
435                             val ref =
436                                 when {
437                                     otherwise is PsiReferenceExpression -> otherwise.referenceName
438                                             ?: ""
439                                     otherwise != null -> otherwise.text
440                                     else -> ""
441                                 }
442                             flags = getVisibilityFlag(ref, flags)
443                         }
444 
445                         PsiAnnotationItem.create(codebase, it, qualifiedName)
446                     }
447                     .filter { !it.isDeprecatedForSdk() }
448                     .toMutableList()
449             DefaultModifierList(codebase, flags, annotations)
450         }
451     }
452 
453     private fun create(
454         codebase: PsiBasedCodebase,
455         element: PsiModifierListOwner,
456         annotated: UAnnotated
457     ): DefaultModifierList {
458         val modifierList = element.modifierList ?: return DefaultModifierList(codebase)
459         val uAnnotations = annotated.uAnnotations
460         val psiAnnotations =
461             modifierList.annotations.takeIf { it.isNotEmpty() }
462                 ?: (annotated.javaPsi as? PsiModifierListOwner)?.annotations
463                     ?: PsiAnnotation.EMPTY_ARRAY
464 
465         var flags = computeFlag(element, modifierList)
466 
467         return if (uAnnotations.isEmpty()) {
468             if (psiAnnotations.isNotEmpty()) {
469                 val annotations: MutableList<AnnotationItem> =
470                     psiAnnotations.map { PsiAnnotationItem.create(codebase, it) }.toMutableList()
471                 DefaultModifierList(codebase, flags, annotations)
472             } else {
473                 DefaultModifierList(codebase, flags)
474             }
475         } else {
476             val isPrimitiveVariable = element is UVariable && element.type is PsiPrimitiveType
477 
478             val annotations: MutableList<AnnotationItem> =
479                 uAnnotations
480                     // Uast sometimes puts nullability annotations on primitives!?
481                     .filter {
482                         !isPrimitiveVariable ||
483                             it.qualifiedName == null ||
484                             !it.isKotlinNullabilityAnnotation
485                     }
486                     .map {
487                         val qualifiedName = it.qualifiedName
488                         if (qualifiedName == ANDROIDX_VISIBLE_FOR_TESTING) {
489                             val otherwise = it.findAttributeValue(ATTR_OTHERWISE)
490                             val ref =
491                                 when {
492                                     otherwise is PsiReferenceExpression -> otherwise.referenceName
493                                             ?: ""
494                                     otherwise != null -> otherwise.asSourceString()
495                                     else -> ""
496                                 }
497                             flags = getVisibilityFlag(ref, flags)
498                         }
499 
500                         UAnnotationItem.create(codebase, it, qualifiedName)
501                     }
502                     .filter { !it.isDeprecatedForSdk() }
503                     .toMutableList()
504 
505             if (!isPrimitiveVariable) {
506                 if (psiAnnotations.isNotEmpty() && annotations.none { it.isNullnessAnnotation() }) {
507                     val ktNullAnnotation =
508                         psiAnnotations.firstOrNull { psiAnnotation ->
509                             psiAnnotation.qualifiedName?.let { isNullnessAnnotation(it) } == true
510                         }
511                     ktNullAnnotation?.let {
512                         annotations.add(PsiAnnotationItem.create(codebase, it))
513                     }
514                 }
515             }
516 
517             DefaultModifierList(codebase, flags, annotations)
518         }
519     }
520 
521     /** Returns whether this is a `@DeprecatedForSdk` annotation **that should be skipped**. */
522     private fun AnnotationItem.isDeprecatedForSdk(): Boolean {
523         if (qualifiedName != ANDROID_DEPRECATED_FOR_SDK) {
524             return false
525         }
526 
527         val allowIn = findAttribute(ATTR_ALLOW_IN) ?: return false
528 
529         for (api in allowIn.leafValues()) {
530             val annotationName = api.value() as? String ?: continue
531             if (codebase.annotationManager.isShowAnnotationName(annotationName)) {
532                 return true
533             }
534         }
535 
536         return false
537     }
538 
539     private val NOT_NULL = NotNull::class.qualifiedName
540     private val NULLABLE = Nullable::class.qualifiedName
541 
542     private val UAnnotation.isKotlinNullabilityAnnotation: Boolean
543         get() = qualifiedName == NOT_NULL || qualifiedName == NULLABLE
544 
545     /** Modifies the modifier flags based on the VisibleForTesting otherwise constants */
546     private fun getVisibilityFlag(ref: String, flags: Int): Int {
547         val visibilityFlags =
548             if (ref.endsWith("PROTECTED")) {
549                 PROTECTED
550             } else if (ref.endsWith("PACKAGE_PRIVATE")) {
551                 PACKAGE_PRIVATE
552             } else if (ref.endsWith("PRIVATE") || ref.endsWith("NONE")) {
553                 PRIVATE
554             } else {
555                 flags and VISIBILITY_MASK
556             }
557 
558         return (flags and VISIBILITY_MASK.inv()) or visibilityFlags
559     }
560 }
561 
562 private const val ANDROIDX_VISIBLE_FOR_TESTING = "androidx.annotation.VisibleForTesting"
563 private const val ATTR_OTHERWISE = "otherwise"
564 private const val ATTR_ALLOW_IN = "allowIn"
565