Skip to content
Snippets Groups Projects
Commit bff0085a authored by Tor Norbye's avatar Tor Norbye
Browse files

127458147: Include IntDef annotations in current.txt

Adds a new mode, off by default, which lets you either include
typedef annotation references in the signature files (without
the typedef classes themselves), or inline the typedef constants
themselves into all the annotated sites.

Usage:
--typedefs-in-signatures <ref|inline>
     Whether to include typedef annotations in
     signature files. `--typedefs-in-signatures ref`
     will include just a reference to the typedef
     class, which is not itself part of the API and
     is not included as a class, and
     `--typedefs-in-signatures inline` will include
     the constants themselves into each usage site.
     You can also supply `--typedefs-in-signatures
     none` to explicitly turn it off, if the default
     ever changes.

Bug: 127458147
Test: Unit tests included
Change-Id: I9aee3043fb7d0a12bebe23e02ecce405723478f0
parent 9bd504bf
No related branches found
No related tags found
No related merge requests found
......@@ -148,6 +148,7 @@ const val ARG_STUB_PACKAGES = "--stub-packages"
const val ARG_STUB_IMPORT_PACKAGES = "--stub-import-packages"
const val ARG_DELETE_EMPTY_BASELINES = "--delete-empty-baselines"
const val ARG_SUBTRACT_API = "--subtract-api"
const val ARG_TYPEDEFS_IN_SIGNATURES = "--typedefs-in-signatures"
class Options(
private val args: Array<String>,
......@@ -556,6 +557,15 @@ class Options(
/** List of signature files to export as JDiff files */
val convertToXmlFiles: List<ConvertFile> = mutableConvertToXmlFiles
enum class TypedefMode {
NONE,
REFERENCE,
INLINE
}
/** How to handle typedef annotations in signature files; corresponds to $ARG_TYPEDEFS_IN_SIGNATURES */
var typedefMode = TypedefMode.NONE
/** File conversion tasks */
data class ConvertFile(
val fromApiFile: File,
......@@ -769,6 +779,17 @@ class Options(
mutableSkipEmitPackages += packages.split(File.pathSeparatorChar)
}
ARG_TYPEDEFS_IN_SIGNATURES -> {
val type = getValue(args, ++index)
typedefMode = when (type) {
"ref" -> TypedefMode.REFERENCE
"inline" -> TypedefMode.INLINE
"none" -> TypedefMode.NONE
else -> throw DriverException(
stderr = "$ARG_TYPEDEFS_IN_SIGNATURES must be one of ref, inline, none; was $type")
}
}
ARG_BASELINE -> {
val relative = getValue(args, ++index)
assert(baselineFile == null) { "Only one baseline is allowed; found both $baselineFile and $relative" }
......@@ -1922,6 +1943,12 @@ class Options(
"$ARG_SUBTRACT_API <api file>", "Subtracts the API in the given signature or jar file from the " +
"current API being emitted via $ARG_API, $ARG_STUBS, $ARG_DOC_STUBS, etc. " +
"Note that the subtraction only applies to classes; it does not subtract members.",
"$ARG_TYPEDEFS_IN_SIGNATURES <ref|inline>", "Whether to include typedef annotations in signature " +
"files. `$ARG_TYPEDEFS_IN_SIGNATURES ref` will include just a reference to the typedef class, " +
"which is not itself part of the API and is not included as a class, and " +
"`$ARG_TYPEDEFS_IN_SIGNATURES inline` will include the constants themselves into each usage " +
"site. You can also supply `$ARG_TYPEDEFS_IN_SIGNATURES none` to explicitly turn it off, if the " +
"default ever changes.",
"", "\nDocumentation:",
ARG_PUBLIC, "Only include elements that are public",
......
......@@ -32,6 +32,7 @@ import com.android.tools.metalava.ANDROID_NULLABLE
import com.android.tools.metalava.ANDROID_SUPPORT_ANNOTATION_PREFIX
import com.android.tools.metalava.Compatibility
import com.android.tools.metalava.JAVA_LANG_PREFIX
import com.android.tools.metalava.Options
import com.android.tools.metalava.RECENTLY_NONNULL
import com.android.tools.metalava.RECENTLY_NULLABLE
import com.android.tools.metalava.doclava1.ApiPredicate
......@@ -85,6 +86,9 @@ interface AnnotationItem {
/** True if this annotation represents @IntDef, @LongDef or @StringDef */
fun isTypeDefAnnotation(): Boolean {
val name = qualifiedName() ?: return false
if (!(name.endsWith("Def"))) {
return false
}
return (INT_DEF_ANNOTATION.isEquals(name) ||
STRING_DEF_ANNOTATION.isEquals(name) ||
LONG_DEF_ANNOTATION.isEquals(name) ||
......@@ -120,6 +124,12 @@ interface AnnotationItem {
return codebase.findClass(qualifiedName() ?: return null)
}
/** If this annotation has a typedef annotation associated with it, return it */
fun findTypedefAnnotation(): AnnotationItem? {
val className = originalName() ?: return null
return codebase.findClass(className)?.modifiers?.annotations()?.firstOrNull { it.isTypeDefAnnotation() }
}
/** Returns the retention of this annotation */
val retention: AnnotationRetention
get() {
......@@ -466,6 +476,12 @@ interface AnnotationItem {
// See if the annotation is pointing to an annotation class that is part of the API; if not, skip it.
val cls = codebase.findClass(qualifiedName) ?: return NO_ANNOTATION_TARGETS
if (!ApiPredicate().test(cls)) {
if (options.typedefMode != Options.TypedefMode.NONE) {
if (cls.modifiers.annotations().any { it.isTypeDefAnnotation() }) {
return ANNOTATION_SIGNATURE_ONLY
}
}
return NO_ANNOTATION_TARGETS
}
......
......@@ -16,6 +16,9 @@
package com.android.tools.metalava.model
import com.android.tools.metalava.Options
import com.android.tools.metalava.options
/** Various places where a given annotation can be written */
enum class AnnotationTarget {
/** Write the annotation into the signature file */
......@@ -80,4 +83,11 @@ val ANNOTATION_IN_DOC_STUBS_AND_EXTERNAL = setOf(
val ANNOTATION_EXTERNAL = setOf(AnnotationTarget.SIGNATURE_FILE, AnnotationTarget.EXTERNAL_ANNOTATIONS_FILE)
/** Write it only into the external annotations file, not the signature file */
val ANNOTATION_EXTERNAL_ONLY = setOf(AnnotationTarget.SIGNATURE_FILE, AnnotationTarget.EXTERNAL_ANNOTATIONS_FILE)
val ANNOTATION_EXTERNAL_ONLY = if (options.typedefMode == Options.TypedefMode.INLINE ||
options.typedefMode == Options.TypedefMode.NONE) // just here for compatibility purposes
setOf(AnnotationTarget.SIGNATURE_FILE, AnnotationTarget.EXTERNAL_ANNOTATIONS_FILE)
else
setOf(AnnotationTarget.EXTERNAL_ANNOTATIONS_FILE)
/** Write it only into the he signature file */
val ANNOTATION_SIGNATURE_ONLY = setOf(AnnotationTarget.SIGNATURE_FILE)
......@@ -493,6 +493,7 @@ interface ModifierList {
continue
}
var printAnnotation = annotation
if (!annotation.targets().contains(target)) {
continue
} else if ((annotation.isNullnessAnnotation())) {
......@@ -502,6 +503,23 @@ interface ModifierList {
} else if (annotation.qualifiedName() == "java.lang.Deprecated") {
// Special cased in stubs and signature files: emitted first
continue
} else if (options.typedefMode == Options.TypedefMode.INLINE) {
val typedef = annotation.findTypedefAnnotation()
if (typedef != null) {
printAnnotation = typedef
}
} else if (options.typedefMode == Options.TypedefMode.REFERENCE &&
annotation.targets() === ANNOTATION_SIGNATURE_ONLY &&
annotation.findTypedefAnnotation() != null) {
// For annotation references, only include the simple name
writer.write("@")
writer.write(annotation.resolve()?.simpleName() ?: annotation.qualifiedName()!!)
if (separateLines) {
writer.write("\n")
} else {
writer.write(" ")
}
continue
}
// Optionally filter out duplicates
......@@ -520,7 +538,8 @@ interface ModifierList {
}
}
val source = annotation.toSource(target)
val source = printAnnotation.toSource(target)
if (omitCommonPackages) {
writer.write(AnnotationItem.shortenAnnotation(source))
} else {
......
......@@ -21,13 +21,9 @@ import org.junit.Test
@SuppressWarnings("ALL") // Sample code
class ExtractAnnotationsTest : DriverTest() {
@Test
fun `Check java typedef extraction and warning about non-source retention of typedefs`() {
check(
includeSourceRetentionAnnotations = false,
sourceFiles = *arrayOf(
java(
"""
private val sourceFiles1 = arrayOf(
java(
"""
package test.pkg;
import android.annotation.IntDef;
......@@ -70,10 +66,17 @@ class ExtractAnnotationsTest : DriverTest() {
}
}
"""
).indented(),
intDefAnnotationSource,
intRangeAnnotationSource
),
).indented(),
intDefAnnotationSource,
intRangeAnnotationSource
)
@Test
fun `Check java typedef extraction and warning about non-source retention of typedefs`() {
check(
includeSourceRetentionAnnotations = false,
format = FileFormat.V2,
sourceFiles = *sourceFiles1,
warnings = "src/test/pkg/IntDefTest.java:11: error: This typedef annotation class should have @Retention(RetentionPolicy.SOURCE) [AnnotationExtraction]",
extractAnnotations = mapOf(
"test.pkg" to """
......@@ -509,4 +512,112 @@ class ExtractAnnotationsTest : DriverTest() {
)
)
}
@Test
fun `No typedef signatures in api files`() {
check(
includeSourceRetentionAnnotations = false,
extraArguments = arrayOf(
ARG_HIDE_PACKAGE, "android.annotation",
ARG_TYPEDEFS_IN_SIGNATURES, "none"
),
format = FileFormat.V2,
sourceFiles = *sourceFiles1,
api = """
// Signature format: 2.0
package test.pkg {
public class IntDefTest {
ctor public IntDefTest();
method public void setFlags(Object, int);
method public void setStyle(int, int);
method public void testIntDef(int);
field public static final int STYLE_NORMAL = 0; // 0x0
field public static final int STYLE_NO_FRAME = 2; // 0x2
field public static final int STYLE_NO_INPUT = 3; // 0x3
field public static final int STYLE_NO_TITLE = 1; // 0x1
field public static final String TYPE_1 = "type1";
field public static final String TYPE_2 = "type2";
field public static final int UNRELATED = 3; // 0x3
field public static final String UNRELATED_TYPE = "other";
}
public static class IntDefTest.Inner {
ctor public IntDefTest.Inner();
method public void setInner(int);
}
}
"""
)
}
@Test
fun `Inlining typedef signatures in api files`() {
check(
includeSourceRetentionAnnotations = false,
extraArguments = arrayOf(
ARG_HIDE_PACKAGE, "android.annotation",
ARG_TYPEDEFS_IN_SIGNATURES, "inline"
),
format = FileFormat.V2,
sourceFiles = *sourceFiles1,
api = """
// Signature format: 2.0
package test.pkg {
public class IntDefTest {
ctor public IntDefTest();
method public void setFlags(Object, @IntDef(value={test.pkg.IntDefTest.STYLE_NORMAL, test.pkg.IntDefTest.STYLE_NO_TITLE, test.pkg.IntDefTest.STYLE_NO_FRAME, test.pkg.IntDefTest.STYLE_NO_INPUT, 3, 3 + 1}, flag=true) int);
method public void setStyle(@IntDef({test.pkg.IntDefTest.STYLE_NORMAL, test.pkg.IntDefTest.STYLE_NO_TITLE, test.pkg.IntDefTest.STYLE_NO_FRAME, test.pkg.IntDefTest.STYLE_NO_INPUT}) int, int);
method public void testIntDef(int);
field public static final int STYLE_NORMAL = 0; // 0x0
field public static final int STYLE_NO_FRAME = 2; // 0x2
field public static final int STYLE_NO_INPUT = 3; // 0x3
field public static final int STYLE_NO_TITLE = 1; // 0x1
field public static final String TYPE_1 = "type1";
field public static final String TYPE_2 = "type2";
field public static final int UNRELATED = 3; // 0x3
field public static final String UNRELATED_TYPE = "other";
}
public static class IntDefTest.Inner {
ctor public IntDefTest.Inner();
method public void setInner(@IntDef(value={test.pkg.IntDefTest.STYLE_NORMAL, test.pkg.IntDefTest.STYLE_NO_TITLE, test.pkg.IntDefTest.STYLE_NO_FRAME, test.pkg.IntDefTest.STYLE_NO_INPUT, 3, 3 + 1}, flag=true) int);
}
}
"""
)
}
@Test
fun `Referencing typedef signatures in api files`() {
check(
includeSourceRetentionAnnotations = false,
extraArguments = arrayOf(
ARG_HIDE_PACKAGE, "android.annotation",
ARG_TYPEDEFS_IN_SIGNATURES, "ref"
),
format = FileFormat.V2,
sourceFiles = *sourceFiles1,
api = """
// Signature format: 2.0
package test.pkg {
public class IntDefTest {
ctor public IntDefTest();
method public void setFlags(Object, @DialogFlags int);
method public void setStyle(@DialogStyle int, int);
method public void testIntDef(int);
field public static final int STYLE_NORMAL = 0; // 0x0
field public static final int STYLE_NO_FRAME = 2; // 0x2
field public static final int STYLE_NO_INPUT = 3; // 0x3
field public static final int STYLE_NO_TITLE = 1; // 0x1
field public static final String TYPE_1 = "type1";
field public static final String TYPE_2 = "type2";
field public static final int UNRELATED = 3; // 0x3
field public static final String UNRELATED_TYPE = "other";
}
public static class IntDefTest.Inner {
ctor public IntDefTest.Inner();
method public void setInner(@DialogFlags int);
}
}
"""
)
}
}
\ No newline at end of file
......@@ -121,6 +121,16 @@ API sources:
--api, --stubs, --doc-stubs, etc. Note that the
subtraction only applies to classes; it does not
subtract members.
--typedefs-in-signatures <ref|inline> Whether to include typedef annotations in
signature files. `--typedefs-in-signatures ref`
will include just a reference to the typedef
class, which is not itself part of the API and
is not included as a class, and
`--typedefs-in-signatures inline` will include
the constants themselves into each usage site.
You can also supply `--typedefs-in-signatures
none` to explicitly turn it off, if the default
ever changes.
Documentation:
--public Only include elements that are public
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment