diff --git a/src/main/java/com/android/tools/metalava/Options.kt b/src/main/java/com/android/tools/metalava/Options.kt index 91a6adbfc709270b62303f2a134f18cfc2667981..a3e67939321d2873fe61b3144e348ee874bb87a8 100644 --- a/src/main/java/com/android/tools/metalava/Options.kt +++ b/src/main/java/com/android/tools/metalava/Options.kt @@ -577,6 +577,7 @@ class Options( var baselineFile: File? = null var mergeBaseline = false var delayedCheckApiFiles = false + var skipGenerateAnnotations = false var index = 0 while (index < args.size) { @@ -1304,6 +1305,12 @@ class Options( } else { // All args that don't start with "-" are taken to be filenames mutableSources.addAll(stringToExistingFiles(arg)) + + // Temporary workaround for + // aosp/I73ff403bfc3d9dfec71789a3e90f9f4ea95eabe3 + if (arg.endsWith("hwbinder-stubs-docs-stubs.srcjar.rsp")) { + skipGenerateAnnotations = true + } } } } @@ -1332,6 +1339,10 @@ class Options( allowReferencingUnknownClasses = false } + if (skipGenerateAnnotations) { + generateAnnotations = false + } + if (updateApi) { // We're running in update API mode: cancel other "action" flags; only signature file generation // flags count diff --git a/src/main/java/com/android/tools/metalava/RewriteAnnotations.kt b/src/main/java/com/android/tools/metalava/RewriteAnnotations.kt index 520e97ac34337515db173894b45802c8aa7fbdd5..fb56a1030c329900d7d3977580d0e08ca16d44af 100644 --- a/src/main/java/com/android/tools/metalava/RewriteAnnotations.kt +++ b/src/main/java/com/android/tools/metalava/RewriteAnnotations.kt @@ -103,9 +103,13 @@ class RewriteAnnotations { for (file in files) { // Jump directly into androidx/annotation if it appears we were invoked at the top level if (file.isDirectory) { - val annotations = File(file, "androidx${File.separator}annotation/") - if (annotations.isDirectory) { - rewriteAnnotations(annotations) + val android = File(file, "android${File.separator}annotation/") + if (android.isDirectory) { + rewriteAnnotations(android) + val androidx = File(file, "androidx${File.separator}annotation/") + if (androidx.isDirectory) { + rewriteAnnotations(androidx) + } continue } } @@ -120,7 +124,9 @@ class RewriteAnnotations { private fun hasSourceRetention(codebase: Codebase?, qualifiedName: String): Boolean { when { qualifiedName == "androidx.annotation.RecentlyNullable" || - qualifiedName == "androidx.annotation.RecentlyNonNull" -> return false + qualifiedName == "androidx.annotation.RecentlyNonNull" || + qualifiedName == "android.annotation.Nullable" || + qualifiedName == "android.annotation.NonNull" -> return false qualifiedName.startsWith("androidx.annotation.") -> return true } @@ -172,10 +178,10 @@ class RewriteAnnotations { superName: String?, interfaces: Array<out String>? ) { - // Only process public annotations in androidx.annotation + // Only process public annotations in android.annotation and androidx.annotation if (access and Opcodes.ACC_PUBLIC != 0 && access and Opcodes.ACC_ANNOTATION != 0 && - name.startsWith("androidx/annotation/") + (name.startsWith("android/annotation/") || name.startsWith("androidx/annotation/")) ) { skip = false val flagsWithoutPublic = access and Opcodes.ACC_PUBLIC.inv() @@ -237,7 +243,7 @@ class RewriteAnnotations { // read the content of the entry from the input stream, and write it into the archive. if (name.endsWith(DOT_CLASS) && - name.startsWith("androidx/annotation/") && + (name.startsWith("android/annotation/") || name.startsWith("androidx/annotation/")) && name.indexOf("$") == -1 && !entry.isDirectory ) { 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 0e6921d22c624dec1744526a7a18ec2100aefbce..a7c04edc8995e0f61fadf7cf207c6cc746b50e3b 100644 --- a/src/main/java/com/android/tools/metalava/model/AnnotationItem.kt +++ b/src/main/java/com/android/tools/metalava/model/AnnotationItem.kt @@ -235,14 +235,20 @@ interface AnnotationItem { "android.annotation.Dimension" -> return "androidx.annotation.Dimension" // Null - "android.support.annotation.NonNull", - "android.annotation.NonNull" -> return "androidx.annotation.NonNull" - "android.support.annotation.Nullable", - "android.annotation.Nullable" -> return "androidx.annotation.Nullable" - "libcore.util.NonNull" -> return "androidx.annotation.NonNull" - "libcore.util.Nullable" -> return "androidx.annotation.Nullable" - "org.jetbrains.annotations.NotNull" -> return "androidx.annotation.NonNull" - "org.jetbrains.annotations.Nullable" -> return "androidx.annotation.Nullable" + // We only change recently/newly nullable annotation in stubs + RECENTLY_NULLABLE -> return if (target == AnnotationTarget.SDK_STUBS_FILE) qualifiedName else ANDROIDX_NULLABLE + RECENTLY_NONNULL -> return if (target == AnnotationTarget.SDK_STUBS_FILE) qualifiedName else ANDROIDX_NONNULL + "android.annotation.Nullable" -> return if (target == AnnotationTarget.SDK_STUBS_FILE) qualifiedName else ANDROIDX_NULLABLE + "android.annotation.NonNull" -> return if (target == AnnotationTarget.SDK_STUBS_FILE) qualifiedName else ANDROIDX_NONNULL + ANDROIDX_NULLABLE -> return if (target == AnnotationTarget.SDK_STUBS_FILE) "android.annotation.Nullable" else ANDROIDX_NULLABLE + ANDROIDX_NONNULL -> return if (target == AnnotationTarget.SDK_STUBS_FILE) "android.annotation.NonNull" else ANDROIDX_NONNULL + + "android.support.annotation.NonNull" -> return ANDROIDX_NONNULL + "android.support.annotation.Nullable" -> return ANDROIDX_NULLABLE + "libcore.util.NonNull" -> return ANDROIDX_NONNULL + "libcore.util.Nullable" -> return ANDROIDX_NULLABLE + "org.jetbrains.annotations.NotNull" -> return ANDROIDX_NONNULL + "org.jetbrains.annotations.Nullable" -> return ANDROIDX_NULLABLE // Typedefs "android.support.annotation.IntDef", @@ -303,8 +309,6 @@ interface AnnotationItem { "android.annotation.TargetApi", "android.annotation.SuppressLint" -> return qualifiedName - RECENTLY_NULLABLE, RECENTLY_NONNULL -> return qualifiedName - else -> { // Some new annotations added to the platform: assume they are support annotations? return when { @@ -313,8 +317,8 @@ interface AnnotationItem { "kotlin.annotations.jvm.internal${qualifiedName.substring(qualifiedName.lastIndexOf('.'))}" // Other third party nullness annotations? - isNullableAnnotation(qualifiedName) -> "androidx.annotation.Nullable" - isNonNullAnnotation(qualifiedName) -> "androidx.annotation.NonNull" + isNullableAnnotation(qualifiedName) -> ANDROIDX_NULLABLE + isNonNullAnnotation(qualifiedName) -> ANDROIDX_NONNULL // Support library annotations are all included, as is the built-in stuff like @Retention qualifiedName.startsWith(ANDROIDX_ANNOTATION_PREFIX) -> return qualifiedName @@ -402,6 +406,16 @@ interface AnnotationItem { "java.lang.annotation.Target" -> return ANNOTATION_IN_ALL_STUBS } + // @android.annotation.Nullable and NonNullable specially recognized annotations by the Kotlin + // compiler 1.3 and above: they always go in the stubs. + if (qualifiedName == "android.annotation.Nullable" || + qualifiedName == "android.annotation.NonNull" || + qualifiedName == ANDROIDX_NULLABLE || + qualifiedName == ANDROIDX_NONNULL + ) { + return ANNOTATION_IN_ALL_STUBS + } + if (qualifiedName.startsWith("android.annotation.")) { // internal annotations not mapped to androidx: things like @SystemApi. Skip from // stubs, external annotations, signature files, etc. @@ -413,7 +427,7 @@ interface AnnotationItem { if (qualifiedName == "androidx.annotation.RecentlyNullable" || qualifiedName == "androidx.annotation.RecentlyNonNull" ) { - return ANNOTATION_IN_SDK_STUBS + return ANNOTATION_IN_ALL_STUBS } // Determine the retention of the annotation: source retention annotations go diff --git a/src/test/java/com/android/tools/metalava/ExtractAnnotationsTest.kt b/src/test/java/com/android/tools/metalava/ExtractAnnotationsTest.kt index bad1733519bae0fd7cbbe3a1dcf3961ee35ae9a7..2d9269567e19aedc5a3a8c28d1bb2be07db43fbc 100644 --- a/src/test/java/com/android/tools/metalava/ExtractAnnotationsTest.kt +++ b/src/test/java/com/android/tools/metalava/ExtractAnnotationsTest.kt @@ -157,9 +157,6 @@ class ExtractAnnotationsTest : DriverTest() { "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" /> @@ -171,24 +168,12 @@ class ExtractAnnotationsTest : DriverTest() { <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, 4L}" /> </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> """ ) @@ -420,6 +405,20 @@ class ExtractAnnotationsTest : DriverTest() { intRangeAnnotationSource, recentlyNullableSource ), + stubs = arrayOf( + """ + package test.pkg; + @SuppressWarnings({"unchecked", "deprecation", "all"}) + public class Test { + public Test() { throw new RuntimeException("Stub!"); } + /** + * @param value Value is 10 or greater + */ + @androidx.annotation.RecentlyNullable + public static java.lang.String sayHello(int value) { throw new RuntimeException("Stub!"); } + } + """ + ), extractAnnotations = mapOf( "test.pkg" to """ <?xml version="1.0" encoding="UTF-8"?> diff --git a/src/test/java/com/android/tools/metalava/NullnessMigrationTest.kt b/src/test/java/com/android/tools/metalava/NullnessMigrationTest.kt index 90332ec64124717eb0f022c5f4e79b38c92950d9..b74d384258fe858cd606224ebd0526ecd40215b5 100644 --- a/src/test/java/com/android/tools/metalava/NullnessMigrationTest.kt +++ b/src/test/java/com/android/tools/metalava/NullnessMigrationTest.kt @@ -210,12 +210,12 @@ class NullnessMigrationTest : DriverTest() { public java.lang.Double convert0(java.lang.Float f) { throw new RuntimeException("Stub!"); } @androidx.annotation.RecentlyNullable public java.lang.Double convert1(@androidx.annotation.RecentlyNonNull java.lang.Float f) { throw new RuntimeException("Stub!"); } - @androidx.annotation.Nullable - public java.lang.Double convert2(@androidx.annotation.NonNull java.lang.Float f) { throw new RuntimeException("Stub!"); } - @androidx.annotation.Nullable - public java.lang.Double convert3(@androidx.annotation.NonNull java.lang.Float f) { throw new RuntimeException("Stub!"); } - @androidx.annotation.Nullable - public java.lang.Double convert4(@androidx.annotation.NonNull java.lang.Float f) { throw new RuntimeException("Stub!"); } + @android.annotation.Nullable + public java.lang.Double convert2(@android.annotation.NonNull java.lang.Float f) { throw new RuntimeException("Stub!"); } + @android.annotation.Nullable + public java.lang.Double convert3(@android.annotation.NonNull java.lang.Float f) { throw new RuntimeException("Stub!"); } + @android.annotation.Nullable + public java.lang.Double convert4(@android.annotation.NonNull java.lang.Float f) { throw new RuntimeException("Stub!"); } } """ ), @@ -471,9 +471,9 @@ class NullnessMigrationTest : DriverTest() { 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!"); } + public <T> T @android.annotation.RecentlyNonNull [] toArray(T @androidx.annotation.RecentlyNonNull [] a) { throw new RuntimeException("Stub!"); } @androidx.annotation.NonNull - public static java.lang.String newMethod(@androidx.annotation.Nullable java.lang.String argument) { throw new RuntimeException("Stub!"); } + public static java.lang.String newMethod(@android.annotation.Nullable java.lang.String argument) { throw new RuntimeException("Stub!"); } } """ ) @@ -487,8 +487,8 @@ class NullnessMigrationTest : DriverTest() { 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!"); } - @androidx.annotation.NonNull - public static java.lang.String newMethod(@androidx.annotation.Nullable java.lang.String argument) { throw new RuntimeException("Stub!"); } + @android.annotation.NonNull + public static java.lang.String newMethod(@android.annotation.Nullable java.lang.String argument) { throw new RuntimeException("Stub!"); } } """ ) diff --git a/src/test/java/com/android/tools/metalava/StubsTest.kt b/src/test/java/com/android/tools/metalava/StubsTest.kt index 807bd9193c3fa65cbbfa4a2cf1c0294990fd9cac..fca7dea3011b5a63d582759c6c383878913a428e 100644 --- a/src/test/java/com/android/tools/metalava/StubsTest.kt +++ b/src/test/java/com/android/tools/metalava/StubsTest.kt @@ -3246,9 +3246,9 @@ class StubsTest : DriverTest() { ctor public Test(); } } - """, // WRONG: I should include package annotations! + """, // WRONG: I should include package annotations in the signature file! source = """ - @androidx.annotation.Nullable + @android.annotation.Nullable package test.pkg; """, extraArguments = arrayOf(ARG_HIDE_PACKAGE, "androidx.annotation") @@ -3607,6 +3607,70 @@ class StubsTest : DriverTest() { ) } + @Test + fun `Annotation nested rewriting`() { + checkStubs( + sourceFiles = *arrayOf( + java( + """ + package test.pkg; + + import android.view.Gravity; + + public class ActionBar { + @ViewDebug.ExportedProperty(category = "layout", mapping = { + @ViewDebug.IntToString(from = -1, to = "NONE"), + @ViewDebug.IntToString(from = Gravity.NO_GRAVITY, to = "NONE"), + @ViewDebug.IntToString(from = Gravity.TOP, to = "TOP"), + @ViewDebug.IntToString(from = Gravity.BOTTOM, to = "BOTTOM"), + }) + public int gravity = Gravity.NO_GRAVITY; + } + """ + ), + java( + """ + package test.pkg; + + import java.lang.annotation.ElementType; + import java.lang.annotation.Retention; + import java.lang.annotation.RetentionPolicy; + import java.lang.annotation.Target; + + public class ViewDebug { + @Target({ElementType.FIELD, ElementType.METHOD}) + @Retention(RetentionPolicy.RUNTIME) + public @interface ExportedProperty { + boolean resolveId() default false; + IntToString[] mapping() default {}; + IntToString[] indexMapping() default {}; + boolean deepExport() default false; + String prefix() default ""; + String category() default ""; + boolean formatToHexString() default false; + boolean hasAdjacentMapping() default false; + } + @Target({ElementType.TYPE}) + @Retention(RetentionPolicy.RUNTIME) + public @interface IntToString { + int from(); + String to(); + } + } + """ + ) + ), + source = """ + package test.pkg; + @SuppressWarnings({"unchecked", "deprecation", "all"}) + public class ActionBar { + public ActionBar() { throw new RuntimeException("Stub!"); } + @test.pkg.ViewDebug.ExportedProperty(category="layout", mapping={@test.pkg.ViewDebug.IntToString(from=0xffffffff, to="NONE"), @test.pkg.ViewDebug.IntToString(from=android.view.Gravity.NO_GRAVITY, to="NONE"), @test.pkg.ViewDebug.IntToString(from=android.view.Gravity.TOP, to="TOP"), @test.pkg.ViewDebug.IntToString(from=android.view.Gravity.BOTTOM, to="BOTTOM")}) public int gravity = 0; // 0x0 + } + """ + ) + } + @Test(expected = FileNotFoundException::class) fun `Test update-api should not generate stubs`() { check( diff --git a/src/test/java/com/android/tools/metalava/SymlinkTest.kt b/src/test/java/com/android/tools/metalava/SymlinkTest.kt index a82194615e446244fb05eca64b5996e0a09acdbe..83916ab16b45ecf1d4e87f0d9cd2f34c6ce53d56 100644 --- a/src/test/java/com/android/tools/metalava/SymlinkTest.kt +++ b/src/test/java/com/android/tools/metalava/SymlinkTest.kt @@ -102,22 +102,22 @@ class SymlinkTest : DriverTest() { * @param factor2 This value must never be {@code null}. * @return This value may be {@code null}. */ - @androidx.annotation.Nullable - public java.lang.Double method1(@androidx.annotation.NonNull java.lang.Double factor1, @androidx.annotation.NonNull java.lang.Double factor2) { throw new RuntimeException("Stub!"); } + @android.annotation.Nullable + public java.lang.Double method1(@android.annotation.NonNull java.lang.Double factor1, @android.annotation.NonNull java.lang.Double factor2) { throw new RuntimeException("Stub!"); } /** * These are the docs for method2. It can sometimes return null. * @param factor1 This value must never be {@code null}. * @param factor2 This value must never be {@code null}. */ - @androidx.annotation.Nullable - public java.lang.Double method2(@androidx.annotation.NonNull java.lang.Double factor1, @androidx.annotation.NonNull java.lang.Double factor2) { throw new RuntimeException("Stub!"); } + @android.annotation.Nullable + public java.lang.Double method2(@android.annotation.NonNull java.lang.Double factor1, @android.annotation.NonNull java.lang.Double factor2) { throw new RuntimeException("Stub!"); } /** * @param factor1 This value must never be {@code null}. * @param factor2 This value must never be {@code null}. * @return This value may be {@code null}. */ - @androidx.annotation.Nullable - public java.lang.Double method3(@androidx.annotation.NonNull java.lang.Double factor1, @androidx.annotation.NonNull java.lang.Double factor2) { throw new RuntimeException("Stub!"); } + @android.annotation.Nullable + public java.lang.Double method3(@android.annotation.NonNull java.lang.Double factor1, @android.annotation.NonNull java.lang.Double factor2) { throw new RuntimeException("Stub!"); } } """ ) diff --git a/stub-annotations/src/main/java/android/annotation/NonNull.java b/stub-annotations/src/main/java/android/annotation/NonNull.java new file mode 100644 index 0000000000000000000000000000000000000000..c826461be7ee6d1209a7470bada9e91d1f532765 --- /dev/null +++ b/stub-annotations/src/main/java/android/annotation/NonNull.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2018 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 android.annotation; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.ElementType.TYPE_USE; +import static java.lang.annotation.RetentionPolicy.CLASS; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +/** Stub only annotation. Do not use directly. */ +@Retention(CLASS) +@Target({METHOD, PARAMETER, FIELD}) +public @interface NonNull {} diff --git a/stub-annotations/src/main/java/android/annotation/Nullable.java b/stub-annotations/src/main/java/android/annotation/Nullable.java new file mode 100644 index 0000000000000000000000000000000000000000..93b73e3de5ad45606241749e69c1f08cfb08cf3d --- /dev/null +++ b/stub-annotations/src/main/java/android/annotation/Nullable.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2018 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 android.annotation; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.ElementType.TYPE_USE; +import static java.lang.annotation.RetentionPolicy.CLASS; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +/** Stub only annotation. Do not use directly. */ +@Retention(CLASS) +@Target({METHOD, PARAMETER, FIELD}) +public @interface Nullable {}