Merge remote-tracking branch 'aosp/metalava-main' into 'aosp/main'
Merge performed by:
scripts/merge-from-metalava-main.sh
This merge includes a number of changes so this contains a list of all
the affected bugs.
Bug: 298052340
Bug: 299048934
Bug: 302579866
Bug: 302654813
Bug: 303787736
Bug: 303842244
Bug: 304226685
Bug: 304503426
Test: m checkapi
(cherry picked from https://android-review.googlesource.com/q/commit:51c3353bb95eedced2ec230c44f8ab5dcd690a72)
Merged-In: I3da9b00ba29ff97155038adf21c3b59e6c2271d3
Change-Id: I3da9b00ba29ff97155038adf21c3b59e6c2271d3
diff --git a/metalava-model-psi/src/main/java/com/android/tools/metalava/model/psi/PsiPackageItem.kt b/metalava-model-psi/src/main/java/com/android/tools/metalava/model/psi/PsiPackageItem.kt
index 9b08fd5..e5fb166 100644
--- a/metalava-model-psi/src/main/java/com/android/tools/metalava/model/psi/PsiPackageItem.kt
+++ b/metalava-model-psi/src/main/java/com/android/tools/metalava/model/psi/PsiPackageItem.kt
@@ -47,12 +47,9 @@
lateinit var containingPackageField: PsiPackageItem
- override fun containingClass(strict: Boolean): ClassItem? = null
+ override fun containingClass(): ClassItem? = null
- override fun containingPackage(strict: Boolean): PackageItem? {
- if (!strict) {
- return this
- }
+ override fun containingPackage(): PackageItem? {
return if (qualifiedName.isEmpty()) null
else {
if (!::containingPackageField.isInitialized) {
diff --git a/metalava-model-psi/src/main/java/com/android/tools/metalava/model/psi/PsiSourceFileItem.kt b/metalava-model-psi/src/main/java/com/android/tools/metalava/model/psi/PsiSourceFileItem.kt
index 62f19f9..d20d68e 100644
--- a/metalava-model-psi/src/main/java/com/android/tools/metalava/model/psi/PsiSourceFileItem.kt
+++ b/metalava-model-psi/src/main/java/com/android/tools/metalava/model/psi/PsiSourceFileItem.kt
@@ -229,7 +229,7 @@
.orEmpty()
}
- override fun containingPackage(strict: Boolean): PackageItem? {
+ override fun containingPackage(): PackageItem? {
return when {
uFile != null -> codebase.findPackage(uFile.packageName)
file is PsiJavaFile -> codebase.findPackage(file.packageName)
diff --git a/metalava-model-testsuite/src/main/java/com/android/tools/metalava/model/testsuite/BootstrapSourceModelProviderTest.kt b/metalava-model-testsuite/src/main/java/com/android/tools/metalava/model/testsuite/BootstrapSourceModelProviderTest.kt
index 54b891b..3bac336 100644
--- a/metalava-model-testsuite/src/main/java/com/android/tools/metalava/model/testsuite/BootstrapSourceModelProviderTest.kt
+++ b/metalava-model-testsuite/src/main/java/com/android/tools/metalava/model/testsuite/BootstrapSourceModelProviderTest.kt
@@ -94,6 +94,7 @@
val testClass = codebase.assertClass("test.pkg.Test")
val fieldItem = testClass.assertField("field")
assertEquals("field", fieldItem.name())
+ assertEquals(testClass, fieldItem.containingClass())
}
}
@@ -234,11 +235,14 @@
) { codebase ->
val packageItem = codebase.assertPackage("test.pkg")
val parentPackageItem = codebase.assertPackage("test")
+ val rootPackageItem = codebase.assertPackage("")
val classItem = codebase.assertClass("test.pkg.Test")
val innerClassItem = codebase.assertClass("test.pkg.Test.Inner")
assertEquals(1, packageItem.topLevelClasses().count())
assertEquals(0, parentPackageItem.topLevelClasses().count())
assertEquals(parentPackageItem, packageItem.containingPackage())
+ assertEquals(rootPackageItem, parentPackageItem.containingPackage())
+ assertEquals(null, rootPackageItem.containingPackage())
assertEquals(packageItem, classItem.containingPackage())
assertEquals(packageItem, innerClassItem.containingPackage())
}
diff --git a/metalava-model-text/src/main/java/com/android/tools/metalava/model/text/TextPackageItem.kt b/metalava-model-text/src/main/java/com/android/tools/metalava/model/text/TextPackageItem.kt
index e8f01a9..4a3c694 100644
--- a/metalava-model-text/src/main/java/com/android/tools/metalava/model/text/TextPackageItem.kt
+++ b/metalava-model-text/src/main/java/com/android/tools/metalava/model/text/TextPackageItem.kt
@@ -61,7 +61,7 @@
override fun qualifiedName(): String = name
- override fun containingClass(strict: Boolean): ClassItem? = null
+ override fun containingClass(): ClassItem? = null
override fun equals(other: Any?): Boolean {
if (this === other) return true
diff --git a/metalava-model-turbine/src/main/java/com/android/tools/metalava/model/turbine/TurbineBasedCodebase.kt b/metalava-model-turbine/src/main/java/com/android/tools/metalava/model/turbine/TurbineBasedCodebase.kt
index 5541790..cce0e82 100644
--- a/metalava-model-turbine/src/main/java/com/android/tools/metalava/model/turbine/TurbineBasedCodebase.kt
+++ b/metalava-model-turbine/src/main/java/com/android/tools/metalava/model/turbine/TurbineBasedCodebase.kt
@@ -90,6 +90,10 @@
classMap.put(classItem.qualifiedName(), classItem)
}
+ fun addPackage(packageItem: TurbinePackageItem) {
+ packageMap.put(packageItem.qualifiedName(), packageItem)
+ }
+
fun initialize() {
topLevelClassesFromSource = ArrayList(CLASS_ESTIMATE)
classMap = HashMap(CLASS_ESTIMATE)
diff --git a/metalava-model-turbine/src/main/java/com/android/tools/metalava/model/turbine/TurbineClassItem.kt b/metalava-model-turbine/src/main/java/com/android/tools/metalava/model/turbine/TurbineClassItem.kt
index 10c676e..104d9c1 100644
--- a/metalava-model-turbine/src/main/java/com/android/tools/metalava/model/turbine/TurbineClassItem.kt
+++ b/metalava-model-turbine/src/main/java/com/android/tools/metalava/model/turbine/TurbineClassItem.kt
@@ -21,14 +21,12 @@
import com.android.tools.metalava.model.Codebase
import com.android.tools.metalava.model.ConstructorItem
import com.android.tools.metalava.model.FieldItem
-import com.android.tools.metalava.model.ItemVisitor
import com.android.tools.metalava.model.MethodItem
import com.android.tools.metalava.model.ModifierList
import com.android.tools.metalava.model.PackageItem
import com.android.tools.metalava.model.PropertyItem
import com.android.tools.metalava.model.TypeItem
import com.android.tools.metalava.model.TypeParameterList
-import com.android.tools.metalava.model.TypeVisitor
open class TurbineClassItem(
override val codebase: Codebase,
@@ -58,6 +56,10 @@
private var allInterfaces: List<TurbineClassItem>? = null
+ internal lateinit var containingPackage: TurbinePackageItem
+
+ internal lateinit var fields: List<TurbineFieldItem>
+
override fun allInterfaces(): Sequence<TurbineClassItem> {
if (allInterfaces == null) {
val interfaces = mutableSetOf<TurbineClassItem>()
@@ -87,13 +89,10 @@
override fun containingClass(): ClassItem? = containingClass
- override fun containingPackage(): PackageItem {
- TODO("b/295800205")
- }
+ override fun containingPackage(): PackageItem =
+ containingClass?.containingPackage() ?: containingPackage
- override fun fields(): List<FieldItem> {
- TODO("b/295800205")
- }
+ override fun fields(): List<FieldItem> = fields
override fun getRetention(): AnnotationRetention {
TODO("b/295800205")
@@ -158,17 +157,12 @@
TODO("b/295800205")
}
- override fun accept(visitor: ItemVisitor) {
- TODO("b/295800205")
- }
-
- override fun acceptTypes(visitor: TypeVisitor) {
- TODO("b/295800205")
- }
-
override fun hashCode(): Int = qualifiedName.hashCode()
override fun equals(other: Any?): Boolean {
+ if (this === other) {
+ return true
+ }
return other is ClassItem && qualifiedName() == other.qualifiedName()
}
}
diff --git a/metalava-model-turbine/src/main/java/com/android/tools/metalava/model/turbine/TurbineCodebaseInitialiser.kt b/metalava-model-turbine/src/main/java/com/android/tools/metalava/model/turbine/TurbineCodebaseInitialiser.kt
index a0decf2..438a386 100644
--- a/metalava-model-turbine/src/main/java/com/android/tools/metalava/model/turbine/TurbineCodebaseInitialiser.kt
+++ b/metalava-model-turbine/src/main/java/com/android/tools/metalava/model/turbine/TurbineCodebaseInitialiser.kt
@@ -27,7 +27,9 @@
import com.google.turbine.tree.Tree
import com.google.turbine.tree.Tree.CompUnit
import com.google.turbine.tree.Tree.Kind.TY_DECL
+import com.google.turbine.tree.Tree.Kind.VAR_DECL
import com.google.turbine.tree.Tree.TyDecl
+import com.google.turbine.tree.Tree.VarDecl
import java.io.File
import java.util.Optional
@@ -62,6 +64,8 @@
for (unit in units) {
createItems(unit)
}
+ // Create root package
+ findOrCreatePackage("")
// This method sets up hierarchy using only parsed source files
setInnerClassHierarchy(units)
@@ -81,7 +85,7 @@
}
/** Extracts data from the compilation units. A unit corresponds to one parsed source file. */
- fun createItems(unit: CompUnit) {
+ private fun createItems(unit: CompUnit) {
val optPkg = unit.pkg()
val pkg = if (optPkg.isPresent()) optPkg.get() else null
var pkgName = ""
@@ -89,22 +93,40 @@
val pkgNameList = pkg.name().map { it.value() }
pkgName = pkgNameList.joinToString(separator = ".")
}
+ val pkgItem = findOrCreatePackage(pkgName)
+
val typeDecls = unit.decls()
for (typeDecl in typeDecls) {
- populateClass(typeDecl, pkgName, null, true)
+ populateClass(typeDecl, pkgItem, null, true)
+ }
+ }
+
+ /**
+ * Searches for the package with supplied name in the codebase's package map and if not found
+ * creates the corresponding TurbinePackageItem and adds it to the package map.
+ */
+ private fun findOrCreatePackage(name: String): TurbinePackageItem {
+ val pkgItem = codebase.findPackage(name)
+ if (pkgItem != null) {
+ return pkgItem as TurbinePackageItem
+ } else {
+ val modifers = DefaultModifierList(codebase)
+ val turbinePkgItem = TurbinePackageItem(codebase, name, modifers)
+ codebase.addPackage(turbinePkgItem)
+ return turbinePkgItem
}
}
/** Creates a TurbineClassItem and adds the classitem to the various maps in codebase. */
- fun populateClass(
+ private fun populateClass(
typeDecl: TyDecl,
- pkgName: String,
+ pkgItem: TurbinePackageItem,
containingClass: TurbineClassItem?,
isTopClass: Boolean,
) {
val className = typeDecl.name().value()
val fullName = if (isTopClass) className else containingClass?.fullName() + "." + className
- val qualifiedName = pkgName + "." + fullName
+ val qualifiedName = pkgItem.qualifiedName() + "." + fullName
val modifers = DefaultModifierList(codebase)
val classItem =
TurbineClassItem(
@@ -118,23 +140,37 @@
)
val members = typeDecl.members()
+ val fields = mutableListOf<TurbineFieldItem>()
for (member in members) {
when (member.kind()) {
// A class or an interface declaration
TY_DECL -> {
- populateClass(member as TyDecl, pkgName, classItem, false)
+ populateClass(member as TyDecl, pkgItem, classItem, false)
+ }
+ // A field declaration
+ VAR_DECL -> {
+ val field = member as VarDecl
+ val fieldItem =
+ TurbineFieldItem(codebase, field.name().value(), classItem, modifers)
+ fields.add(fieldItem)
}
else -> {
// Do nothing for now
}
}
}
+ classItem.fields = fields
codebase.addClass(classItem, isTopClass)
itemMap.put(typeDecl, qualifiedName)
+
+ if (isTopClass) {
+ classItem.containingPackage = pkgItem
+ pkgItem.addTopClass(classItem)
+ }
}
/** This method sets up inner class hierarchy without using binder. */
- fun setInnerClassHierarchy(units: List<CompUnit>) {
+ private fun setInnerClassHierarchy(units: List<CompUnit>) {
for (unit in units) {
val typeDecls = unit.decls()
for (typeDecl in typeDecls) {
@@ -144,7 +180,7 @@
}
/** Method to setup innerclasses for a single class */
- fun setInnerClasses(typeDecl: TyDecl) {
+ private fun setInnerClasses(typeDecl: TyDecl) {
val className = itemMap[typeDecl]!!
val classItem = codebase.findClass(className)!!
val innerClasses =
@@ -159,7 +195,7 @@
}
/** This method uses output from binder to setup superclass and implemented interfaces */
- fun setSuperClassHierarchy(units: ImmutableMap<ClassSymbol, SourceTypeBoundClass>) {
+ private fun setSuperClassHierarchy(units: ImmutableMap<ClassSymbol, SourceTypeBoundClass>) {
for ((sym, cls) in units) {
val classItem = codebase.findClass(sym.toString())
if (classItem != null) {
diff --git a/metalava-model-turbine/src/main/java/com/android/tools/metalava/model/turbine/TurbineFieldItem.kt b/metalava-model-turbine/src/main/java/com/android/tools/metalava/model/turbine/TurbineFieldItem.kt
new file mode 100644
index 0000000..1b8e79e
--- /dev/null
+++ b/metalava-model-turbine/src/main/java/com/android/tools/metalava/model/turbine/TurbineFieldItem.kt
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tools.metalava.model.turbine
+
+import com.android.tools.metalava.model.ClassItem
+import com.android.tools.metalava.model.Codebase
+import com.android.tools.metalava.model.FieldItem
+import com.android.tools.metalava.model.ModifierList
+import com.android.tools.metalava.model.TypeItem
+
+class TurbineFieldItem(
+ override val codebase: Codebase,
+ private val name: String,
+ private val containingClass: TurbineClassItem,
+ override val modifiers: ModifierList,
+) : TurbineItem(codebase, modifiers), FieldItem {
+
+ override var inheritedFrom: ClassItem? = null
+
+ override var inheritedField: Boolean = false
+
+ override fun name(): String = name
+
+ override fun containingClass(): ClassItem = containingClass
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) {
+ return true
+ }
+ return other is FieldItem &&
+ name == other.name() &&
+ containingClass == other.containingClass()
+ }
+
+ override fun hashCode(): Int = name.hashCode()
+
+ override fun type(): TypeItem {
+ TODO("b/295800205")
+ }
+
+ override fun duplicate(targetContainingClass: ClassItem): FieldItem {
+ TODO("b/295800205")
+ }
+
+ override fun initialValue(requireConstant: Boolean): Any? {
+ TODO("b/295800205")
+ }
+
+ override fun isEnumConstant(): Boolean {
+ TODO("b/295800205")
+ }
+}
diff --git a/metalava-model-turbine/src/main/java/com/android/tools/metalava/model/turbine/TurbinePackageItem.kt b/metalava-model-turbine/src/main/java/com/android/tools/metalava/model/turbine/TurbinePackageItem.kt
new file mode 100644
index 0000000..89409b2
--- /dev/null
+++ b/metalava-model-turbine/src/main/java/com/android/tools/metalava/model/turbine/TurbinePackageItem.kt
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tools.metalava.model.turbine
+
+import com.android.tools.metalava.model.ClassItem
+import com.android.tools.metalava.model.Codebase
+import com.android.tools.metalava.model.ModifierList
+import com.android.tools.metalava.model.PackageItem
+
+class TurbinePackageItem(
+ override val codebase: Codebase,
+ private val qualifiedName: String,
+ override val modifiers: ModifierList,
+) : PackageItem, TurbineItem(codebase, modifiers) {
+
+ private var topClasses = mutableListOf<TurbineClassItem>()
+
+ private var containingPackage: PackageItem? = null
+
+ // N.A. a package cannot be contained in a class
+ override fun containingClass(): ClassItem? = null
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) {
+ return true
+ }
+ return other is PackageItem && qualifiedName == other.qualifiedName()
+ }
+
+ override fun hashCode(): Int = qualifiedName.hashCode()
+
+ override fun qualifiedName(): String = qualifiedName
+
+ override fun topLevelClasses(): Sequence<ClassItem> = topClasses.asSequence()
+
+ internal fun addTopClass(classItem: TurbineClassItem) {
+ topClasses.add(classItem)
+ }
+
+ override fun containingPackage(): PackageItem? {
+ // if this package is root package, then return null
+ return if (qualifiedName.isEmpty()) null
+ else {
+ if (containingPackage == null) {
+ // If package is of the form A.B then the containing package is A
+ // If package is top level, then containing package is the root package
+ val name = qualifiedName()
+ val lastDot = name.lastIndexOf('.')
+ containingPackage =
+ if (lastDot != -1) codebase.findPackage(name.substring(0, lastDot))
+ else codebase.findPackage("")
+ }
+ return containingPackage
+ }
+ }
+}
diff --git a/metalava-model-turbine/src/test/resources/model-test-suite-baseline.txt b/metalava-model-turbine/src/test/resources/model-test-suite-baseline.txt
index e17e011..b2f1a41 100644
--- a/metalava-model-turbine/src/test/resources/model-test-suite-baseline.txt
+++ b/metalava-model-turbine/src/test/resources/model-test-suite-baseline.txt
@@ -1,9 +1,6 @@
com.android.tools.metalava.model.testsuite.BootstrapSourceModelProviderTest
- 040 - check package exists[turbine,java]
- 050 - check field exists[turbine,java]
060 - check method exists[turbine,java]
070 - check constructor exists[turbine,java]
- 110 - advanced package test[turbine,java]
com.android.tools.metalava.model.testsuite.CommonClassItemTest
empty class[turbine,java]
diff --git a/metalava-model/src/main/java/com/android/tools/metalava/model/ClassItem.kt b/metalava-model/src/main/java/com/android/tools/metalava/model/ClassItem.kt
index 7151bc7..6913e01 100644
--- a/metalava-model/src/main/java/com/android/tools/metalava/model/ClassItem.kt
+++ b/metalava-model/src/main/java/com/android/tools/metalava/model/ClassItem.kt
@@ -194,16 +194,10 @@
fun isClass(): Boolean = !isInterface() && !isAnnotationType() && !isEnum()
/** The containing class, for inner classes */
- @MetalavaApi fun containingClass(): ClassItem?
+ @MetalavaApi override fun containingClass(): ClassItem?
/** The containing package */
- fun containingPackage(): PackageItem
-
- override fun containingPackage(strict: Boolean): PackageItem = containingPackage()
-
- override fun containingClass(strict: Boolean): ClassItem? {
- return if (strict) containingClass() else this
- }
+ override fun containingPackage(): PackageItem
/** Gets the type for this class */
fun toType(): TypeItem
diff --git a/metalava-model/src/main/java/com/android/tools/metalava/model/ConstructorItem.kt b/metalava-model/src/main/java/com/android/tools/metalava/model/ConstructorItem.kt
index f6807ea..5d71275 100644
--- a/metalava-model/src/main/java/com/android/tools/metalava/model/ConstructorItem.kt
+++ b/metalava-model/src/main/java/com/android/tools/metalava/model/ConstructorItem.kt
@@ -23,9 +23,8 @@
override fun internalName(): String = "<init>"
/**
- * The constructor that this method delegates to initially (e.g. super- or this- or
- * default/implicit null constructor). Note that it may not be in a super class, as in the case
- * of a this-call.
+ * The constructor that the stub version of this constructor must delegate to in its `super`
+ * call. Is `null` if the super class has a default constructor.
*/
var superConstructor: ConstructorItem?
diff --git a/metalava-model/src/main/java/com/android/tools/metalava/model/Item.kt b/metalava-model/src/main/java/com/android/tools/metalava/model/Item.kt
index 98414a7..83d12f9 100644
--- a/metalava-model/src/main/java/com/android/tools/metalava/model/Item.kt
+++ b/metalava-model/src/main/java/com/android/tools/metalava/model/Item.kt
@@ -270,19 +270,11 @@
*/
fun describe(capitalize: Boolean = false) = describe(this, capitalize)
- /**
- * Returns the package that contains this item. If [strict] is false, this will return self if
- * called on a package, otherwise it will return the containing package (e.g. "foo" for
- * "foo.bar"). The parameter is ignored on other item types.
- */
- fun containingPackage(strict: Boolean = true): PackageItem?
+ /** Returns the package that contains this item. */
+ fun containingPackage(): PackageItem?
- /**
- * Returns the class that contains this item. If [strict] is false, this will return self if
- * called on a class, otherwise it will return the outer class, if any. The parameter is ignored
- * on other item types.
- */
- fun containingClass(strict: Boolean = true): ClassItem?
+ /** Returns the class that contains this item. */
+ fun containingClass(): ClassItem?
/**
* Returns the associated type if any. For example, for a field, property or parameter, this is
diff --git a/metalava-model/src/main/java/com/android/tools/metalava/model/MemberItem.kt b/metalava-model/src/main/java/com/android/tools/metalava/model/MemberItem.kt
index 04baf26..a57ec0f 100644
--- a/metalava-model/src/main/java/com/android/tools/metalava/model/MemberItem.kt
+++ b/metalava-model/src/main/java/com/android/tools/metalava/model/MemberItem.kt
@@ -27,12 +27,9 @@
fun internalName(): String = name()
/** The containing class */
- @MetalavaApi fun containingClass(): ClassItem
+ @MetalavaApi override fun containingClass(): ClassItem
- override fun containingClass(strict: Boolean): ClassItem = containingClass()
-
- override fun containingPackage(strict: Boolean): PackageItem =
- containingClass().containingPackage(false)
+ override fun containingPackage(): PackageItem = containingClass().containingPackage()
override fun parent(): ClassItem? = containingClass()
diff --git a/metalava-model/src/main/java/com/android/tools/metalava/model/MethodItem.kt b/metalava-model/src/main/java/com/android/tools/metalava/model/MethodItem.kt
index fa98dbe..899eae0 100644
--- a/metalava-model/src/main/java/com/android/tools/metalava/model/MethodItem.kt
+++ b/metalava-model/src/main/java/com/android/tools/metalava/model/MethodItem.kt
@@ -575,6 +575,30 @@
}
}
+ private fun getUniqueSuperInterfaceMethods(
+ superInterfaceMethods: List<MethodItem>
+ ): List<MethodItem> {
+ val visitCountMap = mutableMapOf<ClassItem, Int>()
+
+ // perform BFS on all super interfaces of each super interface methods'
+ // containing interface to determine the leaf interface of each unique hierarchy.
+ superInterfaceMethods.forEach {
+ val superInterface = it.containingClass()
+ val queue = mutableListOf(superInterface)
+ while (queue.isNotEmpty()) {
+ val s = queue.removeFirst()
+ visitCountMap[s] = visitCountMap.getOrDefault(s, 0) + 1
+ queue.addAll(
+ s.interfaceTypes().mapNotNull { interfaceType -> interfaceType.asClass() }
+ )
+ }
+ }
+
+ // If visit count is greater than 1, it means the interface is within the hierarchy of
+ // another method, thus filter out.
+ return superInterfaceMethods.filter { visitCountMap[it.containingClass()]!! == 1 }
+ }
+
/**
* Determines if the method needs to be added to the signature file in order to prevent errors
* when compiling the stubs or the reverse dependencies of the stubs.
@@ -593,10 +617,34 @@
// changes)
!sameSignature(this, it.first())
} else {
- it.all { superMethod ->
- superMethod.containingClass().isJavaLangObject() ||
- superMethod.requiresOverride()
- }
+ // Since a class can extend a single class except Object,
+ // there is only one non-Object super class method at max.
+ val superClassMethods =
+ it.firstOrNull { superMethod ->
+ superMethod.containingClass().isClass() &&
+ !superMethod.containingClass().isJavaLangObject()
+ }
+
+ // Assume a class implements two interfaces A and B;
+ // A provides a default super method, and B provides an abstract super method.
+ // In such case, the child method is a required overriding method when:
+ // - A and B do not extend each other or
+ // - A is a super interface of B
+ // On the other hand, the child method is not a required overriding method when:
+ // - B is a super interface of A
+ // Given this, we should make decisions only based on the leaf interface of each
+ // unique hierarchy.
+ val uniqueSuperInterfaceMethods =
+ getUniqueSuperInterfaceMethods(
+ it.filter { superMethod -> superMethod.containingClass().isInterface() }
+ )
+
+ // If super method is non-null, whether this method is required
+ // is determined by whether the super method requires override.
+ // If super method is null, this method is required if there is a
+ // unique super interface that requires override.
+ superClassMethods?.requiresOverride()
+ ?: uniqueSuperInterfaceMethods.any { s -> s.requiresOverride() }
}
}) ||
// To inherit methods with override-equivalent signatures
diff --git a/metalava-model/src/main/java/com/android/tools/metalava/model/PackageItem.kt b/metalava-model/src/main/java/com/android/tools/metalava/model/PackageItem.kt
index 3ee5e12..fd303d8 100644
--- a/metalava-model/src/main/java/com/android/tools/metalava/model/PackageItem.kt
+++ b/metalava-model/src/main/java/com/android/tools/metalava/model/PackageItem.kt
@@ -36,10 +36,7 @@
override fun parent(): PackageItem? =
if (qualifiedName().isEmpty()) null else containingPackage()
- override fun containingPackage(strict: Boolean): PackageItem? {
- if (!strict) {
- return this
- }
+ override fun containingPackage(): PackageItem? {
val name = qualifiedName()
val lastDot = name.lastIndexOf('.')
return if (lastDot != -1) {
diff --git a/metalava-model/src/main/java/com/android/tools/metalava/model/ParameterItem.kt b/metalava-model/src/main/java/com/android/tools/metalava/model/ParameterItem.kt
index 9ea761b..6630cbf 100644
--- a/metalava-model/src/main/java/com/android/tools/metalava/model/ParameterItem.kt
+++ b/metalava-model/src/main/java/com/android/tools/metalava/model/ParameterItem.kt
@@ -126,11 +126,9 @@
return null
}
- override fun containingClass(strict: Boolean): ClassItem? =
- containingMethod().containingClass(false)
+ override fun containingClass(): ClassItem? = containingMethod().containingClass()
- override fun containingPackage(strict: Boolean): PackageItem? =
- containingMethod().containingPackage(false)
+ override fun containingPackage(): PackageItem? = containingMethod().containingPackage()
// TODO: modifier list
}
diff --git a/metalava-model/src/main/java/com/android/tools/metalava/model/SourceFileItem.kt b/metalava-model/src/main/java/com/android/tools/metalava/model/SourceFileItem.kt
index 16faaa1..c4e09ed 100644
--- a/metalava-model/src/main/java/com/android/tools/metalava/model/SourceFileItem.kt
+++ b/metalava-model/src/main/java/com/android/tools/metalava/model/SourceFileItem.kt
@@ -29,7 +29,7 @@
override fun parent(): PackageItem? = containingPackage()
- override fun containingClass(strict: Boolean): ClassItem? = null
+ override fun containingClass(): ClassItem? = null
override fun type(): TypeItem? = null
diff --git a/metalava/src/main/java/com/android/tools/metalava/ApiAnalyzer.kt b/metalava/src/main/java/com/android/tools/metalava/ApiAnalyzer.kt
index 1904ec9..68f3b9c 100644
--- a/metalava/src/main/java/com/android/tools/metalava/ApiAnalyzer.kt
+++ b/metalava/src/main/java/com/android/tools/metalava/ApiAnalyzer.kt
@@ -162,16 +162,14 @@
}
/**
- * Handle computing constructor hierarchy. We'll be setting several attributes:
- * [ClassItem.stubConstructor] : The default constructor to invoke in this class from
- * subclasses. **NOTE**: This constructor may not be part of the [ClassItem.constructors] list,
- * e.g. for package private default constructors we've inserted (because there were no public
- * constructors or constructors not using hidden parameter types.)
+ * Handle computing constructor hierarchy.
*
- * If we can find a public constructor we'll put that here instead.
+ * We'll be setting several attributes: [ClassItem.stubConstructor] : The default constructor to
+ * invoke in this class from subclasses. **NOTE**: This constructor may not be part of the
+ * [ClassItem.constructors] list, e.g. for package private default constructors we've inserted
+ * (because there were no public constructors or constructors not using hidden parameter types.)
*
- * [ConstructorItem.superConstructor] The default constructor to invoke. If set, use this rather
- * than the [ClassItem.stubConstructor].
+ * [ConstructorItem.superConstructor] : The default constructor to invoke.
*
* @param visited contains the [ClassItem]s that have already been visited; this method adds
* [cls] to it so [cls] will not be visited again.
@@ -186,33 +184,25 @@
// public class A { public A(int) }
// package bar
// public class B extends A { public B(int) }
- // If I just try inserting package private constructors here things will NOT work:
+ // If we just try inserting package private constructors here things will NOT work:
// package foo:
// public class A { public A(int); A() {} }
// package bar
// public class B extends A { public B(int); B() }
- // because A <() is not accessible from B() -- it's outside the same package.
+ // because A <() is not accessible from B() -- it's outside the same package.
//
- // So, I'll need to model the real constructors for all the scenarios where that
- // works.
+ // So, we'll need to model the real constructors for all the scenarios where that works.
//
- // The remaining challenge is that there will be some gaps: when I don't have
- // a default constructor, subclass constructors will have to have an explicit
- // super(args) call to pick the parent constructor to use. And which one?
- // It generally doesn't matter; just pick one, but unfortunately, the super
- // constructor can throw exceptions, and in that case the subclass constructor
- // must also throw all those constructors (you can't surround a super call
- // with try/catch.) Luckily, the source code already needs to do this to
- // compile, so we can just use the same constructor as the super call.
- // But there are two cases we have to deal with:
- // (1) the constructor doesn't call a super constructor; it calls another
- // constructor on this class.
- // (2) the super constructor it *does* call isn't available.
+ // The remaining challenge is that there will be some gaps: when we don't have a default
+ // constructor, subclass constructors will have to have an explicit super(args) call to pick
+ // the parent constructor to use. And which one? It generally doesn't matter; just pick one,
+ // but unfortunately, the super constructor can throw exceptions, and in that case the
+ // subclass constructor must also throw all those exceptions (you can't surround a super
+ // call with try/catch.)
//
- // For (1), this means that our stub code generator should be prepared to
- // handle both super- and this- dispatches; we'll handle this by pointing
- // it to the constructor to use, and it checks to see if the containing class
- // for the constructor is the same to decide whether to emit "this" or "super".
+ // Luckily, this does not seem to be an actual problem with any of the source code that
+ // metalava currently processes. If it did become a problem then the solution would be to
+ // pick super constructors with a compatible set of throws.
if (cls in visited) {
return
@@ -234,45 +224,40 @@
val superClass = cls.filteredSuperclass(filter)
superClass?.let { addConstructors(it, filter, visited) }
- if (superClass != null) {
- val superDefaultConstructor = superClass.stubConstructor
- if (superDefaultConstructor != null) {
- val constructors = cls.constructors()
- for (constructor in constructors) {
- val superConstructor = constructor.superConstructor
- if (
- superConstructor == null ||
- (superConstructor.containingClass() != superClass &&
- superConstructor.containingClass() != cls)
- ) {
- constructor.superConstructor = superDefaultConstructor
- }
- }
+ val superDefaultConstructor = superClass?.stubConstructor
+ if (superDefaultConstructor != null) {
+ cls.constructors().forEach { constructor ->
+ constructor.superConstructor = superDefaultConstructor
}
}
// Find default constructor, if one doesn't exist
- val constructors = cls.filteredConstructors(filter).toList()
- if (constructors.isNotEmpty()) {
- // Try to pick the constructor, select first by fewest throwables,
- // then fewest parameters, then based on order in listFilter.test(cls)
- cls.stubConstructor = constructors.reduce { first, second -> pickBest(first, second) }
- return
- }
+ val filteredConstructors = cls.filteredConstructors(filter).toList()
+ cls.stubConstructor =
+ if (filteredConstructors.isNotEmpty()) {
+ // Try to pick the constructor, select first by fewest throwables,
+ // then fewest parameters, then based on order in listFilter.test(cls)
+ filteredConstructors.reduce { first, second -> pickBest(first, second) }
+ } else if (
+ cls.constructors().isNotEmpty() ||
+ // For text based codebase, stub constructor needs to be generated even if
+ // cls.constructors() is empty, so that public default constructor is not
+ // created.
+ cls.codebase.preFiltered
+ ) {
- // For text based codebase, stub constructor needs to be generated even if
- // cls.constructors() is empty, so that public default constructor is not created.
- if (cls.constructors().isNotEmpty() || cls.codebase.preFiltered) {
- // No accessible constructors are available so a package private constructor is created.
- // Technically, the stub now has a constructor that isn't available at runtime,
- // but apps creating subclasses inside the android.* package is not supported.
- cls.stubConstructor =
+ // No accessible constructors are available so a package private constructor is
+ // created. Technically, the stub now has a constructor that isn't available at
+ // runtime, but apps creating subclasses inside the android.* package is not
+ // supported.
cls.createDefaultConstructor().also {
it.mutableModifiers().setVisibilityLevel(VisibilityLevel.PACKAGE_PRIVATE)
it.hidden = false
- it.superConstructor = superClass?.stubConstructor
+ it.superConstructor = superDefaultConstructor
}
- }
+ } else {
+ null
+ }
}
// TODO: Annotation test: @ParameterName, if present, must be supplied on *all* the arguments!
diff --git a/metalava/src/main/java/com/android/tools/metalava/DefaultReporter.kt b/metalava/src/main/java/com/android/tools/metalava/DefaultReporter.kt
index bc69406..6ca5ea0 100644
--- a/metalava/src/main/java/com/android/tools/metalava/DefaultReporter.kt
+++ b/metalava/src/main/java/com/android/tools/metalava/DefaultReporter.kt
@@ -22,6 +22,7 @@
import com.android.tools.metalava.model.AnnotationArrayAttributeValue
import com.android.tools.metalava.model.Item
import com.android.tools.metalava.model.Location
+import com.android.tools.metalava.model.PackageItem
import com.android.tools.metalava.reporter.Baseline
import com.android.tools.metalava.reporter.IssueConfiguration
import com.android.tools.metalava.reporter.Issues
@@ -99,7 +100,7 @@
// issues from other packages
if (item != null) {
if (packageFilter != null) {
- val pkg = item.containingPackage(false)
+ val pkg = (item as? PackageItem) ?: item.containingPackage()
if (pkg != null && !packageFilter.matches(pkg)) {
return false
}
diff --git a/metalava/src/main/java/com/android/tools/metalava/lint/ApiLint.kt b/metalava/src/main/java/com/android/tools/metalava/lint/ApiLint.kt
index df54cbd..64cee17 100644
--- a/metalava/src/main/java/com/android/tools/metalava/lint/ApiLint.kt
+++ b/metalava/src/main/java/com/android/tools/metalava/lint/ApiLint.kt
@@ -1594,15 +1594,6 @@
}
}
- fun Item.containingClass(): ClassItem? {
- return when (this) {
- is MemberItem -> this.containingClass()
- is ParameterItem -> this.containingMethod().containingClass()
- is ClassItem -> this
- else -> null
- }
- }
-
private fun checkNullableCollections(type: TypeItem, item: Item) {
if (type is PrimitiveTypeItem) return
if (!item.modifiers.isNullable()) return
@@ -1785,7 +1776,21 @@
}
private fun checkHasFlaggedApi(item: Item) {
- if (!item.modifiers.hasAnnotation { it.qualifiedName == flaggedApi }) {
+ fun itemOrAnyContainingClasses(predicate: Predicate<Item>): Boolean {
+ var it: Item? = item
+ while (it != null) {
+ if (predicate.test(it)) {
+ return true
+ }
+ it = it.containingClass()
+ }
+ return false
+ }
+ if (
+ !itemOrAnyContainingClasses {
+ it.modifiers.hasAnnotation { it.qualifiedName == flaggedApi }
+ }
+ ) {
val elidedField =
if (item is FieldItem) {
val inheritedFrom = item.inheritedFrom
diff --git a/metalava/src/main/java/com/android/tools/metalava/stub/JavaStubWriter.kt b/metalava/src/main/java/com/android/tools/metalava/stub/JavaStubWriter.kt
index 6a76573..ac621c6 100644
--- a/metalava/src/main/java/com/android/tools/metalava/stub/JavaStubWriter.kt
+++ b/metalava/src/main/java/com/android/tools/metalava/stub/JavaStubWriter.kt
@@ -250,24 +250,14 @@
writer.println(" }")
}
- private fun writeConstructorBody(constructor: MethodItem?, superConstructor: MethodItem?) {
+ private fun writeConstructorBody(constructor: MethodItem, superConstructor: MethodItem?) {
// Find any constructor in parent that we can compile against
superConstructor?.let { it ->
val parameters = it.parameters()
- val invokeOnThis =
- constructor != null && constructor.containingClass() == it.containingClass()
- if (invokeOnThis || parameters.isNotEmpty()) {
+ if (parameters.isNotEmpty()) {
val includeCasts =
- parameters.isNotEmpty() &&
- it.containingClass()
- .constructors()
- .filter { filterReference.test(it) }
- .size > 1
- if (invokeOnThis) {
- writer.print("this(")
- } else {
- writer.print("super(")
- }
+ it.containingClass().constructors().filter { filterReference.test(it) }.size > 1
+ writer.print("super(")
parameters.forEachIndexed { index, parameter ->
if (index > 0) {
writer.write(", ")
@@ -284,9 +274,9 @@
// type in this class
val map =
constructor
- ?.containingClass()
- ?.mapTypeVariables(it.containingClass())
- val cast = map?.get(type.toTypeString(context = it)) ?: typeString
+ .containingClass()
+ .mapTypeVariables(it.containingClass())
+ val cast = map.get(type.toTypeString(context = it)) ?: typeString
writer.write(cast)
} else {
writer.write(typeString)
diff --git a/metalava/src/test/java/com/android/tools/metalava/AddAdditionalOverridesTest.kt b/metalava/src/test/java/com/android/tools/metalava/AddAdditionalOverridesTest.kt
index f802880..c1575d3 100644
--- a/metalava/src/test/java/com/android/tools/metalava/AddAdditionalOverridesTest.kt
+++ b/metalava/src/test/java/com/android/tools/metalava/AddAdditionalOverridesTest.kt
@@ -388,4 +388,73 @@
)
)
}
+
+ @Test
+ fun `Add additional overrides -- Method with multiple interface parent methods in same hierarchy not elided`() {
+ checkAddAdditionalOverrides(
+ sourceFiles =
+ arrayOf(
+ // Although ParentInterface provides the default super method, it is abstracted
+ // in AnotherParentInterface and thus ChildClass.Foo() is an essential method.
+ java(
+ """
+ package test.pkg;
+
+ public class ChildClass implements ParentInterface, AnotherParentInterface {
+ public void Foo() {}
+ }
+ """
+ ),
+ java(
+ """
+ package test.pkg;
+
+ public interface ParentInterface {
+ public default void Foo() {}
+ }
+ """
+ ),
+ java(
+ """
+ package test.pkg;
+
+ public interface AnotherParentInterface extends ParentInterface {
+ public void Foo();
+ }
+ """
+ ),
+ ),
+ apiOriginal =
+ """
+ // Signature format: 2.0
+ package test.pkg {
+ public interface AnotherParentInterface extends test.pkg.ParentInterface {
+ method public void Foo();
+ }
+ public class ChildClass implements test.pkg.AnotherParentInterface test.pkg.ParentInterface {
+ ctor public ChildClass();
+ }
+ public interface ParentInterface {
+ method public default void Foo();
+ }
+ }
+ """,
+ apiWithAdditionalOverrides =
+ """
+ // Signature format: 2.0
+ package test.pkg {
+ public interface AnotherParentInterface extends test.pkg.ParentInterface {
+ method public void Foo();
+ }
+ public class ChildClass implements test.pkg.AnotherParentInterface test.pkg.ParentInterface {
+ ctor public ChildClass();
+ method public void Foo();
+ }
+ public interface ParentInterface {
+ method public default void Foo();
+ }
+ }
+ """,
+ )
+ }
}
diff --git a/metalava/src/test/java/com/android/tools/metalava/lint/FlaggedApiLintTest.kt b/metalava/src/test/java/com/android/tools/metalava/lint/FlaggedApiLintTest.kt
index 729d916..db2e69a 100644
--- a/metalava/src/test/java/com/android/tools/metalava/lint/FlaggedApiLintTest.kt
+++ b/metalava/src/test/java/com/android/tools/metalava/lint/FlaggedApiLintTest.kt
@@ -212,15 +212,9 @@
@FlaggedApi("foo/bar")
public class Ok extends ExistingSuperClass implements ExistingInterface {
- @FlaggedApi("foo/bar")
- Ok() {}
- @FlaggedApi("foo/bar")
public static final String OK = "bar";
- @FlaggedApi("foo/bar")
public void ok() {}
- @FlaggedApi("foo/bar")
public interface OkInterface {}
- @FlaggedApi("foo/bar")
public @interface OkAnnotation {}
}
"""
@@ -274,8 +268,8 @@
showAnnotations = arrayOf("android.annotation.SystemApi"),
expectedIssues =
"""
- src/android/foobar/BadHiddenSuperClass.java:7: warning: New API must be flagged with @FlaggedApi: method android.foobar.Bad.badInherited() [UnflaggedApi]
- src/android/foobar/BadHiddenSuperClass.java:6: warning: New API must be flagged with @FlaggedApi: field android.foobar.Bad.BAD_INHERITED [UnflaggedApi]
+ src/android/foobar/BadHiddenSuperClass.java:5: warning: New API must be flagged with @FlaggedApi: method android.foobar.Bad.badInherited() [UnflaggedApi]
+ src/android/foobar/BadHiddenSuperClass.java:4: warning: New API must be flagged with @FlaggedApi: field android.foobar.Bad.BAD_INHERITED [UnflaggedApi]
""",
apiLint =
"""
@@ -329,8 +323,6 @@
"""
package android.foobar;
- import android.annotation.FlaggedApi;
-
public interface ExistingPublicInterface {
public static final String EXISTING_PUBLIC_INTERFACE_FIELD = "foo";
public default void existingPublicInterfaceMethod() {}
@@ -341,8 +333,6 @@
"""
package android.foobar;
- import android.annotation.FlaggedApi;
-
class BadHiddenSuperClass {
public static final String BAD_INHERITED = "foo";
public default void badInherited() {}
@@ -353,8 +343,6 @@
"""
package android.foobar;
- import android.annotation.FlaggedApi;
-
public class ExistingPublicSuperClass {
public static final String EXISTING_PUBLIC_SUPER_FIELD = "foo";
public void existingPublicSuperMethod() {}
@@ -365,13 +353,11 @@
"""
package android.foobar;
- import android.annotation.FlaggedApi;
-
import android.annotation.SystemApi;
/** @hide */
@SystemApi
- @FlaggedApi("namespace/flag")
+ @SuppressWarnings("UnflaggedApi") // Ignore the class itself for this test.
public class Ok extends ExistingSystemSuperClass implements ExistingSystemInterface {
private Ok() {}
}
@@ -381,13 +367,11 @@
"""
package android.foobar;
- import android.annotation.FlaggedApi;
-
import android.annotation.SystemApi;
/** @hide */
@SystemApi
- @FlaggedApi("namespace/flag")
+ @SuppressWarnings("UnflaggedApi") // Ignore the class itself for this test.
public class Bad extends BadHiddenSuperClass {
private Bad() {}
}
@@ -397,12 +381,11 @@
"""
package android.foobar;
- import android.annotation.FlaggedApi;
import android.annotation.SystemApi;
/** @hide */
@SystemApi
- @FlaggedApi("namespace/flag")
+ @SuppressWarnings("UnflaggedApi") // Ignore the class itself for this test.
public class Ok2 extends ExistingPublicSuperClass implements ExistingPublicInterface {
private Ok2() {}
}
@@ -412,7 +395,6 @@
"""
package android.foobar;
- import android.annotation.FlaggedApi;
import android.annotation.SystemApi;
/** @hide */