check-flagged-apis: consider interfaces when looking up symbol

When searching for potential errors, if a symbol can't be found in the
api-verions.xml data, check if it is present in any of the class'
interfaces.

A follow-up CL will add similar logic to handle super classes.

Bug: 334870672
Test: atest --host check-flagged-apis-test
Change-Id: Ia6dfcfa8495b89465db60f6a4eb77d304112046b
This commit is contained in:
Mårten Kongstad
2024-05-06 14:58:18 +02:00
parent 04d8b46c37
commit d2c707613e
2 changed files with 73 additions and 2 deletions

View File

@@ -171,6 +171,47 @@ class CheckFlaggedApisTest {
assertEquals(expected, actual)
}
@Test
fun testFindErrorsVerifyImplements() {
val apiSignature =
"""
// Signature format: 2.0
package android {
@FlaggedApi("android.flag.foo") public final class Clazz implements android.Interface {
method @FlaggedApi("android.flag.foo") public boolean foo();
method @FlaggedApi("android.flag.foo") public boolean bar();
}
public interface Interface {
method public boolean bar();
}
}
"""
.trim()
val apiVersions =
"""
<?xml version="1.0" encoding="utf-8"?>
<api version="3">
<class name="android/Clazz" since="1">
<implements name="android/Interface"/>
<method name="foo()Z"/>
</class>
<class name="android/Interface" since="1">
<method name="bar()Z"/>
</class>
</api>
"""
.trim()
val expected = setOf<ApiError>()
val actual =
findErrors(
parseApiSignature("in-memory", apiSignature.byteInputStream()),
parseFlagValues(generateFlagsProto(ENABLED, ENABLED)),
parseApiVersions(apiVersions.byteInputStream()))
assertEquals(expected, actual)
}
@Test
fun testFindErrorsDisabledFlaggedApiIsPresent() {
val expected =

View File

@@ -333,15 +333,45 @@ internal fun findErrors(
flags: Map<Flag, Boolean>,
symbolsInOutput: Set<Symbol>
): Set<ApiError> {
fun Set<Symbol>.containsSymbol(symbol: Symbol): Boolean {
// trivial case: the symbol is explicitly listed in api-versions.xml
if (contains(symbol)) {
return true
}
// non-trivial case: the symbol could be part of the surrounding class'
// super class or interfaces
val (className, memberName) =
when (symbol) {
is ClassSymbol -> return false
is MemberSymbol -> {
Pair(symbol.clazz, symbol.member)
}
}
val clazz = find { it is ClassSymbol && it.clazz == className } as? ClassSymbol?
if (clazz == null) {
return false
}
for (interfaceName in clazz.interfaces) {
// createMethod is the same as createField, except it allows parenthesis
val interfaceSymbol = Symbol.createMethod(interfaceName, memberName)
if (contains(interfaceSymbol)) {
return true
}
}
return false
}
val errors = mutableSetOf<ApiError>()
for ((symbol, flag) in flaggedSymbolsInSource) {
try {
if (flags.getValue(flag)) {
if (!symbolsInOutput.contains(symbol)) {
if (!symbolsInOutput.containsSymbol(symbol)) {
errors.add(EnabledFlaggedApiNotPresentError(symbol, flag))
}
} else {
if (symbolsInOutput.contains(symbol)) {
if (symbolsInOutput.containsSymbol(symbol)) {
errors.add(DisabledFlaggedApiIsPresentError(symbol, flag))
}
}