blob: 676096f0ac00b7f43c6518982f83564eeb080b97 [file] [log] [blame]
/*
* Copyright 2021 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.
*/
@file:Suppress("UnstableApiUsage")
package androidx.build.lint
import androidx.build.lint.Stubs.Companion.DoNotInline
import androidx.build.lint.Stubs.Companion.IntRange
import androidx.build.lint.Stubs.Companion.RequiresApi
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
@RunWith(JUnit4::class)
class ClassVerificationFailureDetectorTest :
AbstractLintDetectorTest(
useDetector = ClassVerificationFailureDetector(),
useIssues = listOf(ClassVerificationFailureDetector.METHOD_CALL_ISSUE),
stubs =
arrayOf(
// AndroidManifest with minSdkVersion=14
manifest().minSdk(14),
),
) {
@Test
fun `Detection of unsafe references in Java sources`() {
val input =
arrayOf(javaSample("androidx.ClassVerificationFailureFromJava"), RequiresApi, IntRange)
val expected =
"""
src/androidx/ClassVerificationFailureFromJava.java:37: Error: This call references a method added in API level 21; however, the containing class androidx.ClassVerificationFailureFromJava is reachable from earlier API levels and will fail run-time class verification. [ClassVerificationFailure]
view.setBackgroundTintList(tint);
~~~~~~~~~~~~~~~~~~~~~
src/androidx/ClassVerificationFailureFromJava.java:46: Error: This call references a method added in API level 17; however, the containing class androidx.ClassVerificationFailureFromJava is reachable from earlier API levels and will fail run-time class verification. [ClassVerificationFailure]
return View.generateViewId();
~~~~~~~~~~~~~~
src/androidx/ClassVerificationFailureFromJava.java:56: Error: This call references a method added in API level 23; however, the containing class androidx.ClassVerificationFailureFromJava is reachable from earlier API levels and will fail run-time class verification. [ClassVerificationFailure]
return view.getAccessibilityClassName();
~~~~~~~~~~~~~~~~~~~~~~~~~
3 errors, 0 warnings
"""
.trimIndent()
check(*input).expect(expected)
}
@Test
fun `Detection and auto-fix of unsafe references in real-world Java sources`() {
val input =
arrayOf(
javaSample("androidx.sample.core.widget.ListViewCompat"),
)
val expected =
"""
src/androidx/sample/core/widget/ListViewCompat.java:39: Error: This call references a method added in API level 19; however, the containing class androidx.sample.core.widget.ListViewCompat is reachable from earlier API levels and will fail run-time class verification. [ClassVerificationFailure]
listView.scrollListBy(y);
~~~~~~~~~~~~
src/androidx/sample/core/widget/ListViewCompat.java:69: Error: This call references a method added in API level 19; however, the containing class androidx.sample.core.widget.ListViewCompat is reachable from earlier API levels and will fail run-time class verification. [ClassVerificationFailure]
return listView.canScrollList(direction);
~~~~~~~~~~~~~
2 errors, 0 warnings
"""
.trimIndent()
val expectedFix =
"""
Fix for src/androidx/sample/core/widget/ListViewCompat.java line 39: Extract to static inner class:
@@ -39 +39
- listView.scrollListBy(y);
+ Api19Impl.scrollListBy(listView, y);
@@ -91 +91
+ @androidx.annotation.RequiresApi(19)
+ static class Api19Impl {
+ private Api19Impl() {
+ // This class is not instantiable.
+ }
+
+ @androidx.annotation.DoNotInline
+ static void scrollListBy(android.widget.AbsListView absListView, int y) {
+ absListView.scrollListBy(y);
+ }
+
@@ -92 +103
+ }
Fix for src/androidx/sample/core/widget/ListViewCompat.java line 69: Extract to static inner class:
@@ -69 +69
- return listView.canScrollList(direction);
+ return Api19Impl.canScrollList(listView, direction);
@@ -91 +91
+ @androidx.annotation.RequiresApi(19)
+ static class Api19Impl {
+ private Api19Impl() {
+ // This class is not instantiable.
+ }
+
+ @androidx.annotation.DoNotInline
+ static boolean canScrollList(android.widget.AbsListView absListView, int direction) {
+ return absListView.canScrollList(direction);
+ }
+
@@ -92 +103
+ }
"""
.trimIndent()
check(*input).expect(expected).expectFixDiffs(expectedFix)
}
@Test
fun `Detection and auto-fix of unsafe references in real-world Kotlin sources`() {
val input =
arrayOf(
ktSample("androidx.sample.core.widget.ListViewCompatKotlin"),
)
val expected =
"""
src/androidx/sample/core/widget/ListViewCompatKotlin.kt:33: Error: This call references a method added in API level 19; however, the containing class androidx.sample.core.widget.ListViewCompatKotlin is reachable from earlier API levels and will fail run-time class verification. [ClassVerificationFailure]
listView.scrollListBy(y)
~~~~~~~~~~~~
src/androidx/sample/core/widget/ListViewCompatKotlin.kt:56: Error: This call references a method added in API level 19; however, the containing class androidx.sample.core.widget.ListViewCompatKotlin is reachable from earlier API levels and will fail run-time class verification. [ClassVerificationFailure]
listView.canScrollList(direction)
~~~~~~~~~~~~~
2 errors, 0 warnings
"""
.trimIndent()
check(*input).expect(expected)
}
@Test
fun `Detection of RequiresApi annotation in outer class in Java source`() {
val input = arrayOf(javaSample("androidx.RequiresApiJava"), RequiresApi)
val expected =
"""
No warnings.
"""
.trimIndent()
check(*input).expect(expected)
}
@Test
fun `Detection of RequiresApi annotation in outer class in Kotlin source`() {
val input = arrayOf(ktSample("androidx.RequiresApiKotlin"), RequiresApi)
val expected =
"""
src/androidx/RequiresApiKotlinOuter19Passes.kt:67: Error: This call references a method added in API level 19; however, the containing class androidx.RequiresApiKotlinNoAnnotationFails.MyStaticClass is reachable from earlier API levels and will fail run-time class verification. [ClassVerificationFailure]
Character.isSurrogate(c)
~~~~~~~~~~~
src/androidx/RequiresApiKotlinOuter19Passes.kt:77: Error: This call references a method added in API level 19; however, the containing class androidx.RequiresApiKotlinOuter16Fails.MyStaticClass is reachable from earlier API levels and will fail run-time class verification. [ClassVerificationFailure]
Character.isSurrogate(c)
~~~~~~~~~~~
src/androidx/RequiresApiKotlinOuter19Passes.kt:87: Error: This call references a method added in API level 19; however, the containing class androidx.RequiresApiKotlinInner16Fails.MyStaticClass is reachable from earlier API levels and will fail run-time class verification. [ClassVerificationFailure]
Character.isSurrogate(c)
~~~~~~~~~~~
src/androidx/RequiresApiKotlinOuter19Passes.kt:98: Error: This call references a method added in API level 19; however, the containing class androidx.RequiresApiKotlinInner16Outer16Fails.MyStaticClass is reachable from earlier API levels and will fail run-time class verification. [ClassVerificationFailure]
Character.isSurrogate(c)
~~~~~~~~~~~
4 errors, 0 warnings
"""
.trimIndent()
check(*input).expect(expected)
}
@Test
fun `Auto-fix unsafe void-type method reference in Java source`() {
val input =
arrayOf(
javaSample("androidx.AutofixUnsafeVoidMethodReferenceJava"),
)
val expectedFix =
"""
Fix for src/androidx/AutofixUnsafeVoidMethodReferenceJava.java line 34: Extract to static inner class:
@@ -34 +34
- view.setBackgroundTintList(new ColorStateList(null, null));
+ Api21Impl.setBackgroundTintList(view, new ColorStateList(null, null));
@@ -37 +37
+ @androidx.annotation.RequiresApi(21)
+ static class Api21Impl {
+ private Api21Impl() {
+ // This class is not instantiable.
+ }
+
+ @androidx.annotation.DoNotInline
+ static void setBackgroundTintList(View view, ColorStateList tint) {
+ view.setBackgroundTintList(tint);
+ }
+
@@ -38 +49
+ }
"""
.trimIndent()
check(*input).expectFixDiffs(expectedFix)
}
@Test
fun `Auto-fix unsafe constructor reference in Java source`() {
val input =
arrayOf(
javaSample("androidx.AutofixUnsafeConstructorReferenceJava"),
)
val expectedFix =
"""
Fix for src/androidx/AutofixUnsafeConstructorReferenceJava.java line 35: Extract to static inner class:
@@ -35 +35
- AccessibilityNodeInfo node = new AccessibilityNodeInfo(new View(context), 1);
+ AccessibilityNodeInfo node = Api30Impl.createAccessibilityNodeInfo(new View(context), 1);
@@ -38 +38
+ @androidx.annotation.RequiresApi(30)
+ static class Api30Impl {
+ private Api30Impl() {
+ // This class is not instantiable.
+ }
+
+ @androidx.annotation.DoNotInline
+ static AccessibilityNodeInfo createAccessibilityNodeInfo(View root, int virtualDescendantId) {
+ return new AccessibilityNodeInfo(root, virtualDescendantId);
+ }
+
@@ -39 +50
+ }
"""
.trimIndent()
check(*input).expectFixDiffs(expectedFix)
}
@Test
fun `Auto-fix unsafe static method reference in Java source`() {
val input =
arrayOf(
javaSample("androidx.AutofixUnsafeStaticMethodReferenceJava"),
)
val expectedFix =
"""
Fix for src/androidx/AutofixUnsafeStaticMethodReferenceJava.java line 33: Extract to static inner class:
@@ -33 +33
- return View.generateViewId();
+ return Api17Impl.generateViewId();
@@ -37 +37
+ @androidx.annotation.RequiresApi(17)
+ static class Api17Impl {
+ private Api17Impl() {
+ // This class is not instantiable.
+ }
+
+ @androidx.annotation.DoNotInline
+ static int generateViewId() {
+ return View.generateViewId();
+ }
+
@@ -38 +49
+ }
"""
.trimIndent()
check(*input).expectFixDiffs(expectedFix)
}
@Test
fun `Auto-fix unsafe generic-type method reference in Java source`() {
val input =
arrayOf(
javaSample("androidx.AutofixUnsafeGenericMethodReferenceJava"),
)
val expectedFix =
"""
Fix for src/androidx/AutofixUnsafeGenericMethodReferenceJava.java line 34: Extract to static inner class:
@@ -34 +34
- return context.getSystemService(serviceClass);
+ return Api23Impl.getSystemService(context, serviceClass);
@@ -38 +38
+ @androidx.annotation.RequiresApi(23)
+ static class Api23Impl {
+ private Api23Impl() {
+ // This class is not instantiable.
+ }
+
+ @androidx.annotation.DoNotInline
+ static <T> T getSystemService(Context context, java.lang.Class<T> serviceClass) {
+ return context.getSystemService(serviceClass);
+ }
+
@@ -39 +50
+ }
"""
.trimIndent()
check(*input).expectFixDiffs(expectedFix)
}
@Test
fun `Auto-fix unsafe reference in Java source with existing inner class`() {
val input =
arrayOf(javaSample("androidx.AutofixUnsafeReferenceWithExistingClassJava"), RequiresApi)
val expectedFix =
"""
Fix for src/androidx/AutofixUnsafeReferenceWithExistingClassJava.java line 36: Extract to static inner class:
@@ -36 +36
- view.setBackgroundTintList(new ColorStateList(null, null));
+ Api21Impl.setBackgroundTintList(view, new ColorStateList(null, null));
@@ -46 +46
+ @RequiresApi(21)
+ static class Api21Impl {
+ private Api21Impl() {
+ // This class is not instantiable.
+ }
+
+ @DoNotInline
+ static void setBackgroundTintList(View view, ColorStateList tint) {
+ view.setBackgroundTintList(tint);
+ }
+
@@ -47 +58
+ }
"""
.trimIndent()
check(*input).expectFixDiffs(expectedFix)
}
@Test
fun `Auto-fix unsafe reference in Java source when the fix code already exists`() {
val input =
arrayOf(
javaSample("androidx.AutofixUnsafeReferenceWithExistingFix"),
RequiresApi,
DoNotInline
)
val expected =
"""
src/androidx/AutofixUnsafeReferenceWithExistingFix.java:37: Error: This call references a method added in API level 21; however, the containing class androidx.AutofixUnsafeReferenceWithExistingFix is reachable from earlier API levels and will fail run-time class verification. [ClassVerificationFailure]
view.setBackgroundTintList(new ColorStateList(null, null));
~~~~~~~~~~~~~~~~~~~~~
src/androidx/AutofixUnsafeReferenceWithExistingFix.java:45: Error: This call references a method added in API level 21; however, the containing class androidx.AutofixUnsafeReferenceWithExistingFix is reachable from earlier API levels and will fail run-time class verification. [ClassVerificationFailure]
drawable.getOutline(null);
~~~~~~~~~~
2 errors, 0 warnings
"""
val expectedFix =
"""
Fix for src/androidx/AutofixUnsafeReferenceWithExistingFix.java line 37: Extract to static inner class:
@@ -37 +37
- view.setBackgroundTintList(new ColorStateList(null, null));
+ Api21Impl.setBackgroundTintList(view, new ColorStateList(null, null));
Fix for src/androidx/AutofixUnsafeReferenceWithExistingFix.java line 45: Extract to static inner class:
@@ -45 +45
- drawable.getOutline(null);
+ Api21Impl.getOutline(drawable, null);
@@ -58 +58
- }
+ @DoNotInline
+ static void getOutline(Drawable drawable, android.graphics.Outline outline) {
+ drawable.getOutline(outline);
@@ -60 +62
+ }
+ }
"""
check(*input).expect(expected).expectFixDiffs(expectedFix)
}
@Test
fun `Detection and auto-fix for qualified expressions (issue 205026874)`() {
val input =
arrayOf(
javaSample("androidx.sample.appcompat.widget.ActionBarBackgroundDrawable"),
RequiresApi
)
val expected =
"""
src/androidx/sample/appcompat/widget/ActionBarBackgroundDrawable.java:71: Error: This call references a method added in API level 21; however, the containing class androidx.sample.appcompat.widget.ActionBarBackgroundDrawable is reachable from earlier API levels and will fail run-time class verification. [ClassVerificationFailure]
mContainer.mSplitBackground.getOutline(outline);
~~~~~~~~~~
src/androidx/sample/appcompat/widget/ActionBarBackgroundDrawable.java:76: Error: This call references a method added in API level 21; however, the containing class androidx.sample.appcompat.widget.ActionBarBackgroundDrawable is reachable from earlier API levels and will fail run-time class verification. [ClassVerificationFailure]
mContainer.mBackground.getOutline(outline);
~~~~~~~~~~
2 errors, 0 warnings
"""
.trimIndent()
val expectedFix =
"""
Fix for src/androidx/sample/appcompat/widget/ActionBarBackgroundDrawable.java line 71: Extract to static inner class:
@@ -71 +71
- mContainer.mSplitBackground.getOutline(outline);
+ Api21Impl.getOutline(mContainer.mSplitBackground, outline);
@@ -90 +90
+ @RequiresApi(21)
+ static class Api21Impl {
+ private Api21Impl() {
+ // This class is not instantiable.
+ }
+
+ @DoNotInline
+ static void getOutline(Drawable drawable, Outline outline) {
+ drawable.getOutline(outline);
+ }
+
@@ -91 +102
+ }
Fix for src/androidx/sample/appcompat/widget/ActionBarBackgroundDrawable.java line 76: Extract to static inner class:
@@ -76 +76
- mContainer.mBackground.getOutline(outline);
+ Api21Impl.getOutline(mContainer.mBackground, outline);
@@ -90 +90
+ @RequiresApi(21)
+ static class Api21Impl {
+ private Api21Impl() {
+ // This class is not instantiable.
+ }
+
+ @DoNotInline
+ static void getOutline(Drawable drawable, Outline outline) {
+ drawable.getOutline(outline);
+ }
+
@@ -91 +102
+ }
"""
.trimIndent()
check(*input).expect(expected).expectFixDiffs(expectedFix)
}
@Test
fun `Auto-fix includes fully qualified class name (issue 205035683, 236721202)`() {
val input =
arrayOf(javaSample("androidx.AutofixUnsafeMethodWithQualifiedClass"), RequiresApi)
val expected =
"""
src/androidx/AutofixUnsafeMethodWithQualifiedClass.java:40: Error: This call references a method added in API level 19; however, the containing class androidx.AutofixUnsafeMethodWithQualifiedClass is reachable from earlier API levels and will fail run-time class verification. [ClassVerificationFailure]
return builder.setMediaSize(mediaSize);
~~~~~~~~~~~~
1 errors, 0 warnings
"""
val expectedFixDiffs =
"""
Fix for src/androidx/AutofixUnsafeMethodWithQualifiedClass.java line 40: Extract to static inner class:
@@ -40 +40
+ return Api19Impl.setMediaSize(builder, mediaSize);
+ }
+ @RequiresApi(19)
+ static class Api19Impl {
+ private Api19Impl() {
+ // This class is not instantiable.
+ }
+
+ @DoNotInline
+ static PrintAttributes.Builder setMediaSize(PrintAttributes.Builder builder, PrintAttributes.MediaSize mediaSize) {
@@ -42 +52
+
@@ -43 +54
+ }
"""
check(*input).expect(expected).expectFixDiffs(expectedFixDiffs)
}
@Test
fun `Auto-fix for unsafe method call on this`() {
val input = arrayOf(javaSample("androidx.AutofixUnsafeCallToThis"))
val expected =
"""
src/androidx/AutofixUnsafeCallToThis.java:39: Error: This call references a method added in API level 21; however, the containing class androidx.AutofixUnsafeCallToThis is reachable from earlier API levels and will fail run-time class verification. [ClassVerificationFailure]
getClipToPadding();
~~~~~~~~~~~~~~~~
src/androidx/AutofixUnsafeCallToThis.java:48: Error: This call references a method added in API level 21; however, the containing class androidx.AutofixUnsafeCallToThis is reachable from earlier API levels and will fail run-time class verification. [ClassVerificationFailure]
this.getClipToPadding();
~~~~~~~~~~~~~~~~
src/androidx/AutofixUnsafeCallToThis.java:57: Error: This call references a method added in API level 21; however, the containing class androidx.AutofixUnsafeCallToThis is reachable from earlier API levels and will fail run-time class verification. [ClassVerificationFailure]
super.getClipToPadding();
~~~~~~~~~~~~~~~~
3 errors, 0 warnings
"""
val expectedFix =
"""
Fix for src/androidx/AutofixUnsafeCallToThis.java line 39: Extract to static inner class:
@@ -39 +39
- getClipToPadding();
+ Api21Impl.getClipToPadding(this);
@@ -60 +60
+ @androidx.annotation.RequiresApi(21)
+ static class Api21Impl {
+ private Api21Impl() {
+ // This class is not instantiable.
+ }
+
+ @androidx.annotation.DoNotInline
+ static boolean getClipToPadding(ViewGroup viewGroup) {
+ return viewGroup.getClipToPadding();
+ }
+
@@ -61 +72
+ }
Fix for src/androidx/AutofixUnsafeCallToThis.java line 48: Extract to static inner class:
@@ -48 +48
- this.getClipToPadding();
+ Api21Impl.getClipToPadding(this);
@@ -60 +60
+ @androidx.annotation.RequiresApi(21)
+ static class Api21Impl {
+ private Api21Impl() {
+ // This class is not instantiable.
+ }
+
+ @androidx.annotation.DoNotInline
+ static boolean getClipToPadding(ViewGroup viewGroup) {
+ return viewGroup.getClipToPadding();
+ }
+
@@ -61 +72
+ }
Fix for src/androidx/AutofixUnsafeCallToThis.java line 57: Extract to static inner class:
@@ -57 +57
- super.getClipToPadding();
+ Api21Impl.getClipToPadding(super);
@@ -60 +60
+ @androidx.annotation.RequiresApi(21)
+ static class Api21Impl {
+ private Api21Impl() {
+ // This class is not instantiable.
+ }
+
+ @androidx.annotation.DoNotInline
+ static boolean getClipToPadding(ViewGroup viewGroup) {
+ return viewGroup.getClipToPadding();
+ }
+
@@ -61 +72
+ }
"""
check(*input).expect(expected).expectFixDiffs(expectedFix)
}
@Test
fun `Auto-fix for unsafe method call on cast object (issue 206111383)`() {
val input = arrayOf(javaSample("androidx.AutofixUnsafeCallOnCast"))
val expected =
"""
src/androidx/AutofixUnsafeCallOnCast.java:32: Error: This call references a method added in API level 28; however, the containing class androidx.AutofixUnsafeCallOnCast is reachable from earlier API levels and will fail run-time class verification. [ClassVerificationFailure]
((DisplayCutout) secretDisplayCutout).getSafeInsetTop();
~~~~~~~~~~~~~~~
1 errors, 0 warnings
"""
val expectedFix =
"""
Fix for src/androidx/AutofixUnsafeCallOnCast.java line 32: Extract to static inner class:
@@ -32 +32
- ((DisplayCutout) secretDisplayCutout).getSafeInsetTop();
+ Api28Impl.getSafeInsetTop((DisplayCutout) secretDisplayCutout);
@@ -35 +35
+ @androidx.annotation.RequiresApi(28)
+ static class Api28Impl {
+ private Api28Impl() {
+ // This class is not instantiable.
+ }
+
+ @androidx.annotation.DoNotInline
+ static int getSafeInsetTop(DisplayCutout displayCutout) {
+ return displayCutout.getSafeInsetTop();
+ }
+
@@ -36 +47
+ }
"""
check(*input).expect(expected).expectFixDiffs(expectedFix)
}
@Test
fun `Auto-fix with implicit class cast from new return type (issue 214389795)`() {
val input =
arrayOf(javaSample("androidx.AutofixUnsafeCallWithImplicitReturnCast"), RequiresApi)
val expected =
"""
src/androidx/AutofixUnsafeCallWithImplicitReturnCast.java:36: Error: This call references a method added in API level 26; however, the containing class androidx.AutofixUnsafeCallWithImplicitReturnCast is reachable from earlier API levels and will fail run-time class verification. [ClassVerificationFailure]
return new AdaptiveIconDrawable(null, null);
~~~~~~~~~~~~~~~~~~~~~~~~
src/androidx/AutofixUnsafeCallWithImplicitReturnCast.java:44: Error: This call references a method added in API level 26; however, the containing class androidx.AutofixUnsafeCallWithImplicitReturnCast is reachable from earlier API levels and will fail run-time class verification. [ClassVerificationFailure]
return new AdaptiveIconDrawable(null, null);
~~~~~~~~~~~~~~~~~~~~~~~~
src/androidx/AutofixUnsafeCallWithImplicitReturnCast.java:52: Error: This call references a method added in API level 26; however, the containing class androidx.AutofixUnsafeCallWithImplicitReturnCast is reachable from earlier API levels and will fail run-time class verification. [ClassVerificationFailure]
return Icon.createWithAdaptiveBitmap(null);
~~~~~~~~~~~~~~~~~~~~~~~~
src/androidx/AutofixUnsafeCallWithImplicitReturnCast.java:60: Error: This call references a method added in API level 26; however, the containing class androidx.AutofixUnsafeCallWithImplicitReturnCast is reachable from earlier API levels and will fail run-time class verification. [ClassVerificationFailure]
return Icon.createWithAdaptiveBitmap(null);
~~~~~~~~~~~~~~~~~~~~~~~~
src/androidx/AutofixUnsafeCallWithImplicitReturnCast.java:68: Error: This call references a method added in API level 24; however, the containing class androidx.AutofixUnsafeCallWithImplicitReturnCast is reachable from earlier API levels and will fail run-time class verification. [ClassVerificationFailure]
useStyle(new Notification.DecoratedCustomViewStyle());
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 errors, 0 warnings
"""
val expectedFix =
"""
Fix for src/androidx/AutofixUnsafeCallWithImplicitReturnCast.java line 36: Extract to static inner class:
@@ -36 +36
- return new AdaptiveIconDrawable(null, null);
+ return Api26Impl.createAdaptiveIconDrawableReturnsDrawable(null, null);
@@ -77 +77
+ @RequiresApi(26)
+ static class Api26Impl {
+ private Api26Impl() {
+ // This class is not instantiable.
+ }
+
+ @DoNotInline
+ static Drawable createAdaptiveIconDrawableReturnsDrawable(Drawable backgroundDrawable, Drawable foregroundDrawable) {
+ return new AdaptiveIconDrawable(backgroundDrawable, foregroundDrawable);
+ }
+
@@ -78 +89
+ }
Fix for src/androidx/AutofixUnsafeCallWithImplicitReturnCast.java line 44: Extract to static inner class:
@@ -44 +44
- return new AdaptiveIconDrawable(null, null);
+ return Api26Impl.createAdaptiveIconDrawable(null, null);
@@ -77 +77
+ @RequiresApi(26)
+ static class Api26Impl {
+ private Api26Impl() {
+ // This class is not instantiable.
+ }
+
+ @DoNotInline
+ static AdaptiveIconDrawable createAdaptiveIconDrawable(Drawable backgroundDrawable, Drawable foregroundDrawable) {
+ return new AdaptiveIconDrawable(backgroundDrawable, foregroundDrawable);
+ }
+
@@ -78 +89
+ }
Fix for src/androidx/AutofixUnsafeCallWithImplicitReturnCast.java line 52: Extract to static inner class:
@@ -52 +52
- return Icon.createWithAdaptiveBitmap(null);
+ return Api26Impl.createWithAdaptiveBitmapReturnsObject(null);
@@ -77 +77
+ @RequiresApi(26)
+ static class Api26Impl {
+ private Api26Impl() {
+ // This class is not instantiable.
+ }
+
+ @DoNotInline
+ static java.lang.Object createWithAdaptiveBitmapReturnsObject(android.graphics.Bitmap bits) {
+ return Icon.createWithAdaptiveBitmap(bits);
+ }
+
@@ -78 +89
+ }
Fix for src/androidx/AutofixUnsafeCallWithImplicitReturnCast.java line 60: Extract to static inner class:
@@ -60 +60
- return Icon.createWithAdaptiveBitmap(null);
+ return Api26Impl.createWithAdaptiveBitmap(null);
@@ -77 +77
+ @RequiresApi(26)
+ static class Api26Impl {
+ private Api26Impl() {
+ // This class is not instantiable.
+ }
+
+ @DoNotInline
+ static Icon createWithAdaptiveBitmap(android.graphics.Bitmap bits) {
+ return Icon.createWithAdaptiveBitmap(bits);
+ }
+
@@ -78 +89
+ }
Fix for src/androidx/AutofixUnsafeCallWithImplicitReturnCast.java line 68: Extract to static inner class:
@@ -68 +68
- useStyle(new Notification.DecoratedCustomViewStyle());
+ useStyle(Api24Impl.createDecoratedCustomViewStyleReturnsStyle());
@@ -77 +77
+ @RequiresApi(24)
+ static class Api24Impl {
+ private Api24Impl() {
+ // This class is not instantiable.
+ }
+
+ @DoNotInline
+ static Notification.Style createDecoratedCustomViewStyleReturnsStyle() {
+ return new Notification.DecoratedCustomViewStyle();
+ }
+
@@ -78 +89
+ }
"""
check(*input).expect(expected).expectFixDiffs(expectedFix)
}
@Test
fun `Auto-fix for constructor needs qualified class name (issue 244714253)`() {
val input =
arrayOf(javaSample("androidx.AutofixUnsafeConstructorQualifiedClass"), RequiresApi)
val expected =
"""
src/androidx/AutofixUnsafeConstructorQualifiedClass.java:32: Error: This call references a method added in API level 24; however, the containing class androidx.AutofixUnsafeConstructorQualifiedClass is reachable from earlier API levels and will fail run-time class verification. [ClassVerificationFailure]
return new Notification.DecoratedCustomViewStyle();
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1 errors, 0 warnings
"""
val expectedFix =
"""
Fix for src/androidx/AutofixUnsafeConstructorQualifiedClass.java line 32: Extract to static inner class:
@@ -32 +32
+ return Api24Impl.createDecoratedCustomViewStyle();
+ }
+ @RequiresApi(24)
+ static class Api24Impl {
+ private Api24Impl() {
+ // This class is not instantiable.
+ }
+
+ @DoNotInline
+ static Notification.DecoratedCustomViewStyle createDecoratedCustomViewStyle() {
@@ -34 +44
+
@@ -35 +46
+ }
"""
check(*input).expect(expected).expectFixDiffs(expectedFix)
}
@Test
fun `Auto-fix with implicit class cast from new parameter type (issue 266845827)`() {
val input =
arrayOf(javaSample("androidx.AutofixUnsafeCallWithImplicitParamCast"), RequiresApi)
val expected =
"""
src/androidx/AutofixUnsafeCallWithImplicitParamCast.java:34: Error: This call references a method added in API level 16; however, the containing class androidx.AutofixUnsafeCallWithImplicitParamCast is reachable from earlier API levels and will fail run-time class verification. [ClassVerificationFailure]
style.setBuilder(builder);
~~~~~~~~~~
src/androidx/AutofixUnsafeCallWithImplicitParamCast.java:43: Error: This call references a method added in API level 20; however, the containing class androidx.AutofixUnsafeCallWithImplicitParamCast is reachable from earlier API levels and will fail run-time class verification. [ClassVerificationFailure]
builder.extend(extender);
~~~~~~
2 errors, 0 warnings
"""
val expectedFix =
"""
Fix for src/androidx/AutofixUnsafeCallWithImplicitParamCast.java line 34: Extract to static inner class:
@@ -34 +34
- style.setBuilder(builder);
+ Api16Impl.setBuilder(style, builder);
@@ -45 +45
+ @RequiresApi(16)
+ static class Api16Impl {
+ private Api16Impl() {
+ // This class is not instantiable.
+ }
+
+ @DoNotInline
+ static void setBuilder(Notification.Style style, Notification.Builder builder) {
+ style.setBuilder(builder);
+ }
+
@@ -46 +57
+ }
Fix for src/androidx/AutofixUnsafeCallWithImplicitParamCast.java line 43: Extract to static inner class:
@@ -43 +43
- builder.extend(extender);
+ Api20Impl.extend(builder, extender);
@@ -45 +45
+ @RequiresApi(20)
+ static class Api20Impl {
+ private Api20Impl() {
+ // This class is not instantiable.
+ }
+
+ @DoNotInline
+ static Notification.Builder extend(Notification.Builder builder, Notification.CarExtender extender) {
+ return builder.extend(extender);
+ }
+
@@ -46 +57
+ }
"""
check(*input).expect(expected).expectFixDiffs(expectedFix)
}
@Test
fun `Auto-fix for method with varargs that are implicitly cast (issue 266845827)`() {
val input =
arrayOf(javaSample("androidx.AutofixOnUnsafeCallWithImplicitVarArgsCast"), RequiresApi)
val expected =
"""
src/androidx/AutofixOnUnsafeCallWithImplicitVarArgsCast.java:35: Error: This call references a method added in API level 27; however, the containing class androidx.AutofixOnUnsafeCallWithImplicitVarArgsCast is reachable from earlier API levels and will fail run-time class verification. [ClassVerificationFailure]
adapter.setAutofillOptions();
~~~~~~~~~~~~~~~~~~
src/androidx/AutofixOnUnsafeCallWithImplicitVarArgsCast.java:43: Error: This call references a method added in API level 27; however, the containing class androidx.AutofixOnUnsafeCallWithImplicitVarArgsCast is reachable from earlier API levels and will fail run-time class verification. [ClassVerificationFailure]
adapter.setAutofillOptions(vararg);
~~~~~~~~~~~~~~~~~~
src/androidx/AutofixOnUnsafeCallWithImplicitVarArgsCast.java:52: Error: This call references a method added in API level 27; however, the containing class androidx.AutofixOnUnsafeCallWithImplicitVarArgsCast is reachable from earlier API levels and will fail run-time class verification. [ClassVerificationFailure]
adapter.setAutofillOptions(vararg1, vararg2, vararg3);
~~~~~~~~~~~~~~~~~~
3 errors, 0 warnings
"""
val expectedFix =
"""
Fix for src/androidx/AutofixOnUnsafeCallWithImplicitVarArgsCast.java line 35: Extract to static inner class:
@@ -35 +35
- adapter.setAutofillOptions();
+ Api27Impl.setAutofillOptions(adapter);
@@ -54 +54
+ @RequiresApi(27)
+ static class Api27Impl {
+ private Api27Impl() {
+ // This class is not instantiable.
+ }
+
+ @DoNotInline
+ static void setAutofillOptions(BaseAdapter baseAdapter, java.lang.CharSequence... options) {
+ baseAdapter.setAutofillOptions(options);
+ }
+
@@ -55 +66
+ }
Fix for src/androidx/AutofixOnUnsafeCallWithImplicitVarArgsCast.java line 43: Extract to static inner class:
@@ -43 +43
- adapter.setAutofillOptions(vararg);
+ Api27Impl.setAutofillOptions(adapter, vararg);
@@ -54 +54
+ @RequiresApi(27)
+ static class Api27Impl {
+ private Api27Impl() {
+ // This class is not instantiable.
+ }
+
+ @DoNotInline
+ static void setAutofillOptions(BaseAdapter baseAdapter, java.lang.CharSequence... options) {
+ baseAdapter.setAutofillOptions(options);
+ }
+
@@ -55 +66
+ }
Fix for src/androidx/AutofixOnUnsafeCallWithImplicitVarArgsCast.java line 52: Extract to static inner class:
@@ -52 +52
- adapter.setAutofillOptions(vararg1, vararg2, vararg3);
+ Api27Impl.setAutofillOptions(adapter, vararg1, vararg2, vararg3);
@@ -54 +54
+ @RequiresApi(27)
+ static class Api27Impl {
+ private Api27Impl() {
+ // This class is not instantiable.
+ }
+
+ @DoNotInline
+ static void setAutofillOptions(BaseAdapter baseAdapter, java.lang.CharSequence... options) {
+ baseAdapter.setAutofillOptions(options);
+ }
+
@@ -55 +66
+ }
"""
check(*input).expect(expected).expectFixDiffs(expectedFix)
}
}