111216907: Private stub annotation classes breaks compilation
This CL updates metalava such that it *only* packages
the @RecentlyNullable and @RecentlyNonNull annotations
as package private stub annotations; the rest of the
annotations are packaged as external annotations (which
was the case prior to P anyway).
It also turns off type-use annotations, since they are
not properly supported anyway; their restoral is tracked
in issue 111253910.
Fixes: 111216907
Test: Unit tests updated
Change-Id: Ia9b85bf75e143ae3dd622300f1731a30df2a16cd
Merged-In: I1d454024aeaee60daa8c717d40e352300ff768cc
diff --git a/src/main/java/com/android/tools/metalava/AnnotationsMerger.kt b/src/main/java/com/android/tools/metalava/AnnotationsMerger.kt
index 11ff5d5..c32c7df 100644
--- a/src/main/java/com/android/tools/metalava/AnnotationsMerger.kt
+++ b/src/main/java/com/android/tools/metalava/AnnotationsMerger.kt
@@ -57,6 +57,7 @@
import com.android.tools.metalava.model.Item
import com.android.tools.metalava.model.MethodItem
import com.android.tools.metalava.model.PackageItem
+import com.android.tools.metalava.model.SUPPORT_TYPE_USE_ANNOTATIONS
import com.android.tools.metalava.model.psi.PsiAnnotationItem
import com.android.tools.metalava.model.visitors.ApiVisitor
import com.android.utils.XmlUtils
@@ -267,7 +268,7 @@
null
}
curr = parameterItem
- } else if (line.startsWith("type: ")) {
+ } else if (line.startsWith("type: ") && SUPPORT_TYPE_USE_ANNOTATIONS) {
val typeAnnotation = line.substring("type: ".length)
if (curr != null) {
mergeJaifAnnotation(path, curr, typeAnnotation)
@@ -277,7 +278,7 @@
if (methodItem != null) {
mergeJaifAnnotation(path, methodItem, annotation)
}
- } else if (line.startsWith("inner-type")) {
+ } else if (line.startsWith("inner-type") && SUPPORT_TYPE_USE_ANNOTATIONS) {
warning("$path: Skipping inner-type annotations for now ($line)")
} else if (line.startsWith("int ")) {
// warning("Skipping int attribute definitions for annotations now ($line)")
diff --git a/src/main/java/com/android/tools/metalava/Driver.kt b/src/main/java/com/android/tools/metalava/Driver.kt
index e7e5eff..ed27efb 100644
--- a/src/main/java/com/android/tools/metalava/Driver.kt
+++ b/src/main/java/com/android/tools/metalava/Driver.kt
@@ -345,7 +345,9 @@
// Support pointing to both stub-annotations and stub-annotations/src/main/java
val src = File(stubAnnotations, "src${File.separator}main${File.separator}java")
val source = if (src.isDirectory) src else stubAnnotations
- RewriteAnnotations().copyAnnotations(source, it)
+ source.listFiles()?.forEach { file ->
+ RewriteAnnotations().copyAnnotations(file, File(it, file.name))
+ }
}
}
diff --git a/src/main/java/com/android/tools/metalava/ExtractAnnotations.kt b/src/main/java/com/android/tools/metalava/ExtractAnnotations.kt
index dd3fb67..b0a7e8b 100644
--- a/src/main/java/com/android/tools/metalava/ExtractAnnotations.kt
+++ b/src/main/java/com/android/tools/metalava/ExtractAnnotations.kt
@@ -101,25 +101,35 @@
val pairs = packageToAnnotationPairs[pkg] ?: continue
+ // Ensure stable output
+ if (pairs.size > 1) {
+ pairs.sortBy { it.first.getExternalAnnotationSignature() }
+ }
+
StringPrintWriter.create().use { writer ->
writer.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<root>")
+ var open = false
var prev: Item? = null
for ((item, annotation) in pairs) {
- // that we only do analysis for IntDef/LongDef
- assert(item != prev) // should be only one annotation per element now
+ if (item != prev) {
+ if (open) {
+ writer.print(" </item>")
+ writer.println()
+ }
+ writer.print(" <item name=\"")
+ writer.print(item.getExternalAnnotationSignature())
+ writer.println("\">")
+ open = true
+ }
prev = item
- writer.print(" <item name=\"")
- writer.print(item.getExternalAnnotationSignature())
- writer.println("\">")
-
writeAnnotation(writer, item, annotation)
-
+ }
+ if (open) {
writer.print(" </item>")
writer.println()
}
-
writer.println("</root>\n")
writer.close()
val bytes = writer.contents.toByteArray(Charsets.UTF_8)
@@ -131,15 +141,7 @@
}
}
- /** For a given item, extract the relevant annotations for that item.
- *
- * Currently, we're only extracting typedef annotations. Everything else
- * has class retention.
- */
- private fun checkItem(item: Item) {
- // field, method or parameter
- val typedef = findTypeDef(item) ?: return
-
+ private fun addItem(item: Item, annotation: AnnotationHolder) {
val pkg = when (item) {
is MemberItem -> item.containingClass().containingPackage()
is ParameterItem -> item.containingMethod().containingClass().containingPackage()
@@ -152,7 +154,7 @@
packageToAnnotationPairs[pkg] = new
new
}
- list.add(Pair(item, typedef))
+ list.add(Pair(item, annotation))
}
override fun visitField(field: FieldItem) {
@@ -167,7 +169,8 @@
checkItem(parameter)
}
- private fun findTypeDef(item: Item): AnnotationHolder? {
+ /** For a given item, extract the relevant annotations for that item */
+ private fun checkItem(item: Item) {
for (annotation in item.modifiers.annotations()) {
val qualifiedName = annotation.qualifiedName() ?: continue
if (qualifiedName.startsWith(JAVA_LANG_PREFIX) ||
@@ -177,7 +180,11 @@
) {
if (annotation.isTypeDefAnnotation()) {
// Imported typedef
- return AnnotationHolder(null, annotation, null)
+ addItem(item, AnnotationHolder(null, annotation, null))
+ } else if (annotation.isSignificant() && !annotation.hasClassRetention() &&
+ !options.includeSourceRetentionAnnotations
+ ) {
+ addItem(item, AnnotationHolder(null, annotation, null))
}
continue
@@ -188,7 +195,8 @@
if (typeDefClass.isAnnotationType()) {
val cached = classToAnnotationHolder[className]
if (cached != null) {
- return cached
+ addItem(item, cached)
+ continue
}
val typeDefAnnotation = typeDefClass.modifiers.annotations().firstOrNull {
@@ -220,12 +228,12 @@
)
)
classToAnnotationHolder[className] = result
- return result
+ addItem(item, result)
+ continue
}
}
}
}
- return null
}
private fun hasSourceRetention(annotationClass: ClassItem): Boolean {
@@ -374,112 +382,115 @@
annotationHolder: AnnotationHolder
) {
val annotationItem = annotationHolder.annotationItem
+ val uAnnotation = annotationHolder.uAnnotation
+ ?: if (annotationItem is PsiAnnotationItem) {
+ // Imported annotation
+ JavaUAnnotation.wrap(annotationItem.psiAnnotation)
+ } else {
+ return
+ }
val qualifiedName = annotationItem.qualifiedName()
writer.mark()
writer.print(" <annotation name=\"")
writer.print(qualifiedName)
+ var attributes = uAnnotation.attributeValues
+ if (attributes.isEmpty()) {
+ writer.print("\"/>")
+ writer.println()
+ return
+ }
+
writer.print("\">")
writer.println()
- val uAnnotation = annotationHolder.uAnnotation
- ?: if (annotationItem is PsiAnnotationItem) {
- // Imported annotation
- JavaUAnnotation.wrap(annotationItem.psiAnnotation)
- } else {
- null
- }
- if (uAnnotation != null) {
- var attributes = uAnnotation.attributeValues
+ // noinspection PointlessBooleanExpression,ConstantConditions
+ if (attributes.size > 1 && sortAnnotations) {
+ // Ensure mutable
+ attributes = ArrayList(attributes)
- // noinspection PointlessBooleanExpression,ConstantConditions
- if (attributes.size > 1 && sortAnnotations) {
- // Ensure mutable
- attributes = ArrayList(attributes)
+ // Ensure that the value attribute is written first
+ attributes.sortedWith(object : Comparator<UNamedExpression> {
+ private fun getName(pair: UNamedExpression): String {
+ val name = pair.name
+ return name ?: SdkConstants.ATTR_VALUE
+ }
- // Ensure that the value attribute is written first
- attributes.sortedWith(object : Comparator<UNamedExpression> {
- private fun getName(pair: UNamedExpression): String {
- val name = pair.name
- return name ?: SdkConstants.ATTR_VALUE
- }
+ private fun rank(pair: UNamedExpression): Int {
+ return if (SdkConstants.ATTR_VALUE == getName(pair)) -1 else 0
+ }
- private fun rank(pair: UNamedExpression): Int {
- return if (SdkConstants.ATTR_VALUE == getName(pair)) -1 else 0
- }
+ override fun compare(o1: UNamedExpression, o2: UNamedExpression): Int {
+ val r1 = rank(o1)
+ val r2 = rank(o2)
+ val delta = r1 - r2
+ return if (delta != 0) {
+ delta
+ } else getName(o1).compareTo(getName(o2))
+ }
+ })
+ }
- override fun compare(o1: UNamedExpression, o2: UNamedExpression): Int {
- val r1 = rank(o1)
- val r2 = rank(o2)
- val delta = r1 - r2
- return if (delta != 0) {
- delta
- } else getName(o1).compareTo(getName(o2))
- }
- })
- }
-
- if (attributes.size == 1 && Extractor.REQUIRES_PERMISSION.isPrefix(qualifiedName, true)) {
- val expression = attributes[0].expression
- if (expression is UAnnotation) {
- // The external annotations format does not allow for nested/complex annotations.
- // However, these special annotations (@RequiresPermission.Read,
- // @RequiresPermission.Write, etc) are known to only be simple containers with a
- // single permission child, so instead we "inline" the content:
- // @Read(@RequiresPermission(allOf={P1,P2},conditional=true)
- // =>
- // @RequiresPermission.Read(allOf({P1,P2},conditional=true)
- // That's setting attributes that don't actually exist on the container permission,
- // but we'll counteract that on the read-annotations side.
- val annotation = expression as UAnnotation
+ if (attributes.size == 1 && Extractor.REQUIRES_PERMISSION.isPrefix(qualifiedName, true)) {
+ val expression = attributes[0].expression
+ if (expression is UAnnotation) {
+ // The external annotations format does not allow for nested/complex annotations.
+ // However, these special annotations (@RequiresPermission.Read,
+ // @RequiresPermission.Write, etc) are known to only be simple containers with a
+ // single permission child, so instead we "inline" the content:
+ // @Read(@RequiresPermission(allOf={P1,P2},conditional=true)
+ // =>
+ // @RequiresPermission.Read(allOf({P1,P2},conditional=true)
+ // That's setting attributes that don't actually exist on the container permission,
+ // but we'll counteract that on the read-annotations side.
+ val annotation = expression as UAnnotation
+ attributes = annotation.attributeValues
+ } else if (expression is JavaUAnnotationCallExpression) {
+ val annotation = expression.uAnnotation
+ attributes = annotation.attributeValues
+ } else if (expression is UastEmptyExpression && attributes[0].sourcePsi is PsiNameValuePair) {
+ val memberValue = (attributes[0].sourcePsi as PsiNameValuePair).value
+ if (memberValue is PsiAnnotation) {
+ val annotation = JavaUAnnotation.wrap(memberValue)
attributes = annotation.attributeValues
- } else if (expression is JavaUAnnotationCallExpression) {
- val annotation = expression.uAnnotation
- attributes = annotation.attributeValues
- } else if (expression is UastEmptyExpression && attributes[0].sourcePsi is PsiNameValuePair) {
- val memberValue = (attributes[0].sourcePsi as PsiNameValuePair).value
- if (memberValue is PsiAnnotation) {
- val annotation = JavaUAnnotation.wrap(memberValue)
- attributes = annotation.attributeValues
- }
}
}
+ }
- val inlineConstants = isInlinedConstant(annotationItem)
- var empty = true
- for (pair in attributes) {
- val expression = pair.expression
- val value = attributeString(expression, inlineConstants) ?: continue
- empty = false
- var name = pair.name
- if (name == null) {
- name = SdkConstants.ATTR_VALUE // default name
- }
-
- // Platform typedef annotations now declare a prefix attribute for
- // documentation generation purposes; this should not be part of the
- // extracted metadata.
- if (("prefix" == name || "suffix" == name) && annotationItem.isTypeDefAnnotation()) {
- reporter.report(
- Errors.SUPERFLUOUS_PREFIX, item,
- "Superfluous $name attribute on typedef"
- )
- continue
- }
-
- writer.print(" <val name=\"")
- writer.print(name)
- writer.print("\" val=\"")
- writer.print(escapeXml(value))
- writer.println("\" />")
+ val inlineConstants = isInlinedConstant(annotationItem)
+ var empty = true
+ for (pair in attributes) {
+ val expression = pair.expression
+ val value = attributeString(expression, inlineConstants) ?: continue
+ empty = false
+ var name = pair.name
+ if (name == null) {
+ name = SdkConstants.ATTR_VALUE // default name
}
- if (empty) {
- // All items were filtered out: don't write the annotation at all
- writer.reset()
- return
+ // Platform typedef annotations now declare a prefix attribute for
+ // documentation generation purposes; this should not be part of the
+ // extracted metadata.
+ if (("prefix" == name || "suffix" == name) && annotationItem.isTypeDefAnnotation()) {
+ reporter.report(
+ Errors.SUPERFLUOUS_PREFIX, item,
+ "Superfluous $name attribute on typedef"
+ )
+ continue
}
+
+ writer.print(" <val name=\"")
+ writer.print(name)
+ writer.print("\" val=\"")
+ writer.print(escapeXml(value))
+ writer.println("\" />")
+ }
+
+ if (empty && attributes.isNotEmpty()) {
+ // All items were filtered out: don't write the annotation at all
+ writer.reset()
+ return
}
writer.println(" </annotation>")
diff --git a/src/main/java/com/android/tools/metalava/Options.kt b/src/main/java/com/android/tools/metalava/Options.kt
index ed177f5..5bd8415 100644
--- a/src/main/java/com/android/tools/metalava/Options.kt
+++ b/src/main/java/com/android/tools/metalava/Options.kt
@@ -110,6 +110,7 @@
private const val ARG_COPY_ANNOTATIONS = "--copy-annotations"
private const val ARG_INCLUDE_ANNOTATION_CLASSES = "--include-annotation-classes"
private const val ARG_REWRITE_ANNOTATIONS = "--rewrite-annotations"
+private const val ARG_INCLUDE_SOURCE_RETENTION = "--include-source-retention"
class Options(
args: Array<String>,
@@ -267,10 +268,19 @@
/** For [ARG_COPY_ANNOTATIONS], the target directory to write converted stub annotations from */
var privateAnnotationsTarget: File? = null
- /** For [ARG_INCLUDE_ANNOTATION_CLASSES], the directory to copy stub annotation source files into the
- * stubs folder from */
+ /**
+ * For [ARG_INCLUDE_ANNOTATION_CLASSES], the directory to copy stub annotation source files into the
+ * stubs folder from
+ */
var copyStubAnnotationsFrom: File? = null
+ /**
+ * For [ARG_INCLUDE_SOURCE_RETENTION], true if we want to include source-retention annotations
+ * both in the set of files emitted by [ARG_INCLUDE_ANNOTATION_CLASSES] and into the stubs
+ * themselves
+ */
+ var includeSourceRetentionAnnotations = false
+
/** For [ARG_REWRITE_ANNOTATIONS], the jar or bytecode folder to rewrite annotations in */
var rewriteAnnotations: List<File>? = null
@@ -545,6 +555,7 @@
}
ARG_REWRITE_ANNOTATIONS -> rewriteAnnotations = stringToExistingDirsOrJars(getValue(args, ++index))
ARG_INCLUDE_ANNOTATION_CLASSES -> copyStubAnnotationsFrom = stringToExistingDir(getValue(args, ++index))
+ ARG_INCLUDE_SOURCE_RETENTION -> includeSourceRetentionAnnotations = true
ARG_PREVIOUS_API -> previousApi = stringToExistingFile(getValue(args, ++index))
ARG_CURRENT_API -> currentApi = stringToExistingFile(getValue(args, ++index))
@@ -1315,7 +1326,9 @@
// "sources, generates corresponding package private versions of the same annotations.",
"$ARG_REWRITE_ANNOTATIONS <dir/jar>", "For a bytecode folder or output jar, rewrites the " +
"androidx annotations to be package private",
-
+ ARG_INCLUDE_SOURCE_RETENTION, "If true, include source-retention annotations in the stub files. Does " +
+ "not apply to signature files. Source retention annotations are extracted into the external " +
+ "annotations files instead.",
"", "\nInjecting API Levels:",
"$ARG_APPLY_API_LEVELS <api-versions.xml>", "Reads an XML file containing API level descriptions " +
"and merges the information into the documentation",
diff --git a/src/main/java/com/android/tools/metalava/RewriteAnnotations.kt b/src/main/java/com/android/tools/metalava/RewriteAnnotations.kt
index 283ac7d..78117c1 100644
--- a/src/main/java/com/android/tools/metalava/RewriteAnnotations.kt
+++ b/src/main/java/com/android/tools/metalava/RewriteAnnotations.kt
@@ -19,6 +19,7 @@
import com.android.SdkConstants
import com.android.SdkConstants.DOT_CLASS
import com.android.SdkConstants.DOT_JAR
+import com.android.tools.metalava.model.AnnotationItem
import com.google.common.io.Closer
import org.objectweb.asm.ClassReader
import org.objectweb.asm.ClassVisitor
@@ -46,14 +47,24 @@
class RewriteAnnotations {
/** Copies annotation source files from [source] to [target] */
- fun copyAnnotations(source: File, target: File) {
- if (source.name.endsWith(SdkConstants.DOT_JAVA)) {
+ fun copyAnnotations(source: File, target: File, pkg: String = "") {
+ val fileName = source.name
+ if (fileName.endsWith(SdkConstants.DOT_JAVA)) {
+ if (!options.includeSourceRetentionAnnotations) {
+ // Only copy non-source retention annotation classes
+ val qualifiedName = pkg + "." + fileName.substring(0, fileName.indexOf('.'))
+ if (!AnnotationItem.hasClassRetention(qualifiedName)) {
+ return
+ }
+ }
+
// Copy and convert
target.parentFile.mkdirs()
source.copyTo(target)
} else if (source.isDirectory) {
+ val newPackage = if (pkg.isEmpty()) fileName else "$pkg.$fileName"
source.listFiles()?.forEach {
- copyAnnotations(it, File(target, it.name))
+ copyAnnotations(it, File(target, it.name), newPackage)
}
}
}
diff --git a/src/main/java/com/android/tools/metalava/SignatureWriter.kt b/src/main/java/com/android/tools/metalava/SignatureWriter.kt
index ad4f947..5577234 100644
--- a/src/main/java/com/android/tools/metalava/SignatureWriter.kt
+++ b/src/main/java/com/android/tools/metalava/SignatureWriter.kt
@@ -150,7 +150,8 @@
includeAnnotations = compatibility.annotationsInSignatures,
skipNullnessAnnotations = options.outputKotlinStyleNulls,
omitCommonPackages = options.omitCommonPackages,
- onlyIncludeSignatureAnnotations = true
+ onlyIncludeSignatureAnnotations = true,
+ onlyIncludeClassRetentionAnnotations = false
)
}
diff --git a/src/main/java/com/android/tools/metalava/StubWriter.kt b/src/main/java/com/android/tools/metalava/StubWriter.kt
index 40a6a5a..06d5221 100644
--- a/src/main/java/com/android/tools/metalava/StubWriter.kt
+++ b/src/main/java/com/android/tools/metalava/StubWriter.kt
@@ -141,6 +141,7 @@
// here; make sure the are filtered out
filterDuplicates = true,
onlyIncludeSignatureAnnotations = true,
+ onlyIncludeClassRetentionAnnotations = true,
writer = writer
)
writer.println("package ${pkg.qualifiedName()};")
@@ -302,7 +303,8 @@
ModifierList.write(
writer, modifiers, item, removeAbstract = removeAbstract, removeFinal = removeFinal,
addPublic = addPublic, includeAnnotations = generateAnnotations,
- onlyIncludeSignatureAnnotations = true
+ onlyIncludeSignatureAnnotations = true,
+ onlyIncludeClassRetentionAnnotations = true
)
}
diff --git a/src/main/java/com/android/tools/metalava/model/AnnotationItem.kt b/src/main/java/com/android/tools/metalava/model/AnnotationItem.kt
index aa1acba..52743fd 100644
--- a/src/main/java/com/android/tools/metalava/model/AnnotationItem.kt
+++ b/src/main/java/com/android/tools/metalava/model/AnnotationItem.kt
@@ -57,6 +57,14 @@
return includeInSignatures(qualifiedName() ?: return false)
}
+ /**
+ * Whether this annotation has class retention. Only class retention annotations are
+ * inserted into the stubs, the rest are extracted into the separate external annotations file.
+ */
+ fun hasClassRetention(): Boolean {
+ return hasClassRetention(qualifiedName())
+ }
+
/** Attributes of the annotation (may be empty) */
fun attributes(): List<AnnotationAttribute>
@@ -250,6 +258,8 @@
"android.annotation.CheckResult" -> return "androidx.annotation.CheckResult"
"android.support.annotation.RequiresPermission",
"android.annotation.RequiresPermission" -> return "androidx.annotation.RequiresPermission"
+ "android.annotation.RequiresPermission.Read" -> return "androidx.annotation.RequiresPermission.Read"
+ "android.annotation.RequiresPermission.Write" -> return "androidx.annotation.RequiresPermission.Write"
// These aren't support annotations, but could/should be:
"android.annotation.CurrentTimeMillisLong",
@@ -388,6 +398,21 @@
}
}
}
+
+ fun hasClassRetention(qualifiedName: String?): Boolean {
+ // For now, we treat everything except the recently nullable annotations
+ // as source retention; this works around the bug that we don't want to
+ // reference (from the .class files) annotations that aren't part of the SDK
+ // except for those that we include with the stubs
+
+ qualifiedName ?: return false
+
+ return when (qualifiedName) {
+ "androidx.annotation.RecentlyNullable",
+ "androidx.annotation.RecentlyNonNull" -> true
+ else -> false
+ }
+ }
}
}
diff --git a/src/main/java/com/android/tools/metalava/model/Codebase.kt b/src/main/java/com/android/tools/metalava/model/Codebase.kt
index 41ce549..3c86e61 100644
--- a/src/main/java/com/android/tools/metalava/model/Codebase.kt
+++ b/src/main/java/com/android/tools/metalava/model/Codebase.kt
@@ -155,7 +155,7 @@
override var manifest: File? = null
private var permissions: Map<String, String>? = null
override var original: Codebase? = null
- override var supportsStagedNullability: Boolean = false
+ override var supportsStagedNullability: Boolean = true
override var units: List<PsiFile> = emptyList()
override var apiLevel: Int = -1
diff --git a/src/main/java/com/android/tools/metalava/model/ModifierList.kt b/src/main/java/com/android/tools/metalava/model/ModifierList.kt
index 435baf8..d192cd3 100644
--- a/src/main/java/com/android/tools/metalava/model/ModifierList.kt
+++ b/src/main/java/com/android/tools/metalava/model/ModifierList.kt
@@ -184,8 +184,8 @@
removeAbstract: Boolean = false,
removeFinal: Boolean = false,
addPublic: Boolean = false,
- onlyIncludeSignatureAnnotations: Boolean = true
-
+ onlyIncludeSignatureAnnotations: Boolean = true,
+ onlyIncludeClassRetentionAnnotations: Boolean = false
) {
val list = if (removeAbstract || removeFinal || addPublic) {
@@ -214,7 +214,8 @@
omitCommonPackages = omitCommonPackages,
separateLines = false,
writer = writer,
- onlyIncludeSignatureAnnotations = onlyIncludeSignatureAnnotations
+ onlyIncludeSignatureAnnotations = onlyIncludeSignatureAnnotations,
+ onlyIncludeClassRetentionAnnotations = onlyIncludeClassRetentionAnnotations
)
}
@@ -387,19 +388,23 @@
separateLines: Boolean = false,
filterDuplicates: Boolean = false,
writer: Writer,
- onlyIncludeSignatureAnnotations: Boolean = true
+ onlyIncludeSignatureAnnotations: Boolean = true,
+ onlyIncludeClassRetentionAnnotations: Boolean = false
) {
val annotations = list.annotations()
if (annotations.isNotEmpty()) {
var index = -1
for (annotation in annotations) {
index++
- if ((annotation.isNonNull() || annotation.isNullable())) {
+ if (onlyIncludeSignatureAnnotations && !annotation.isSignificant()) {
+ continue
+ } else if (onlyIncludeClassRetentionAnnotations && !annotation.hasClassRetention() &&
+ !options.includeSourceRetentionAnnotations) {
+ continue
+ } else if ((annotation.isNonNull() || annotation.isNullable())) {
if (skipNullnessAnnotations) {
continue
}
- } else if (onlyIncludeSignatureAnnotations && !annotation.isSignificant()) {
- continue
}
// Optionally filter out duplicates
diff --git a/src/main/java/com/android/tools/metalava/model/TypeItem.kt b/src/main/java/com/android/tools/metalava/model/TypeItem.kt
index 5d1eaa8..b1ac351 100644
--- a/src/main/java/com/android/tools/metalava/model/TypeItem.kt
+++ b/src/main/java/com/android/tools/metalava/model/TypeItem.kt
@@ -23,6 +23,14 @@
import com.android.tools.metalava.options
import java.util.function.Predicate
+/**
+ * Whether metalava supports type use annotations.
+ * Note that you can't just turn this flag back on; you have to
+ * also add TYPE_USE back to the handful of nullness
+ * annotations in stub-annotations/src/main/java/.
+ */
+const val SUPPORT_TYPE_USE_ANNOTATIONS = false
+
/** Represents a type */
interface TypeItem {
/**
diff --git a/src/main/java/com/android/tools/metalava/model/psi/PsiMethodItem.kt b/src/main/java/com/android/tools/metalava/model/psi/PsiMethodItem.kt
index a54dc24..bbcae2a 100644
--- a/src/main/java/com/android/tools/metalava/model/psi/PsiMethodItem.kt
+++ b/src/main/java/com/android/tools/metalava/model/psi/PsiMethodItem.kt
@@ -250,7 +250,8 @@
ModifierList.write(
modifierString, method.modifiers, method, removeAbstract = false,
removeFinal = false, addPublic = true,
- onlyIncludeSignatureAnnotations = true
+ onlyIncludeSignatureAnnotations = true,
+ onlyIncludeClassRetentionAnnotations = true
)
sb.append(modifierString.toString())
diff --git a/src/main/java/com/android/tools/metalava/model/psi/PsiTypeItem.kt b/src/main/java/com/android/tools/metalava/model/psi/PsiTypeItem.kt
index 248d047..be0d36c 100644
--- a/src/main/java/com/android/tools/metalava/model/psi/PsiTypeItem.kt
+++ b/src/main/java/com/android/tools/metalava/model/psi/PsiTypeItem.kt
@@ -24,6 +24,7 @@
import com.android.tools.metalava.model.Codebase
import com.android.tools.metalava.model.Item
import com.android.tools.metalava.model.MemberItem
+import com.android.tools.metalava.model.SUPPORT_TYPE_USE_ANNOTATIONS
import com.android.tools.metalava.model.TypeItem
import com.android.tools.metalava.model.TypeParameterItem
import com.android.tools.metalava.model.text.TextTypeItem
@@ -73,7 +74,7 @@
assert(innerAnnotations || !outerAnnotations) // Can't supply outer=true,inner=false
return if (erased) {
- if (innerAnnotations || outerAnnotations) {
+ if (SUPPORT_TYPE_USE_ANNOTATIONS && (innerAnnotations || outerAnnotations)) {
// Not cached: Not common
toTypeString(codebase, psiType, outerAnnotations, innerAnnotations, erased)
} else {
@@ -84,7 +85,7 @@
}
} else {
when {
- outerAnnotations -> {
+ SUPPORT_TYPE_USE_ANNOTATIONS && outerAnnotations -> {
if (toAnnotatedString == null) {
toAnnotatedString = TypeItem.formatType(
toTypeString(
@@ -98,7 +99,7 @@
}
toAnnotatedString!!
}
- innerAnnotations -> {
+ SUPPORT_TYPE_USE_ANNOTATIONS && innerAnnotations -> {
if (toInnerAnnotatedString == null) {
toInnerAnnotatedString = TypeItem.formatType(
toTypeString(
@@ -339,7 +340,7 @@
)
}
- if (outerAnnotations || innerAnnotations) {
+ if (SUPPORT_TYPE_USE_ANNOTATIONS && (innerAnnotations || outerAnnotations)) {
val typeString = mapAnnotations(codebase, getCanonicalText(type, true))
if (!outerAnnotations && typeString.contains("@")) {
// Temporary hack: should use PSI type visitor instead
@@ -404,8 +405,8 @@
}
private fun getCanonicalText(type: PsiType, annotated: Boolean): String {
- val typeString = type.getCanonicalText(annotated)
- if (!annotated) {
+ val typeString = type.getCanonicalText(annotated && SUPPORT_TYPE_USE_ANNOTATIONS)
+ if (!annotated || !SUPPORT_TYPE_USE_ANNOTATIONS) {
return typeString
}
@@ -764,4 +765,4 @@
}
}
}
-}
\ No newline at end of file
+}
diff --git a/src/main/resources/version.properties b/src/main/resources/version.properties
index dd2d971..ab0e374 100644
--- a/src/main/resources/version.properties
+++ b/src/main/resources/version.properties
@@ -1,4 +1,4 @@
# Version definition
# This file is read by gradle build scripts, but also packaged with metalava
# as a resource for the Version classes to read.
-metalavaVersion=0.9.12
+metalavaVersion=1.0.0
diff --git a/src/test/java/com/android/tools/metalava/AnnotationStatisticsTest.kt b/src/test/java/com/android/tools/metalava/AnnotationStatisticsTest.kt
index f62c805..4a1cf39 100644
--- a/src/test/java/com/android/tools/metalava/AnnotationStatisticsTest.kt
+++ b/src/test/java/com/android/tools/metalava/AnnotationStatisticsTest.kt
@@ -163,8 +163,8 @@
}
"""
),
- supportNonNullSource,
- supportNullableSource
+ androidxNonNullSource,
+ androidxNullableSource
),
expectedOutput = """
6 methods and fields were missing nullness annotations out of 8 total API references.
diff --git a/src/test/java/com/android/tools/metalava/AnnotationsMergerTest.kt b/src/test/java/com/android/tools/metalava/AnnotationsMergerTest.kt
index 654ed1a..003e7ba 100644
--- a/src/test/java/com/android/tools/metalava/AnnotationsMergerTest.kt
+++ b/src/test/java/com/android/tools/metalava/AnnotationsMergerTest.kt
@@ -16,6 +16,7 @@
package com.android.tools.metalava
+import com.android.tools.metalava.model.SUPPORT_TYPE_USE_ANNOTATIONS
import org.junit.Test
class AnnotationsMergerTest : DriverTest() {
@@ -50,8 +51,8 @@
),
uiThreadSource,
intRangeAnnotationSource,
- supportNonNullSource,
- supportNullableSource
+ androidxNonNullSource,
+ androidxNullableSource
),
// Skip the annotations themselves from the output
extraArguments = arrayOf(
@@ -162,14 +163,25 @@
// Is expected to return self
return: @libcore.util.NonNull
""",
- api = """
- package test.pkg {
- public interface Appendable {
- method @androidx.annotation.NonNull public test.pkg.Appendable append(@androidx.annotation.Nullable java.lang.CharSequence);
- method public java.lang.String reverse(java.lang.String);
- }
- }
+ api = if (SUPPORT_TYPE_USE_ANNOTATIONS) {
"""
+ package test.pkg {
+ public interface Appendable {
+ method @androidx.annotation.NonNull public test.pkg.Appendable append(@androidx.annotation.Nullable java.lang.CharSequence);
+ method public java.lang.String reverse(java.lang.String);
+ }
+ }
+ """
+ } else {
+ """
+ package test.pkg {
+ public interface Appendable {
+ method @androidx.annotation.NonNull public test.pkg.Appendable append(java.lang.CharSequence);
+ method public java.lang.String reverse(java.lang.String);
+ }
+ }
+ """
+ }
)
}
diff --git a/src/test/java/com/android/tools/metalava/ApiFileTest.kt b/src/test/java/com/android/tools/metalava/ApiFileTest.kt
index 5c66e1c..387c24e 100644
--- a/src/test/java/com/android/tools/metalava/ApiFileTest.kt
+++ b/src/test/java/com/android/tools/metalava/ApiFileTest.kt
@@ -381,8 +381,8 @@
inline operator fun <F, S> PlatformJavaPair<F, S>.component1() = first
"""
),
- supportNonNullSource,
- supportNullableSource
+ androidxNonNullSource,
+ androidxNullableSource
),
api = """
package androidx.util {
diff --git a/src/test/java/com/android/tools/metalava/DriverTest.kt b/src/test/java/com/android/tools/metalava/DriverTest.kt
index 13ad8cd..1449791 100644
--- a/src/test/java/com/android/tools/metalava/DriverTest.kt
+++ b/src/test/java/com/android/tools/metalava/DriverTest.kt
@@ -219,7 +219,12 @@
/** Map from artifact id to artifact descriptor */
artifacts: Map<String, String>? = null,
/** Extract annotations and check that the given packages contain the given extracted XML files */
- extractAnnotations: Map<String, String>? = null
+ extractAnnotations: Map<String, String>? = null,
+ /**
+ * Whether to include source retention annotations in the stubs (in that case they do not
+ * go into the extracted annotations zip file)
+ */
+ includeSourceRetentionAnnotations: Boolean = true
) {
System.setProperty("METALAVA_TESTS_RUNNING", VALUE_TRUE)
@@ -407,7 +412,14 @@
if (showUnannotated) {
arrayOf("--show-unannotated")
} else {
- emptyArray<String>()
+ emptyArray()
+ }
+
+ val includeSourceRetentionAnnotationArgs =
+ if (includeSourceRetentionAnnotations) {
+ arrayOf("--include-source-retention")
+ } else {
+ emptyArray()
}
var removedApiFile: File? = null
@@ -606,6 +618,7 @@
*applyApiLevelsXmlArgs,
*showAnnotationArguments,
*showUnannotatedArgs,
+ *includeSourceRetentionAnnotationArgs,
*sdkFilesArgs,
*importedPackageArgs.toTypedArray(),
*skipEmitPackagesArgs.toTypedArray(),
@@ -1310,8 +1323,16 @@
String[] allOf() default {};
String[] anyOf() default {};
boolean conditional() default false;
+ @Target({FIELD, METHOD, PARAMETER})
+ @interface Read {
+ RequiresPermission value() default @RequiresPermission;
+ }
+ @Target({FIELD, METHOD, PARAMETER})
+ @interface Write {
+ RequiresPermission value() default @RequiresPermission;
+ }
}
- """
+ """
).indented()
val requiresFeatureSource: TestFile = java(
@@ -1325,7 +1346,7 @@
public @interface RequiresFeature {
String value();
}
- """
+ """
).indented()
val requiresApiSource: TestFile = java(
@@ -1340,7 +1361,7 @@
int value() default 1;
int api() default 1;
}
- """
+ """
).indented()
val sdkConstantSource: TestFile = java(
@@ -1355,23 +1376,23 @@
}
SdkConstantType value();
}
- """
+ """
).indented()
val broadcastBehaviorSource: TestFile = java(
"""
- package android.annotation;
- import java.lang.annotation.*;
- /** @hide */
- @Target({ ElementType.FIELD })
- @Retention(RetentionPolicy.SOURCE)
- public @interface BroadcastBehavior {
- boolean explicitOnly() default false;
- boolean registeredOnly() default false;
- boolean includeBackground() default false;
- boolean protectedBroadcast() default false;
- }
- """
+ package android.annotation;
+ import java.lang.annotation.*;
+ /** @hide */
+ @Target({ ElementType.FIELD })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface BroadcastBehavior {
+ boolean explicitOnly() default false;
+ boolean registeredOnly() default false;
+ boolean includeBackground() default false;
+ boolean protectedBroadcast() default false;
+ }
+ """
).indented()
val nullableSource: TestFile = java(
@@ -1396,7 +1417,7 @@
val supportNonNullSource: TestFile = java(
"""
- package androidx.annotation;
+ package android.support.annotation;
import java.lang.annotation.*;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.SOURCE;
@@ -1410,17 +1431,17 @@
val supportNullableSource: TestFile = java(
"""
-package androidx.annotation;
-import java.lang.annotation.*;
-import static java.lang.annotation.ElementType.*;
-import static java.lang.annotation.RetentionPolicy.SOURCE;
-@SuppressWarnings("WeakerAccess")
-@Retention(SOURCE)
-@Target({METHOD, PARAMETER, FIELD, TYPE_USE})
-public @interface Nullable {
-}
- """
-)
+ package android.support.annotation;
+ import java.lang.annotation.*;
+ import static java.lang.annotation.ElementType.*;
+ import static java.lang.annotation.RetentionPolicy.SOURCE;
+ @SuppressWarnings("WeakerAccess")
+ @Retention(SOURCE)
+ @Target({METHOD, PARAMETER, FIELD, TYPE_USE})
+ public @interface Nullable {
+ }
+ """
+).indented()
val androidxNonNullSource: TestFile = java(
"""
@@ -1438,17 +1459,45 @@
val androidxNullableSource: TestFile = java(
"""
-package androidx.annotation;
-import java.lang.annotation.*;
-import static java.lang.annotation.ElementType.*;
-import static java.lang.annotation.RetentionPolicy.SOURCE;
-@SuppressWarnings("WeakerAccess")
-@Retention(SOURCE)
-@Target({METHOD, PARAMETER, FIELD, TYPE_USE})
-public @interface Nullable {
-}
- """
-)
+ package androidx.annotation;
+ import java.lang.annotation.*;
+ import static java.lang.annotation.ElementType.*;
+ import static java.lang.annotation.RetentionPolicy.SOURCE;
+ @SuppressWarnings("WeakerAccess")
+ @Retention(SOURCE)
+ @Target({METHOD, PARAMETER, FIELD, TYPE_USE})
+ public @interface Nullable {
+ }
+ """
+).indented()
+
+val recentlyNonNullSource: TestFile = java(
+ """
+ package androidx.annotation;
+ import java.lang.annotation.*;
+ import static java.lang.annotation.ElementType.*;
+ import static java.lang.annotation.RetentionPolicy.SOURCE;
+ @SuppressWarnings("WeakerAccess")
+ @Retention(SOURCE)
+ @Target({METHOD, PARAMETER, FIELD, TYPE_USE})
+ public @interface RecentlyNonNull {
+ }
+ """
+).indented()
+
+val recentlyNullableSource: TestFile = java(
+ """
+ package androidx.annotation;
+ import java.lang.annotation.*;
+ import static java.lang.annotation.ElementType.*;
+ import static java.lang.annotation.RetentionPolicy.SOURCE;
+ @SuppressWarnings("WeakerAccess")
+ @Retention(SOURCE)
+ @Target({METHOD, PARAMETER, FIELD, TYPE_USE})
+ public @interface RecentlyNullable {
+ }
+ """
+).indented()
val supportParameterName: TestFile = java(
"""
@@ -1527,17 +1576,17 @@
val suppressLintSource: TestFile = java(
"""
-package android.annotation;
+ package android.annotation;
-import static java.lang.annotation.ElementType.*;
-import java.lang.annotation.*;
-@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
-@Retention(RetentionPolicy.CLASS)
-public @interface SuppressLint {
- String[] value();
-}
- """
-)
+ import static java.lang.annotation.ElementType.*;
+ import java.lang.annotation.*;
+ @Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
+ @Retention(RetentionPolicy.CLASS)
+ public @interface SuppressLint {
+ String[] value();
+ }
+ """
+).indented()
val systemServiceSource: TestFile = java(
"""
diff --git a/src/test/java/com/android/tools/metalava/ExtractAnnotationsTest.kt b/src/test/java/com/android/tools/metalava/ExtractAnnotationsTest.kt
index 427e41d..5a8ee6f 100644
--- a/src/test/java/com/android/tools/metalava/ExtractAnnotationsTest.kt
+++ b/src/test/java/com/android/tools/metalava/ExtractAnnotationsTest.kt
@@ -25,9 +25,10 @@
@Test
fun `Check java typedef extraction and warning about non-source retention of typedefs`() {
check(
+ includeSourceRetentionAnnotations = false,
sourceFiles = *arrayOf(
java(
- """
+ """
package test.pkg;
import android.annotation.IntDef;
@@ -75,7 +76,8 @@
intRangeAnnotationSource
),
warnings = "src/test/pkg/IntDefTest.java:11: error: This typedef annotation class should have @Retention(RetentionPolicy.SOURCE) [AnnotationExtraction:146]",
- extractAnnotations = mapOf("test.pkg" to """
+ extractAnnotations = mapOf(
+ "test.pkg" to """
<?xml version="1.0" encoding="UTF-8"?>
<root>
<item name="test.pkg.IntDefTest void setFlags(java.lang.Object, int) 1">
@@ -104,6 +106,7 @@
@Test
fun `Check Kotlin and referencing hidden constants from typedef`() {
check(
+ includeSourceRetentionAnnotations = false,
sourceFiles = *arrayOf(
kotlin(
"""
@@ -151,24 +154,187 @@
longDefAnnotationSource
),
warnings = "src/test/pkg/LongDefTest.kt:12: error: Typedef class references hidden field field LongDefTestKt.HIDDEN: removed from typedef metadata [HiddenTypedefConstant:148]",
- extractAnnotations = mapOf("test.pkg" to """
+ extractAnnotations = mapOf(
+ "test.pkg" to """
+ <?xml version="1.0" encoding="UTF-8"?>
+ <root>
+ <item name="test.pkg.LongDefTest void setFlags(java.lang.Object, int) 0">
+ <annotation name="androidx.annotation.NonNull"/>
+ </item>
+ <item name="test.pkg.LongDefTest void setFlags(java.lang.Object, int) 1">
+ <annotation name="androidx.annotation.LongDef">
+ <val name="flag" val="true" />
+ <val name="value" val="{test.pkg.LongDefTestKt.STYLE_NORMAL, test.pkg.LongDefTestKt.STYLE_NO_TITLE, test.pkg.LongDefTestKt.STYLE_NO_FRAME, test.pkg.LongDefTestKt.STYLE_NO_INPUT, 3, 4}" />
+ </annotation>
+ </item>
+ <item name="test.pkg.LongDefTest void setStyle(int, int) 0">
+ <annotation name="androidx.annotation.LongDef">
+ <val name="value" val="{test.pkg.LongDefTestKt.STYLE_NORMAL, test.pkg.LongDefTestKt.STYLE_NO_TITLE, test.pkg.LongDefTestKt.STYLE_NO_FRAME, test.pkg.LongDefTestKt.STYLE_NO_INPUT}" />
+ </annotation>
+ </item>
+ <item name="test.pkg.LongDefTest.Inner boolean isNull(java.lang.String) 0">
+ <annotation name="androidx.annotation.Nullable"/>
+ </item>
+ <item name="test.pkg.LongDefTest.Inner void setInner(int) 0">
+ <annotation name="androidx.annotation.LongDef">
+ <val name="flag" val="true" />
+ <val name="value" val="{test.pkg.LongDefTestKt.STYLE_NORMAL, test.pkg.LongDefTestKt.STYLE_NO_TITLE, test.pkg.LongDefTestKt.STYLE_NO_FRAME, test.pkg.LongDefTestKt.STYLE_NO_INPUT, 3, 4}" />
+ </annotation>
+ </item>
+ <item name="test.pkg.LongDefTestKt TYPE_1">
+ <annotation name="androidx.annotation.NonNull"/>
+ </item>
+ <item name="test.pkg.LongDefTestKt TYPE_2">
+ <annotation name="androidx.annotation.NonNull"/>
+ </item>
+ <item name="test.pkg.LongDefTestKt UNRELATED_TYPE">
+ <annotation name="androidx.annotation.NonNull"/>
+ </item>
+ </root>
+ """
+ )
+ )
+ }
+
+ @Test
+ fun `Check including only class retention annotations other than typedefs`() {
+ check(
+ includeSourceRetentionAnnotations = true,
+ sourceFiles = *arrayOf(
+ kotlin(
+ """
+ @file:Suppress("unused", "UseExpressionBody")
+
+ package test.pkg
+
+ import android.annotation.LongDef
+
+ const val STYLE_NORMAL = 0L
+ const val STYLE_NO_TITLE = 1L
+ const val STYLE_NO_FRAME = 2L
+ const val STYLE_NO_INPUT = 3L
+ const val UNRELATED = 3L
+ private const val HIDDEN = 4
+
+ const val TYPE_1 = "type1"
+ const val TYPE_2 = "type2"
+ const val UNRELATED_TYPE = "other"
+
+ class LongDefTest {
+
+ /** @hide */
+ @LongDef(STYLE_NORMAL, STYLE_NO_TITLE, STYLE_NO_FRAME, STYLE_NO_INPUT, HIDDEN)
+ @Retention(AnnotationRetention.SOURCE)
+ private annotation class DialogStyle
+
+ fun setStyle(@DialogStyle style: Int, theme: Int) {}
+
+ fun testLongDef(arg: Int) {
+ }
+
+ @LongDef(STYLE_NORMAL, STYLE_NO_TITLE, STYLE_NO_FRAME, STYLE_NO_INPUT, 3L, 3L + 1L, flag = true)
+ @Retention(AnnotationRetention.SOURCE)
+ private annotation class DialogFlags
+
+ fun setFlags(first: Any, @DialogFlags flags: Int) {}
+
+ class Inner {
+ fun setInner(@DialogFlags flags: Int) {}
+ fun isNull(value: String?): Boolean
+ }
+ }"""
+ ).indented(),
+ longDefAnnotationSource
+ ),
+ warnings = "src/test/pkg/LongDefTest.kt:12: error: Typedef class references hidden field field LongDefTestKt.HIDDEN: removed from typedef metadata [HiddenTypedefConstant:148]",
+ extractAnnotations = mapOf(
+ "test.pkg" to """
+ <?xml version="1.0" encoding="UTF-8"?>
+ <root>
+ <item name="test.pkg.LongDefTest void setFlags(java.lang.Object, int) 1">
+ <annotation name="androidx.annotation.LongDef">
+ <val name="flag" val="true" />
+ <val name="value" val="{test.pkg.LongDefTestKt.STYLE_NORMAL, test.pkg.LongDefTestKt.STYLE_NO_TITLE, test.pkg.LongDefTestKt.STYLE_NO_FRAME, test.pkg.LongDefTestKt.STYLE_NO_INPUT, 3, 4}" />
+ </annotation>
+ </item>
+ <item name="test.pkg.LongDefTest void setStyle(int, int) 0">
+ <annotation name="androidx.annotation.LongDef">
+ <val name="value" val="{test.pkg.LongDefTestKt.STYLE_NORMAL, test.pkg.LongDefTestKt.STYLE_NO_TITLE, test.pkg.LongDefTestKt.STYLE_NO_FRAME, test.pkg.LongDefTestKt.STYLE_NO_INPUT}" />
+ </annotation>
+ </item>
+ <item name="test.pkg.LongDefTest.Inner void setInner(int) 0">
+ <annotation name="androidx.annotation.LongDef">
+ <val name="flag" val="true" />
+ <val name="value" val="{test.pkg.LongDefTestKt.STYLE_NORMAL, test.pkg.LongDefTestKt.STYLE_NO_TITLE, test.pkg.LongDefTestKt.STYLE_NO_FRAME, test.pkg.LongDefTestKt.STYLE_NO_INPUT, 3, 4}" />
+ </annotation>
+ </item>
+ </root>
+ """
+ )
+ )
+ }
+
+ @Test
+ fun `Extract permission annotations`() {
+ check(
+ includeSourceRetentionAnnotations = false,
+ sourceFiles = *arrayOf(
+ java(
+ """
+ package test.pkg;
+
+ import android.annotation.RequiresPermission;
+
+ public class PermissionsTest {
+ @RequiresPermission(Manifest.permission.MY_PERMISSION)
+ public void myMethod() {
+ }
+ @RequiresPermission(anyOf={Manifest.permission.MY_PERMISSION,Manifest.permission.MY_PERMISSION2})
+ public void myMethod2() {
+ }
+
+ @RequiresPermission.Read(@RequiresPermission(Manifest.permission.MY_READ_PERMISSION))
+ @RequiresPermission.Write(@RequiresPermission(Manifest.permission.MY_WRITE_PERMISSION))
+ public static final String CONTENT_URI = "";
+ }
+ """
+ ).indented(),
+ java(
+ """
+ package test.pkg;
+
+ public class Manifest {
+ public static final class permission {
+ public static final String MY_PERMISSION = "android.permission.MY_PERMISSION_STRING";
+ public static final String MY_PERMISSION2 = "android.permission.MY_PERMISSION_STRING2";
+ public static final String MY_READ_PERMISSION = "android.permission.MY_READ_PERMISSION_STRING";
+ public static final String MY_WRITE_PERMISSION = "android.permission.MY_WRITE_PERMISSION_STRING";
+ }
+ }
+ """
+ ).indented(),
+ requiresPermissionSource
+ ),
+ extractAnnotations = mapOf(
+ "test.pkg" to """
<?xml version="1.0" encoding="UTF-8"?>
<root>
- <item name="test.pkg.LongDefTest void setFlags(java.lang.Object, int) 1">
- <annotation name="androidx.annotation.LongDef">
- <val name="flag" val="true" />
- <val name="value" val="{test.pkg.LongDefTestKt.STYLE_NORMAL, test.pkg.LongDefTestKt.STYLE_NO_TITLE, test.pkg.LongDefTestKt.STYLE_NO_FRAME, test.pkg.LongDefTestKt.STYLE_NO_INPUT, 3, 4}" />
+ <item name="test.pkg.PermissionsTest CONTENT_URI">
+ <annotation name="androidx.annotation.RequiresPermission.Read">
+ <val name="value" val=""android.permission.MY_READ_PERMISSION_STRING"" />
+ </annotation>
+ <annotation name="androidx.annotation.RequiresPermission.Write">
+ <val name="value" val=""android.permission.MY_WRITE_PERMISSION_STRING"" />
</annotation>
</item>
- <item name="test.pkg.LongDefTest void setStyle(int, int) 0">
- <annotation name="androidx.annotation.LongDef">
- <val name="value" val="{test.pkg.LongDefTestKt.STYLE_NORMAL, test.pkg.LongDefTestKt.STYLE_NO_TITLE, test.pkg.LongDefTestKt.STYLE_NO_FRAME, test.pkg.LongDefTestKt.STYLE_NO_INPUT}" />
+ <item name="test.pkg.PermissionsTest void myMethod()">
+ <annotation name="androidx.annotation.RequiresPermission">
+ <val name="value" val=""android.permission.MY_PERMISSION_STRING"" />
</annotation>
</item>
- <item name="test.pkg.LongDefTest.Inner void setInner(int) 0">
- <annotation name="androidx.annotation.LongDef">
- <val name="flag" val="true" />
- <val name="value" val="{test.pkg.LongDefTestKt.STYLE_NORMAL, test.pkg.LongDefTestKt.STYLE_NO_TITLE, test.pkg.LongDefTestKt.STYLE_NO_FRAME, test.pkg.LongDefTestKt.STYLE_NO_INPUT, 3, 4}" />
+ <item name="test.pkg.PermissionsTest void myMethod2()">
+ <annotation name="androidx.annotation.RequiresPermission">
+ <val name="anyOf" val="{"android.permission.MY_PERMISSION_STRING", "android.permission.MY_PERMISSION_STRING2"}" />
</annotation>
</item>
</root>
@@ -181,6 +347,7 @@
@Test
fun `Include merged annotations in exported source annotations`() {
check(
+ includeSourceRetentionAnnotations = true,
compatibilityMode = false,
outputKotlinStyleNulls = false,
includeSystemApiAnnotations = false,
@@ -205,7 +372,8 @@
</item>
</root>
""",
- extractAnnotations = mapOf("test.pkg" to """
+ extractAnnotations = mapOf(
+ "test.pkg" to """
<?xml version="1.0" encoding="UTF-8"?>
<root>
<item name="test.pkg.MyTest void test(int) 0">
@@ -218,4 +386,43 @@
)
)
}
+
+ @Test
+ fun `Only including class retention annotations in stubs`() {
+ check(
+ includeSourceRetentionAnnotations = false,
+ compatibilityMode = false,
+ outputKotlinStyleNulls = false,
+ includeSystemApiAnnotations = false,
+ omitCommonPackages = false,
+ sourceFiles =
+ *arrayOf(
+ java(
+ """
+ package test.pkg;
+ import android.annotation.IntRange;
+ import androidx.annotation.RecentlyNullable;
+ public class Test {
+ @RecentlyNullable
+ public static String sayHello(@IntRange(from = 10) int value) { return "hello " + value; }
+ }
+ """
+ ),
+ intRangeAnnotationSource,
+ recentlyNullableSource
+ ),
+ extractAnnotations = mapOf(
+ "test.pkg" to """
+ <?xml version="1.0" encoding="UTF-8"?>
+ <root>
+ <item name="test.pkg.Test java.lang.String sayHello(int) 0">
+ <annotation name="androidx.annotation.IntRange">
+ <val name="from" val="10" />
+ </annotation>
+ </item>
+ </root>
+ """
+ )
+ )
+ }
}
\ No newline at end of file
diff --git a/src/test/java/com/android/tools/metalava/NullnessMigrationTest.kt b/src/test/java/com/android/tools/metalava/NullnessMigrationTest.kt
index 0557168..ed328ae 100644
--- a/src/test/java/com/android/tools/metalava/NullnessMigrationTest.kt
+++ b/src/test/java/com/android/tools/metalava/NullnessMigrationTest.kt
@@ -16,6 +16,7 @@
package com.android.tools.metalava
+import com.android.tools.metalava.model.SUPPORT_TYPE_USE_ANNOTATIONS
import org.junit.Test
class NullnessMigrationTest : DriverTest() {
@@ -273,11 +274,12 @@
}
"""
),
- supportNonNullSource,
- supportNullableSource
+ androidxNonNullSource,
+ androidxNullableSource
),
extraArguments = arrayOf("--hide-package", "androidx.annotation"),
- api = """
+ api = if (SUPPORT_TYPE_USE_ANNOTATIONS) {
+ """
package test.pkg {
public class Test {
ctor public Test();
@@ -286,6 +288,17 @@
}
}
"""
+ } else {
+ """
+ package test.pkg {
+ public class Test {
+ ctor public Test();
+ method @Nullable public Integer compute1(@Nullable java.util.List<java.lang.String>);
+ method @Nullable public Integer compute2(@Nullable java.util.List<java.util.List<?>>);
+ }
+ }
+ """
+ }
)
}
@@ -315,7 +328,8 @@
androidxNullableSource
),
extraArguments = arrayOf("--hide-package", "androidx.annotation"),
- api = """
+ api = if (SUPPORT_TYPE_USE_ANNOTATIONS) {
+ """
package test.pkg {
public class Test {
ctor public Test();
@@ -324,6 +338,17 @@
}
}
"""
+ } else {
+ """
+ package test.pkg {
+ public class Test {
+ ctor public Test();
+ method @Nullable public Integer compute1(@Nullable java.util.List<java.lang.String>);
+ method @Nullable public Integer compute2(@NonNull java.util.List<java.util.List<?>>);
+ }
+ }
+ """
+ }
)
}
@@ -362,18 +387,33 @@
}
}
""",
- stubs = arrayOf(
- """
- package test.pkg;
- @SuppressWarnings({"unchecked", "deprecation", "all"})
- public class Foo {
- public Foo() { throw new RuntimeException("Stub!"); }
- public static char @androidx.annotation.RecentlyNonNull [] toChars(int codePoint) { throw new RuntimeException("Stub!"); }
- public static int codePointAt(char @androidx.annotation.RecentlyNonNull [] a, int index) { throw new RuntimeException("Stub!"); }
- public <T> T @androidx.annotation.RecentlyNonNull [] toArray(T @androidx.annotation.RecentlyNonNull [] a) { throw new RuntimeException("Stub!"); }
- }
- """
- )
+ stubs = if (SUPPORT_TYPE_USE_ANNOTATIONS) {
+ arrayOf(
+ """
+ package test.pkg;
+ @SuppressWarnings({"unchecked", "deprecation", "all"})
+ public class Foo {
+ public Foo() { throw new RuntimeException("Stub!"); }
+ public static char @androidx.annotation.RecentlyNonNull [] toChars(int codePoint) { throw new RuntimeException("Stub!"); }
+ public static int codePointAt(char @androidx.annotation.RecentlyNonNull [] a, int index) { throw new RuntimeException("Stub!"); }
+ public <T> T @androidx.annotation.RecentlyNonNull [] toArray(T @androidx.annotation.RecentlyNonNull [] a) { throw new RuntimeException("Stub!"); }
+ }
+ """
+ )
+ } else {
+ arrayOf(
+ """
+ package test.pkg;
+ @SuppressWarnings({"unchecked", "deprecation", "all"})
+ public class Foo {
+ public Foo() { throw new RuntimeException("Stub!"); }
+ public static char[] toChars(int codePoint) { throw new RuntimeException("Stub!"); }
+ public static int codePointAt(char[] a, int index) { throw new RuntimeException("Stub!"); }
+ public <T> T[] toArray(T[] a) { throw new RuntimeException("Stub!"); }
+ }
+ """
+ )
+ }
)
}
@@ -412,18 +452,33 @@
}
}
""",
- stubs = arrayOf(
- """
- package test.pkg;
- @SuppressWarnings({"unchecked", "deprecation", "all"})
- public class Foo {
- public Foo() { throw new RuntimeException("Stub!"); }
- public static char @androidx.annotation.RecentlyNonNull [] toChars(int codePoint) { throw new RuntimeException("Stub!"); }
- public static int codePointAt(char @androidx.annotation.RecentlyNonNull [] a, int index) { throw new RuntimeException("Stub!"); }
- public <T> T @androidx.annotation.RecentlyNonNull [] toArray(T @androidx.annotation.RecentlyNonNull [] a) { throw new RuntimeException("Stub!"); }
- }
- """
- )
+ stubs = if (SUPPORT_TYPE_USE_ANNOTATIONS) {
+ arrayOf(
+ """
+ package test.pkg;
+ @SuppressWarnings({"unchecked", "deprecation", "all"})
+ public class Foo {
+ public Foo() { throw new RuntimeException("Stub!"); }
+ public static char @androidx.annotation.RecentlyNonNull [] toChars(int codePoint) { throw new RuntimeException("Stub!"); }
+ public static int codePointAt(char @androidx.annotation.RecentlyNonNull [] a, int index) { throw new RuntimeException("Stub!"); }
+ public <T> T @androidx.annotation.RecentlyNonNull [] toArray(T @androidx.annotation.RecentlyNonNull [] a) { throw new RuntimeException("Stub!"); }
+ }
+ """
+ )
+ } else {
+ arrayOf(
+ """
+ package test.pkg;
+ @SuppressWarnings({"unchecked", "deprecation", "all"})
+ public class Foo {
+ public Foo() { throw new RuntimeException("Stub!"); }
+ public static char[] toChars(int codePoint) { throw new RuntimeException("Stub!"); }
+ public static int codePointAt(char[] a, int index) { throw new RuntimeException("Stub!"); }
+ public <T> T[] toArray(T[] a) { throw new RuntimeException("Stub!"); }
+ }
+ """
+ )
+ }
)
}
}
\ No newline at end of file
diff --git a/src/test/java/com/android/tools/metalava/OptionsTest.kt b/src/test/java/com/android/tools/metalava/OptionsTest.kt
index 9da7caa..5497e8a 100644
--- a/src/test/java/com/android/tools/metalava/OptionsTest.kt
+++ b/src/test/java/com/android/tools/metalava/OptionsTest.kt
@@ -181,6 +181,10 @@
metalava/stub-annotations/src/main/java/.
--rewrite-annotations <dir/jar> For a bytecode folder or output jar, rewrites the
androidx annotations to be package private
+--include-source-retention If true, include source-retention annotations in
+ the stub files. Does not apply to signature files.
+ Source retention annotations are extracted into the
+ external annotations files instead.
Injecting API Levels:
--apply-api-levels <api-versions.xml> Reads an XML file containing API level descriptions
diff --git a/src/test/java/com/android/tools/metalava/StubsTest.kt b/src/test/java/com/android/tools/metalava/StubsTest.kt
index 5d7f7df..f42d4bb 100644
--- a/src/test/java/com/android/tools/metalava/StubsTest.kt
+++ b/src/test/java/com/android/tools/metalava/StubsTest.kt
@@ -19,6 +19,7 @@
package com.android.tools.metalava
import com.android.tools.lint.checks.infrastructure.TestFile
+import com.android.tools.metalava.model.SUPPORT_TYPE_USE_ANNOTATIONS
import org.intellij.lang.annotations.Language
import org.junit.Test
@@ -1345,16 +1346,28 @@
"""
)
),
- api = """
+ api = if (SUPPORT_TYPE_USE_ANNOTATIONS) {
+ """
package test.pkg {
public class Foo {
ctor public Foo();
method public void foo(int, java.util.Map<java.lang.String, java.lang.String>!);
}
}
- """,
+ """
+ } else {
+ """
+ package test.pkg {
+ public class Foo {
+ ctor public Foo();
+ method public void foo(int, java.util.Map<java.lang.String,java.lang.String>!);
+ }
+ }
+ """
+ },
- source = """
+ source = if (SUPPORT_TYPE_USE_ANNOTATIONS) {
+ """
package test.pkg;
@SuppressWarnings({"unchecked", "deprecation", "all"})
public class Foo {
@@ -1362,6 +1375,16 @@
public void foo(int p1, java.util.Map<java.lang.String, java.lang.String> p2) { throw new RuntimeException("Stub!"); }
}
"""
+ } else {
+ """
+ package test.pkg;
+ @SuppressWarnings({"unchecked", "deprecation", "all"})
+ public class Foo {
+ public Foo() { throw new RuntimeException("Stub!"); }
+ public void foo(int p1, java.util.Map<java.lang.String,java.lang.String> p2) { throw new RuntimeException("Stub!"); }
+ }
+ """
+ }
)
}
@@ -1764,40 +1787,52 @@
}
}
""",
- stubs = arrayOf(
- """
- package my.pkg;
- @SuppressWarnings({"unchecked", "deprecation", "all"})
- public class String {
- public String(char @androidx.annotation.NonNull [] value) { throw new RuntimeException("Stub!"); }
- }
- """
- )
+ stubs = if (SUPPORT_TYPE_USE_ANNOTATIONS) {
+ arrayOf(
+ """
+ package my.pkg;
+ @SuppressWarnings({"unchecked", "deprecation", "all"})
+ public class String {
+ public String(char @androidx.annotation.NonNull [] value) { throw new RuntimeException("Stub!"); }
+ }
+ """
+ )
+ } else {
+ arrayOf(
+ """
+ package my.pkg;
+ @SuppressWarnings({"unchecked", "deprecation", "all"})
+ public class String {
+ public String(char[] value) { throw new RuntimeException("Stub!"); }
+ }
+ """
+ )
+ }
)
}
- @Test
- fun `Test inaccessible constructors`() {
- // If the constructors of a class are not visible, and the class has subclasses,
- // those subclass stubs will need to reference these inaccessible constructors.
- // This generally only happens when the constructors are package private (and
- // therefore hidden) but the subclass using it is also in the same package.
+@Test
+fun `Test inaccessible constructors`() {
+ // If the constructors of a class are not visible, and the class has subclasses,
+ // those subclass stubs will need to reference these inaccessible constructors.
+ // This generally only happens when the constructors are package private (and
+ // therefore hidden) but the subclass using it is also in the same package.
- check(
- checkDoclava1 = false,
- checkCompilation = true,
- sourceFiles =
- *arrayOf(
- java(
- """
+ check(
+ checkDoclava1 = false,
+ checkCompilation = true,
+ sourceFiles =
+ *arrayOf(
+ java(
+ """
package test.pkg;
public class MyClass1 {
MyClass1(int myVar) { }
}
"""
- ),
- java(
- """
+ ),
+ java(
+ """
package test.pkg;
import java.io.IOException;
@SuppressWarnings("RedundantThrows")
@@ -1805,27 +1840,27 @@
MySubClass1(int myVar) throws IOException { super(myVar); }
}
"""
- ),
- java(
- """
+ ),
+ java(
+ """
package test.pkg;
public class MyClass2 {
/** @hide */
public MyClass2(int myVar) { }
}
"""
- ),
- java(
- """
+ ),
+ java(
+ """
package test.pkg;
public class MySubClass2 extends MyClass2 {
public MySubClass2() { super(5); }
}
"""
- )
- ),
- warnings = "",
- api = """
+ )
+ ),
+ warnings = "",
+ api = """
package test.pkg {
public class MyClass1 {
}
@@ -1838,22 +1873,22 @@
}
}
""",
- stubs = arrayOf(
- """
+ stubs = arrayOf(
+ """
package test.pkg;
@SuppressWarnings({"unchecked", "deprecation", "all"})
public class MyClass1 {
MyClass1(int myVar) { throw new RuntimeException("Stub!"); }
}
""",
- """
+ """
package test.pkg;
@SuppressWarnings({"unchecked", "deprecation", "all"})
public class MySubClass1 extends test.pkg.MyClass1 {
MySubClass1(int myVar) throws java.io.IOException { super(0); throw new RuntimeException("Stub!"); }
}
""",
- """
+ """
package test.pkg;
@SuppressWarnings({"unchecked", "deprecation", "all"})
public class MyClass2 {
@@ -1861,32 +1896,32 @@
MyClass2(int myVar) { throw new RuntimeException("Stub!"); }
}
""",
- """
+ """
package test.pkg;
@SuppressWarnings({"unchecked", "deprecation", "all"})
public class MySubClass2 extends test.pkg.MyClass2 {
public MySubClass2() { super(0); throw new RuntimeException("Stub!"); }
}
"""
- )
)
- }
+ )
+}
- @Test
- fun `Generics Variable Rewriting`() {
- // When we move methods from hidden superclasses into the subclass since they
- // provide the implementation for a required method, it's possible that the
- // method we copied in is referencing generics with a different variable than
- // in the current class, so we need to handle this
+@Test
+fun `Generics Variable Rewriting`() {
+ // When we move methods from hidden superclasses into the subclass since they
+ // provide the implementation for a required method, it's possible that the
+ // method we copied in is referencing generics with a different variable than
+ // in the current class, so we need to handle this
- checkStubs(
- checkDoclava1 = false,
- sourceFiles =
- *arrayOf(
- // TODO: Try using prefixes like "A", and "AA" to make sure my generics
- // variable renaming doesn't do something really dumb
- java(
- """
+ checkStubs(
+ checkDoclava1 = false,
+ sourceFiles =
+ *arrayOf(
+ // TODO: Try using prefixes like "A", and "AA" to make sure my generics
+ // variable renaming doesn't do something really dumb
+ java(
+ """
package test.pkg;
import java.util.List;
@@ -1916,10 +1951,10 @@
}
}
"""
- )
- ),
- warnings = "",
- source = """
+ )
+ ),
+ warnings = "",
+ source = """
package test.pkg;
@SuppressWarnings({"unchecked", "deprecation", "all"})
public class Generics {
@@ -1945,18 +1980,18 @@
}
}
"""
- )
- }
+ )
+}
- @Test
- fun `Rewriting type parameters in interfaces from hidden super classes and in throws lists`() {
- checkStubs(
- extraArguments = arrayOf("--skip-inherited-methods=false"),
- checkDoclava1 = false,
- sourceFiles =
- *arrayOf(
- java(
- """
+@Test
+fun `Rewriting type parameters in interfaces from hidden super classes and in throws lists`() {
+ checkStubs(
+ extraArguments = arrayOf("--skip-inherited-methods=false"),
+ checkDoclava1 = false,
+ sourceFiles =
+ *arrayOf(
+ java(
+ """
package test.pkg;
import java.io.IOException;
@@ -1991,10 +2026,10 @@
}
}
"""
- )
- ),
- warnings = "",
- api = """
+ )
+ ),
+ warnings = "",
+ api = """
package test.pkg {
public class Generics {
ctor public Generics();
@@ -2013,41 +2048,66 @@
}
}
""",
- source = """
- package test.pkg;
- @SuppressWarnings({"unchecked", "deprecation", "all"})
- public class Generics {
- public Generics() { throw new RuntimeException("Stub!"); }
- @SuppressWarnings({"unchecked", "deprecation", "all"})
- public class MyClass<X, Y extends java.lang.Number> extends test.pkg.Generics.PublicParent<X,Y> implements test.pkg.Generics.PublicInterface<X,Y> {
- public MyClass() { throw new RuntimeException("Stub!"); }
- public java.util.List<X> foo() { throw new RuntimeException("Stub!"); }
- public java.util.Map<X,java.util.Map<Y,java.lang.String>> createMap(java.util.List<X> list) throws java.io.IOException { throw new RuntimeException("Stub!"); }
- }
- @SuppressWarnings({"unchecked", "deprecation", "all"})
- public static interface PublicInterface<A, B> {
- public java.util.Map<A,java.util.Map<B,java.lang.String>> createMap(java.util.List<A> list) throws java.io.IOException;
- }
- @SuppressWarnings({"unchecked", "deprecation", "all"})
- public abstract class PublicParent<A, B extends java.lang.Number> {
- public PublicParent() { throw new RuntimeException("Stub!"); }
- protected abstract java.util.List<A> foo();
- }
- }
- """
- )
- }
+ source = if (SUPPORT_TYPE_USE_ANNOTATIONS) {
+ """
+ package test.pkg;
+ @SuppressWarnings({"unchecked", "deprecation", "all"})
+ public class Generics {
+ public Generics() { throw new RuntimeException("Stub!"); }
+ @SuppressWarnings({"unchecked", "deprecation", "all"})
+ public class MyClass<X, Y extends java.lang.Number> extends test.pkg.Generics.PublicParent<X,Y> implements test.pkg.Generics.PublicInterface<X,Y> {
+ public MyClass() { throw new RuntimeException("Stub!"); }
+ public java.util.List<X> foo() { throw new RuntimeException("Stub!"); }
+ public java.util.Map<X,java.util.Map<Y,java.lang.String>> createMap(java.util.List<X> list) throws java.io.IOException { throw new RuntimeException("Stub!"); }
+ }
+ @SuppressWarnings({"unchecked", "deprecation", "all"})
+ public static interface PublicInterface<A, B> {
+ public java.util.Map<A,java.util.Map<B,java.lang.String>> createMap(java.util.List<A> list) throws java.io.IOException;
+ }
+ @SuppressWarnings({"unchecked", "deprecation", "all"})
+ public abstract class PublicParent<A, B extends java.lang.Number> {
+ public PublicParent() { throw new RuntimeException("Stub!"); }
+ protected abstract java.util.List<A> foo();
+ }
+ }
+ """
+ } else {
+ """
+ package test.pkg;
+ @SuppressWarnings({"unchecked", "deprecation", "all"})
+ public class Generics {
+ public Generics() { throw new RuntimeException("Stub!"); }
+ @SuppressWarnings({"unchecked", "deprecation", "all"})
+ public class MyClass<X, Y extends java.lang.Number> extends test.pkg.Generics.PublicParent<X,Y> implements test.pkg.Generics.PublicInterface<X,Y> {
+ public MyClass() { throw new RuntimeException("Stub!"); }
+ public java.util.List<X> foo() { throw new RuntimeException("Stub!"); }
+ public java.util.Map<X, java.util.Map<Y, java.lang.String>> createMap(java.util.List<X> list) throws java.io.IOException { throw new RuntimeException("Stub!"); }
+ }
+ @SuppressWarnings({"unchecked", "deprecation", "all"})
+ public static interface PublicInterface<A, B> {
+ public java.util.Map<A, java.util.Map<B, java.lang.String>> createMap(java.util.List<A> list) throws java.io.IOException;
+ }
+ @SuppressWarnings({"unchecked", "deprecation", "all"})
+ public abstract class PublicParent<A, B extends java.lang.Number> {
+ public PublicParent() { throw new RuntimeException("Stub!"); }
+ protected abstract java.util.List<A> foo();
+ }
+ }
+ """
+ }
+ )
+}
- @Test
- fun `Picking super class throwables`() {
- // Like previous test, but without compatibility mode: ensures that we
- // use super classes of filtered throwables
- checkStubs(
- compatibilityMode = false,
- sourceFiles =
- *arrayOf(
- java(
- """
+@Test
+fun `Picking super class throwables`() {
+ // Like previous test, but without compatibility mode: ensures that we
+ // use super classes of filtered throwables
+ checkStubs(
+ compatibilityMode = false,
+ sourceFiles =
+ *arrayOf(
+ java(
+ """
package test.pkg;
import java.io.IOException;
@@ -2082,10 +2142,10 @@
}
}
"""
- )
- ),
- warnings = "",
- api = """
+ )
+ ),
+ warnings = "",
+ api = """
package test.pkg {
public class Generics {
ctor public Generics();
@@ -2104,7 +2164,7 @@
}
}
""",
- source = """
+ source = """
package test.pkg;
@SuppressWarnings({"unchecked", "deprecation", "all"})
public class Generics {
@@ -2126,19 +2186,19 @@
}
}
"""
- )
- }
+ )
+}
- @Test
- fun `Rewriting implements class references`() {
- // Checks some more subtle bugs around generics type variable renaming
- checkStubs(
- extraArguments = arrayOf("--skip-inherited-methods=false"),
- checkDoclava1 = false,
- sourceFiles =
- *arrayOf(
- java(
- """
+@Test
+fun `Rewriting implements class references`() {
+ // Checks some more subtle bugs around generics type variable renaming
+ checkStubs(
+ extraArguments = arrayOf("--skip-inherited-methods=false"),
+ checkDoclava1 = false,
+ sourceFiles =
+ *arrayOf(
+ java(
+ """
package test.pkg;
import java.util.Collection;
@@ -2165,10 +2225,10 @@
}
}
"""
- )
- ),
- warnings = "",
- api = """
+ )
+ ),
+ warnings = "",
+ api = """
package test.pkg {
public class ConcurrentHashMap<K, V> {
ctor public ConcurrentHashMap();
@@ -2181,7 +2241,7 @@
}
}
""",
- source = """
+ source = """
package test.pkg;
@SuppressWarnings({"unchecked", "deprecation", "all"})
public class ConcurrentHashMap<K, V> {
@@ -2195,17 +2255,17 @@
}
}
"""
- )
- }
+ )
+}
- @Test
- fun `Arrays in type arguments`() {
- checkStubs(
- checkDoclava1 = false,
- sourceFiles =
- *arrayOf(
- java(
- """
+@Test
+fun `Arrays in type arguments`() {
+ checkStubs(
+ checkDoclava1 = false,
+ sourceFiles =
+ *arrayOf(
+ java(
+ """
package test.pkg;
public class Generics2 {
@@ -2217,10 +2277,10 @@
}
}
"""
- )
- ),
- warnings = "",
- source = """
+ )
+ ),
+ warnings = "",
+ source = """
package test.pkg;
@SuppressWarnings({"unchecked", "deprecation", "all"})
public class Generics2 {
@@ -2234,20 +2294,20 @@
}
}
"""
- )
- }
+ )
+}
- @Test
- fun `Interface extending multiple interfaces`() {
- // Ensure that we handle sorting correctly where we're mixing super classes and implementing
- // interfaces
- // Real-world example: XmlResourceParser
- check(
- checkDoclava1 = false,
- checkCompilation = true,
- sourceFiles = *arrayOf(
- java(
- """
+@Test
+fun `Interface extending multiple interfaces`() {
+ // Ensure that we handle sorting correctly where we're mixing super classes and implementing
+ // interfaces
+ // Real-world example: XmlResourceParser
+ check(
+ checkDoclava1 = false,
+ checkCompilation = true,
+ sourceFiles = *arrayOf(
+ java(
+ """
package android.content.res;
import android.util.AttributeSet;
import org.xmlpull.v1.XmlPullParser;
@@ -2257,52 +2317,52 @@
public void close();
}
"""
- ),
- java(
- """
+ ),
+ java(
+ """
package android.util;
public interface AttributeSet {
}
"""
- ),
- java(
- """
+ ),
+ java(
+ """
package java.lang;
public interface AutoCloseable {
}
"""
- ),
- java(
- """
+ ),
+ java(
+ """
package org.xmlpull.v1;
public interface XmlPullParser {
}
"""
- )
- ),
- stubs = arrayOf(
- """
+ )
+ ),
+ stubs = arrayOf(
+ """
package android.content.res;
@SuppressWarnings({"unchecked", "deprecation", "all"})
public interface XmlResourceParser extends org.xmlpull.v1.XmlPullParser, android.util.AttributeSet, java.lang.AutoCloseable {
public void close();
}
"""
- )
)
- }
+ )
+}
- // TODO: Add a protected constructor too to make sure my code to make non-public constructors package private
- // don't accidentally demote protected constructors to package private!
+// TODO: Add a protected constructor too to make sure my code to make non-public constructors package private
+// don't accidentally demote protected constructors to package private!
- @Test
- fun `Picking Super Constructors`() {
- checkStubs(
- checkDoclava1 = false,
- sourceFiles =
- *arrayOf(
- java(
- """
+@Test
+fun `Picking Super Constructors`() {
+ checkStubs(
+ checkDoclava1 = false,
+ sourceFiles =
+ *arrayOf(
+ java(
+ """
package test.pkg;
import java.io.IOException;
@@ -2374,10 +2434,10 @@
}
}
"""
- )
- ),
- warnings = "",
- api = """
+ )
+ ),
+ warnings = "",
+ api = """
package test.pkg {
public class PickConstructors {
ctor public PickConstructors();
@@ -2418,7 +2478,7 @@
}
}
""",
- source = """
+ source = """
package test.pkg;
@SuppressWarnings({"unchecked", "deprecation", "all"})
public class PickConstructors {
@@ -2470,17 +2530,17 @@
}
}
"""
- )
- }
+ )
+}
- @Test
- fun `Picking Constructors`() {
- checkStubs(
- checkDoclava1 = false,
- sourceFiles =
- *arrayOf(
- java(
- """
+@Test
+fun `Picking Constructors`() {
+ checkStubs(
+ checkDoclava1 = false,
+ sourceFiles =
+ *arrayOf(
+ java(
+ """
package test.pkg;
@SuppressWarnings({"WeakerAccess", "unused"})
@@ -2557,10 +2617,10 @@
}
}
"""
- )
- ),
- warnings = "",
- source = """
+ )
+ ),
+ warnings = "",
+ source = """
package test.pkg;
@SuppressWarnings({"unchecked", "deprecation", "all"})
public class Constructors2 {
@@ -2604,18 +2664,18 @@
}
}
"""
- )
- }
+ )
+}
- @Test
- fun `Another Constructor Test`() {
- // A specific scenario triggered in the API where the right super class detector was not chosen
- checkStubs(
- checkDoclava1 = false,
- sourceFiles =
- *arrayOf(
- java(
- """
+@Test
+fun `Another Constructor Test`() {
+ // A specific scenario triggered in the API where the right super class detector was not chosen
+ checkStubs(
+ checkDoclava1 = false,
+ sourceFiles =
+ *arrayOf(
+ java(
+ """
package test.pkg;
@SuppressWarnings({"RedundantThrows", "JavaDoc", "WeakerAccess"})
@@ -2641,10 +2701,10 @@
}
}
"""
- )
- ),
- warnings = "",
- source = """
+ )
+ ),
+ warnings = "",
+ source = """
package test.pkg;
@SuppressWarnings({"unchecked", "deprecation", "all"})
public class PickConstructors2 {
@@ -2665,18 +2725,18 @@
}
}
"""
- )
- }
+ )
+}
- @Test
- fun `Overriding protected methods`() {
- // Checks a scenario where the stubs were missing overrides
- checkStubs(
- checkDoclava1 = false,
- sourceFiles =
- *arrayOf(
- java(
- """
+@Test
+fun `Overriding protected methods`() {
+ // Checks a scenario where the stubs were missing overrides
+ checkStubs(
+ checkDoclava1 = false,
+ sourceFiles =
+ *arrayOf(
+ java(
+ """
package test.pkg;
@SuppressWarnings("all")
@@ -2699,10 +2759,10 @@
}
}
"""
- )
- ),
- warnings = "",
- api = """
+ )
+ ),
+ warnings = "",
+ api = """
package test.pkg {
public class Layouts {
ctor public Layouts();
@@ -2720,7 +2780,7 @@
}
}
""",
- source = """
+ source = """
package test.pkg;
@SuppressWarnings({"unchecked", "deprecation", "all"})
public class Layouts {
@@ -2742,18 +2802,18 @@
}
}
"""
- )
- }
+ )
+}
- @Test
- fun `Missing overridden method`() {
- // Another special case where overridden methods were missing
- checkStubs(
- checkDoclava1 = false,
- sourceFiles =
- *arrayOf(
- java(
- """
+@Test
+fun `Missing overridden method`() {
+ // Another special case where overridden methods were missing
+ checkStubs(
+ checkDoclava1 = false,
+ sourceFiles =
+ *arrayOf(
+ java(
+ """
package test.pkg;
import java.util.Collection;
@@ -2780,10 +2840,10 @@
}
}
"""
- )
- ),
- warnings = "",
- source = """
+ )
+ ),
+ warnings = "",
+ source = """
package test.pkg;
@SuppressWarnings({"unchecked", "deprecation", "all"})
public class SpanTest {
@@ -2805,18 +2865,18 @@
}
}
"""
- )
- }
+ )
+}
- @Test
- fun `Skip type variables in casts`() {
- // When generating casts in super constructor calls, use raw types
- checkStubs(
- checkDoclava1 = false,
- sourceFiles =
- *arrayOf(
- java(
- """
+@Test
+fun `Skip type variables in casts`() {
+ // When generating casts in super constructor calls, use raw types
+ checkStubs(
+ checkDoclava1 = false,
+ sourceFiles =
+ *arrayOf(
+ java(
+ """
package test.pkg;
@SuppressWarnings("all")
@@ -2836,10 +2896,10 @@
}
}
"""
- )
- ),
- warnings = "",
- source = """
+ )
+ ),
+ warnings = "",
+ source = """
package test.pkg;
@SuppressWarnings({"unchecked", "deprecation", "all"})
public class Properties {
@@ -2855,18 +2915,18 @@
}
}
"""
- )
- }
+ )
+}
- @Test
- fun `Rewrite relative documentation links`() {
- // When generating casts in super constructor calls, use raw types
- checkStubs(
- checkDoclava1 = false,
- sourceFiles =
- *arrayOf(
- java(
- """
+@Test
+fun `Rewrite relative documentation links`() {
+ // When generating casts in super constructor calls, use raw types
+ checkStubs(
+ checkDoclava1 = false,
+ sourceFiles =
+ *arrayOf(
+ java(
+ """
package test.pkg1;
import java.io.IOException;
import test.pkg2.OtherClass;
@@ -2898,9 +2958,9 @@
public boolean importance;
}
"""
- ),
- java(
- """
+ ),
+ java(
+ """
package test.pkg2;
@SuppressWarnings("all")
@@ -2911,19 +2971,19 @@
public void bar(int baz, boolean bar);
}
"""
- ),
- java(
- """
+ ),
+ java(
+ """
package test.pkg1;
@SuppressWarnings("all")
public class LocalClass {
}
"""
- )
- ),
- warnings = "",
- source = """
+ )
+ ),
+ warnings = "",
+ source = """
package test.pkg1;
import test.pkg2.OtherClass;
import java.io.IOException;
@@ -2955,52 +3015,52 @@
public boolean importance;
}
"""
- )
- }
+ )
+}
- @Test
- fun `Check writing package info file`() {
- checkStubs(
- sourceFiles =
- *arrayOf(
- java(
- """
+@Test
+fun `Check writing package info file`() {
+ checkStubs(
+ sourceFiles =
+ *arrayOf(
+ java(
+ """
@androidx.annotation.Nullable
package test.pkg;
"""
- ),
- java(
- """
+ ),
+ java(
+ """
package test.pkg;
@SuppressWarnings("all")
public class Test {
}
"""
- ),
-
- supportNullableSource
),
- warnings = "",
- api = """
+
+ androidxNullableSource
+ ),
+ warnings = "",
+ api = """
package test.pkg {
public class Test {
ctor public Test();
}
}
""", // WRONG: I should include package annotations!
- source = """
+ source = """
@androidx.annotation.Nullable
package test.pkg;
""",
- extraArguments = arrayOf("--hide-package", "androidx.annotation")
- )
- }
+ extraArguments = arrayOf("--hide-package", "androidx.annotation")
+ )
+}
- // TODO: Add in some type variables in method signatures and constructors!
- // TODO: Test what happens when a class extends a hidden extends a public in separate packages,
- // and the hidden has a @hide constructor so the stub in the leaf class doesn't compile -- I should
- // check for this and fail build.
+// TODO: Add in some type variables in method signatures and constructors!
+// TODO: Test what happens when a class extends a hidden extends a public in separate packages,
+// and the hidden has a @hide constructor so the stub in the leaf class doesn't compile -- I should
+// check for this and fail build.
- // TODO: Test -stubPackages
+// TODO: Test -stubPackages
}
\ No newline at end of file
diff --git a/stub-annotations/src/main/java/androidx/annotation/NonNull.java b/stub-annotations/src/main/java/androidx/annotation/NonNull.java
index 3b41cc8..647ee60 100644
--- a/stub-annotations/src/main/java/androidx/annotation/NonNull.java
+++ b/stub-annotations/src/main/java/androidx/annotation/NonNull.java
@@ -28,5 +28,5 @@
/** Stub only annotation. Do not use directly. */
@Retention(CLASS)
-@Target({METHOD, PARAMETER, FIELD, PACKAGE, TYPE_PARAMETER, TYPE_USE})
+@Target({METHOD, PARAMETER, FIELD, PACKAGE, TYPE_PARAMETER})
public @interface NonNull {}
diff --git a/stub-annotations/src/main/java/androidx/annotation/Nullable.java b/stub-annotations/src/main/java/androidx/annotation/Nullable.java
index 0e6abce..b9c630f 100644
--- a/stub-annotations/src/main/java/androidx/annotation/Nullable.java
+++ b/stub-annotations/src/main/java/androidx/annotation/Nullable.java
@@ -28,5 +28,5 @@
/** Stub only annotation. Do not use directly. */
@Retention(CLASS)
-@Target({METHOD, PARAMETER, FIELD, PACKAGE, TYPE_PARAMETER, TYPE_USE})
+@Target({METHOD, PARAMETER, FIELD, PACKAGE, TYPE_PARAMETER})
public @interface Nullable {}
diff --git a/stub-annotations/src/main/java/androidx/annotation/RecentlyNonNull.java b/stub-annotations/src/main/java/androidx/annotation/RecentlyNonNull.java
index 575b3eb..9ad4f5e 100644
--- a/stub-annotations/src/main/java/androidx/annotation/RecentlyNonNull.java
+++ b/stub-annotations/src/main/java/androidx/annotation/RecentlyNonNull.java
@@ -26,5 +26,5 @@
/** Stub only annotation. Do not use directly. */
@Retention(CLASS)
-@Target({METHOD, PARAMETER, FIELD, TYPE_USE})
+@Target({METHOD, PARAMETER, FIELD})
public @interface RecentlyNonNull {}
diff --git a/stub-annotations/src/main/java/androidx/annotation/RecentlyNullable.java b/stub-annotations/src/main/java/androidx/annotation/RecentlyNullable.java
index 8a9141e..8ad2a2d 100644
--- a/stub-annotations/src/main/java/androidx/annotation/RecentlyNullable.java
+++ b/stub-annotations/src/main/java/androidx/annotation/RecentlyNullable.java
@@ -26,5 +26,5 @@
/** Stub only annotation. Do not use directly. */
@Retention(CLASS)
-@Target({METHOD, PARAMETER, FIELD, TYPE_USE})
+@Target({METHOD, PARAMETER, FIELD})
public @interface RecentlyNullable {}