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