1 /* 2 * Copyright (C) 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.tools.metalava.lint 18 19 import com.android.tools.metalava.DriverTest 20 import com.android.tools.metalava.androidxNonNullSource 21 import com.android.tools.metalava.androidxNullableSource 22 import com.android.tools.metalava.cli.common.ARG_ERROR 23 import com.android.tools.metalava.cli.common.ARG_HIDE 24 import com.android.tools.metalava.cli.lint.ARG_API_LINT 25 import com.android.tools.metalava.model.provider.Capability 26 import com.android.tools.metalava.model.testing.FilterAction 27 import com.android.tools.metalava.model.testing.FilterByProvider 28 import com.android.tools.metalava.model.testing.RequiresCapabilities 29 import com.android.tools.metalava.nonNullSource 30 import com.android.tools.metalava.testing.java 31 import com.android.tools.metalava.testing.kotlin 32 import org.junit.Test 33 34 class ApiLintTest : DriverTest() { 35 36 @RequiresCapabilities(Capability.KOTLIN) 37 @Test Test namesnull38 fun `Test names`() { 39 // Make sure we only flag issues in new API 40 check( 41 apiLint = "", // enabled 42 extraArguments = arrayOf(ARG_HIDE, "MissingNullability"), 43 expectedIssues = 44 """ 45 src/Dp.kt:3: warning: Acronyms should not be capitalized in method names: was `badCALL`, should this be `badCall`? [AcronymName] 46 src/android/pkg/ALL_CAPS.java:3: warning: Acronyms should not be capitalized in class names: was `ALL_CAPS`, should this be `AllCaps`? [AcronymName] 47 src/android/pkg/HTMLWriter.java:3: warning: Acronyms should not be capitalized in class names: was `HTMLWriter`, should this be `HtmlWriter`? [AcronymName] 48 src/android/pkg/MyStringImpl.java:3: error: Don't expose your implementation details: `MyStringImpl` ends with `Impl` [EndsWithImpl] 49 src/android/pkg/badlyNamedClass.java:5: error: Class must start with uppercase char: badlyNamedClass [StartWithUpper] 50 src/android/pkg/badlyNamedClass.java:6: error: Constant field names must be named with only upper case characters: `android.pkg.badlyNamedClass#BadlyNamedField`, should be `BADLY_NAMED_FIELD`? [AllUpper] 51 src/android/pkg/badlyNamedClass.java:7: error: Method name must start with lowercase char: BadlyNamedMethod1 [StartWithLower] 52 src/android/pkg/badlyNamedClass.java:9: warning: Acronyms should not be capitalized in method names: was `fromHTMLToHTML`, should this be `fromHtmlToHtml`? [AcronymName] 53 src/android/pkg/badlyNamedClass.java:10: warning: Acronyms should not be capitalized in method names: was `toXML`, should this be `toXml`? [AcronymName] 54 src/android/pkg/badlyNamedClass.java:12: warning: Acronyms should not be capitalized in method names: was `getID`, should this be `getId`? [AcronymName] 55 """, 56 expectedFail = DefaultLintErrorMessage, 57 sourceFiles = 58 arrayOf( 59 java( 60 """ 61 package android.pkg; 62 63 import androidx.annotation.Nullable; 64 65 public class badlyNamedClass { 66 public static final int BadlyNamedField = 1; 67 public void BadlyNamedMethod1() { } 68 69 public void fromHTMLToHTML() { } 70 public void toXML() { } 71 @Nullable 72 public String getID() { return null; } 73 public void setZOrderOnTop() { } // OK 74 } 75 """ 76 ), 77 java( 78 """ 79 package android.pkg; 80 81 public class ALL_CAPS { // like android.os.Build.VERSION_CODES 82 } 83 """ 84 ), 85 java( 86 """ 87 package android.pkg; 88 89 public class HTMLWriter { 90 } 91 """ 92 ), 93 java( 94 """ 95 package android.pkg; 96 97 public class MyStringImpl { 98 } 99 """ 100 ), 101 kotlin( 102 """ 103 inline class Dp(val value: Float) 104 fun greatCall(width: Dp) 105 fun badCALL(width: Dp) 106 """ 107 ), 108 androidxNullableSource 109 ) 110 ) 111 } 112 113 @Test Test names against previous APInull114 fun `Test names against previous API`() { 115 check( 116 apiLint = 117 """ 118 package android.pkg { 119 public class badlyNamedClass { 120 ctor public badlyNamedClass(); 121 method public void BadlyNamedMethod1(); 122 method public void fromHTMLToHTML(); 123 method public String getID(); 124 method public void toXML(); 125 field public static final int BadlyNamedField = 1; // 0x1 126 } 127 } 128 """ 129 .trimIndent(), 130 expectedIssues = 131 """ 132 src/android/pkg/badlyNamedClass.java:8: warning: Acronyms should not be capitalized in method names: was `toXML2`, should this be `toXmL2`? [AcronymName] 133 src/android/pkg2/HTMLWriter.java:3: warning: Acronyms should not be capitalized in class names: was `HTMLWriter`, should this be `HtmlWriter`? [AcronymName] 134 src/android/pkg2/HTMLWriter.java:4: warning: Acronyms should not be capitalized in method names: was `fromHTMLToHTML`, should this be `fromHtmlToHtml`? [AcronymName] 135 """, 136 sourceFiles = 137 arrayOf( 138 java( 139 """ 140 package android.pkg; 141 142 public class badlyNamedClass { 143 public static final int BadlyNamedField = 1; 144 145 public void fromHTMLToHTML() { } 146 public void toXML() { } 147 public void toXML2() { } 148 public String getID() { return null; } 149 } 150 """ 151 ), 152 java( 153 """ 154 package android.pkg2; 155 156 public class HTMLWriter { 157 public void fromHTMLToHTML() { } 158 } 159 """ 160 ) 161 ) 162 ) 163 } 164 165 @Test Test constantsnull166 fun `Test constants`() { 167 check( 168 apiLint = "", // enabled 169 expectedIssues = 170 """ 171 src/android/pkg/Constants.java:8: error: Constant field names must be named with only upper case characters: `android.pkg.Constants#strings`, should be `STRINGS`? [AllUpper] 172 src/android/pkg/Constants.java:10: error: Constant field names must be named with only upper case characters: `android.pkg.Constants#myStrings`, should be `MY_STRINGS`? [AllUpper] 173 src/android/pkg/Constants.java:11: warning: If min/max could change in future, make them dynamic methods: android.pkg.Constants#MIN_FOO [MinMaxConstant] 174 src/android/pkg/Constants.java:12: warning: If min/max could change in future, make them dynamic methods: android.pkg.Constants#MAX_FOO [MinMaxConstant] 175 src/android/pkg/Constants.java:14: error: All constants must be defined at compile time: android.pkg.Constants#FOO [CompileTimeConstant] 176 """, 177 expectedFail = DefaultLintErrorMessage, 178 sourceFiles = 179 arrayOf( 180 java( 181 """ 182 package android.pkg; 183 184 import androidx.annotation.NonNull; 185 186 public class Constants { 187 private Constants() { } 188 @NonNull 189 public static final String[] strings = { "NONE", "WPA_PSK" }; 190 @NonNull 191 public static final String[] myStrings = { "NONE", "WPA_PSK" }; 192 public static final int MIN_FOO = 1; 193 public static final int MAX_FOO = 10; 194 @NonNull 195 public static final String FOO = System.getProperty("foo"); 196 } 197 """ 198 ), 199 androidxNonNullSource, 200 androidxNullableSource 201 ) 202 ) 203 } 204 205 @Test No enumsnull206 fun `No enums`() { 207 check( 208 apiLint = "", // enabled 209 expectedIssues = 210 """ 211 src/android/pkg/MyEnum.java:3: error: Enums are discouraged in Android APIs [Enum] 212 """, 213 expectedFail = DefaultLintErrorMessage, 214 sourceFiles = 215 arrayOf( 216 java( 217 """ 218 package android.pkg; 219 220 public enum MyEnum { 221 FOO, BAR 222 } 223 """ 224 ) 225 ) 226 ) 227 } 228 229 @Test Test callbacksnull230 fun `Test callbacks`() { 231 check( 232 extraArguments = arrayOf(ARG_ERROR, "CallbackInterface"), 233 apiLint = "", // enabled 234 expectedIssues = 235 """ 236 src/android/pkg/MyCallback.java:9: error: Callback method names must follow the on<Something> style: bar [CallbackMethodName] 237 src/android/pkg/MyCallbacks.java:3: error: Callback class names should be singular: MyCallbacks [SingularCallback] 238 src/android/pkg/MyInterfaceCallback.java:3: error: Callbacks must be abstract class instead of interface to enable extension in future API levels: MyInterfaceCallback [CallbackInterface] 239 src/android/pkg/MyObserver.java:3: warning: Class should be named MyCallback [CallbackName] 240 """, 241 expectedFail = DefaultLintErrorMessage, 242 sourceFiles = 243 arrayOf( 244 java( 245 """ 246 package android.pkg; 247 248 public class MyCallbacks { 249 } 250 """ 251 ), 252 java( 253 """ 254 package android.pkg; 255 256 public final class RemoteCallback { 257 public void sendResult(); 258 } 259 """ 260 ), 261 java( 262 """ 263 package android.pkg; 264 265 public class MyObserver { 266 } 267 """ 268 ), 269 java( 270 """ 271 package android.pkg; 272 273 public interface MyInterfaceCallback { 274 } 275 """ 276 ), 277 java( 278 """ 279 package android.pkg; 280 281 public class MyCallback { 282 public void onFoo() { 283 } 284 public void onAnimationStart() { 285 } 286 public void onN1GoodMethod() { } 287 public void bar() { 288 } 289 public static void staticMethod() { 290 } 291 public final void finalMethod() { 292 } 293 } 294 """ 295 ) 296 ) 297 ) 298 } 299 300 @Test Test listenersnull301 fun `Test listeners`() { 302 check( 303 apiLint = "", // enabled 304 expectedIssues = 305 """ 306 src/android/pkg/MyClassListener.java:3: error: Listeners should be an interface, or otherwise renamed Callback: MyClassListener [ListenerInterface] 307 src/android/pkg/MyListener.java:6: error: Listener method names must follow the on<Something> style: bar [CallbackMethodName] 308 """, 309 expectedFail = DefaultLintErrorMessage, 310 sourceFiles = 311 arrayOf( 312 java( 313 """ 314 package android.pkg; 315 316 public abstract class MyClassListener { 317 } 318 """ 319 ), 320 java( 321 """ 322 package android.pkg; 323 324 public interface OnFooBarListener1 { 325 void bar(); 326 } 327 """ 328 ), 329 java( 330 """ 331 package android.pkg; 332 333 public interface OnFooBarListener2 { 334 void onFooBar(); // OK 335 } 336 """ 337 ), 338 java( 339 """ 340 package android.pkg; 341 342 public interface MyInterfaceListener { 343 } 344 """ 345 ), 346 java( 347 """ 348 package android.pkg; 349 350 public interface MyListener { 351 public void onFoo() { 352 } 353 public void bar() { 354 } 355 public static void staticMethod() { 356 } 357 public static void finalMethod() { 358 } 359 } 360 """ 361 ) 362 ) 363 ) 364 } 365 366 @Test Test actionsnull367 fun `Test actions`() { 368 check( 369 apiLint = "", // enabled 370 expectedIssues = 371 """ 372 src/android/accounts/Actions.java:6: error: Inconsistent action value; expected `android.accounts.action.ACCOUNT_OPENED`, was `android.accounts.ACCOUNT_OPENED` [ActionValue] 373 src/android/accounts/Actions.java:7: error: Intent action constant name must be ACTION_FOO: ACCOUNT_ADDED [IntentName] 374 src/android/accounts/Actions.java:8: error: Intent action constant name must be ACTION_FOO: SOMETHING [IntentName] 375 """, 376 expectedFail = DefaultLintErrorMessage, 377 sourceFiles = 378 arrayOf( 379 java( 380 """ 381 package android.accounts; 382 383 public class Actions { 384 private Actions() { } 385 public static final String ACTION_ACCOUNT_REMOVED = "android.accounts.action.ACCOUNT_REMOVED"; 386 public static final String ACTION_ACCOUNT_OPENED = "android.accounts.ACCOUNT_OPENED"; 387 public static final String ACCOUNT_ADDED = "android.accounts.action.ACCOUNT_ADDED"; 388 public static final String SOMETHING = "android.accounts.action.ACCOUNT_MOVED"; 389 } 390 """ 391 ) 392 ) 393 ) 394 } 395 396 @Test Test extrasnull397 fun `Test extras`() { 398 check( 399 apiLint = "", // enabled 400 expectedIssues = 401 """ 402 src/android/accounts/Extras.java:5: error: Inconsistent extra value; expected `android.accounts.extra.AUTOMATIC_RULE_ID`, was `android.app.extra.AUTOMATIC_RULE_ID` [ActionValue] 403 src/android/accounts/Extras.java:6: error: Intent extra constant name must be EXTRA_FOO: SOMETHING_EXTRA [IntentName] 404 src/android/accounts/Extras.java:7: error: Intent extra constant name must be EXTRA_FOO: RULE_ID [IntentName] 405 """, 406 expectedFail = DefaultLintErrorMessage, 407 sourceFiles = 408 arrayOf( 409 java( 410 """ 411 package android.accounts; 412 413 public class Extras { 414 private Extras() { } 415 public static final String EXTRA_AUTOMATIC_RULE_ID = "android.app.extra.AUTOMATIC_RULE_ID"; 416 public static final String SOMETHING_EXTRA = "something.here"; 417 public static final String RULE_ID = "android.app.extra.AUTOMATIC_RULE_ID"; 418 } 419 """ 420 ) 421 ) 422 ) 423 } 424 425 @Test Test Parcelablenull426 fun `Test Parcelable`() { 427 check( 428 apiLint = "", // enabled 429 expectedIssues = 430 """ 431 src/android/pkg/MissingCreator.java:5: error: Parcelable requires a `CREATOR` field; missing in android.pkg.MissingCreator [ParcelCreator] 432 src/android/pkg/MissingDescribeContents.java:5: error: Parcelable requires `public int describeContents()`; missing in android.pkg.MissingDescribeContents [ParcelCreator] 433 src/android/pkg/MissingWriteToParcel.java:5: error: Parcelable requires `void writeToParcel(Parcel, int)`; missing in android.pkg.MissingWriteToParcel [ParcelCreator] 434 src/android/pkg/NonFinalParcelable.java:5: error: Parcelable classes must be final: android.pkg.NonFinalParcelable is not final [ParcelNotFinal] 435 src/android/pkg/ParcelableConstructor.java:6: error: Parcelable inflation is exposed through CREATOR, not raw constructors, in android.pkg.ParcelableConstructor [ParcelConstructor] 436 """, 437 expectedFail = DefaultLintErrorMessage, 438 sourceFiles = 439 arrayOf( 440 java( 441 """ 442 package android.pkg; 443 444 import androidx.annotation.Nullable; 445 446 public final class ParcelableConstructor implements android.os.Parcelable { 447 public ParcelableConstructor(@Nullable android.os.Parcel p) { } 448 public int describeContents() { return 0; } 449 public void writeToParcel(@Nullable android.os.Parcel p, int f) { } 450 @Nullable 451 public static final android.os.Parcelable.Creator<android.app.WallpaperColors> CREATOR = null; 452 } 453 """ 454 ), 455 java( 456 """ 457 package android.pkg; 458 459 import androidx.annotation.Nullable; 460 461 public class NonFinalParcelable implements android.os.Parcelable { 462 public NonFinalParcelable() { } 463 public int describeContents() { return 0; } 464 public void writeToParcel(@Nullable android.os.Parcel p, int f) { } 465 @Nullable 466 public static final android.os.Parcelable.Creator<android.app.WallpaperColors> CREATOR = null; 467 } 468 """ 469 ), 470 java( 471 """ 472 package android.pkg; 473 474 import androidx.annotation.Nullable; 475 476 public final class MissingCreator implements android.os.Parcelable { 477 public MissingCreator() { } 478 public int describeContents() { return 0; } 479 public void writeToParcel(@Nullable android.os.Parcel p, int f) { } 480 } 481 """ 482 ), 483 java( 484 """ 485 package android.pkg; 486 487 import androidx.annotation.Nullable; 488 489 public final class MissingDescribeContents implements android.os.Parcelable { 490 public MissingDescribeContents() { } 491 public void writeToParcel(@Nullable android.os.Parcel p, int f) { } 492 @Nullable 493 public static final android.os.Parcelable.Creator<android.app.WallpaperColors> CREATOR = null; 494 } 495 """ 496 ), 497 java( 498 """ 499 package android.pkg; 500 501 import androidx.annotation.Nullable; 502 503 public final class MissingWriteToParcel implements android.os.Parcelable { 504 public MissingWriteToParcel() { } 505 public int describeContents() { return 0; } 506 @Nullable 507 public static final android.os.Parcelable.Creator<android.app.WallpaperColors> CREATOR = null; 508 } 509 """ 510 ), 511 androidxNullableSource 512 ) 513 ) 514 } 515 516 @Test No protected methods or fields are allowednull517 fun `No protected methods or fields are allowed`() { 518 check( 519 apiLint = "", // enabled 520 expectedIssues = 521 """ 522 src/android/pkg/MyClass.java:6: error: Protected methods not allowed; must be public: method android.pkg.MyClass.wrong()} [ProtectedMember] 523 src/android/pkg/MyClass.java:8: error: Protected fields not allowed; must be public: field android.pkg.MyClass.wrong} [ProtectedMember] 524 """, 525 expectedFail = DefaultLintErrorMessage, 526 sourceFiles = 527 arrayOf( 528 java( 529 """ 530 package android.pkg; 531 532 public abstract class MyClass implements AutoCloseable { 533 public void ok() { } 534 protected void finalize() { } // OK 535 protected void wrong() { } 536 public final int ok = 42; 537 protected final int wrong = 5; 538 private int ok2 = 2; 539 } 540 """ 541 ) 542 ) 543 ) 544 } 545 546 @RequiresCapabilities(Capability.KOTLIN) 547 @Test Fields must be final and properly namednull548 fun `Fields must be final and properly named`() { 549 check( 550 apiLint = "", // enabled 551 expectedIssues = 552 """ 553 src/android/pkg/MyClass.java:5: error: Bare field badMutable must be marked final, or moved behind accessors if mutable [MutableBareField] 554 src/android/pkg/MyClass.java:6: error: Internal field mBadName must not be exposed [InternalField] 555 src/android/pkg/MyClass.java:7: error: Non-static field AlsoBadName must be named using fooBar style [StartWithLower] 556 src/android/pkg/MyClass.java:8: error: Constant field names must be named with only upper case characters: `android.pkg.MyClass#sBadStaticName`, should be `S_BAD_STATIC_NAME`? [AllUpper] 557 src/android/pkg/MyClass.java:8: error: Internal field sBadStaticName must not be exposed [InternalField] 558 src/android/pkg/MyClass.java:9: error: Bare field badStaticMutable must be marked final, or moved behind accessors if mutable [MutableBareField] 559 src/android/pkg/MyClass.java:10: error: Constant BAD_CONSTANT must be marked static final [AllUpper] 560 src/android/pkg/MyClass.java:10: error: Bare field BAD_CONSTANT must be marked final, or moved behind accessors if mutable [MutableBareField] 561 src/android/pkg/MyClass.java:11: error: Constant ALSO_BAD_CONSTANT must be marked static final [AllUpper] 562 src/android/pkg/MyClass.java:11: error: Non-static field ALSO_BAD_CONSTANT must be named using fooBar style [StartWithLower] 563 src/android/pkg/MyClass.java:15: error: Internal field mBad must not be exposed [InternalField] 564 """, 565 expectedFail = DefaultLintErrorMessage, 566 sourceFiles = 567 arrayOf( 568 java( 569 """ 570 package android.pkg; 571 572 public class MyClass { 573 private int mOk; 574 public int badMutable; 575 public final int mBadName; 576 public final int AlsoBadName; 577 public static final int sBadStaticName; 578 public static int badStaticMutable; 579 public static int BAD_CONSTANT; 580 public final int ALSO_BAD_CONSTANT; 581 582 public static class LayoutParams { 583 public int ok; 584 public int mBad; 585 } 586 } 587 """ 588 ), 589 kotlin( 590 """ 591 package android.pkg 592 593 class KotlinClass(val ok: Int) { 594 companion object { 595 const val OkConstant = 1 596 const val OK_CONSTANT = 2 597 @JvmField 598 val OkSingleton = KotlinClass(3) 599 @JvmField 600 val OK_SINGLETON = KotlinClass(4) 601 } 602 603 object OkObject 604 } 605 """ 606 ) 607 ) 608 ) 609 } 610 611 @Test Only android_net_Uri allowednull612 fun `Only android_net_Uri allowed`() { 613 check( 614 apiLint = "", // enabled 615 expectedIssues = 616 """ 617 src/android/pkg/MyClass.java:7: error: Use android.net.Uri instead of java.net.URL (method android.pkg.MyClass.bad1()) [AndroidUri] 618 src/android/pkg/MyClass.java:8: error: Use android.net.Uri instead of java.net.URI (parameter param in android.pkg.MyClass.bad2(java.util.List<java.net.URI> param)) [AndroidUri] 619 src/android/pkg/MyClass.java:9: error: Use android.net.Uri instead of android.net.URL (parameter param in android.pkg.MyClass.bad3(android.net.URL param)) [AndroidUri] 620 """, 621 expectedFail = DefaultLintErrorMessage, 622 extraArguments = arrayOf(ARG_HIDE, "AcronymName"), 623 sourceFiles = 624 arrayOf( 625 java( 626 """ 627 package android.pkg; 628 629 import java.util.List;import java.util.concurrent.CompletableFuture;import java.util.concurrent.Future; 630 import androidx.annotation.NonNull; 631 632 public final class MyClass { 633 public @NonNull java.net.URL bad1() { throw new RuntimeException(); } 634 public void bad2(@NonNull List<java.net.URI> param) { } 635 public void bad3(@NonNull android.net.URL param) { } 636 public void good(@NonNull android.net.Uri param) { } 637 } 638 """ 639 ), 640 java( 641 """ 642 package android.net; 643 public class URL { 644 private URL() {} 645 } 646 """ 647 ), 648 java( 649 """ 650 package android.net; 651 public class Uri {} 652 """ 653 ), 654 androidxNonNullSource 655 ) 656 ) 657 } 658 659 @Test CompletableFuture and plain Future not allowednull660 fun `CompletableFuture and plain Future not allowed`() { 661 check( 662 apiLint = "", // enabled 663 expectedIssues = 664 """ 665 src/android/pkg/MyClass.java:9: error: Use ListenableFuture (library), or a combination of OutcomeReceiver<R,E>, Executor, and CancellationSignal (platform) instead of java.util.concurrent.CompletableFuture (method android.pkg.MyClass.bad1()) [BadFuture] 666 src/android/pkg/MyClass.java:10: error: Use ListenableFuture (library), or a combination of OutcomeReceiver<R,E>, Executor, and CancellationSignal (platform) instead of java.util.concurrent.CompletableFuture (parameter param in android.pkg.MyClass.bad2(java.util.concurrent.CompletableFuture<java.lang.String> param)) [BadFuture] 667 src/android/pkg/MyClass.java:11: error: Use ListenableFuture (library), or a combination of OutcomeReceiver<R,E>, Executor, and CancellationSignal (platform) instead of java.util.concurrent.Future (method android.pkg.MyClass.bad3()) [BadFuture] 668 src/android/pkg/MyClass.java:12: error: Use ListenableFuture (library), or a combination of OutcomeReceiver<R,E>, Executor, and CancellationSignal (platform) instead of java.util.concurrent.Future (parameter param in android.pkg.MyClass.bad4(java.util.concurrent.Future<java.lang.String> param)) [BadFuture] 669 src/android/pkg/MyClass.java:17: error: BadFuture should not extend `java.util.concurrent.Future`. In AndroidX, use (but do not extend) ListenableFuture. In platform, use a combination of OutcomeReceiver<R,E>, Executor, and CancellationSignal`. [BadFuture] 670 src/android/pkg/MyClass.java:19: error: BadFutureClass should not implement `java.util.concurrent.Future`. In AndroidX, use (but do not extend) ListenableFuture. In platform, use a combination of OutcomeReceiver<R,E>, Executor, and CancellationSignal`. [BadFuture] 671 src/android/pkg/MyClass.java:21: error: BadCompletableFuture should not extend `java.util.concurrent.CompletableFuture`. In AndroidX, use (but do not extend) ListenableFuture. In platform, use a combination of OutcomeReceiver<R,E>, Executor, and CancellationSignal`. [BadFuture] 672 """, 673 expectedFail = DefaultLintErrorMessage, 674 sourceFiles = 675 arrayOf( 676 java( 677 """ 678 package android.pkg; 679 680 import java.util.concurrent.CompletableFuture; 681 import java.util.concurrent.Future; 682 import androidx.annotation.Nullable; 683 import com.google.common.util.concurrent.ListenableFuture; 684 685 public final class MyClass { 686 public @Nullable CompletableFuture<String> bad1() { return null; } 687 public void bad2(@Nullable CompletableFuture<String> param) { } 688 public @Nullable Future<String> bad3() { return null; } 689 public void bad4(@Nullable Future<String> param) { } 690 691 public @Nullable ListenableFuture<String> okAsync() { return null; } 692 public void ok2(@Nullable ListenableFuture<String> param) { } 693 694 public interface BadFuture<T> extends AnotherInterface, Future<T> { 695 } 696 public static abstract class BadFutureClass<T> implements Future<T> { 697 } 698 public class BadCompletableFuture<T> extends CompletableFuture<T> { 699 } 700 public interface AnotherInterface { 701 } 702 } 703 """ 704 ), 705 java( 706 """ 707 package com.google.common.util.concurrent; 708 public class ListenableFuture<T> { 709 } 710 """ 711 ), 712 androidxNullableSource 713 ) 714 ) 715 } 716 717 @Test Typedef must be hiddennull718 fun `Typedef must be hidden`() { 719 check( 720 apiLint = "", // enabled 721 expectedIssues = 722 """ 723 src/android/pkg/MyClass.java:14: error: Don't expose @StringDef: SomeString must be hidden. [PublicTypedef] 724 src/android/pkg/MyClass.java:19: error: Don't expose @IntDef: SomeInt must be hidden. [PublicTypedef] 725 src/android/pkg/MyClass.java:24: error: Don't expose @LongDef: SomeLong must be hidden. [PublicTypedef] 726 """, 727 expectedFail = DefaultLintErrorMessage, 728 sourceFiles = 729 arrayOf( 730 java( 731 """ 732 package android.pkg; 733 734 public final class MyClass { 735 private MyClass() {} 736 737 public static final String SOME_STRING = "abc"; 738 public static final int SOME_INT = 1; 739 public static final long SOME_LONG = 1L; 740 741 @android.annotation.StringDef(value = { 742 SOME_STRING 743 }) 744 @Retention(RetentionPolicy.SOURCE) 745 public @interface SomeString {} 746 @android.annotation.IntDef(value = { 747 SOME_INT 748 }) 749 @Retention(RetentionPolicy.SOURCE) 750 public @interface SomeInt {} 751 @android.annotation.LongDef(value = { 752 SOME_LONG 753 }) 754 @Retention(RetentionPolicy.SOURCE) 755 public @interface SomeLong {} 756 } 757 """ 758 ) 759 ) 760 ) 761 } 762 763 @Test Ensure registration methods are matchednull764 fun `Ensure registration methods are matched`() { 765 check( 766 apiLint = "", // enabled 767 expectedIssues = 768 """ 769 src/android/pkg/RegistrationInterface.java:6: error: Found registerOverriddenUnpairedCallback but not unregisterOverriddenUnpairedCallback in android.pkg.RegistrationInterface [PairedRegistration] 770 src/android/pkg/RegistrationMethods.java:8: error: Found registerUnpairedCallback but not unregisterUnpairedCallback in android.pkg.RegistrationMethods [PairedRegistration] 771 src/android/pkg/RegistrationMethods.java:12: error: Found unregisterMismatchedCallback but not registerMismatchedCallback in android.pkg.RegistrationMethods [PairedRegistration] 772 src/android/pkg/RegistrationMethods.java:13: error: Found addUnpairedCallback but not removeUnpairedCallback in android.pkg.RegistrationMethods [PairedRegistration] 773 src/android/pkg/RegistrationMethods.java:18: error: Found addUnpairedListener but not removeUnpairedListener in android.pkg.RegistrationMethods [PairedRegistration] 774 src/android/pkg/RegistrationMethods.java:19: error: Found removeMismatchedListener but not addMismatchedListener in android.pkg.RegistrationMethods [PairedRegistration] 775 src/android/pkg/RegistrationMethods.java:20: error: Found registerUnpairedListener but not unregisterUnpairedListener in android.pkg.RegistrationMethods [PairedRegistration] 776 """, 777 expectedFail = DefaultLintErrorMessage, 778 sourceFiles = 779 arrayOf( 780 java( 781 """ 782 package android.pkg; 783 784 import androidx.annotation.Nullable; 785 786 public class RegistrationMethods implements RegistrationInterface { 787 public void registerOkCallback(@Nullable Runnable r) { } // OK 788 public void unregisterOkCallback(@Nullable Runnable r) { } // OK 789 public void registerUnpairedCallback(@Nullable Runnable r) { } 790 // OK here because it is override 791 @Override 792 public void registerOverriddenUnpairedCallback(@Nullable Runnable r) { } 793 public void unregisterMismatchedCallback(@Nullable Runnable r) { } 794 public void addUnpairedCallback(@Nullable Runnable r) { } 795 796 public void addOkListener(@Nullable Runnable r) { } // OK 797 public void removeOkListener(@Nullable Runnable r) { } // OK 798 799 public void addUnpairedListener(@Nullable Runnable r) { } 800 public void removeMismatchedListener(@Nullable Runnable r) { } 801 public void registerUnpairedListener(@Nullable Runnable r) { } 802 } 803 """ 804 ), 805 java( 806 """ 807 package android.pkg; 808 809 import androidx.annotation.Nullable; 810 811 public interface RegistrationInterface { 812 void registerOverriddenUnpairedCallback(@Nullable Runnable r) { } 813 } 814 """ 815 ), 816 androidxNullableSource 817 ) 818 ) 819 } 820 821 @Test Check intent builder namesnull822 fun `Check intent builder names`() { 823 check( 824 apiLint = "", // enabled 825 expectedIssues = 826 """ 827 src/android/pkg/IntentBuilderNames.java:9: warning: Methods creating an Intent should be named `create<Foo>Intent()`, was `makeMyIntent` [IntentBuilderName] 828 src/android/pkg/IntentBuilderNames.java:11: warning: Methods creating an Intent should be named `create<Foo>Intent()`, was `createIntentNow` [IntentBuilderName] 829 """, 830 sourceFiles = 831 arrayOf( 832 java( 833 """ 834 package android.pkg; 835 import android.content.Intent; 836 import androidx.annotation.Nullable; 837 838 public class IntentBuilderNames { 839 @Nullable 840 public Intent createEnrollIntent() { return null; } // OK 841 @Nullable 842 public Intent makeMyIntent() { return null; } // WARN 843 @Nullable 844 public Intent createIntentNow() { return null; } // WARN 845 } 846 """ 847 ), 848 androidxNullableSource 849 ) 850 ) 851 } 852 853 @Test Check helper classesnull854 fun `Check helper classes`() { 855 check( 856 apiLint = "", // enabled 857 expectedIssues = 858 """ 859 src/android/pkg/MyClass1.java:3: error: Inconsistent class name; should be `<Foo>Activity`, was `MyClass1` [ContextNameSuffix] 860 src/android/pkg/MyClass1.java:3: error: MyClass1 should not extend `Activity`. Activity subclasses are impossible to compose. Expose a composable API instead. [ForbiddenSuperClass] 861 src/android/pkg/MyClass1.java:6: warning: Methods implemented by developers should follow the on<Something> style, was `badlyNamedAbstractMethod` [OnNameExpected] 862 src/android/pkg/MyClass1.java:7: warning: If implemented by developer, should follow the on<Something> style; otherwise consider marking final [OnNameExpected] 863 src/android/pkg/MyClass2.java:3: error: Inconsistent class name; should be `<Foo>Provider`, was `MyClass2` [ContextNameSuffix] 864 src/android/pkg/MyClass3.java:3: error: Inconsistent class name; should be `<Foo>Service`, was `MyClass3` [ContextNameSuffix] 865 src/android/pkg/MyClass4.java:3: error: Inconsistent class name; should be `<Foo>Receiver`, was `MyClass4` [ContextNameSuffix] 866 src/android/pkg/MyOkActivity.java:3: error: MyOkActivity should not extend `Activity`. Activity subclasses are impossible to compose. Expose a composable API instead. [ForbiddenSuperClass] 867 """, 868 expectedFail = DefaultLintErrorMessage, 869 sourceFiles = 870 arrayOf( 871 java( 872 """ 873 package android.pkg; 874 875 public class MyClass1 extends android.app.Activity { 876 public void onOk() { } 877 public final void ok() { } 878 public abstract void badlyNamedAbstractMethod(); 879 public void badlyNamedMethod() { } 880 public static void staticOk() { } 881 } 882 """ 883 ), 884 java( 885 """ 886 package android.pkg; 887 888 public class MyClass2 extends android.content.ContentProvider { 889 public static final String PROVIDER_INTERFACE = "android.pkg.MyClass2"; 890 public final void ok(); 891 } 892 """ 893 ), 894 java( 895 """ 896 package android.pkg; 897 898 public class MyClass3 extends android.app.Service { 899 public static final String SERVICE_INTERFACE = "android.pkg.MyClass3"; 900 public final void ok(); 901 } 902 """ 903 ), 904 java( 905 """ 906 package android.pkg; 907 908 public class MyClass4 extends android.content.BroadcastReceiver { 909 } 910 """ 911 ), 912 java( 913 """ 914 package android.pkg; 915 916 public class MyOkActivity extends android.app.Activity { 917 } 918 """ 919 ) 920 ) 921 ) 922 } 923 924 @Test Check suppress works on inherited methodsnull925 fun `Check suppress works on inherited methods`() { 926 check( 927 apiLint = "", // enabled 928 expectedIssues = 929 """ 930 src/android/pkg/Ok.java:11: warning: Should avoid odd sized primitives; use `int` instead of `short` in method android.pkg.Ok.Public.shouldFail(PublicT) [NoByteOrShort] 931 """, 932 sourceFiles = 933 arrayOf( 934 java( 935 """ 936 package android.pkg; 937 938 import androidx.annotation.NonNull; 939 940 public class Ok { 941 942 static final class Hidden<HiddenT> { 943 @SuppressWarnings("NoByteOrShort") 944 public short suppressed(HiddenT t) { return null; } 945 946 public short shouldFail(HiddenT t) { return null; } 947 } 948 949 public static final class Public<PublicT> extends Hidden<PublicT> { 950 } 951 } 952 """ 953 ), 954 androidxNonNullSource 955 ) 956 ) 957 } 958 959 @Test Raw AIDLnull960 fun `Raw AIDL`() { 961 check( 962 apiLint = "", // enabled 963 expectedIssues = 964 """ 965 src/android/pkg/MyClass1.java:3: error: Raw AIDL interfaces must not be exposed: MyClass1 extends Binder [RawAidl] 966 src/android/pkg/MyClass2.java:3: error: Raw AIDL interfaces must not be exposed: MyClass2 implements IInterface [RawAidl] 967 """, 968 expectedFail = DefaultLintErrorMessage, 969 sourceFiles = 970 arrayOf( 971 java( 972 """ 973 package android.pkg; 974 975 public abstract class MyClass1 extends android.os.Binder { 976 } 977 """ 978 ), 979 java( 980 """ 981 package android.pkg; 982 983 public abstract class MyClass2 implements android.os.IInterface { 984 } 985 """ 986 ), 987 java( 988 """ 989 package android.pkg; 990 // Ensure that we don't flag transitively implementing IInterface 991 public class MyClass3 extends MyClass1 implements MyClass2 { 992 } 993 """ 994 ) 995 ) 996 ) 997 } 998 999 @Test Internal packagesnull1000 fun `Internal packages`() { 1001 check( 1002 apiLint = "", // enabled 1003 expectedIssues = 1004 """ 1005 src/com/android/pkg/MyClass.java:3: error: Internal classes must not be exposed [InternalClasses] 1006 """, 1007 expectedFail = DefaultLintErrorMessage, 1008 sourceFiles = 1009 arrayOf( 1010 java( 1011 """ 1012 package com.android.pkg; 1013 1014 public class MyClass { 1015 } 1016 """ 1017 ) 1018 ) 1019 ) 1020 } 1021 1022 @Test Check package layeringnull1023 fun `Check package layering`() { 1024 check( 1025 apiLint = "", // enabled 1026 expectedIssues = 1027 """ 1028 src/android/content/MyClass1.java:8: warning: Field type `android.view.View` violates package layering: nothing in `package android.content` should depend on `package android.view` [PackageLayering] 1029 src/android/content/MyClass1.java:8: warning: Method parameter type `android.view.View` violates package layering: nothing in `package android.content` should depend on `package android.view` [PackageLayering] 1030 src/android/content/MyClass1.java:8: warning: Method parameter type `android.view.View` violates package layering: nothing in `package android.content` should depend on `package android.view` [PackageLayering] 1031 src/android/content/MyClass1.java:8: warning: Method return type `android.view.View` violates package layering: nothing in `package android.content` should depend on `package android.view` [PackageLayering] 1032 """, 1033 sourceFiles = 1034 arrayOf( 1035 java( 1036 """ 1037 package android.content; 1038 1039 import android.graphics.drawable.Drawable; 1040 import android.graphics.Bitmap; 1041 import android.view.View; 1042 import androidx.annotation.Nullable; 1043 1044 public class MyClass1 { 1045 @Nullable 1046 public final View view = null; 1047 @Nullable 1048 public final Drawable drawable = null; 1049 @Nullable 1050 public final Bitmap bitmap = null; 1051 @Nullable 1052 public View ok(@Nullable View view, @Nullable Drawable drawable) { return null; } 1053 @Nullable 1054 public Bitmap wrong(@Nullable View view, @Nullable Bitmap bitmap) { return null; } 1055 } 1056 """ 1057 ), 1058 androidxNullableSource 1059 ) 1060 ) 1061 } 1062 1063 @Test Check boolean getter and setter naming patternsnull1064 fun `Check boolean getter and setter naming patterns`() { 1065 check( 1066 apiLint = "", // enabled 1067 expectedIssues = 1068 """ 1069 src/android/pkg/MyClass.java:25: error: Symmetric method for `isVisibleBad` must be named `setVisibleBad`; was `setIsVisibleBad` [GetterSetterNames] 1070 src/android/pkg/MyClass.java:28: error: Symmetric method for `hasTransientStateBad` must be named `setHasTransientStateBad`; was `setTransientStateBad` [GetterSetterNames] 1071 src/android/pkg/MyClass.java:32: error: Symmetric method for `setHasTransientStateAlsoBad` must be named `hasTransientStateAlsoBad`; was `isHasTransientStateAlsoBad` [GetterSetterNames] 1072 src/android/pkg/MyClass.java:35: error: Symmetric method for `setCanRecordBad` must be named `canRecordBad`; was `isCanRecordBad` [GetterSetterNames] 1073 src/android/pkg/MyClass.java:38: error: Symmetric method for `setShouldFitWidthBad` must be named `shouldFitWidthBad`; was `isShouldFitWidthBad` [GetterSetterNames] 1074 src/android/pkg/MyClass.java:41: error: Symmetric method for `setWiFiRoamingSettingEnabledBad` must be named `isWiFiRoamingSettingEnabledBad`; was `getWiFiRoamingSettingEnabledBad` [GetterSetterNames] 1075 src/android/pkg/MyClass.java:44: error: Symmetric method for `setEnabledBad` must be named `isEnabledBad`; was `getEnabledBad` [GetterSetterNames] 1076 """, 1077 expectedFail = DefaultLintErrorMessage, 1078 sourceFiles = 1079 arrayOf( 1080 java( 1081 """ 1082 package android.pkg; 1083 import androidx.annotation.NonNull; 1084 public class MyClass { 1085 // Correct 1086 public void setVisible(boolean visible) {} 1087 public boolean isVisible() { return false; } 1088 1089 public void setHasTransientState(boolean hasTransientState) {} 1090 public boolean hasTransientState() { return false; } 1091 1092 public void setCanRecord(boolean canRecord) {} 1093 public boolean canRecord() { return false; } 1094 1095 public void setShouldFitWidth(boolean shouldFitWidth) {} 1096 public boolean shouldFitWidth() { return false; } 1097 1098 public void setWiFiRoamingSettingEnabled(boolean enabled) {} 1099 public boolean isWiFiRoamingSettingEnabled() { return false; } 1100 1101 public boolean hasRoute() { return false; } 1102 public @NonNull String getRoute() { return ""; } 1103 public void setRoute(@NonNull String route) {} 1104 1105 // Bad 1106 public void setIsVisibleBad(boolean visible) {} 1107 public boolean isVisibleBad() { return false; } 1108 1109 public void setTransientStateBad(boolean hasTransientState) {} 1110 public boolean hasTransientStateBad() { return false; } 1111 1112 public void setHasTransientStateAlsoBad(boolean hasTransientState) {} 1113 public boolean isHasTransientStateAlsoBad() { return false; } 1114 1115 public void setCanRecordBad(boolean canRecord) {} 1116 public boolean isCanRecordBad() { return false; } 1117 1118 public void setShouldFitWidthBad(boolean shouldFitWidth) {} 1119 public boolean isShouldFitWidthBad() { return false; } 1120 1121 public void setWiFiRoamingSettingEnabledBad(boolean enabled) {} 1122 public boolean getWiFiRoamingSettingEnabledBad() { return false; } 1123 1124 public void setEnabledBad(boolean enabled) {} 1125 public boolean getEnabledBad() { return false; } 1126 } 1127 """ 1128 ), 1129 androidxNonNullSource 1130 ) 1131 ) 1132 } 1133 1134 @RequiresCapabilities(Capability.KOTLIN) 1135 @Test Check boolean property accessor naming patterns in Kotlinnull1136 fun `Check boolean property accessor naming patterns in Kotlin`() { 1137 check( 1138 apiLint = "", // enabled 1139 expectedIssues = 1140 """ 1141 src/android/pkg/MyClass.kt:19: error: Invalid name for boolean property `visibleBad`. Should start with one of `has`, `can`, `should`, `is`. [GetterSetterNames] 1142 src/android/pkg/MyClass.kt:22: error: Invalid name for boolean property setter `setIsVisibleBad`, should be `setVisibleSetterBad`. [GetterSetterNames] 1143 src/android/pkg/MyClass.kt:25: error: Invalid name for boolean property `transientStateBad`. Should start with one of `has`, `can`, `should`, `is`. [GetterSetterNames] 1144 src/android/pkg/MyClass.kt:27: error: Invalid prefix `isHas` for boolean property `isHasTransientStateAlsoBad`. [GetterSetterNames] 1145 src/android/pkg/MyClass.kt:29: error: Invalid prefix `isCan` for boolean property `isCanRecordBad`. [GetterSetterNames] 1146 src/android/pkg/MyClass.kt:31: error: Invalid prefix `isShould` for boolean property `isShouldFitWidthBad`. [GetterSetterNames] 1147 src/android/pkg/MyClass.kt:33: error: Invalid name for boolean property `wiFiRoamingSettingEnabledBad`. Should start with one of `has`, `can`, `should`, `is`. [GetterSetterNames] 1148 src/android/pkg/MyClass.kt:35: error: Invalid name for boolean property `enabledBad`. Should start with one of `has`, `can`, `should`, `is`. [GetterSetterNames] 1149 src/android/pkg/MyClass.kt:37: error: Getter for boolean property `hasTransientStateGetterBad` is named `getHasTransientStateGetterBad` but should match the property name. Use `@get:JvmName` to rename. [GetterSetterNames] 1150 src/android/pkg/MyClass.kt:39: error: Getter for boolean property `canRecordGetterBad` is named `getCanRecordGetterBad` but should match the property name. Use `@get:JvmName` to rename. [GetterSetterNames] 1151 src/android/pkg/MyClass.kt:41: error: Getter for boolean property `shouldFitWidthGetterBad` is named `getShouldFitWidthGetterBad` but should match the property name. Use `@get:JvmName` to rename. [GetterSetterNames] 1152 """, 1153 expectedFail = DefaultLintErrorMessage, 1154 sourceFiles = 1155 arrayOf( 1156 kotlin( 1157 """ 1158 package android.pkg 1159 1160 class MyClass { 1161 // Correct 1162 var isVisible: Boolean = false 1163 1164 @get:JvmName("hasTransientState") 1165 var hasTransientState: Boolean = false 1166 1167 @get:JvmName("canRecord") 1168 var canRecord: Boolean = false 1169 1170 @get:JvmName("shouldFitWidth") 1171 var shouldFitWidth: Boolean = false 1172 1173 var isWiFiRoamingSettingEnabled: Boolean = false 1174 1175 // Bad 1176 var visibleBad: Boolean = false 1177 1178 @set:JvmName("setIsVisibleBad") 1179 var isVisibleSetterBad: Boolean = false 1180 1181 @get:JvmName("hasTransientStateBad") 1182 var transientStateBad: Boolean = false 1183 1184 var isHasTransientStateAlsoBad: Boolean = false 1185 1186 var isCanRecordBad: Boolean = false 1187 1188 var isShouldFitWidthBad: Boolean = false 1189 1190 var wiFiRoamingSettingEnabledBad: Boolean = false 1191 1192 var enabledBad: Boolean = false 1193 1194 var hasTransientStateGetterBad: Boolean = false 1195 1196 var canRecordGetterBad: Boolean = false 1197 1198 var shouldFitWidthGetterBad: Boolean = false 1199 } 1200 """ 1201 ) 1202 ) 1203 ) 1204 } 1205 Check boolean constructor parameter accessor naming patterns in Kotlinnull1206 private fun `Check boolean constructor parameter accessor naming patterns in Kotlin`( 1207 expectedIssues: String?, 1208 ) { 1209 check( 1210 apiLint = "", // enabled 1211 expectedIssues = expectedIssues, 1212 expectedFail = DefaultLintErrorMessage, 1213 sourceFiles = 1214 arrayOf( 1215 kotlin( 1216 """ 1217 package android.pkg 1218 1219 class MyClass( 1220 // Correct 1221 var isVisible: Boolean, 1222 1223 @get:JvmName("hasTransientState") 1224 var hasTransientState: Boolean, 1225 1226 @get:JvmName("canRecord") 1227 var canRecord: Boolean, 1228 1229 @get:JvmName("shouldFitWidth") 1230 var shouldFitWidth: Boolean, 1231 1232 var isWiFiRoamingSettingEnabled: Boolean, 1233 1234 // Bad 1235 var visibleBad: Boolean, 1236 1237 @set:JvmName("setIsVisibleBad") 1238 var isVisibleSetterBad: Boolean, 1239 1240 @get:JvmName("hasTransientStateBad") 1241 var transientStateBad: Boolean, 1242 1243 var isHasTransientStateAlsoBad: Boolean, 1244 1245 var isCanRecordBad: Boolean, 1246 1247 var isShouldFitWidthBad: Boolean, 1248 1249 var wiFiRoamingSettingEnabledBad: Boolean, 1250 1251 var enabledBad: Boolean, 1252 1253 var hasTransientStateGetterBad: Boolean, 1254 1255 var canRecordGetterBad: Boolean, 1256 1257 var shouldFitWidthGetterBad: Boolean 1258 ) 1259 """ 1260 ) 1261 ) 1262 ) 1263 } 1264 1265 @RequiresCapabilities(Capability.KOTLIN) 1266 @FilterByProvider("psi", "k2", action = FilterAction.EXCLUDE) 1267 @Test Check boolean constructor parameter accessor naming patterns in Kotlin -- K1null1268 fun `Check boolean constructor parameter accessor naming patterns in Kotlin -- K1`() { 1269 `Check boolean constructor parameter accessor naming patterns in Kotlin`( 1270 // missing errors for `isVisibleSetterBad`, 1271 // `hasTransientStateGetterBad`, `canRecordGetterBad`, `shouldFitWidthGetterBad` 1272 expectedIssues = 1273 """ 1274 src/android/pkg/MyClass.kt:19: error: Invalid name for boolean property `visibleBad`. Should start with one of `has`, `can`, `should`, `is`. [GetterSetterNames] 1275 src/android/pkg/MyClass.kt:25: error: Invalid name for boolean property `transientStateBad`. Should start with one of `has`, `can`, `should`, `is`. [GetterSetterNames] 1276 src/android/pkg/MyClass.kt:27: error: Invalid prefix `isHas` for boolean property `isHasTransientStateAlsoBad`. [GetterSetterNames] 1277 src/android/pkg/MyClass.kt:29: error: Invalid prefix `isCan` for boolean property `isCanRecordBad`. [GetterSetterNames] 1278 src/android/pkg/MyClass.kt:31: error: Invalid prefix `isShould` for boolean property `isShouldFitWidthBad`. [GetterSetterNames] 1279 src/android/pkg/MyClass.kt:33: error: Invalid name for boolean property `wiFiRoamingSettingEnabledBad`. Should start with one of `has`, `can`, `should`, `is`. [GetterSetterNames] 1280 src/android/pkg/MyClass.kt:35: error: Invalid name for boolean property `enabledBad`. Should start with one of `has`, `can`, `should`, `is`. [GetterSetterNames] 1281 """, 1282 ) 1283 } 1284 1285 @RequiresCapabilities(Capability.KOTLIN) 1286 @FilterByProvider("psi", "k1", action = FilterAction.EXCLUDE) 1287 @Test Check boolean constructor parameter accessor naming patterns in Kotlin -- K2null1288 fun `Check boolean constructor parameter accessor naming patterns in Kotlin -- K2`() { 1289 `Check boolean constructor parameter accessor naming patterns in Kotlin`( 1290 expectedIssues = 1291 """ 1292 src/android/pkg/MyClass.kt:19: error: Invalid name for boolean property `visibleBad`. Should start with one of `has`, `can`, `should`, `is`. [GetterSetterNames] 1293 src/android/pkg/MyClass.kt:22: error: Invalid name for boolean property setter `setIsVisibleBad`, should be `setVisibleSetterBad`. [GetterSetterNames] 1294 src/android/pkg/MyClass.kt:25: error: Invalid name for boolean property `transientStateBad`. Should start with one of `has`, `can`, `should`, `is`. [GetterSetterNames] 1295 src/android/pkg/MyClass.kt:27: error: Invalid prefix `isHas` for boolean property `isHasTransientStateAlsoBad`. [GetterSetterNames] 1296 src/android/pkg/MyClass.kt:29: error: Invalid prefix `isCan` for boolean property `isCanRecordBad`. [GetterSetterNames] 1297 src/android/pkg/MyClass.kt:31: error: Invalid prefix `isShould` for boolean property `isShouldFitWidthBad`. [GetterSetterNames] 1298 src/android/pkg/MyClass.kt:33: error: Invalid name for boolean property `wiFiRoamingSettingEnabledBad`. Should start with one of `has`, `can`, `should`, `is`. [GetterSetterNames] 1299 src/android/pkg/MyClass.kt:35: error: Invalid name for boolean property `enabledBad`. Should start with one of `has`, `can`, `should`, `is`. [GetterSetterNames] 1300 src/android/pkg/MyClass.kt:37: error: Getter for boolean property `hasTransientStateGetterBad` is named `getHasTransientStateGetterBad` but should match the property name. Use `@get:JvmName` to rename. [GetterSetterNames] 1301 src/android/pkg/MyClass.kt:39: error: Getter for boolean property `canRecordGetterBad` is named `getCanRecordGetterBad` but should match the property name. Use `@get:JvmName` to rename. [GetterSetterNames] 1302 src/android/pkg/MyClass.kt:41: error: Getter for boolean property `shouldFitWidthGetterBad` is named `getShouldFitWidthGetterBad` but should match the property name. Use `@get:JvmName` to rename. [GetterSetterNames] 1303 """, 1304 ) 1305 } 1306 1307 @Test Check banned collectionsnull1308 fun `Check banned collections`() { 1309 check( 1310 apiLint = "", // enabled 1311 expectedIssues = 1312 """ 1313 src/android/pkg/MyClass.java:6: error: Parameter type is concrete collection (`java.util.HashMap`); must be higher-level interface [ConcreteCollection] 1314 src/android/pkg/MyClass.java:10: error: Parameter type is concrete collection (`java.util.LinkedList`); must be higher-level interface [ConcreteCollection] 1315 src/android/pkg/MyClass.java:10: error: Return type is concrete collection (`java.util.Vector`); must be higher-level interface [ConcreteCollection] 1316 """, 1317 expectedFail = DefaultLintErrorMessage, 1318 sourceFiles = 1319 arrayOf( 1320 java( 1321 """ 1322 package android.pkg; 1323 1324 import androidx.annotation.NonNull; 1325 1326 public class MyClass { 1327 public MyClass(@NonNull java.util.HashMap<String,String> map1, 1328 @NonNull java.util.Map<String,String> map2) { 1329 } 1330 @NonNull 1331 public java.util.Vector<String> getList(@NonNull java.util.LinkedList<String> list) { 1332 throw new RuntimeException(); 1333 } 1334 } 1335 """ 1336 ), 1337 androidxNonNullSource 1338 ) 1339 ) 1340 } 1341 1342 @Test Check non-overlapping flagsnull1343 fun `Check non-overlapping flags`() { 1344 check( 1345 apiLint = "", // enabled 1346 expectedIssues = 1347 """ 1348 src/android/accounts/OverlappingFlags.java:19: warning: Found overlapping flag constant values: `TEST1_FLAG_SECOND` with value 3 (0x3) and overlapping flag value 1 (0x1) from `TEST1_FLAG_FIRST` [OverlappingConstants] 1349 """, 1350 sourceFiles = 1351 arrayOf( 1352 java( 1353 """ 1354 package android.accounts; 1355 1356 public class OverlappingFlags { 1357 private OverlappingFlags() { } 1358 public static final int DRAG_FLAG_GLOBAL_PREFIX_URI_PERMISSION = 128; // 0x80 1359 public static final int DRAG_FLAG_GLOBAL_URI_READ = 1; // 0x1 1360 public static final int DRAG_FLAG_GLOBAL_URI_WRITE = 2; // 0x2 1361 public static final int DRAG_FLAG_OPAQUE = 512; // 0x200 1362 public static final int SYSTEM_UI_FLAG_FULLSCREEN = 4; // 0x4 1363 public static final int SYSTEM_UI_FLAG_HIDE_NAVIGATION = 2; // 0x2 1364 public static final int SYSTEM_UI_FLAG_IMMERSIVE = 2048; // 0x800 1365 public static final int SYSTEM_UI_FLAG_IMMERSIVE_STICKY = 4096; // 0x1000 1366 public static final int SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN = 1024; // 0x400 1367 public static final int SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION = 512; // 0x200 1368 public static final int SYSTEM_UI_FLAG_LAYOUT_STABLE = 256; // 0x100 1369 public static final int SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR = 16; // 0x10 1370 1371 public static final int TEST1_FLAG_FIRST = 1; 1372 public static final int TEST1_FLAG_SECOND = 3; 1373 public static final int TEST2_FLAG_FIRST = 5; 1374 } 1375 """ 1376 ) 1377 ) 1378 ) 1379 } 1380 1381 @Test Check no mentions of Google in APIsnull1382 fun `Check no mentions of Google in APIs`() { 1383 check( 1384 apiLint = "", // enabled 1385 expectedIssues = 1386 """ 1387 src/android/pkg/MyClass.java:4: error: Must never reference Google (`MyGoogleService`) [MentionsGoogle] 1388 src/android/pkg/MyClass.java:5: error: Must never reference Google (`callGoogle`) [MentionsGoogle] 1389 """, 1390 expectedFail = DefaultLintErrorMessage, 1391 sourceFiles = 1392 arrayOf( 1393 java( 1394 """ 1395 package android.pkg; 1396 1397 public class MyClass { 1398 public static class MyGoogleService { 1399 public void callGoogle() { } 1400 } 1401 } 1402 """ 1403 ) 1404 ) 1405 ) 1406 } 1407 1408 @Test Check no usages of heavy BitSetnull1409 fun `Check no usages of heavy BitSet`() { 1410 check( 1411 apiLint = "", // enabled 1412 expectedIssues = 1413 """ 1414 src/android/pkg/MyClass.java:7: error: Type must not be heavy BitSet (field android.pkg.MyClass.bitset) [HeavyBitSet] 1415 src/android/pkg/MyClass.java:9: error: Type must not be heavy BitSet (method android.pkg.MyClass.reverse(java.util.BitSet)) [HeavyBitSet] 1416 src/android/pkg/MyClass.java:9: error: Type must not be heavy BitSet (parameter bitset in android.pkg.MyClass.reverse(java.util.BitSet bitset)) [HeavyBitSet] 1417 """, 1418 expectedFail = DefaultLintErrorMessage, 1419 sourceFiles = 1420 arrayOf( 1421 java( 1422 """ 1423 package android.pkg; 1424 import androidx.annotation.Nullable; 1425 import java.util.BitSet; 1426 1427 public class MyClass { 1428 @Nullable 1429 public final BitSet bitset; 1430 @Nullable 1431 public BitSet reverse(@Nullable BitSet bitset) { return null; } 1432 } 1433 """ 1434 ), 1435 androidxNullableSource 1436 ) 1437 ) 1438 } 1439 1440 @Test Check Manager related issuesnull1441 fun `Check Manager related issues`() { 1442 check( 1443 apiLint = "", // enabled 1444 expectedIssues = 1445 """ 1446 src/android/pkg/MyFirstManager.java:6: error: Managers must always be obtained from Context; no direct constructors [ManagerConstructor] 1447 src/android/pkg/MyFirstManager.java:9: error: Managers must always be obtained from Context (`get`) [ManagerLookup] 1448 """, 1449 expectedFail = DefaultLintErrorMessage, 1450 sourceFiles = 1451 arrayOf( 1452 java( 1453 """ 1454 package android.pkg; 1455 1456 import androidx.annotation.Nullable; 1457 1458 public class MyFirstManager { 1459 public MyFirstManager() { 1460 } 1461 @Nullable 1462 public MyFirstManager get() { return null; } 1463 @Nullable 1464 public MySecondManager ok() { return null; } 1465 } 1466 """ 1467 ), 1468 java( 1469 """ 1470 package android.pkg; 1471 1472 public class MySecondManager { 1473 private MySecondManager() { 1474 } 1475 } 1476 """ 1477 ), 1478 androidxNullableSource 1479 ) 1480 ) 1481 } 1482 1483 @Test Check static utilitiesnull1484 fun `Check static utilities`() { 1485 check( 1486 apiLint = "", // enabled 1487 expectedIssues = 1488 """ 1489 src/android/pkg/MyUtils1.java:3: error: Fully-static utility classes must not have constructor [StaticUtils] 1490 src/android/pkg/MyUtils2.java:3: error: Fully-static utility classes must not have constructor [StaticUtils] 1491 """, 1492 expectedFail = DefaultLintErrorMessage, 1493 sourceFiles = 1494 arrayOf( 1495 java( 1496 """ 1497 package android.pkg; 1498 1499 public class MyUtils1 { 1500 // implicit constructor 1501 public static void foo() { } 1502 } 1503 """ 1504 ), 1505 java( 1506 """ 1507 package android.pkg; 1508 1509 public class MyUtils2 { 1510 public MyUtils2() { } 1511 public static void foo() { } 1512 } 1513 """ 1514 ), 1515 java( 1516 """ 1517 package android.pkg; 1518 1519 public class MyUtils3 { 1520 private MyUtils3() { } 1521 public static void foo() { } 1522 } 1523 """ 1524 ), 1525 java( 1526 """ 1527 package android.pkg; 1528 1529 public class MyUtils4 { 1530 // OK: instance method 1531 public void foo() { } 1532 } 1533 """ 1534 ), 1535 java( 1536 """ 1537 package android.pkg; 1538 1539 public class MyUtils5 { 1540 // OK: instance field 1541 public final int foo = 42; 1542 public static void foo() { } 1543 } 1544 """ 1545 ) 1546 ) 1547 ) 1548 } 1549 1550 @RequiresCapabilities(Capability.KOTLIN) 1551 @Test Check context firstnull1552 fun `Check context first`() { 1553 check( 1554 apiLint = "", // enabled 1555 expectedIssues = 1556 """ 1557 src/android/pkg/MyClass.java:11: error: Context is distinct, so it must be the first argument (method `wrong`) [ContextFirst] 1558 src/android/pkg/MyClass.java:12: error: ContentResolver is distinct, so it must be the first argument (method `wrong`) [ContextFirst] 1559 src/android/pkg/test.kt:5: error: Context is distinct, so it must be the first argument (method `badCall`) [ContextFirst] 1560 """, 1561 expectedFail = DefaultLintErrorMessage, 1562 sourceFiles = 1563 arrayOf( 1564 java( 1565 """ 1566 package android.pkg; 1567 import android.content.Context; 1568 import android.content.ContentResolver; 1569 import androidx.annotation.Nullable; 1570 1571 public class MyClass { 1572 public MyClass(@Nullable Context context1, @Nullable Context context2) { 1573 } 1574 public void ok(@Nullable ContentResolver resolver, int i) { } 1575 public void ok(@Nullable Context context, int i) { } 1576 public void wrong(int i, @Nullable Context context) { } 1577 public void wrong(int i, @Nullable ContentResolver resolver) { } 1578 } 1579 """ 1580 ), 1581 kotlin( 1582 """ 1583 package android.pkg 1584 import android.content.Context 1585 fun String.okCall(context: Context) {} 1586 fun String.okCall(context: Context, value: Int) {} 1587 fun String.badCall(value: Int, context: Context) {} 1588 """ 1589 ), 1590 androidxNullableSource 1591 ) 1592 ) 1593 } 1594 1595 @Test Check listener lastnull1596 fun `Check listener last`() { 1597 check( 1598 extraArguments = arrayOf(ARG_API_LINT, ARG_HIDE, "ExecutorRegistration"), 1599 expectedIssues = 1600 """ 1601 src/android/pkg/MyClass.java:7: warning: Listeners should always be at end of argument list (method `MyClass`) [ListenerLast] 1602 src/android/pkg/MyClass.java:10: warning: Listeners should always be at end of argument list (method `wrong`) [ListenerLast] 1603 """, 1604 sourceFiles = 1605 arrayOf( 1606 java( 1607 """ 1608 package android.pkg; 1609 import android.pkg.MyCallback; 1610 import android.content.Context; 1611 import androidx.annotation.Nullable; 1612 1613 public class MyClass { 1614 public MyClass(@Nullable MyCallback listener, int i) { 1615 } 1616 public void ok(@Nullable Context context, int i, @Nullable MyCallback listener) { } 1617 public void wrong(@Nullable MyCallback listener, int i) { } 1618 } 1619 """ 1620 ), 1621 java( 1622 """ 1623 package android.pkg; 1624 1625 @SuppressWarnings("WeakerAccess") 1626 public abstract class MyCallback { 1627 } 1628 """ 1629 ), 1630 androidxNullableSource 1631 ) 1632 ) 1633 } 1634 1635 @RequiresCapabilities(Capability.KOTLIN) 1636 @Test Check listener last for suspend functionsnull1637 fun `Check listener last for suspend functions`() { 1638 check( 1639 extraArguments = arrayOf(ARG_API_LINT, ARG_HIDE, "ExecutorRegistration"), 1640 expectedIssues = 1641 """ 1642 src/android/pkg/MyClass.kt:6: warning: Listeners should always be at end of argument list (method `wrong`) [ListenerLast] 1643 """, 1644 sourceFiles = 1645 arrayOf( 1646 java( 1647 """ 1648 package android.pkg; 1649 1650 @SuppressWarnings("WeakerAccess") 1651 public abstract class MyCallback { 1652 } 1653 """ 1654 ), 1655 kotlin( 1656 """ 1657 package android.pkg 1658 import android.pkg.MyCallback 1659 1660 class MyClass { 1661 suspend fun ok(i: Int, callback: MyCallback) {} 1662 suspend fun wrong(callback: MyCallback, i: Int) {} 1663 } 1664 """ 1665 ) 1666 ) 1667 ) 1668 } 1669 1670 @Test Check overloaded argumentsnull1671 fun `Check overloaded arguments`() { 1672 // TODO: This check is not yet hooked up 1673 check( 1674 apiLint = "", // enabled 1675 expectedIssues = """ 1676 """, 1677 sourceFiles = 1678 arrayOf( 1679 java( 1680 """ 1681 package android.pkg; 1682 1683 public class MyClass { 1684 private MyClass() { 1685 } 1686 1687 public void name1() { } 1688 public void name1(int i) { } 1689 public void name1(int i, int j) { } 1690 public void name1(int i, int j, int k) { } 1691 public void name1(int i, int j, int k, float f) { } 1692 1693 public void name2(int i) { } 1694 public void name2(int i, int j) { } 1695 public void name2(int i, float j, float k) { } 1696 public void name2(int i, int j, int k, float f) { } 1697 public void name2(int i, float f, int j) { } 1698 1699 public void name3() { } 1700 public void name3(int i) { } 1701 public void name3(int i, int j) { } 1702 public void name3(int i, float j, int k) { } 1703 1704 public void name4(int i, int j, float f, long z) { } 1705 public void name4(double d, int i, int j, float f) { } 1706 } 1707 """ 1708 ) 1709 ) 1710 ) 1711 } 1712 1713 @Test Check Callback Handlersnull1714 fun `Check Callback Handlers`() { 1715 check( 1716 apiLint = "", // enabled 1717 expectedIssues = 1718 """ 1719 src/android/pkg/MyClass.java:6: warning: Registration methods should have overload that accepts delivery Executor: `MyClass` [ExecutorRegistration] 1720 src/android/pkg/MyClass.java:16: warning: Registration methods should have overload that accepts delivery Executor: `registerWrongCallback` [ExecutorRegistration] 1721 """, 1722 sourceFiles = 1723 arrayOf( 1724 java( 1725 """ 1726 package android.pkg; 1727 1728 import androidx.annotation.Nullable; 1729 1730 public class MyClass { 1731 public MyClass(@Nullable MyCallback callback) { 1732 } 1733 1734 public void registerStreamEventCallback(@Nullable MyCallback callback); 1735 public void unregisterStreamEventCallback(@Nullable MyCallback callback); 1736 public void registerStreamEventCallback(@Nullable java.util.concurrent.Executor executor, 1737 @Nullable MyCallback callback); 1738 public void unregisterStreamEventCallback(@Nullable java.util.concurrent.Executor executor, 1739 @Nullable MyCallback callback); 1740 1741 public void registerWrongCallback(@Nullable MyCallback callback); 1742 public void unregisterWrongCallback(@Nullable MyCallback callback); 1743 } 1744 """ 1745 ), 1746 java( 1747 """ 1748 package android.graphics; 1749 1750 import android.pkg.MyCallback; 1751 import androidx.annotation.Nullable; 1752 1753 public class MyUiClass { 1754 public void registerWrongCallback(@Nullable MyCallback callback); 1755 public void unregisterWrongCallback(@Nullable MyCallback callback); 1756 } 1757 """ 1758 ), 1759 java( 1760 """ 1761 package android.pkg; 1762 1763 @SuppressWarnings("WeakerAccess") 1764 public abstract class MyCallback { 1765 } 1766 """ 1767 ), 1768 androidxNullableSource 1769 ) 1770 ) 1771 } 1772 1773 @Test Check resource namesnull1774 fun `Check resource names`() { 1775 check( 1776 apiLint = "", // enabled 1777 expectedIssues = 1778 """ 1779 src/android/R.java:11: error: Expected resource name in `android.R.id` to be in the `fooBarBaz` style, was `wrong_style` [ResourceValueFieldName] 1780 src/android/R.java:17: error: Expected config name to be in the `config_fooBarBaz` style, was `config_wrong_config_style` [ConfigFieldName] 1781 src/android/R.java:20: error: Expected resource name in `android.R.layout` to be in the `foo_bar_baz` style, was `wrongNameStyle` [ResourceFieldName] 1782 src/android/R.java:31: error: Expected resource name in `android.R.style` to be in the `FooBar_Baz` style, was `wrong_style_name` [ResourceStyleFieldName] 1783 """, 1784 expectedFail = DefaultLintErrorMessage, 1785 sourceFiles = 1786 arrayOf( 1787 java( 1788 """ 1789 package android; 1790 1791 public final class R { 1792 public static final class id { 1793 public static final int text = 7000; 1794 public static final int config_fooBar = 7001; 1795 public static final int layout_fooBar = 7002; 1796 public static final int state_foo = 7003; 1797 public static final int foo = 7004; 1798 public static final int fooBar = 7005; 1799 public static final int wrong_style = 7006; 1800 } 1801 public static final class layout { 1802 public static final int text = 7000; 1803 public static final int config_fooBar = 7001; 1804 public static final int config_foo = 7002; 1805 public static final int config_wrong_config_style = 7003; 1806 1807 public static final int ok_name_style = 7004; 1808 public static final int wrongNameStyle = 7005; 1809 } 1810 public static final class style { 1811 public static final int TextAppearance_Compat_Notification = 0x7f0c00ec; 1812 public static final int TextAppearance_Compat_Notification_Info = 0x7f0c00ed; 1813 public static final int TextAppearance_Compat_Notification_Line2 = 0x7f0c00ef; 1814 public static final int TextAppearance_Compat_Notification_Time = 0x7f0c00f2; 1815 public static final int TextAppearance_Compat_Notification_Title = 0x7f0c00f4; 1816 public static final int Widget_Compat_NotificationActionContainer = 0x7f0c015d; 1817 public static final int Widget_Compat_NotificationActionText = 0x7f0c015e; 1818 1819 public static final int wrong_style_name = 7000; 1820 } 1821 } 1822 """ 1823 ) 1824 ) 1825 ) 1826 } 1827 1828 @Test Check filesnull1829 fun `Check files`() { 1830 check( 1831 apiLint = "", // enabled 1832 expectedIssues = 1833 """ 1834 src/android/pkg/CheckFiles.java:9: warning: Methods accepting `File` should also accept `FileDescriptor` or streams: constructor android.pkg.CheckFiles(android.content.Context,java.io.File) [StreamFiles] 1835 src/android/pkg/CheckFiles.java:13: warning: Methods accepting `File` should also accept `FileDescriptor` or streams: method android.pkg.CheckFiles.error(int,java.io.File) [StreamFiles] 1836 """, 1837 sourceFiles = 1838 arrayOf( 1839 java( 1840 """ 1841 package android.pkg; 1842 import android.content.Context; 1843 import android.content.ContentResolver; 1844 import androidx.annotation.Nullable; 1845 import java.io.File; 1846 import java.io.InputStream; 1847 1848 public class CheckFiles { 1849 public CheckFiles(@Nullable Context context, @Nullable File file) { 1850 } 1851 public void ok(int i, @Nullable File file) { } 1852 public void ok(int i, @Nullable InputStream stream) { } 1853 public void error(int i, @Nullable File file) { } 1854 } 1855 """ 1856 ), 1857 androidxNullableSource 1858 ) 1859 ) 1860 } 1861 1862 @Test Check parcelable listsnull1863 fun `Check parcelable lists`() { 1864 check( 1865 apiLint = "", // enabled 1866 expectedIssues = 1867 """ 1868 src/android/pkg/CheckFiles.java:9: warning: Methods accepting `File` should also accept `FileDescriptor` or streams: constructor android.pkg.CheckFiles(android.content.Context,java.io.File) [StreamFiles] 1869 src/android/pkg/CheckFiles.java:13: warning: Methods accepting `File` should also accept `FileDescriptor` or streams: method android.pkg.CheckFiles.error(int,java.io.File) [StreamFiles] 1870 """, 1871 sourceFiles = 1872 arrayOf( 1873 java( 1874 """ 1875 package android.pkg; 1876 import android.content.Context; 1877 import android.content.ContentResolver; 1878 import androidx.annotation.Nullable; 1879 import java.io.File; 1880 import java.io.InputStream; 1881 1882 public class CheckFiles { 1883 public CheckFiles(@Nullable Context context, @Nullable File file) { 1884 } 1885 public void ok(int i, @Nullable File file) { } 1886 public void ok(int i, @Nullable InputStream stream) { } 1887 public void error(int i, @Nullable File file) { } 1888 } 1889 """ 1890 ), 1891 androidxNullableSource 1892 ) 1893 ) 1894 } 1895 1896 @Test Check abstract innernull1897 fun `Check abstract inner`() { 1898 check( 1899 apiLint = "", // enabled 1900 expectedIssues = 1901 """ 1902 src/android/pkg/MyManager.java:9: warning: Abstract inner classes should be static to improve testability: class android.pkg.MyManager.MyInnerManager [AbstractInner] 1903 """, 1904 sourceFiles = 1905 arrayOf( 1906 java( 1907 """ 1908 package android.pkg; 1909 import android.content.Context; 1910 import android.content.ContentResolver; 1911 import java.io.File; 1912 import java.io.InputStream; 1913 1914 public abstract class MyManager { 1915 private MyManager() {} 1916 public abstract class MyInnerManager { 1917 private MyInnerManager() {} 1918 } 1919 public abstract static class MyOkManager { 1920 private MyOkManager() {} 1921 } 1922 } 1923 """ 1924 ) 1925 ) 1926 ) 1927 } 1928 1929 @Test Check for extending errorsnull1930 fun `Check for extending errors`() { 1931 check( 1932 apiLint = "", // enabled 1933 expectedIssues = 1934 """ 1935 src/android/pkg/MyClass.java:3: error: Trouble must be reported through an `Exception`, not an `Error` (`MyClass` extends `Error`) [ExtendsError] 1936 src/android/pkg/MySomething.java:3: error: Exceptions must be named `FooException`, was `MySomething` [ExceptionName] 1937 """, 1938 expectedFail = DefaultLintErrorMessage, 1939 sourceFiles = 1940 arrayOf( 1941 java( 1942 """ 1943 package android.pkg; 1944 1945 public class MyClass extends Error { 1946 } 1947 """ 1948 ), 1949 java( 1950 """ 1951 package android.pkg; 1952 1953 public class MySomething extends RuntimeException { 1954 } 1955 """ 1956 ) 1957 ) 1958 ) 1959 } 1960 1961 @Test Check units and method namesnull1962 fun `Check units and method names`() { 1963 check( 1964 extraArguments = arrayOf(ARG_API_LINT, ARG_HIDE, "NoByteOrShort"), 1965 expectedIssues = 1966 """ 1967 src/android/pkg/UnitNameTest.java:7: error: Expected method name units to be `Hours`, was `Hr` in `getErrorHr` [MethodNameUnits] 1968 src/android/pkg/UnitNameTest.java:8: error: Expected method name units to be `Nanos`, was `Ns` in `getErrorNs` [MethodNameUnits] 1969 src/android/pkg/UnitNameTest.java:9: error: Expected method name units to be `Bytes`, was `Byte` in `getErrorByte` [MethodNameUnits] 1970 src/android/pkg/UnitNameTest.java:14: error: Fractions must use floats, was `int` in `getErrorFraction` [FractionFloat] 1971 src/android/pkg/UnitNameTest.java:15: error: Fractions must use floats, was `int` in `setErrorFraction` [FractionFloat] 1972 src/android/pkg/UnitNameTest.java:19: error: Percentage must use ints, was `float` in `getErrorPercentage` [PercentageInt] 1973 src/android/pkg/UnitNameTest.java:20: error: Percentage must use ints, was `float` in `setErrorPercentage` [PercentageInt] 1974 src/android/pkg/UnitNameTest.java:22: error: Expected method name units to be `Bytes`, was `Byte` in `readSingleByte` [MethodNameUnits] 1975 """, 1976 expectedFail = DefaultLintErrorMessage, 1977 sourceFiles = 1978 arrayOf( 1979 androidxNonNullSource, 1980 java( 1981 """ 1982 package android.pkg; 1983 1984 import androidx.annotation.NonNull; 1985 1986 public class UnitNameTest { 1987 public int okay() { return 0; } 1988 public int getErrorHr() { return 0; } 1989 public int getErrorNs() { return 0; } 1990 public short getErrorByte() { return (short)0; } 1991 1992 public float getOkFraction() { return 0f; } 1993 public void setOkFraction(float f) { } 1994 public void setOkFraction(int n, int d) { } 1995 public int getErrorFraction() { return 0; } 1996 public void setErrorFraction(int i) { } 1997 1998 public int getOkPercentage() { return 0f; } 1999 public void setOkPercentage(int i) { } 2000 public float getErrorPercentage() { return 0f; } 2001 public void setErrorPercentage(float f) { } 2002 2003 public int readSingleByte() { return 0; } 2004 2005 public static final class UnitNameTestBuilder { 2006 public UnitNameTestBuilder() {} 2007 2008 @NonNull 2009 public UnitNameTest build() { return null; } 2010 2011 @NonNull 2012 public UnitNameTestBuilder setOkFraction(float f) { return this; } 2013 2014 @NonNull 2015 public UnitNameTestBuilder setOkPercentage(int i) { return this; } 2016 } 2017 2018 @NamedDataSpace 2019 public int getOkDataSpace() { return 0; } 2020 2021 /** @hide */ 2022 @android.annotation.IntDef 2023 public @interface NamedDataSpace {} 2024 } 2025 """ 2026 ) 2027 ) 2028 ) 2029 } 2030 2031 @Test Check closeablenull2032 fun `Check closeable`() { 2033 check( 2034 apiLint = "", // enabled 2035 expectedIssues = 2036 """ 2037 src/android/pkg/MyErrorClass1.java:3: warning: Classes that release resources (close()) should implement AutoCloseable and CloseGuard: class android.pkg.MyErrorClass1 [NotCloseable] 2038 src/android/pkg/MyErrorClass2.java:3: warning: Classes that release resources (finalize(), shutdown()) should implement AutoCloseable and CloseGuard: class android.pkg.MyErrorClass2 [NotCloseable] 2039 """, 2040 sourceFiles = 2041 arrayOf( 2042 java( 2043 """ 2044 package android.pkg; 2045 2046 public abstract class MyOkClass1 implements java.io.Closeable { 2047 public void close() {} 2048 } 2049 """ 2050 ), 2051 java( 2052 """ 2053 package android.pkg; 2054 2055 // Ok: indirectly implementing AutoCloseable 2056 public abstract class MyOkClass2 implements MyInterface { 2057 public void close() {} 2058 } 2059 """ 2060 ), 2061 java( 2062 """ 2063 package android.pkg; 2064 2065 public class MyInterface extends AutoCloseable { 2066 public void close() {} 2067 } 2068 """ 2069 ), 2070 java( 2071 """ 2072 package android.pkg; 2073 2074 public abstract class MyErrorClass1 { 2075 public void close() {} 2076 } 2077 """ 2078 ), 2079 java( 2080 """ 2081 package android.pkg; 2082 2083 public abstract class MyErrorClass2 { 2084 public void finalize() {} 2085 public void shutdown() {} 2086 } 2087 """ 2088 ) 2089 ) 2090 ) 2091 } 2092 2093 @Test Check closeable for minSdkVersion 19null2094 fun `Check closeable for minSdkVersion 19`() { 2095 check( 2096 apiLint = "", // enabled 2097 expectedIssues = 2098 """ 2099 src/android/pkg/MyErrorClass1.java:3: warning: Classes that release resources (close()) should implement AutoCloseable and CloseGuard: class android.pkg.MyErrorClass1 [NotCloseable] 2100 src/android/pkg/MyErrorClass2.java:3: warning: Classes that release resources (finalize(), shutdown()) should implement AutoCloseable and CloseGuard: class android.pkg.MyErrorClass2 [NotCloseable] 2101 """, 2102 manifest = 2103 """<?xml version="1.0" encoding="UTF-8"?> 2104 <manifest xmlns:android="http://schemas.android.com/apk/res/android"> 2105 <uses-sdk android:minSdkVersion="19" /> 2106 </manifest> 2107 """ 2108 .trimIndent(), 2109 sourceFiles = 2110 arrayOf( 2111 java( 2112 """ 2113 package android.pkg; 2114 2115 public abstract class MyErrorClass1 { 2116 public void close() {} 2117 } 2118 """ 2119 ), 2120 java( 2121 """ 2122 package android.pkg; 2123 2124 public abstract class MyErrorClass2 { 2125 public void finalize() {} 2126 public void shutdown() {} 2127 } 2128 """ 2129 ) 2130 ) 2131 ) 2132 } 2133 2134 @Test Do not check closeable for minSdkVersion less than 19null2135 fun `Do not check closeable for minSdkVersion less than 19`() { 2136 check( 2137 apiLint = "", // enabled 2138 expectedIssues = "", 2139 manifest = 2140 """<?xml version="1.0" encoding="UTF-8"?> 2141 <manifest xmlns:android="http://schemas.android.com/apk/res/android"> 2142 <uses-sdk android:minSdkVersion="18" /> 2143 </manifest> 2144 """ 2145 .trimIndent(), 2146 sourceFiles = 2147 arrayOf( 2148 java( 2149 """ 2150 package android.pkg; 2151 2152 public abstract class MyErrorClass1 { 2153 public void close() {} 2154 } 2155 """ 2156 ), 2157 java( 2158 """ 2159 package android.pkg; 2160 2161 public abstract class MyErrorClass2 { 2162 public void finalize() {} 2163 public void shutdown() {} 2164 } 2165 """ 2166 ) 2167 ) 2168 ) 2169 } 2170 2171 @Test Check ICU types for minSdkVersion 24null2172 fun `Check ICU types for minSdkVersion 24`() { 2173 check( 2174 apiLint = "", // enabled 2175 expectedIssues = 2176 """ 2177 src/android/pkg/MyErrorClass1.java:8: warning: Type `java.text.NumberFormat` should be replaced with richer ICU type `android.icu.text.NumberFormat` [UseIcu] 2178 """, 2179 manifest = 2180 """<?xml version="1.0" encoding="UTF-8"?> 2181 <manifest xmlns:android="http://schemas.android.com/apk/res/android"> 2182 <uses-sdk android:minSdkVersion="24" /> 2183 </manifest> 2184 """ 2185 .trimIndent(), 2186 sourceFiles = 2187 arrayOf( 2188 java( 2189 """ 2190 package android.pkg; 2191 2192 import android.annotation.NonNull; 2193 import java.text.NumberFormat; 2194 2195 public abstract class MyErrorClass1 { 2196 @NonNull 2197 public NumberFormat getDefaultNumberFormat() { 2198 return NumberFormat.getInstance(); 2199 } 2200 } 2201 """ 2202 ), 2203 nonNullSource 2204 ) 2205 ) 2206 } 2207 2208 @Test Do not check ICU types for minSdkVersion less than 24null2209 fun `Do not check ICU types for minSdkVersion less than 24`() { 2210 check( 2211 apiLint = "", // enabled 2212 expectedIssues = "", 2213 manifest = 2214 """<?xml version="1.0" encoding="UTF-8"?> 2215 <manifest xmlns:android="http://schemas.android.com/apk/res/android"> 2216 <uses-sdk android:minSdkVersion="23" /> 2217 </manifest> 2218 """ 2219 .trimIndent(), 2220 sourceFiles = 2221 arrayOf( 2222 java( 2223 """ 2224 package android.pkg; 2225 2226 import android.annotation.NonNull; 2227 import java.text.NumberFormat; 2228 2229 public abstract class MyErrorClass1 { 2230 @NonNull 2231 public NumberFormat getDefaultNumberFormat() { 2232 return NumberFormat.getInstance(); 2233 } 2234 } 2235 """ 2236 ), 2237 nonNullSource 2238 ) 2239 ) 2240 } 2241 2242 @Test Check Kotlin keywordsnull2243 fun `Check Kotlin keywords`() { 2244 check( 2245 apiLint = "", // enabled 2246 expectedIssues = 2247 """ 2248 src/android/pkg/KotlinKeywordTest.java:7: error: Avoid method names that are Kotlin hard keywords ("fun"); see https://android.github.io/kotlin-guides/interop.html#no-hard-keywords [KotlinKeyword] 2249 src/android/pkg/KotlinKeywordTest.java:8: error: Avoid field names that are Kotlin hard keywords ("as"); see https://android.github.io/kotlin-guides/interop.html#no-hard-keywords [KotlinKeyword] 2250 """, 2251 expectedFail = DefaultLintErrorMessage, 2252 sourceFiles = 2253 arrayOf( 2254 java( 2255 """ 2256 package android.pkg; 2257 2258 public class KotlinKeywordTest { 2259 public void okay(); 2260 public final int okay = 0; 2261 2262 public void fun() {} // error 2263 public final int as = 0; // error 2264 } 2265 """ 2266 ), 2267 androidxNonNullSource, 2268 androidxNullableSource 2269 ) 2270 ) 2271 } 2272 2273 @RequiresCapabilities(Capability.KOTLIN) 2274 @Test Return collections instead of arraysnull2275 fun `Return collections instead of arrays`() { 2276 check( 2277 extraArguments = arrayOf(ARG_API_LINT, ARG_HIDE, "AutoBoxing"), 2278 expectedIssues = 2279 """ 2280 src/android/pkg/ArrayTest.java:13: warning: Method should return Collection<Object> (or subclass) instead of raw array; was `java.lang.Object[]` [ArrayReturn] 2281 src/android/pkg/ArrayTest.java:14: warning: Method parameter should be Collection<Number> (or subclass) instead of raw array; was `java.lang.Number[]` [ArrayReturn] 2282 """, 2283 sourceFiles = 2284 arrayOf( 2285 java( 2286 """ 2287 package android.pkg; 2288 2289 import androidx.annotation.NonNull; 2290 import androidx.annotation.Nullable; 2291 2292 public class ArrayTest { 2293 @NonNull 2294 public int[] ok1() { throw new RuntimeException(); } 2295 @NonNull 2296 public String[] ok2() { throw new RuntimeException(); } 2297 public void ok3(@Nullable int[] i) { } 2298 @NonNull 2299 public Object[] error1() { throw new RuntimeException(); } 2300 public void error2(@NonNull Number[] i) { } 2301 public void ok(@NonNull Number... args) { } 2302 } 2303 """ 2304 ), 2305 kotlin( 2306 """ 2307 package test.pkg 2308 fun okMethod(vararg values: Integer, foo: Float, bar: Float) 2309 """ 2310 ), 2311 androidxNonNullSource, 2312 androidxNullableSource, 2313 ) 2314 ) 2315 } 2316 2317 @Test Check user handle namesnull2318 fun `Check user handle names`() { 2319 check( 2320 apiLint = "", // enabled 2321 expectedIssues = 2322 """ 2323 src/android/pkg/MyManager.java:7: warning: When a method overload is needed to target a specific UserHandle, callers should be directed to use Context.createPackageContextAsUser() and re-obtain the relevant Manager, and no new API should be added [UserHandle] 2324 src/android/pkg/UserHandleTest.java:8: warning: Method taking UserHandle should be named `doFooAsUser` or `queryFooForUser`, was `error` [UserHandleName] 2325 """, 2326 sourceFiles = 2327 arrayOf( 2328 java( 2329 """ 2330 package android.pkg; 2331 import android.os.UserHandle; 2332 import androidx.annotation.Nullable; 2333 2334 public class UserHandleTest { 2335 public void doFooAsUser(int i, @Nullable UserHandle handle) {} //OK 2336 public void doFooForUser(int i, @Nullable UserHandle handle) {} //OK 2337 public void error(int i, @Nullable UserHandle handle) {} 2338 } 2339 """ 2340 ), 2341 java( 2342 """ 2343 package android.pkg; 2344 import android.os.UserHandle; 2345 import androidx.annotation.Nullable; 2346 2347 public class MyManager { 2348 private MyManager() { } 2349 public void error(int i, @Nullable UserHandle handle) {} 2350 } 2351 """ 2352 ), 2353 androidxNullableSource 2354 ) 2355 ) 2356 } 2357 2358 @Test Check parametersnull2359 fun `Check parameters`() { 2360 check( 2361 apiLint = "", // enabled 2362 expectedIssues = 2363 """ 2364 src/android/pkg/FooOptions.java:3: warning: Classes holding a set of parameters should be called `FooParams`, was `FooOptions` [UserHandleName] 2365 """, 2366 sourceFiles = 2367 arrayOf( 2368 java( 2369 """ 2370 package android.pkg; 2371 2372 public class FooOptions { 2373 } 2374 """ 2375 ), 2376 java( 2377 """ 2378 package android.app; 2379 2380 public class ActivityOptions { 2381 } 2382 """ 2383 ) 2384 ) 2385 ) 2386 } 2387 2388 @Test Check service namesnull2389 fun `Check service names`() { 2390 check( 2391 apiLint = "", // enabled 2392 expectedIssues = 2393 """ 2394 src/android/content/Context.java:10: error: Inconsistent service value; expected `other`, was `something` (Note: Do not change the name of already released services, which will break tools using `adb shell dumpsys`. Instead add `@SuppressLint("ServiceName"))` [ServiceName] 2395 src/android/content/Context.java:11: error: Inconsistent service constant name; expected `SOMETHING_SERVICE`, was `OTHER_MANAGER` [ServiceName] 2396 src/android/content/Context.java:12: error: Inconsistent service constant name; expected `OTHER_SERVICE`, was `OTHER_MANAGER_SERVICE` [ServiceName] 2397 """, 2398 expectedFail = DefaultLintErrorMessage, 2399 sourceFiles = 2400 arrayOf( 2401 java( 2402 """ 2403 package android.content; 2404 2405 public class Context { 2406 private Context() { } 2407 // Good 2408 public static final String FOO_BAR_SERVICE = "foo_bar"; 2409 // Unrelated 2410 public static final int NON_STRING_SERVICE = 42; 2411 // Bad 2412 public static final String OTHER_SERVICE = "something"; 2413 public static final String OTHER_MANAGER = "something"; 2414 public static final String OTHER_MANAGER_SERVICE = "other_manager"; 2415 } 2416 """ 2417 ), 2418 java( 2419 """ 2420 package android.pkg; 2421 2422 // Unrelated 2423 public class ServiceNameTest { 2424 private ServiceNameTest() { } 2425 public static final String FOO_BAR_SERVICE = "foo_bar"; 2426 public static final String OTHER_SERVICE = "something"; 2427 public static final int NON_STRING_SERVICE = 42; 2428 public static final String BIND_SOME_SERVICE = "android.permission.BIND_SOME_SERVICE"; 2429 } 2430 """ 2431 ) 2432 ) 2433 ) 2434 } 2435 2436 @Test Check method name tensenull2437 fun `Check method name tense`() { 2438 check( 2439 apiLint = "", // enabled 2440 expectedIssues = 2441 """ 2442 src/android/pkg/MethodNameTest.java:6: warning: Unexpected tense; probably meant `enabled`, was `fooEnable` [MethodNameTense] 2443 src/android/pkg/MethodNameTest.java:7: warning: Unexpected tense; probably meant `enabled`, was `mustEnable` [MethodNameTense] 2444 """, 2445 sourceFiles = 2446 arrayOf( 2447 java( 2448 """ 2449 package android.pkg; 2450 2451 public class MethodNameTest { 2452 private MethodNameTest() { } 2453 public void enable() { } // ok, not Enable 2454 public void fooEnable() { } // warn 2455 public boolean mustEnable() { return true; } // warn 2456 public boolean isEnabled() { return true; } 2457 } 2458 """ 2459 ) 2460 ) 2461 ) 2462 } 2463 2464 @Test Check no clonenull2465 fun `Check no clone`() { 2466 check( 2467 apiLint = "", // enabled 2468 expectedIssues = 2469 """ 2470 src/android/pkg/CloneTest.java:8: error: Provide an explicit copy constructor instead of implementing `clone()` [NoClone] 2471 """, 2472 expectedFail = DefaultLintErrorMessage, 2473 sourceFiles = 2474 arrayOf( 2475 java( 2476 """ 2477 package android.pkg; 2478 2479 import androidx.annotation.NonNull; 2480 2481 public class CloneTest { 2482 public void clone(int i) { } // ok 2483 @NonNull 2484 public CloneTest clone() { return super.clone(); } // error 2485 } 2486 """ 2487 ), 2488 androidxNonNullSource 2489 ) 2490 ) 2491 } 2492 2493 @Test Check ICU typesnull2494 fun `Check ICU types`() { 2495 check( 2496 apiLint = "", // enabled 2497 expectedIssues = 2498 """ 2499 src/android/pkg/IcuTest.java:6: warning: Type `java.text.NumberFormat` should be replaced with richer ICU type `android.icu.text.NumberFormat` [UseIcu] 2500 src/android/pkg/IcuTest.java:8: warning: Type `java.text.BreakIterator` should be replaced with richer ICU type `android.icu.text.BreakIterator` [UseIcu] 2501 src/android/pkg/IcuTest.java:8: warning: Type `java.text.Collator` should be replaced with richer ICU type `android.icu.text.Collator` [UseIcu] 2502 """, 2503 sourceFiles = 2504 arrayOf( 2505 java( 2506 """ 2507 package android.pkg; 2508 2509 import androidx.annotation.Nullable; 2510 2511 public abstract class IcuTest { 2512 public IcuTest(@Nullable java.text.NumberFormat nf) { } 2513 @Nullable 2514 public abstract java.text.BreakIterator foo(@Nullable java.text.Collator collator); 2515 } 2516 """ 2517 ), 2518 androidxNullableSource 2519 ) 2520 ) 2521 } 2522 2523 @Test Check using parcel file descriptorsnull2524 fun `Check using parcel file descriptors`() { 2525 check( 2526 apiLint = "", // enabled 2527 expectedIssues = 2528 """ 2529 src/android/pkg/PdfTest.java:6: error: Must use ParcelFileDescriptor instead of FileDescriptor in parameter fd in android.pkg.PdfTest.error1(java.io.FileDescriptor fd) [UseParcelFileDescriptor] 2530 src/android/pkg/PdfTest.java:7: error: Must use ParcelFileDescriptor instead of FileDescriptor in method android.pkg.PdfTest.getFileDescriptor() [UseParcelFileDescriptor] 2531 """, 2532 expectedFail = DefaultLintErrorMessage, 2533 sourceFiles = 2534 arrayOf( 2535 java( 2536 """ 2537 package android.pkg; 2538 2539 import androidx.annotation.Nullable; 2540 2541 public abstract class PdfTest { 2542 public void error1(@Nullable java.io.FileDescriptor fd) { } 2543 public int getFileDescriptor() { return -1; } 2544 public void ok(@Nullable android.os.ParcelFileDescriptor fd) { } 2545 } 2546 """ 2547 ), 2548 java( 2549 """ 2550 package android.system; 2551 2552 import androidx.annotation.Nullable; 2553 2554 public class Os { 2555 public void ok(@Nullable java.io.FileDescriptor fd) { } 2556 public int getFileDescriptor() { return -1; } 2557 } 2558 """ 2559 ), 2560 java( 2561 """ 2562 package android.yada; 2563 2564 import android.annotation.Nullable; 2565 2566 public class YadaService extends android.app.Service { 2567 @Override 2568 public final void dump(@Nullable java.io.FileDescriptor fd, @Nullable java.io.PrintWriter pw, @Nullable String[] args) { 2569 } 2570 } 2571 """ 2572 ), 2573 androidxNullableSource, 2574 nonNullSource 2575 ) 2576 ) 2577 } 2578 2579 @Test Check using bytes and shortsnull2580 fun `Check using bytes and shorts`() { 2581 check( 2582 apiLint = "", // enabled 2583 expectedIssues = 2584 """ 2585 src/android/pkg/ByteTest.java:4: warning: Should avoid odd sized primitives; use `int` instead of `byte` in parameter b in android.pkg.ByteTest.error1(byte b) [NoByteOrShort] 2586 src/android/pkg/ByteTest.java:5: warning: Should avoid odd sized primitives; use `int` instead of `short` in parameter s in android.pkg.ByteTest.error2(short s) [NoByteOrShort] 2587 """, 2588 sourceFiles = 2589 arrayOf( 2590 java( 2591 """ 2592 package android.pkg; 2593 2594 public abstract class ByteTest { 2595 public void error1(byte b) { } 2596 public void error2(short s) { } 2597 } 2598 """ 2599 ) 2600 ) 2601 ) 2602 } 2603 2604 @Test Check singleton constructorsnull2605 fun `Check singleton constructors`() { 2606 check( 2607 apiLint = "", // enabled 2608 expectedIssues = 2609 """ 2610 src/android/pkg/MySingleton.java:8: error: Singleton classes should use `getInstance()` methods: `MySingleton` [SingletonConstructor] 2611 """, 2612 expectedFail = DefaultLintErrorMessage, 2613 sourceFiles = 2614 arrayOf( 2615 java( 2616 """ 2617 package android.pkg; 2618 2619 import androidx.annotation.Nullable; 2620 2621 public abstract class MySingleton { 2622 @Nullable 2623 public static MySingleton getMyInstance() { return null; } 2624 public MySingleton() { } 2625 public void foo() { } 2626 } 2627 """ 2628 ), 2629 java( 2630 """ 2631 package android.pkg; 2632 2633 import androidx.annotation.Nullable; 2634 2635 public abstract class MySingleton2 { 2636 @Nullable 2637 public static MySingleton2 getMyInstance() { return null; } 2638 private MySingleton2() { } // OK, private 2639 public void foo() { } 2640 } 2641 """ 2642 ), 2643 androidxNullableSource 2644 ) 2645 ) 2646 } 2647 2648 @Test Check forbidden super-classesnull2649 fun `Check forbidden super-classes`() { 2650 check( 2651 apiLint = "", // enabled 2652 expectedIssues = 2653 """ 2654 src/android/pkg/FirstActivity.java:2: error: FirstActivity should not extend `Activity`. Activity subclasses are impossible to compose. Expose a composable API instead. [ForbiddenSuperClass] 2655 src/android/pkg/IndirectActivity.java:2: error: IndirectActivity should not extend `Activity`. Activity subclasses are impossible to compose. Expose a composable API instead. [ForbiddenSuperClass] 2656 src/android/pkg/MyTask.java:2: error: MyTask should not extend `AsyncTask`. AsyncTask is an implementation detail. Expose a listener or, in androidx, a `ListenableFuture` API instead [ForbiddenSuperClass] 2657 """, 2658 expectedFail = DefaultLintErrorMessage, 2659 sourceFiles = 2660 arrayOf( 2661 java( 2662 """ 2663 package android.pkg; 2664 public abstract class FirstActivity extends android.app.Activity { 2665 private FirstActivity() { } 2666 } 2667 """ 2668 ), 2669 java( 2670 """ 2671 package android.pkg; 2672 public abstract class IndirectActivity extends android.app.ListActivity { 2673 private IndirectActivity() { } 2674 } 2675 """ 2676 ), 2677 java( 2678 """ 2679 package android.pkg; 2680 public abstract class MyTask extends android.os.AsyncTask<String,String,String> { 2681 private MyTask() { } 2682 } 2683 """ 2684 ) 2685 ) 2686 ) 2687 } 2688 2689 @Test No new setting keysnull2690 fun `No new setting keys`() { 2691 check( 2692 apiLint = "", // enabled 2693 extraArguments = arrayOf(ARG_ERROR, "NoSettingsProvider"), 2694 expectedIssues = 2695 """ 2696 src/android/provider/Settings.java:9: error: New setting keys are not allowed (Field: BAD1); use getters/setters in relevant manager class [NoSettingsProvider] 2697 src/android/provider/Settings.java:12: error: Bare field okay2 must be marked final, or moved behind accessors if mutable [MutableBareField] 2698 src/android/provider/Settings.java:17: error: New setting keys are not allowed (Field: BAD1); use getters/setters in relevant manager class [NoSettingsProvider] 2699 src/android/provider/Settings.java:21: error: New setting keys are not allowed (Field: BAD1); use getters/setters in relevant manager class [NoSettingsProvider] 2700 """, 2701 expectedFail = DefaultLintErrorMessage, 2702 sourceFiles = 2703 arrayOf( 2704 java( 2705 """ 2706 package android.provider; 2707 2708 import androidx.annotation.Nullable; 2709 2710 public class Settings { 2711 private Settings() { } 2712 public static class Global { 2713 private Global() { } 2714 public static final String BAD1 = ""; 2715 public final String okay1 = ""; 2716 @Nullable 2717 public static String okay2 = ""; 2718 public static final int OKAY3 = 0; 2719 } 2720 public static class Secure { 2721 private Secure() { } 2722 public static final String BAD1 = ""; 2723 } 2724 public static class System { 2725 private System() { } 2726 public static final String BAD1 = ""; 2727 } 2728 public static class Other { 2729 private Other() { } 2730 public static final String OKAY1 = ""; 2731 } 2732 } 2733 """ 2734 ), 2735 androidxNullableSource 2736 ) 2737 ) 2738 } 2739 2740 @RequiresCapabilities(Capability.KOTLIN) 2741 @Test vararg use in annotationsnull2742 fun `vararg use in annotations`() { 2743 check( 2744 apiLint = "", // enabled 2745 expectedIssues = "", 2746 sourceFiles = 2747 arrayOf( 2748 kotlin( 2749 """ 2750 package test.pkg 2751 2752 import kotlin.reflect.KClass 2753 2754 annotation class MyAnnotation( 2755 vararg val markerClass: KClass<out Annotation> 2756 ) 2757 """ 2758 ) 2759 ) 2760 ) 2761 } 2762 2763 @Test Inherited interface constantsnull2764 fun `Inherited interface constants`() { 2765 check( 2766 expectedIssues = "", 2767 expectedFail = "", 2768 apiLint = 2769 """ 2770 package javax.microedition.khronos.egl { 2771 public interface EGL { 2772 } 2773 public interface EGL10 extends javax.microedition.khronos.egl.EGL { 2774 field public static final int EGL_SUCCESS = 0; 2775 } 2776 public interface EGL11 extends javax.microedition.khronos.egl.EGL10 { 2777 field public static final int EGL_CONTEXT_LOST = 1; 2778 } 2779 public interface EGLDisplay { 2780 } 2781 } 2782 """, 2783 sourceFiles = 2784 arrayOf( 2785 java( 2786 """ 2787 package javax.microedition.khronos.egl; 2788 2789 public interface EGL { 2790 } 2791 """ 2792 ), 2793 java( 2794 """ 2795 package javax.microedition.khronos.egl; 2796 2797 public interface EGL10 extends EGL { 2798 int EGL_SUCCESS = 0; 2799 } 2800 """ 2801 ), 2802 java( 2803 """ 2804 package javax.microedition.khronos.egl; 2805 2806 public interface EGL11 extends EGL10 { 2807 int EGL_CONTEXT_LOST = 1; 2808 } 2809 """ 2810 ), 2811 java( 2812 """ 2813 package javax.microedition.khronos.egl; 2814 2815 public abstract class EGLDisplay { 2816 } 2817 """ 2818 ) 2819 ) 2820 ) 2821 } 2822 2823 @Test Inherited interface constants inherited through parents into childrennull2824 fun `Inherited interface constants inherited through parents into children`() { 2825 check( 2826 expectedIssues = "", 2827 expectedFail = "", 2828 apiLint = 2829 """ 2830 package android.provider { 2831 public static final class Settings.Global extends android.provider.Settings.NameValueTable { 2832 } 2833 public static class Settings.NameValueTable implements android.provider.BaseColumns { 2834 } 2835 public interface BaseColumns { 2836 field public static final String _ID = "_id"; 2837 } 2838 } 2839 """, 2840 sourceFiles = 2841 arrayOf( 2842 java( 2843 """ 2844 package android.provider; 2845 2846 public class Settings { 2847 private Settings() { } 2848 public static final class Global extends NameValueTable { 2849 } 2850 public static final class NameValueTable implements BaseColumns { 2851 } 2852 } 2853 """ 2854 ), 2855 java( 2856 """ 2857 package android.provider; 2858 2859 public interface BaseColumns { 2860 public static final String _ID = "_id"; 2861 } 2862 """ 2863 ) 2864 ), 2865 extraArguments = arrayOf("--error", "NoSettingsProvider") 2866 ) 2867 } 2868 2869 @Test Methods returning ListenableFuture end with asyncnull2870 fun `Methods returning ListenableFuture end with async`() { 2871 check( 2872 apiLint = "", // enabled 2873 expectedIssues = 2874 """ 2875 src/android/pkg/MyClass.java:7: error: Methods returning com.google.common.util.concurrent.ListenableFuture should have a suffix *Async to reserve unmodified name for a suspend function [AsyncSuffixFuture] 2876 """, 2877 expectedFail = DefaultLintErrorMessage, 2878 sourceFiles = 2879 arrayOf( 2880 java( 2881 """ 2882 package android.pkg; 2883 2884 import androidx.annotation.Nullable; 2885 import com.google.common.util.concurrent.ListenableFuture; 2886 2887 public final class MyClass { 2888 public @Nullable ListenableFuture<String> bad() { return null; } 2889 public @Nullable ListenableFuture<String> goodAsync() { return null; } 2890 } 2891 """ 2892 ), 2893 java( 2894 """ 2895 package com.google.common.util.concurrent; 2896 public class ListenableFuture<T> { 2897 } 2898 """ 2899 ), 2900 androidxNullableSource 2901 ) 2902 ) 2903 } 2904 2905 @Test Listener replaceable with OutcomeReceiver or ListenableFuturenull2906 fun `Listener replaceable with OutcomeReceiver or ListenableFuture`() { 2907 check( 2908 apiLint = "", // enabled 2909 expectedIssues = 2910 """ 2911 src/android/pkg/Cases.java:7: error: Cases.BadCallback can be replaced with OutcomeReceiver<R,E> (platform) or suspend fun / ListenableFuture (AndroidX). [GenericCallbacks] 2912 src/android/pkg/Cases.java:11: error: Cases.BadListener can be replaced with OutcomeReceiver<R,E> (platform) or suspend fun / ListenableFuture (AndroidX). [GenericCallbacks] 2913 src/android/pkg/Cases.java:15: error: Cases.BadGenericListener can be replaced with OutcomeReceiver<R,E> (platform) or suspend fun / ListenableFuture (AndroidX). [GenericCallbacks] 2914 """, 2915 expectedFail = DefaultLintErrorMessage, 2916 sourceFiles = 2917 arrayOf( 2918 java( 2919 """ 2920 package android.pkg; 2921 2922 import androidx.annotation.NonNull; 2923 import java.io.IOException; 2924 2925 public final class Cases { 2926 public class BadCallback { 2927 public abstract void onSuccess(@NonNull String result); 2928 public void onFailure(@NonNull Throwable error) {} 2929 } 2930 public interface BadListener { 2931 void onResult(@NonNull Object result); 2932 void onError(@NonNull IOException error); 2933 } 2934 public interface BadGenericListener<R, E extends Throwable> { 2935 void onResult(@NonNull R result); 2936 void onError(@NonNull E error); 2937 } 2938 public interface OkListener { 2939 void onResult(@NonNull Object result); 2940 void onError(@NonNull int error); 2941 } 2942 public interface Ok2Listener { 2943 void onResult(@NonNull int result); 2944 void onError(@NonNull Throwable error); 2945 } 2946 public interface Ok3Listener { 2947 void onSuccess(@NonNull String result); 2948 void onOtherThing(@NonNull String result); 2949 void onFailure(@NonNull Throwable error); 2950 } 2951 } 2952 """ 2953 ), 2954 androidxNonNullSource 2955 ) 2956 ) 2957 } 2958 2959 @RequiresCapabilities(Capability.KOTLIN) 2960 @Test No warning on generic return typenull2961 fun `No warning on generic return type`() { 2962 check( 2963 expectedIssues = "", 2964 apiLint = "", 2965 sourceFiles = 2966 arrayOf( 2967 kotlin( 2968 """ 2969 package test.pkg 2970 class SimpleArrayMap<K, V> { 2971 override fun getOrDefault(key: K, defaultValue: V): V {} 2972 } 2973 """ 2974 ) 2975 ) 2976 ) 2977 } 2978 2979 @Test No crash when setter start with numbersnull2980 fun `No crash when setter start with numbers`() { 2981 check( 2982 expectedIssues = "", 2983 apiLint = "", 2984 sourceFiles = 2985 arrayOf( 2986 java( 2987 """ 2988 package test.pkg; 2989 import androidx.annotation.NonNull; 2990 public class Config { 2991 public boolean is80211mcSupported() { return true; } 2992 public static final class Builder { 2993 @NonNull 2994 public Builder set80211mcSupported(boolean supports80211mc) { 2995 } 2996 @NonNull 2997 public Config build() { 2998 return Config(); 2999 } 3000 } 3001 } 3002 """ 3003 ), 3004 androidxNonNullSource, 3005 ) 3006 ) 3007 } 3008 3009 @RequiresCapabilities(Capability.KOTLIN) 3010 @Test Kotlin required parameters must come before optional parametersnull3011 fun `Kotlin required parameters must come before optional parameters`() { 3012 check( 3013 apiLint = "", // enabled 3014 extraArguments = 3015 arrayOf( 3016 // JvmOverloads warning, not what we want to test here 3017 ARG_HIDE, 3018 "MissingJvmstatic" 3019 ), 3020 expectedIssues = 3021 """ 3022 src/android/pkg/Interface.kt:18: error: Parameter `default` has a default value and should come after all parameters without default values (except for a trailing lambda parameter) [KotlinDefaultParameterOrder] 3023 src/android/pkg/Interface.kt:28: error: Parameter `default` has a default value and should come after all parameters without default values (except for a trailing lambda parameter) [KotlinDefaultParameterOrder] 3024 src/android/pkg/Interface.kt:39: error: Parameter `default` has a default value and should come after all parameters without default values (except for a trailing lambda parameter) [KotlinDefaultParameterOrder] 3025 src/android/pkg/Interface.kt:50: error: Parameter `default` has a default value and should come after all parameters without default values (except for a trailing lambda parameter) [KotlinDefaultParameterOrder] 3026 src/android/pkg/Interface.kt:56: error: Parameter `default` has a default value and should come after all parameters without default values (except for a trailing lambda parameter) [KotlinDefaultParameterOrder] 3027 src/android/pkg/Interface.kt:66: error: Parameter `default` has a default value and should come after all parameters without default values (except for a trailing lambda parameter) [KotlinDefaultParameterOrder] 3028 src/android/pkg/Interface.kt:72: error: Parameter `default` has a default value and should come after all parameters without default values (except for a trailing lambda parameter) [KotlinDefaultParameterOrder] 3029 src/android/pkg/Interface.kt:82: error: Parameter `default` has a default value and should come after all parameters without default values (except for a trailing lambda parameter) [KotlinDefaultParameterOrder] 3030 src/android/pkg/Interface.kt:92: error: Parameter `default` has a default value and should come after all parameters without default values (except for a trailing lambda parameter) [KotlinDefaultParameterOrder] 3031 src/android/pkg/Interface.kt:104: error: Parameter `default` has a default value and should come after all parameters without default values (except for a trailing lambda parameter) [KotlinDefaultParameterOrder] 3032 src/android/pkg/Interface.kt:114: error: Parameter `default` has a default value and should come after all parameters without default values (except for a trailing lambda parameter) [KotlinDefaultParameterOrder] 3033 src/android/pkg/Interface.kt:125: error: Parameter `default` has a default value and should come after all parameters without default values (except for a trailing lambda parameter) [KotlinDefaultParameterOrder] 3034 src/android/pkg/Interface.kt:136: error: Parameter `default` has a default value and should come after all parameters without default values (except for a trailing lambda parameter) [KotlinDefaultParameterOrder] 3035 src/android/pkg/Interface.kt:142: error: Parameter `default` has a default value and should come after all parameters without default values (except for a trailing lambda parameter) [KotlinDefaultParameterOrder] 3036 src/android/pkg/Interface.kt:152: error: Parameter `default` has a default value and should come after all parameters without default values (except for a trailing lambda parameter) [KotlinDefaultParameterOrder] 3037 src/android/pkg/Interface.kt:158: error: Parameter `default` has a default value and should come after all parameters without default values (except for a trailing lambda parameter) [KotlinDefaultParameterOrder] 3038 """ 3039 .trimIndent(), 3040 expectedFail = DefaultLintErrorMessage, 3041 sourceFiles = 3042 arrayOf( 3043 java( 3044 """ 3045 package android.pkg; 3046 3047 public interface JavaInterface { 3048 void doSomething(); 3049 void doSomethingElse(); 3050 } 3051 """ 3052 .trimIndent() 3053 ), 3054 java( 3055 """ 3056 package android.pkg; 3057 3058 public interface JavaSamInterface { 3059 void doSomething(); 3060 } 3061 """ 3062 .trimIndent() 3063 ), 3064 kotlin( 3065 """ 3066 package android.pkg 3067 3068 interface Interface { 3069 fun foo() 3070 } 3071 3072 fun interface FunInterface { 3073 fun foo() 3074 } 3075 3076 // Functions 3077 fun ok1( 3078 required: Boolean, 3079 default: Int = 0, 3080 ) {} 3081 3082 fun error1( 3083 default: Int = 0, 3084 required: Boolean, 3085 ) {} 3086 3087 fun ok2( 3088 default: Int = 0, 3089 trailing: () -> Unit 3090 ) {} 3091 3092 fun error2( 3093 default: Int = 0, 3094 required: () -> Unit, 3095 default2: Int = 0, 3096 ) {} 3097 3098 fun ok3( 3099 default: Int = 0, 3100 trailing: suspend () -> Unit 3101 ) {} 3102 3103 fun error3( 3104 default: Int = 0, 3105 required: suspend () -> Unit, 3106 default2: Int = 0, 3107 ) {} 3108 3109 fun ok4( 3110 default: Int = 0, 3111 trailing: FunInterface 3112 ) {} 3113 3114 fun error4( 3115 default: Int = 0, 3116 required: FunInterface, 3117 default2: Int = 0, 3118 ) {} 3119 3120 fun error5( 3121 default: Int = 0, 3122 trailing: Interface 3123 ) {} 3124 3125 fun ok6( 3126 default: Int = 0, 3127 trailing: JavaSamInterface 3128 ) {} 3129 3130 fun error6( 3131 default: Int = 0, 3132 required: JavaSamInterface, 3133 default2: Int = 0, 3134 ) {} 3135 3136 fun error7( 3137 default: Int = 0, 3138 trailing: JavaInterface 3139 ) {} 3140 3141 suspend fun ok8( 3142 required: Boolean, 3143 default: Int = 0, 3144 ) {} 3145 3146 suspend fun error8( 3147 default: Int = 0, 3148 required: Boolean, 3149 ) {} 3150 3151 suspend fun ok9( 3152 default: Int = 0, 3153 trailing: () -> Unit 3154 ) {} 3155 3156 suspend fun error9( 3157 default: Int = 0, 3158 required: () -> Unit, 3159 default2: Int = 0, 3160 ) {} 3161 3162 // Class constructors 3163 class Ok1( 3164 required: Boolean, 3165 default: Int = 0, 3166 ) 3167 3168 class Error1( 3169 default: Int = 0, 3170 required: Boolean, 3171 ) 3172 3173 class Ok2( 3174 default: Int = 0, 3175 trailing: () -> Unit 3176 ) 3177 3178 class Error2( 3179 default: Int = 0, 3180 required: () -> Unit, 3181 default2: Int = 0, 3182 ) 3183 3184 class Ok3( 3185 default: Int = 0, 3186 trailing: suspend () -> Unit 3187 ) 3188 3189 class Error3( 3190 default: Int = 0, 3191 required: suspend () -> Unit, 3192 default2: Int = 0, 3193 ) 3194 3195 class Ok4( 3196 default: Int = 0, 3197 trailing: FunInterface 3198 ) 3199 3200 class Error4( 3201 default: Int = 0, 3202 required: FunInterface, 3203 default2: Int = 0, 3204 ) 3205 3206 class Error5( 3207 default: Int = 0, 3208 trailing: Interface 3209 ) 3210 3211 class Ok6( 3212 default: Int = 0, 3213 trailing: JavaSamInterface 3214 ) 3215 3216 class Error6( 3217 default: Int = 0, 3218 required: JavaSamInterface, 3219 default2: Int = 0, 3220 ) 3221 3222 class Error7( 3223 default: Int = 0, 3224 trailing: JavaInterface 3225 ) 3226 """ 3227 ) 3228 ) 3229 ) 3230 } 3231 3232 @RequiresCapabilities(Capability.KOTLIN) 3233 @Test No parameter ordering for sealed class constructornull3234 fun `No parameter ordering for sealed class constructor`() { 3235 check( 3236 expectedIssues = "", 3237 apiLint = "", 3238 sourceFiles = 3239 arrayOf( 3240 kotlin( 3241 """ 3242 package test.pkg 3243 3244 sealed class Foo( 3245 default: Int = 0, 3246 required: () -> Unit, 3247 ) 3248 """ 3249 ) 3250 ) 3251 ) 3252 } 3253 3254 @RequiresCapabilities(Capability.KOTLIN) 3255 @Test members in sealed class are not hidden abstractnull3256 fun `members in sealed class are not hidden abstract`() { 3257 check( 3258 expectedIssues = "", 3259 apiLint = "", 3260 sourceFiles = 3261 arrayOf( 3262 kotlin( 3263 """ 3264 package test.pkg 3265 3266 sealed class ModifierLocalMap() { 3267 internal abstract operator fun <T> set(key: ModifierLocal<T>, value: T) 3268 internal abstract operator fun <T> get(key: ModifierLocal<T>): T? 3269 internal abstract operator fun contains(key: ModifierLocal<*>): Boolean 3270 } 3271 """ 3272 ) 3273 ) 3274 ) 3275 } 3276 3277 @RequiresCapabilities(Capability.KOTLIN) 3278 @Test throw unresolved errornull3279 fun `throw unresolved error`() { 3280 // Regression test from b/364736827 3281 check( 3282 expectedIssues = "", 3283 apiLint = "", 3284 sourceFiles = 3285 arrayOf( 3286 kotlin( 3287 """ 3288 package test.pkg 3289 3290 import kotlin.random.Random 3291 3292 public class SubspaceSemanticsNodeInteraction() { 3293 /** 3294 * @throws [UnresolvedAssertionError] if failed 3295 */ 3296 public fun assertDoesNotExist() { 3297 if (Random.nextBoolean()) { 3298 throw UnresolvedAssertionError("Failed") 3299 } 3300 } 3301 } 3302 """ 3303 ) 3304 ) 3305 ) 3306 } 3307 } 3308