Merge "check-flagged-apis: handle nested flags" into main am: 7c71d88e0b

Original change: https://android-review.googlesource.com/c/platform/build/+/3079003

Change-Id: I132dd6e63ccd32f5acf28e335cf3326417e16936
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Treehugger Robot
2024-05-13 07:55:44 +00:00
committed by Automerger Merge Worker
2 changed files with 75 additions and 1 deletions

View File

@@ -269,6 +269,42 @@ class CheckFlaggedApisTest {
assertEquals(expected, actual)
}
@Test
fun testNestedFlagsOuterFlagWins() {
val apiSignature =
"""
// Signature format: 2.0
package android {
@FlaggedApi("android.flag.foo") public final class A {
method @FlaggedApi("android.flag.bar") public boolean method();
}
@FlaggedApi("android.flag.bar") public final class B {
method @FlaggedApi("android.flag.foo") public boolean method();
}
}
"""
.trim()
val apiVersions =
"""
<?xml version="1.0" encoding="utf-8"?>
<api version="3">
<class name="android/B" since="1">
<extends name="java/lang/Object"/>
</class>
</api>
"""
.trim()
val expected = setOf<ApiError>()
val actual =
findErrors(
parseApiSignature("in-memory", apiSignature.byteInputStream()),
parseFlagValues(generateFlagsProto(DISABLED, ENABLED)),
parseApiVersions(apiVersions.byteInputStream()))
assertEquals(expected, actual)
}
@Test
fun testFindErrorsDisabledFlaggedApiIsPresent() {
val expected =

View File

@@ -385,10 +385,48 @@ internal fun findErrors(
return false
}
/**
* Returns whether the given flag is enabled for the given symbol.
*
* A flagged member inside a flagged class is ignored (and the flag value considered disabled) if
* the class' flag is disabled.
*
* @param symbol the symbol to check
* @param flag the flag to check
* @return whether the flag is enabled for the given symbol
*/
fun isFlagEnabledForSymbol(symbol: Symbol, flag: Flag): Boolean {
when (symbol) {
is ClassSymbol -> return flags.getValue(flag)
is MemberSymbol -> {
val memberFlagValue = flags.getValue(flag)
if (!memberFlagValue) {
return false
}
// Special case: if the MemberSymbol's flag is enabled, but the outer
// ClassSymbol's flag (if the class is flagged) is disabled, consider
// the MemberSymbol's flag as disabled:
//
// @FlaggedApi(this-flag-is-disabled) Clazz {
// @FlaggedApi(this-flag-is-enabled) method(); // The Clazz' flag "wins"
// }
//
// Note: the current implementation does not handle nested classes.
val classFlagValue =
flaggedSymbolsInSource
.find { it.first.toPrettyString() == symbol.clazz }
?.let { flags.getValue(it.second) }
?: true
return classFlagValue
}
}
}
val errors = mutableSetOf<ApiError>()
for ((symbol, flag) in flaggedSymbolsInSource) {
try {
if (flags.getValue(flag)) {
if (isFlagEnabledForSymbol(symbol, flag)) {
if (!symbolsInOutput.containsSymbol(symbol)) {
errors.add(EnabledFlaggedApiNotPresentError(symbol, flag))
}