check-flagged-apis: handle nested flags
Consider @FlaggedApi(FLAG_OUTER) Clazz { @FlaggedApi(FLAG_INNER) method(); } If FLAG_OUTER is disabled, any class members are ignored. Teach check-flagged-apis to recognize this and stop reporting false positives. Bug: 339183637 Test: atest --host check-flagged-apis-test Change-Id: Ie6799e952dc33874c1239231f841d7dfd947c7ce
This commit is contained in:
parent
32b652fd68
commit
0d44e721ed
2 changed files with 75 additions and 1 deletions
|
@ -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 =
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue