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:
@@ -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 =
|
||||
|
@@ -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))
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user