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 18 19 import org.junit.Test 20 21 class ApiLintTest : DriverTest() { 22 23 @Test Test namesnull24 fun `Test names`() { 25 // Make sure we only flag issues in new API 26 check( 27 apiLint = "", // enabled 28 extraArguments = arrayOf( 29 ARG_API_LINT_IGNORE_PREFIX, 30 "android.icu.", 31 ARG_API_LINT_IGNORE_PREFIX, 32 "java.", 33 ARG_HIDE, "MissingNullability" 34 ), 35 expectedIssues = """ 36 src/Dp.kt:3: warning: Acronyms should not be capitalized in method names: was `badCALL`, should this be `badCall`? [AcronymName] [See https://s.android.com/api-guidelines#acronyms-in-method-name] 37 src/android/pkg/ALL_CAPS.java:3: warning: Acronyms should not be capitalized in class names: was `ALL_CAPS`, should this be `AllCaps`? [AcronymName] [See https://s.android.com/api-guidelines#acronyms-in-method-name] 38 src/android/pkg/HTMLWriter.java:3: warning: Acronyms should not be capitalized in class names: was `HTMLWriter`, should this be `HtmlWriter`? [AcronymName] [See https://s.android.com/api-guidelines#acronyms-in-method-name] 39 src/android/pkg/MyStringImpl.java:3: error: Don't expose your implementation details: `MyStringImpl` ends with `Impl` [EndsWithImpl] [See https://s.android.com/api-guidelines#dont-end-with-impl] 40 src/android/pkg/badlyNamedClass.java:5: error: Class must start with uppercase char: badlyNamedClass [StartWithUpper] [See https://s.android.com/api-guidelines#style-conventions] 41 src/android/pkg/badlyNamedClass.java:7: error: Method name must start with lowercase char: BadlyNamedMethod1 [StartWithLower] [See https://s.android.com/api-guidelines#style-conventions] 42 src/android/pkg/badlyNamedClass.java:9: warning: Acronyms should not be capitalized in method names: was `fromHTMLToHTML`, should this be `fromHtmlToHtml`? [AcronymName] [See https://s.android.com/api-guidelines#acronyms-in-method-name] 43 src/android/pkg/badlyNamedClass.java:10: warning: Acronyms should not be capitalized in method names: was `toXML`, should this be `toXml`? [AcronymName] [See https://s.android.com/api-guidelines#acronyms-in-method-name] 44 src/android/pkg/badlyNamedClass.java:12: warning: Acronyms should not be capitalized in method names: was `getID`, should this be `getId`? [AcronymName] [See https://s.android.com/api-guidelines#acronyms-in-method-name] 45 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] [See https://s.android.com/api-guidelines#constant-naming] 46 """, 47 expectedFail = DefaultLintErrorMessage, 48 sourceFiles = arrayOf( 49 java( 50 """ 51 package android.pkg; 52 53 import androidx.annotation.Nullable; 54 55 public class badlyNamedClass { 56 public static final int BadlyNamedField = 1; 57 public void BadlyNamedMethod1() { } 58 59 public void fromHTMLToHTML() { } 60 public void toXML() { } 61 @Nullable 62 public String getID() { return null; } 63 public void setZOrderOnTop() { } // OK 64 } 65 """ 66 ), 67 java( 68 """ 69 package android.pkg; 70 71 public class ALL_CAPS { // like android.os.Build.VERSION_CODES 72 } 73 """ 74 ), 75 java( 76 """ 77 package android.pkg; 78 79 public class HTMLWriter { 80 } 81 """ 82 ), 83 java( 84 """ 85 package android.pkg; 86 87 public class MyStringImpl { 88 } 89 """ 90 ), 91 java( 92 """ 93 package android.icu; 94 95 import androidx.annotation.Nullable; 96 97 // Same as above android.pkg.badlyNamedClass but in a package 98 // that API lint is supposed to ignore (see ARG_API_LINT_IGNORE_PREFIX) 99 public class badlyNamedClass { 100 public static final int BadlyNamedField = 1; 101 public void BadlyNamedMethod1() { } 102 103 public void toXML() { } 104 @Nullable 105 public String getID() { return null; } 106 public void setZOrderOnTop() { } 107 } 108 """ 109 ), 110 java( 111 """ 112 package android.icu.sub; 113 114 import androidx.annotation.Nullable; 115 116 // Same as above android.pkg.badlyNamedClass but in a package 117 // that API lint is supposed to ignore (see ARG_API_LINT_IGNORE_PREFIX) 118 public class badlyNamedClass { 119 public static final int BadlyNamedField = 1; 120 public void BadlyNamedMethod1() { } 121 122 public void toXML() { } 123 @Nullable 124 public String getID() { return null; } 125 public void setZOrderOnTop() { } 126 } 127 """ 128 ), 129 java( 130 """ 131 package java; 132 133 import androidx.annotation.Nullable; 134 135 // Same as above android.pkg.badlyNamedClass but in a package 136 // that API lint is supposed to ignore (see ARG_API_LINT_IGNORE_PREFIX) 137 public class badlyNamedClass { 138 public static final int BadlyNamedField = 1; 139 public void BadlyNamedMethod1() { } 140 141 public void toXML() { } 142 @Nullable 143 public String getID() { return null; } 144 public void setZOrderOnTop() { } 145 } 146 """ 147 ), 148 kotlin( 149 """ 150 inline class Dp(val value: Float) 151 fun greatCall(width: Dp) 152 fun badCALL(width: Dp) 153 """ 154 ), 155 androidxNullableSource 156 ) 157 /* 158 expectedOutput = """ 159 9 new API lint issues were found. 160 See tools/metalava/API-LINT.md for how to handle these. 161 """ 162 */ 163 ) 164 } 165 166 @Test Test names against previous APInull167 fun `Test names against previous API`() { 168 check( 169 apiLint = """ 170 package android.pkg { 171 public class badlyNamedClass { 172 ctor public badlyNamedClass(); 173 method public void BadlyNamedMethod1(); 174 method public void fromHTMLToHTML(); 175 method public String getID(); 176 method public void toXML(); 177 field public static final int BadlyNamedField = 1; // 0x1 178 } 179 } 180 """.trimIndent(), 181 expectedIssues = """ 182 src/android/pkg/badlyNamedClass.java:8: warning: Acronyms should not be capitalized in method names: was `toXML2`, should this be `toXmL2`? [AcronymName] [See https://s.android.com/api-guidelines#acronyms-in-method-name] 183 src/android/pkg2/HTMLWriter.java:3: warning: Acronyms should not be capitalized in class names: was `HTMLWriter`, should this be `HtmlWriter`? [AcronymName] [See https://s.android.com/api-guidelines#acronyms-in-method-name] 184 src/android/pkg2/HTMLWriter.java:4: warning: Acronyms should not be capitalized in method names: was `fromHTMLToHTML`, should this be `fromHtmlToHtml`? [AcronymName] [See https://s.android.com/api-guidelines#acronyms-in-method-name] 185 """, 186 sourceFiles = arrayOf( 187 java( 188 """ 189 package android.pkg; 190 191 public class badlyNamedClass { 192 public static final int BadlyNamedField = 1; 193 194 public void fromHTMLToHTML() { } 195 public void toXML() { } 196 public void toXML2() { } 197 public String getID() { return null; } 198 } 199 """ 200 ), 201 java( 202 """ 203 package android.pkg2; 204 205 public class HTMLWriter { 206 public void fromHTMLToHTML() { } 207 } 208 """ 209 ) 210 ) 211 ) 212 } 213 214 @Test Test constantsnull215 fun `Test constants`() { 216 check( 217 apiLint = "", // enabled 218 expectedIssues = """ 219 src/android/pkg/Constants.java:14: error: All constants must be defined at compile time: android.pkg.Constants#FOO [CompileTimeConstant] 220 src/android/pkg/Constants.java:12: warning: If min/max could change in future, make them dynamic methods: android.pkg.Constants#MAX_FOO [MinMaxConstant] [See https://s.android.com/api-guidelines#min-max-constants] 221 src/android/pkg/Constants.java:11: warning: If min/max could change in future, make them dynamic methods: android.pkg.Constants#MIN_FOO [MinMaxConstant] [See https://s.android.com/api-guidelines#min-max-constants] 222 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] [See https://s.android.com/api-guidelines#constant-naming] 223 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] [See https://s.android.com/api-guidelines#constant-naming] 224 """, 225 expectedFail = DefaultLintErrorMessage, 226 sourceFiles = arrayOf( 227 java( 228 """ 229 package android.pkg; 230 231 import androidx.annotation.NonNull; 232 233 public class Constants { 234 private Constants() { } 235 @NonNull 236 public static final String[] strings = { "NONE", "WPA_PSK" }; 237 @NonNull 238 public static final String[] myStrings = { "NONE", "WPA_PSK" }; 239 public static final int MIN_FOO = 1; 240 public static final int MAX_FOO = 10; 241 @NonNull 242 public static final String FOO = System.getProperty("foo"); 243 } 244 """ 245 ), 246 androidxNonNullSource, 247 androidxNullableSource 248 ) 249 ) 250 } 251 252 @Test No enumsnull253 fun `No enums`() { 254 check( 255 apiLint = "", // enabled 256 expectedIssues = """ 257 src/android/pkg/MyEnum.java:3: error: Enums are discouraged in Android APIs [Enum] [See https://s.android.com/api-guidelines#avoid-enum] 258 """, 259 expectedFail = DefaultLintErrorMessage, 260 sourceFiles = arrayOf( 261 java( 262 """ 263 package android.pkg; 264 265 public enum MyEnum { 266 FOO, BAR 267 } 268 """ 269 ) 270 ) 271 ) 272 } 273 274 @Test Test callbacksnull275 fun `Test callbacks`() { 276 check( 277 apiLint = "", // enabled 278 expectedIssues = """ 279 src/android/pkg/MyCallback.java:9: error: Callback method names must follow the on<Something> style: bar [CallbackMethodName] [See https://s.android.com/api-guidelines#callback-method-naming] 280 src/android/pkg/MyCallbacks.java:3: error: Callback class names should be singular: MyCallbacks [SingularCallback] [See https://s.android.com/api-guidelines#callback-class-singular] 281 src/android/pkg/MyInterfaceCallback.java:3: error: Callbacks must be abstract class instead of interface to enable extension in future API levels: MyInterfaceCallback [CallbackInterface] [See https://s.android.com/api-guidelines#callback-abstract-instead-of-interface] 282 src/android/pkg/MyObserver.java:3: warning: Class should be named MyCallback [CallbackName] [See https://s.android.com/api-guidelines#observer-should-be-callback] 283 """, 284 expectedFail = DefaultLintErrorMessage, 285 sourceFiles = arrayOf( 286 java( 287 """ 288 package android.pkg; 289 290 public class MyCallbacks { 291 } 292 """ 293 ), 294 java( 295 """ 296 package android.pkg; 297 298 public final class RemoteCallback { 299 public void sendResult(); 300 } 301 """ 302 ), 303 java( 304 """ 305 package android.pkg; 306 307 public class MyObserver { 308 } 309 """ 310 ), 311 java( 312 """ 313 package android.pkg; 314 315 public interface MyInterfaceCallback { 316 } 317 """ 318 ), 319 java( 320 """ 321 package android.pkg; 322 323 public class MyCallback { 324 public void onFoo() { 325 } 326 public void onAnimationStart() { 327 } 328 public void onN1GoodMethod() { } 329 public void bar() { 330 } 331 public static void staticMethod() { 332 } 333 public final void finalMethod() { 334 } 335 } 336 """ 337 ) 338 ) 339 ) 340 } 341 342 @Test Test listenersnull343 fun `Test listeners`() { 344 check( 345 apiLint = "", // enabled 346 expectedIssues = """ 347 src/android/pkg/MyClassListener.java:3: error: Listeners should be an interface, or otherwise renamed Callback: MyClassListener [ListenerInterface] [See https://s.android.com/api-guidelines#callbacks-listener] 348 src/android/pkg/MyListener.java:6: error: Listener method names must follow the on<Something> style: bar [CallbackMethodName] [See https://s.android.com/api-guidelines#callback-method-naming] 349 """, 350 expectedFail = DefaultLintErrorMessage, 351 sourceFiles = arrayOf( 352 java( 353 """ 354 package android.pkg; 355 356 public abstract class MyClassListener { 357 } 358 """ 359 ), 360 java( 361 """ 362 package android.pkg; 363 364 public interface OnFooBarListener { 365 void bar(); 366 } 367 """ 368 ), 369 java( 370 """ 371 package android.pkg; 372 373 public interface OnFooBarListener { 374 void onFooBar(); // OK 375 } 376 """ 377 ), 378 java( 379 """ 380 package android.pkg; 381 382 public interface MyInterfaceListener { 383 } 384 """ 385 ), 386 java( 387 """ 388 package android.pkg; 389 390 public interface MyListener { 391 public void onFoo() { 392 } 393 public void bar() { 394 } 395 public static void staticMethod() { 396 } 397 public static void finalMethod() { 398 } 399 } 400 """ 401 ) 402 ) 403 ) 404 } 405 406 @Test Test actionsnull407 fun `Test actions`() { 408 check( 409 apiLint = "", // enabled 410 expectedIssues = """ 411 src/android/accounts/Actions.java:7: error: Intent action constant name must be ACTION_FOO: ACCOUNT_ADDED [IntentName] [See https://s.android.com/api-guidelines#use-standard-prefixes-for-constants] 412 src/android/accounts/Actions.java:6: error: Inconsistent action value; expected `android.accounts.action.ACCOUNT_OPENED`, was `android.accounts.ACCOUNT_OPENED` [ActionValue] 413 src/android/accounts/Actions.java:8: error: Intent action constant name must be ACTION_FOO: SOMETHING [IntentName] [See https://s.android.com/api-guidelines#use-standard-prefixes-for-constants] 414 """, 415 expectedFail = DefaultLintErrorMessage, 416 sourceFiles = arrayOf( 417 java( 418 """ 419 package android.accounts; 420 421 public class Actions { 422 private Actions() { } 423 public static final String ACTION_ACCOUNT_REMOVED = "android.accounts.action.ACCOUNT_REMOVED"; 424 public static final String ACTION_ACCOUNT_OPENED = "android.accounts.ACCOUNT_OPENED"; 425 public static final String ACCOUNT_ADDED = "android.accounts.action.ACCOUNT_ADDED"; 426 public static final String SOMETHING = "android.accounts.action.ACCOUNT_MOVED"; 427 } 428 """ 429 ) 430 ) 431 ) 432 } 433 434 @Test Test extrasnull435 fun `Test extras`() { 436 check( 437 apiLint = "", // enabled 438 expectedIssues = """ 439 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] 440 src/android/accounts/Extras.java:7: error: Intent extra constant name must be EXTRA_FOO: RULE_ID [IntentName] [See https://s.android.com/api-guidelines#use-standard-prefixes-for-constants] 441 src/android/accounts/Extras.java:6: error: Intent extra constant name must be EXTRA_FOO: SOMETHING_EXTRA [IntentName] [See https://s.android.com/api-guidelines#use-standard-prefixes-for-constants] 442 """, 443 expectedFail = DefaultLintErrorMessage, 444 sourceFiles = arrayOf( 445 java( 446 """ 447 package android.accounts; 448 449 public class Extras { 450 private Extras() { } 451 public static final String EXTRA_AUTOMATIC_RULE_ID = "android.app.extra.AUTOMATIC_RULE_ID"; 452 public static final String SOMETHING_EXTRA = "something.here"; 453 public static final String RULE_ID = "android.app.extra.AUTOMATIC_RULE_ID"; 454 } 455 """ 456 ) 457 ) 458 ) 459 } 460 461 @Test Test equals and hashCodenull462 fun `Test equals and hashCode`() { 463 check( 464 apiLint = "", // enabled 465 expectedIssues = """ 466 src/android/pkg/MissingEquals.java:4: error: Must override both equals and hashCode; missing one in android.pkg.MissingEquals [EqualsAndHashCode] [See https://s.android.com/api-guidelines#equals-and-hashcode] 467 src/android/pkg/MissingHashCode.java:7: error: Must override both equals and hashCode; missing one in android.pkg.MissingHashCode [EqualsAndHashCode] [See https://s.android.com/api-guidelines#equals-and-hashcode] 468 """, 469 expectedFail = DefaultLintErrorMessage, 470 sourceFiles = arrayOf( 471 java( 472 """ 473 package android.pkg; 474 475 import androidx.annotation.Nullable; 476 477 @SuppressWarnings("EqualsWhichDoesntCheckParameterClass") 478 public class Ok { 479 public boolean equals(@Nullable Object other) { return true; } 480 public int hashCode() { return 0; } 481 } 482 """ 483 ), 484 java( 485 """ 486 package android.pkg; 487 488 public class MissingEquals { 489 public int hashCode() { return 0; } 490 } 491 """ 492 ), 493 java( 494 """ 495 package android.pkg; 496 497 import androidx.annotation.Nullable; 498 499 @SuppressWarnings("EqualsWhichDoesntCheckParameterClass") 500 public class MissingHashCode { 501 public boolean equals(@Nullable Object other) { return true; } 502 } 503 """ 504 ), 505 java( 506 """ 507 package android.pkg; 508 509 import androidx.annotation.Nullable; 510 511 public class UnrelatedEquals { 512 @SuppressWarnings("EqualsWhichDoesntCheckParameterClass") 513 public static boolean equals(@Nullable Object other) { return true; } // static 514 public boolean equals(int other) { return false; } // wrong parameter type 515 public boolean equals(@Nullable Object other, int bar) { return false; } // wrong signature 516 } 517 """ 518 ), 519 androidxNullableSource 520 ) 521 ) 522 } 523 524 @Test Test Parcelablenull525 fun `Test Parcelable`() { 526 check( 527 apiLint = "", // enabled 528 expectedIssues = """ 529 src/android/pkg/MissingCreator.java:5: error: Parcelable requires a `CREATOR` field; missing in android.pkg.MissingCreator [ParcelCreator] [See https://s.android.com/api-guidelines#parcelable-creator] 530 src/android/pkg/MissingDescribeContents.java:5: error: Parcelable requires `public int describeContents()`; missing in android.pkg.MissingDescribeContents [ParcelCreator] [See https://s.android.com/api-guidelines#parcelable-creator] 531 src/android/pkg/MissingWriteToParcel.java:5: error: Parcelable requires `void writeToParcel(Parcel, int)`; missing in android.pkg.MissingWriteToParcel [ParcelCreator] [See https://s.android.com/api-guidelines#parcelable-creator] 532 src/android/pkg/NonFinalParcelable.java:5: error: Parcelable classes must be final: android.pkg.NonFinalParcelable is not final [ParcelNotFinal] [See https://s.android.com/api-guidelines#parcelable-final] 533 src/android/pkg/ParcelableConstructor.java:6: error: Parcelable inflation is exposed through CREATOR, not raw constructors, in android.pkg.ParcelableConstructor [ParcelConstructor] [See https://s.android.com/api-guidelines#parcelable-creator] 534 """, 535 expectedFail = DefaultLintErrorMessage, 536 sourceFiles = arrayOf( 537 java( 538 """ 539 package android.pkg; 540 541 import androidx.annotation.Nullable; 542 543 public final class ParcelableConstructor implements android.os.Parcelable { 544 public ParcelableConstructor(@Nullable android.os.Parcel p) { } 545 public int describeContents() { return 0; } 546 public void writeToParcel(@Nullable android.os.Parcel p, int f) { } 547 @Nullable 548 public static final android.os.Parcelable.Creator<android.app.WallpaperColors> CREATOR = null; 549 } 550 """ 551 ), 552 java( 553 """ 554 package android.pkg; 555 556 import androidx.annotation.Nullable; 557 558 public class NonFinalParcelable implements android.os.Parcelable { 559 public NonFinalParcelable() { } 560 public int describeContents() { return 0; } 561 public void writeToParcel(@Nullable android.os.Parcel p, int f) { } 562 @Nullable 563 public static final android.os.Parcelable.Creator<android.app.WallpaperColors> CREATOR = null; 564 } 565 """ 566 ), 567 java( 568 """ 569 package android.pkg; 570 571 import androidx.annotation.Nullable; 572 573 public final class MissingCreator implements android.os.Parcelable { 574 public MissingCreator() { } 575 public int describeContents() { return 0; } 576 public void writeToParcel(@Nullable android.os.Parcel p, int f) { } 577 } 578 """ 579 ), 580 java( 581 """ 582 package android.pkg; 583 584 import androidx.annotation.Nullable; 585 586 public final class MissingDescribeContents implements android.os.Parcelable { 587 public MissingDescribeContents() { } 588 public void writeToParcel(@Nullable android.os.Parcel p, int f) { } 589 @Nullable 590 public static final android.os.Parcelable.Creator<android.app.WallpaperColors> CREATOR = null; 591 } 592 """ 593 ), 594 java( 595 """ 596 package android.pkg; 597 598 import androidx.annotation.Nullable; 599 600 public final class MissingWriteToParcel implements android.os.Parcelable { 601 public MissingWriteToParcel() { } 602 public int describeContents() { return 0; } 603 @Nullable 604 public static final android.os.Parcelable.Creator<android.app.WallpaperColors> CREATOR = null; 605 } 606 """ 607 ), 608 androidxNullableSource 609 ) 610 ) 611 } 612 613 @Test No protected methods or fields are allowednull614 fun `No protected methods or fields are allowed`() { 615 check( 616 apiLint = "", // enabled 617 expectedIssues = """ 618 src/android/pkg/MyClass.java:6: error: Protected methods not allowed; must be public: method android.pkg.MyClass.wrong()} [ProtectedMember] [See https://s.android.com/api-guidelines#avoid-protected] 619 src/android/pkg/MyClass.java:8: error: Protected fields not allowed; must be public: field android.pkg.MyClass.wrong} [ProtectedMember] [See https://s.android.com/api-guidelines#avoid-protected] 620 """, 621 expectedFail = DefaultLintErrorMessage, 622 sourceFiles = arrayOf( 623 java( 624 """ 625 package android.pkg; 626 627 public abstract class MyClass implements AutoCloseable { 628 public void ok() { } 629 protected void finalize() { } // OK 630 protected void wrong() { } 631 public final int ok = 42; 632 protected final int wrong = 5; 633 private int ok2 = 2; 634 } 635 """ 636 ) 637 ) 638 ) 639 } 640 641 @Test Fields must be final and properly namednull642 fun `Fields must be final and properly named`() { 643 check( 644 apiLint = "", // enabled 645 expectedIssues = """ 646 src/android/pkg/MyClass.java:11: error: Non-static field ALSO_BAD_CONSTANT must be named using fooBar style [StartWithLower] [See https://s.android.com/api-guidelines#style-conventions] 647 src/android/pkg/MyClass.java:11: error: Constant ALSO_BAD_CONSTANT must be marked static final [AllUpper] [See https://s.android.com/api-guidelines#constant-naming] 648 src/android/pkg/MyClass.java:7: error: Non-static field AlsoBadName must be named using fooBar style [StartWithLower] [See https://s.android.com/api-guidelines#style-conventions] 649 src/android/pkg/MyClass.java:10: error: Bare field BAD_CONSTANT must be marked final, or moved behind accessors if mutable [MutableBareField] [See https://s.android.com/api-guidelines#mutable-bare-field] 650 src/android/pkg/MyClass.java:10: error: Constant BAD_CONSTANT must be marked static final [AllUpper] [See https://s.android.com/api-guidelines#constant-naming] 651 src/android/pkg/MyClass.java:5: error: Bare field badMutable must be marked final, or moved behind accessors if mutable [MutableBareField] [See https://s.android.com/api-guidelines#mutable-bare-field] 652 src/android/pkg/MyClass.java:9: error: Bare field badStaticMutable must be marked final, or moved behind accessors if mutable [MutableBareField] [See https://s.android.com/api-guidelines#mutable-bare-field] 653 src/android/pkg/MyClass.java:6: error: Internal field mBadName must not be exposed [InternalField] [See https://s.android.com/api-guidelines#internal-fields] 654 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] [See https://s.android.com/api-guidelines#constant-naming] 655 src/android/pkg/MyClass.java:8: error: Internal field sBadStaticName must not be exposed [InternalField] [See https://s.android.com/api-guidelines#internal-fields] 656 src/android/pkg/MyClass.java:15: error: Internal field mBad must not be exposed [InternalField] [See https://s.android.com/api-guidelines#internal-fields] 657 """, 658 expectedFail = DefaultLintErrorMessage, 659 sourceFiles = arrayOf( 660 java( 661 """ 662 package android.pkg; 663 664 public class MyClass { 665 private int mOk; 666 public int badMutable; 667 public final int mBadName; 668 public final int AlsoBadName; 669 public static final int sBadStaticName; 670 public static int badStaticMutable; 671 public static int BAD_CONSTANT; 672 public final int ALSO_BAD_CONSTANT; 673 674 public static class LayoutParams { 675 public int ok; 676 public int mBad; 677 } 678 } 679 """ 680 ), 681 kotlin( 682 """ 683 package android.pkg 684 685 class KotlinClass(val ok: Int) { 686 companion object { 687 const val OkConstant = 1 688 const val OK_CONSTANT = 2 689 @JvmField 690 val OkSingleton = KotlinClass(3) 691 @JvmField 692 val OK_SINGLETON = KotlinClass(4) 693 } 694 695 object OkObject 696 } 697 """ 698 ) 699 ) 700 ) 701 } 702 703 @Test Only android_net_Uri allowednull704 fun `Only android_net_Uri allowed`() { 705 check( 706 apiLint = "", // enabled 707 expectedIssues = """ 708 src/android/pkg/MyClass.java:7: error: Use android.net.Uri instead of java.net.URL (method android.pkg.MyClass.bad1()) [AndroidUri] [See https://s.android.com/api-guidelines#android-uri] 709 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] [See https://s.android.com/api-guidelines#android-uri] 710 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] [See https://s.android.com/api-guidelines#android-uri] 711 """, 712 expectedFail = DefaultLintErrorMessage, 713 sourceFiles = arrayOf( 714 java( 715 """ 716 package android.pkg; 717 718 import java.util.List;import java.util.concurrent.CompletableFuture;import java.util.concurrent.Future; 719 import androidx.annotation.NonNull; 720 721 public final class MyClass { 722 public @NonNull java.net.URL bad1() { throw new RuntimeException(); } 723 public void bad2(@NonNull List<java.net.URI> param) { } 724 public void bad3(@NonNull android.net.URL param) { } 725 } 726 """ 727 ), 728 androidxNonNullSource 729 ) 730 ) 731 } 732 733 @Test CompletableFuture and plain Future not allowednull734 fun `CompletableFuture and plain Future not allowed`() { 735 check( 736 apiLint = "", // enabled 737 expectedIssues = """ 738 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] [See https://s.android.com/api-guidelines#bad-future] 739 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] [See https://s.android.com/api-guidelines#bad-future] 740 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] [See https://s.android.com/api-guidelines#bad-future] 741 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] [See https://s.android.com/api-guidelines#bad-future] 742 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] [See https://s.android.com/api-guidelines#bad-future] 743 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] [See https://s.android.com/api-guidelines#bad-future] 744 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] [See https://s.android.com/api-guidelines#bad-future] 745 """, 746 expectedFail = DefaultLintErrorMessage, 747 sourceFiles = arrayOf( 748 java( 749 """ 750 package android.pkg; 751 752 import java.util.concurrent.CompletableFuture; 753 import java.util.concurrent.Future; 754 import androidx.annotation.Nullable; 755 import com.google.common.util.concurrent.ListenableFuture; 756 757 public final class MyClass { 758 public @Nullable CompletableFuture<String> bad1() { return null; } 759 public void bad2(@Nullable CompletableFuture<String> param) { } 760 public @Nullable Future<String> bad3() { return null; } 761 public void bad4(@Nullable Future<String> param) { } 762 763 public @Nullable ListenableFuture<String> okAsync() { return null; } 764 public void ok2(@Nullable ListenableFuture<String> param) { } 765 766 public interface BadFuture<T> extends Future<T> { 767 } 768 public static abstract class BadFutureClass<T> implements Future<T> { 769 } 770 public class BadCompletableFuture<T> extends CompletableFuture<T> { 771 } 772 } 773 """ 774 ), 775 java( 776 """ 777 package com.google.common.util.concurrent; 778 public class ListenableFuture<T> { 779 } 780 """ 781 ), 782 androidxNullableSource 783 ) 784 ) 785 } 786 787 @Test Typedef must be hiddennull788 fun `Typedef must be hidden`() { 789 check( 790 apiLint = "", // enabled 791 expectedIssues = """ 792 src/android/pkg/MyClass.java:19: error: Don't expose @IntDef: SomeInt must be hidden. [PublicTypedef] [See https://s.android.com/api-guidelines#no-public-typedefs] 793 src/android/pkg/MyClass.java:24: error: Don't expose @LongDef: SomeLong must be hidden. [PublicTypedef] [See https://s.android.com/api-guidelines#no-public-typedefs] 794 src/android/pkg/MyClass.java:14: error: Don't expose @StringDef: SomeString must be hidden. [PublicTypedef] [See https://s.android.com/api-guidelines#no-public-typedefs] 795 """, 796 expectedFail = DefaultLintErrorMessage, 797 sourceFiles = arrayOf( 798 java( 799 """ 800 package android.pkg; 801 802 public final class MyClass { 803 private MyClass() {} 804 805 public static final String SOME_STRING = "abc"; 806 public static final int SOME_INT = 1; 807 public static final long SOME_LONG = 1L; 808 809 @android.annotation.StringDef(value = { 810 SOME_STRING 811 }) 812 @Retention(RetentionPolicy.SOURCE) 813 public @interface SomeString {} 814 @android.annotation.IntDef(value = { 815 SOME_INT 816 }) 817 @Retention(RetentionPolicy.SOURCE) 818 public @interface SomeInt {} 819 @android.annotation.LongDef(value = { 820 SOME_LONG 821 }) 822 @Retention(RetentionPolicy.SOURCE) 823 public @interface SomeLong {} 824 } 825 """ 826 ) 827 ) 828 ) 829 } 830 831 @Test Ensure registration methods are matchednull832 fun `Ensure registration methods are matched`() { 833 check( 834 apiLint = "", // enabled 835 expectedIssues = """ 836 src/android/pkg/RegistrationInterface.java:6: error: Found registerOverriddenUnpairedCallback but not unregisterOverriddenUnpairedCallback in android.pkg.RegistrationInterface [PairedRegistration] [See https://s.android.com/api-guidelines#callbacks-symmetry] 837 src/android/pkg/RegistrationMethods.java:8: error: Found registerUnpairedCallback but not unregisterUnpairedCallback in android.pkg.RegistrationMethods [PairedRegistration] [See https://s.android.com/api-guidelines#callbacks-symmetry] 838 src/android/pkg/RegistrationMethods.java:12: error: Found unregisterMismatchedCallback but not registerMismatchedCallback in android.pkg.RegistrationMethods [PairedRegistration] [See https://s.android.com/api-guidelines#callbacks-symmetry] 839 src/android/pkg/RegistrationMethods.java:13: error: Callback methods should be named register/unregister; was addCallback [RegistrationName] [See https://s.android.com/api-guidelines#callbacks-accessors] 840 src/android/pkg/RegistrationMethods.java:18: error: Found addUnpairedListener but not removeUnpairedListener in android.pkg.RegistrationMethods [PairedRegistration] [See https://s.android.com/api-guidelines#callbacks-symmetry] 841 src/android/pkg/RegistrationMethods.java:19: error: Found removeMismatchedListener but not addMismatchedListener in android.pkg.RegistrationMethods [PairedRegistration] [See https://s.android.com/api-guidelines#callbacks-symmetry] 842 src/android/pkg/RegistrationMethods.java:20: error: Listener methods should be named add/remove; was registerWrongListener [RegistrationName] [See https://s.android.com/api-guidelines#callbacks-accessors] 843 """, 844 expectedFail = DefaultLintErrorMessage, 845 sourceFiles = arrayOf( 846 java( 847 """ 848 package android.pkg; 849 850 import androidx.annotation.Nullable; 851 852 public class RegistrationMethods implements RegistrationInterface { 853 public void registerOkCallback(@Nullable Runnable r) { } // OK 854 public void unregisterOkCallback(@Nullable Runnable r) { } // OK 855 public void registerUnpairedCallback(@Nullable Runnable r) { } 856 // OK here because it is override 857 @Override 858 public void registerOverriddenUnpairedCallback(@Nullable Runnable r) { } 859 public void unregisterMismatchedCallback(@Nullable Runnable r) { } 860 public void addCallback(@Nullable Runnable r) { } 861 862 public void addOkListener(@Nullable Runnable r) { } // OK 863 public void removeOkListener(@Nullable Runnable r) { } // OK 864 865 public void addUnpairedListener(@Nullable Runnable r) { } 866 public void removeMismatchedListener(@Nullable Runnable r) { } 867 public void registerWrongListener(@Nullable Runnable r) { } 868 } 869 """ 870 ), 871 java( 872 """ 873 package android.pkg; 874 875 import androidx.annotation.Nullable; 876 877 public interface RegistrationInterface { 878 void registerOverriddenUnpairedCallback(@Nullable Runnable r) { } 879 } 880 """ 881 ), 882 androidxNullableSource 883 ) 884 ) 885 } 886 887 @Test Api methods should not be synchronized in their signaturenull888 fun `Api methods should not be synchronized in their signature`() { 889 check( 890 apiLint = "", // enabled 891 expectedIssues = """ 892 src/android/pkg/CheckSynchronization.java:12: error: Internal locks must not be exposed: method android.pkg.CheckSynchronization.errorMethod1(Runnable) [VisiblySynchronized] [See https://s.android.com/api-guidelines#avoid-synchronized] 893 src/android/pkg/CheckSynchronization.java:14: error: Internal locks must not be exposed (synchronizing on this or class is still externally observable): method android.pkg.CheckSynchronization.errorMethod2() [VisiblySynchronized] [See https://s.android.com/api-guidelines#avoid-synchronized] 894 src/android/pkg/CheckSynchronization.java:18: error: Internal locks must not be exposed (synchronizing on this or class is still externally observable): method android.pkg.CheckSynchronization.errorMethod2() [VisiblySynchronized] [See https://s.android.com/api-guidelines#avoid-synchronized] 895 src/android/pkg/CheckSynchronization.java:23: error: Internal locks must not be exposed (synchronizing on this or class is still externally observable): method android.pkg.CheckSynchronization.errorMethod3() [VisiblySynchronized] [See https://s.android.com/api-guidelines#avoid-synchronized] 896 src/android/pkg/CheckSynchronization2.kt:5: error: Internal locks must not be exposed (synchronizing on this or class is still externally observable): method android.pkg.CheckSynchronization2.errorMethod1() [VisiblySynchronized] [See https://s.android.com/api-guidelines#avoid-synchronized] 897 src/android/pkg/CheckSynchronization2.kt:8: error: Internal locks must not be exposed (synchronizing on this or class is still externally observable): method android.pkg.CheckSynchronization2.errorMethod2() [VisiblySynchronized] [See https://s.android.com/api-guidelines#avoid-synchronized] 898 src/android/pkg/CheckSynchronization2.kt:13: error: Internal locks must not be exposed (synchronizing on this or class is still externally observable): method android.pkg.CheckSynchronization2.errorMethod3() [VisiblySynchronized] [See https://s.android.com/api-guidelines#avoid-synchronized] 899 src/android/pkg/CheckSynchronization2.kt:16: error: Internal locks must not be exposed (synchronizing on this or class is still externally observable): method android.pkg.CheckSynchronization2.errorMethod4() [VisiblySynchronized] [See https://s.android.com/api-guidelines#avoid-synchronized] 900 src/android/pkg/CheckSynchronization2.kt:18: error: Internal locks must not be exposed (synchronizing on this or class is still externally observable): method android.pkg.CheckSynchronization2.errorMethod5() [VisiblySynchronized] [See https://s.android.com/api-guidelines#avoid-synchronized] 901 """, 902 expectedFail = DefaultLintErrorMessage, 903 sourceFiles = arrayOf( 904 java( 905 """ 906 package android.pkg; 907 908 import androidx.annotation.Nullable; 909 910 public class CheckSynchronization { 911 public void okMethod1(@Nullable Runnable r) { } 912 private static final Object LOCK = new Object(); 913 public void okMethod2() { 914 synchronized(LOCK) { 915 } 916 } 917 public synchronized void errorMethod1(@Nullable Runnable r) { } // ERROR 918 public void errorMethod2() { 919 synchronized(this) { 920 } 921 } 922 public void errorMethod2() { 923 synchronized(CheckSynchronization.class) { 924 } 925 } 926 public void errorMethod3() { 927 if (true) { 928 synchronized(CheckSynchronization.class) { 929 } 930 } 931 } 932 } 933 """ 934 ), 935 kotlin( 936 """ 937 package android.pkg 938 939 class CheckSynchronization2 { 940 fun errorMethod1() { 941 synchronized(this) { println("hello") } 942 } 943 fun errorMethod2() { 944 synchronized(CheckSynchronization2::class.java) { println("hello") } 945 } 946 fun errorMethod3() { 947 @Suppress("ConstantConditionIf") 948 if (true) { 949 synchronized(CheckSynchronization2::class.java) { println("hello") } 950 } 951 } 952 fun errorMethod4() = synchronized(this) { println("hello") } 953 fun errorMethod5() { 954 synchronized(CheckSynchronization2::class) { println("hello") } 955 } 956 fun okMethod() { 957 val lock = Object() 958 synchronized(lock) { println("hello") } 959 } 960 } 961 """ 962 ), 963 androidxNullableSource, 964 nullableSource 965 ) 966 ) 967 } 968 969 @Test Check intent builder namesnull970 fun `Check intent builder names`() { 971 check( 972 apiLint = "", // enabled 973 expectedIssues = """ 974 src/android/pkg/IntentBuilderNames.java:9: warning: Methods creating an Intent should be named `create<Foo>Intent()`, was `makeMyIntent` [IntentBuilderName] [See https://s.android.com/api-guidelines#intent-builder-createintent] 975 src/android/pkg/IntentBuilderNames.java:11: warning: Methods creating an Intent should be named `create<Foo>Intent()`, was `createIntentNow` [IntentBuilderName] [See https://s.android.com/api-guidelines#intent-builder-createintent] 976 """, 977 sourceFiles = arrayOf( 978 java( 979 """ 980 package android.pkg; 981 import android.content.Intent; 982 import androidx.annotation.Nullable; 983 984 public class IntentBuilderNames { 985 @Nullable 986 public Intent createEnrollIntent() { return null; } // OK 987 @Nullable 988 public Intent makeMyIntent() { return null; } // WARN 989 @Nullable 990 public Intent createIntentNow() { return null; } // WARN 991 } 992 """ 993 ), 994 androidxNullableSource 995 ) 996 ) 997 } 998 999 @Test Check helper classesnull1000 fun `Check helper classes`() { 1001 check( 1002 apiLint = "", // enabled 1003 expectedIssues = """ 1004 src/android/pkg/MyClass1.java:3: error: Inconsistent class name; should be `<Foo>Activity`, was `MyClass1` [ContextNameSuffix] [See https://s.android.com/api-guidelines#classes-subclass-naming] 1005 src/android/pkg/MyClass1.java:6: warning: Methods implemented by developers should follow the on<Something> style, was `badlyNamedAbstractMethod` [OnNameExpected] [See https://s.android.com/api-guidelines#callback-method-naming] 1006 src/android/pkg/MyClass1.java:7: warning: If implemented by developer, should follow the on<Something> style; otherwise consider marking final [OnNameExpected] [See https://s.android.com/api-guidelines#callback-method-naming] 1007 src/android/pkg/MyClass1.java:3: error: MyClass1 should not extend `Activity`. Activity subclasses are impossible to compose. Expose a composable API instead. [ForbiddenSuperClass] 1008 src/android/pkg/MyClass2.java:3: error: Inconsistent class name; should be `<Foo>Provider`, was `MyClass2` [ContextNameSuffix] [See https://s.android.com/api-guidelines#classes-subclass-naming] 1009 src/android/pkg/MyClass3.java:3: error: Inconsistent class name; should be `<Foo>Service`, was `MyClass3` [ContextNameSuffix] [See https://s.android.com/api-guidelines#classes-subclass-naming] 1010 src/android/pkg/MyClass4.java:3: error: Inconsistent class name; should be `<Foo>Receiver`, was `MyClass4` [ContextNameSuffix] [See https://s.android.com/api-guidelines#classes-subclass-naming] 1011 src/android/pkg/MyOkActivity.java:3: error: MyOkActivity should not extend `Activity`. Activity subclasses are impossible to compose. Expose a composable API instead. [ForbiddenSuperClass] 1012 """, 1013 expectedFail = DefaultLintErrorMessage, 1014 sourceFiles = arrayOf( 1015 java( 1016 """ 1017 package android.pkg; 1018 1019 public class MyClass1 extends android.app.Activity { 1020 public void onOk() { } 1021 public final void ok() { } 1022 public abstract void badlyNamedAbstractMethod(); 1023 public void badlyNamedMethod() { } 1024 public static void staticOk() { } 1025 } 1026 """ 1027 ), 1028 java( 1029 """ 1030 package android.pkg; 1031 1032 public class MyClass2 extends android.content.ContentProvider { 1033 public static final String PROVIDER_INTERFACE = "android.pkg.MyClass2"; 1034 public final void ok(); 1035 } 1036 """ 1037 ), 1038 java( 1039 """ 1040 package android.pkg; 1041 1042 public class MyClass3 extends android.app.Service { 1043 public static final String SERVICE_INTERFACE = "android.pkg.MyClass3"; 1044 public final void ok(); 1045 } 1046 """ 1047 ), 1048 java( 1049 """ 1050 package android.pkg; 1051 1052 public class MyClass4 extends android.content.BroadcastReceiver { 1053 } 1054 """ 1055 ), 1056 java( 1057 """ 1058 package android.pkg; 1059 1060 public class MyOkActivity extends android.app.Activity { 1061 } 1062 """ 1063 ) 1064 ) 1065 ) 1066 } 1067 1068 @Test Check buildersnull1069 fun `Check builders`() { 1070 check( 1071 apiLint = "", // enabled 1072 expectedIssues = """ 1073 src/android/pkg/Bad.java:12: warning: Builder must be final: android.pkg.Bad.BadBuilder [StaticFinalBuilder] [See https://s.android.com/api-guidelines#builders-static-inner] 1074 src/android/pkg/Bad.java:12: warning: Builder must be static: android.pkg.Bad.BadBuilder [StaticFinalBuilder] [See https://s.android.com/api-guidelines#builders-static-inner] 1075 src/android/pkg/Bad.java:13: warning: Builder constructor arguments must be mandatory (i.e. not @Nullable): parameter badParameter in android.pkg.Bad.BadBuilder(String badParameter) [OptionalBuilderConstructorArgument] [See https://s.android.com/api-guidelines#builders-nonnull-constructors] 1076 src/android/pkg/Bad.java:37: warning: Builder methods names should use setFoo() / addFoo() / clearFoo() style: method android.pkg.Bad.BadBuilder.withBadSetterStyle(boolean) [BuilderSetStyle] [See https://s.android.com/api-guidelines#builder-method-naming] 1077 src/android/pkg/Bad.java:40: warning: Builder setter must be @NonNull: method android.pkg.Bad.BadBuilder.setReturnsNullable(boolean) [SetterReturnsThis] 1078 src/android/pkg/Bad.java:42: warning: Getter should be on the built object, not the builder: method android.pkg.Bad.BadBuilder.getOnBuilder() [GetterOnBuilder] [See https://s.android.com/api-guidelines#getter-on-builder] 1079 src/android/pkg/Bad.java:44: warning: Methods must return the builder object (return type android.pkg.Bad.BadBuilder instead of void): method android.pkg.Bad.BadBuilder.setNotReturningBuilder(boolean) [SetterReturnsThis] 1080 src/android/pkg/Bad.java:19: warning: android.pkg.Bad does not declare a `getWithoutMatchingGetters()` method matching method android.pkg.Bad.BadBuilder.addWithoutMatchingGetter(String) [MissingGetterMatchingBuilder] [See https://s.android.com/api-guidelines#builders-symmetric-setters] 1081 src/android/pkg/Bad.java:22: warning: android.pkg.Bad does not declare a `isWithoutMatchingGetter()` method matching method android.pkg.Bad.BadBuilder.setWithoutMatchingGetter(boolean) [MissingGetterMatchingBuilder] [See https://s.android.com/api-guidelines#builders-symmetric-setters] 1082 src/android/pkg/Bad.java:25: warning: android.pkg.Bad does not declare a `getPluralWithoutMatchingGetters()` method matching method android.pkg.Bad.BadBuilder.addPluralWithoutMatchingGetter(Collection<String>) [MissingGetterMatchingBuilder] [See https://s.android.com/api-guidelines#builders-symmetric-setters] 1083 src/android/pkg/Bad.java:31: warning: android.pkg.Bad does not declare a getter method matching method android.pkg.Bad.BadBuilder.addPluralWithoutMatchingGetters(Collection<String>) (expected one of: [getPluralWithoutMatchingGetters(), getPluralWithoutMatchingGetterses()]) [MissingGetterMatchingBuilder] [See https://s.android.com/api-guidelines#builders-symmetric-setters] 1084 src/android/pkg/Bad.java:44: warning: android.pkg.Bad does not declare a `isNotReturningBuilder()` method matching method android.pkg.Bad.BadBuilder.setNotReturningBuilder(boolean) [MissingGetterMatchingBuilder] [See https://s.android.com/api-guidelines#builders-symmetric-setters] 1085 src/android/pkg/Bad.java:56: warning: Methods must return the builder object (return type android.pkg.Bad.BadGenericBuilder<T> instead of T): method android.pkg.Bad.BadGenericBuilder.setBoolean(boolean) [SetterReturnsThis] 1086 src/android/pkg/Bad.java:50: warning: android.pkg.Bad.NoBuildMethodBuilder does not declare a `build()` method, but builder classes are expected to [MissingBuildMethod] [See https://s.android.com/api-guidelines#builder-must-declare-build] 1087 src/android/pkg/TopLevelBuilder.java:3: warning: Builder should be defined as inner class: android.pkg.TopLevelBuilder [TopLevelBuilder] [See https://s.android.com/api-guidelines#builders-static-inner] 1088 src/android/pkg/TopLevelBuilder.java:3: warning: android.pkg.TopLevelBuilder does not declare a `build()` method, but builder classes are expected to [MissingBuildMethod] [See https://s.android.com/api-guidelines#builder-must-declare-build] 1089 """, 1090 sourceFiles = arrayOf( 1091 java( 1092 """ 1093 package android.pkg; 1094 1095 public final class TopLevelBuilder { 1096 } 1097 """ 1098 ), 1099 java( 1100 """ 1101 package android.pkg; 1102 1103 import androidx.annotation.NonNull; 1104 import androidx.annotation.Nullable; 1105 1106 public class Ok { 1107 1108 public int getInt(); 1109 @NonNull 1110 public List<String> getStrings(); 1111 @NonNull 1112 public List<String> getProperties(); 1113 @NonNull 1114 public List<String> getRays(); 1115 @NonNull 1116 public List<String> getBuses(); 1117 @NonNull 1118 public List<String> getTaxes(); 1119 @NonNull 1120 public List<String> getMessages(); 1121 public boolean isBoolean(); 1122 public boolean hasBoolean2(); 1123 public boolean shouldBoolean3(); 1124 1125 public static final class OkBuilder { 1126 public OkBuilder(@NonNull String goodParameter, int goodParameter2) {} 1127 1128 @NonNull 1129 public Ok build() { return null; } 1130 1131 @NonNull 1132 public OkBuilder setInt(int value) { return this; } 1133 1134 @NonNull 1135 public OkBuilder addString(@NonNull String value) { return this; } 1136 1137 @NonNull 1138 public OkBuilder addProperty(@NonNull String value) { return this; } 1139 1140 @NonNull 1141 public OkBuilder addRay(@NonNull String value) { return this; } 1142 1143 @NonNull 1144 public OkBuilder addBus(@NonNull String value) { return this; } 1145 1146 @NonNull 1147 public OkBuilder addTax(@NonNull String value) { return this; } 1148 1149 @NonNull 1150 public OkBuilder addMessages(@NonNull Collection<String> value) { 1151 return this; 1152 } 1153 1154 @NonNull 1155 public OkBuilder clearStrings() { return this; } 1156 1157 @NonNull 1158 public OkBuilder setBoolean(boolean v) { return this; } 1159 1160 @NonNull 1161 public OkBuilder setHasBoolean2(boolean v) { return this; } 1162 1163 @NonNull 1164 public OkBuilder setShouldBoolean3(boolean v) { return this; } 1165 1166 @NonNull 1167 public OkBuilder clear() { return this; } 1168 1169 @NonNull 1170 public OkBuilder clearAll() { return this; } 1171 } 1172 1173 public static final class GenericBuilder<B extends GenericBuilder> { 1174 @NonNull 1175 public B setBoolean(boolean value) { return this; } 1176 1177 @NonNull 1178 public Ok build() { return null; } 1179 } 1180 } 1181 """ 1182 ), 1183 java( 1184 """ 1185 package android.pkg; 1186 1187 public class SubOk extends Ok { 1188 1189 public static final class Builder { 1190 public Builder() {} 1191 1192 @NonNull 1193 public SubOk build() { return null; } 1194 1195 @NonNull 1196 public Builder setInt(int value) { return this; } 1197 } 1198 } 1199 """ 1200 ), 1201 java( 1202 """ 1203 package android.pkg; 1204 1205 import androidx.annotation.NonNull; 1206 import androidx.annotation.Nullable; 1207 1208 public class Bad { 1209 1210 public boolean isBoolean(); 1211 public boolean getWithoutMatchingGetter(); 1212 public boolean isReturnsNullable(); 1213 1214 public class BadBuilder { 1215 public BadBuilder(@Nullable String badParameter) {} 1216 1217 @NonNull 1218 public Bad build() { return null; } 1219 1220 @NonNull 1221 public BadBuilder addWithoutMatchingGetter(@NonNull String value) { return this; } 1222 1223 @NonNull 1224 public BadBuilder setWithoutMatchingGetter(boolean v) { return this; } 1225 1226 @NonNull 1227 public BadBuilder addPluralWithoutMatchingGetter( 1228 @NonNull Collection<String> value) { 1229 return this; 1230 } 1231 1232 @NonNull 1233 public BadBuilder addPluralWithoutMatchingGetters( 1234 @NonNull Collection<String> value) { 1235 return this; 1236 } 1237 1238 @NonNull 1239 public BadBuilder withBadSetterStyle(boolean v) { return this; } 1240 1241 @Nullable 1242 public BadBuilder setReturnsNullable(boolean v) { return this; } 1243 1244 public boolean getOnBuilder() { return true; } 1245 1246 public void setNotReturningBuilder(boolean v) { return this; } 1247 1248 @NonNull 1249 public BadBuilder () { return this; } 1250 } 1251 1252 public static final class NoBuildMethodBuilder { 1253 public NoBuildMethodBuilder() {} 1254 } 1255 1256 public static final class BadGenericBuilder<T extends Bad> { 1257 @NonNull 1258 public T setBoolean(boolean value) { return this; } 1259 1260 @NonNull 1261 public Bad build() { return null; } 1262 } 1263 } 1264 """ 1265 ), 1266 androidxNonNullSource, 1267 androidxNullableSource 1268 ) 1269 ) 1270 } 1271 1272 @Test Check suppress works on inherited methodsnull1273 fun `Check suppress works on inherited methods`() { 1274 check( 1275 apiLint = "", // enabled 1276 expectedIssues = """ 1277 warning: Should avoid odd sized primitives; use `int` instead of `short` in method android.pkg.Ok.Public.shouldFail(PublicT) [NoByteOrShort] [See https://s.android.com/api-guidelines#avoid-short-byte] 1278 """, 1279 sourceFiles = arrayOf( 1280 java( 1281 """ 1282 package android.pkg; 1283 1284 import androidx.annotation.NonNull; 1285 1286 public class Ok { 1287 1288 static final class Hidden<HiddenT> { 1289 @SuppressWarnings("NoByteOrShort") 1290 public short suppressed(HiddenT t) { return null; } 1291 1292 public short shouldFail(HiddenT t) { return null; } 1293 } 1294 1295 public static final class Public<PublicT> extends Hidden<PublicT> { 1296 } 1297 } 1298 """ 1299 ), 1300 androidxNonNullSource 1301 ) 1302 ) 1303 } 1304 1305 @Test Raw AIDLnull1306 fun `Raw AIDL`() { 1307 check( 1308 apiLint = "", // enabled 1309 expectedIssues = """ 1310 src/android/pkg/MyClass1.java:3: error: Raw AIDL interfaces must not be exposed: MyClass1 extends Binder [RawAidl] [See https://s.android.com/api-guidelines#no-public-binder] 1311 src/android/pkg/MyClass2.java:3: error: Raw AIDL interfaces must not be exposed: MyClass2 implements IInterface [RawAidl] [See https://s.android.com/api-guidelines#no-public-binder] 1312 """, 1313 expectedFail = DefaultLintErrorMessage, 1314 sourceFiles = arrayOf( 1315 java( 1316 """ 1317 package android.pkg; 1318 1319 public abstract class MyClass1 extends android.os.Binder { 1320 } 1321 """ 1322 ), 1323 java( 1324 """ 1325 package android.pkg; 1326 1327 public abstract class MyClass2 implements android.os.IInterface { 1328 } 1329 """ 1330 ), 1331 java( 1332 """ 1333 package android.pkg; 1334 // Ensure that we don't flag transitively implementing IInterface 1335 public class MyClass3 extends MyClass1 implements MyClass2 { 1336 } 1337 """ 1338 ) 1339 ) 1340 ) 1341 } 1342 1343 @Test Internal packagesnull1344 fun `Internal packages`() { 1345 check( 1346 apiLint = "", // enabled 1347 expectedIssues = """ 1348 src/com/android/pkg/MyClass.java:3: error: Internal classes must not be exposed [InternalClasses] 1349 """, 1350 expectedFail = DefaultLintErrorMessage, 1351 sourceFiles = arrayOf( 1352 java( 1353 """ 1354 package com.android.pkg; 1355 1356 public class MyClass { 1357 } 1358 """ 1359 ) 1360 ) 1361 ) 1362 } 1363 1364 @Test Check package layeringnull1365 fun `Check package layering`() { 1366 check( 1367 apiLint = "", // enabled 1368 expectedIssues = """ 1369 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] 1370 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] 1371 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] 1372 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] 1373 """, 1374 sourceFiles = arrayOf( 1375 java( 1376 """ 1377 package android.content; 1378 1379 import android.graphics.drawable.Drawable; 1380 import android.graphics.Bitmap; 1381 import android.view.View; 1382 import androidx.annotation.Nullable; 1383 1384 public class MyClass1 { 1385 @Nullable 1386 public final View view = null; 1387 @Nullable 1388 public final Drawable drawable = null; 1389 @Nullable 1390 public final Bitmap bitmap = null; 1391 @Nullable 1392 public View ok(@Nullable View view, @Nullable Drawable drawable) { return null; } 1393 @Nullable 1394 public Bitmap wrong(@Nullable View view, @Nullable Bitmap bitmap) { return null; } 1395 } 1396 """ 1397 ), 1398 androidxNullableSource 1399 ) 1400 ) 1401 } 1402 1403 @Test Check boolean getter and setter naming patternsnull1404 fun `Check boolean getter and setter naming patterns`() { 1405 check( 1406 apiLint = "", // enabled 1407 expectedIssues = """ 1408 src/android/pkg/MyClass.java:21: error: Symmetric method for `isVisibleBad` must be named `setVisibleBad`; was `setIsVisibleBad` [GetterSetterNames] 1409 src/android/pkg/MyClass.java:24: error: Symmetric method for `hasTransientStateBad` must be named `setHasTransientStateBad`; was `setTransientStateBad` [GetterSetterNames] 1410 src/android/pkg/MyClass.java:28: error: Symmetric method for `setHasTransientStateAlsoBad` must be named `hasTransientStateAlsoBad`; was `isHasTransientStateAlsoBad` [GetterSetterNames] 1411 src/android/pkg/MyClass.java:31: error: Symmetric method for `setCanRecordBad` must be named `canRecordBad`; was `isCanRecordBad` [GetterSetterNames] 1412 src/android/pkg/MyClass.java:34: error: Symmetric method for `setShouldFitWidthBad` must be named `shouldFitWidthBad`; was `isShouldFitWidthBad` [GetterSetterNames] 1413 src/android/pkg/MyClass.java:37: error: Symmetric method for `setWiFiRoamingSettingEnabledBad` must be named `isWiFiRoamingSettingEnabledBad`; was `getWiFiRoamingSettingEnabledBad` [GetterSetterNames] 1414 src/android/pkg/MyClass.java:40: error: Symmetric method for `setEnabledBad` must be named `isEnabledBad`; was `getEnabledBad` [GetterSetterNames] 1415 """, 1416 expectedFail = DefaultLintErrorMessage, 1417 sourceFiles = arrayOf( 1418 java( 1419 """ 1420 package android.pkg; 1421 1422 public class MyClass { 1423 // Correct 1424 public void setVisible(boolean visible) {} 1425 public boolean isVisible() { return false; } 1426 1427 public void setHasTransientState(boolean hasTransientState) {} 1428 public boolean hasTransientState() { return false; } 1429 1430 public void setCanRecord(boolean canRecord) {} 1431 public boolean canRecord() { return false; } 1432 1433 public void setShouldFitWidth(boolean shouldFitWidth) {} 1434 public boolean shouldFitWidth() { return false; } 1435 1436 public void setWiFiRoamingSettingEnabled(boolean enabled) {} 1437 public boolean isWiFiRoamingSettingEnabled() { return false; } 1438 1439 // Bad 1440 public void setIsVisibleBad(boolean visible) {} 1441 public boolean isVisibleBad() { return false; } 1442 1443 public void setTransientStateBad(boolean hasTransientState) {} 1444 public boolean hasTransientStateBad() { return false; } 1445 1446 public void setHasTransientStateAlsoBad(boolean hasTransientState) {} 1447 public boolean isHasTransientStateAlsoBad() { return false; } 1448 1449 public void setCanRecordBad(boolean canRecord) {} 1450 public boolean isCanRecordBad() { return false; } 1451 1452 public void setShouldFitWidthBad(boolean shouldFitWidth) {} 1453 public boolean isShouldFitWidthBad() { return false; } 1454 1455 public void setWiFiRoamingSettingEnabledBad(boolean enabled) {} 1456 public boolean getWiFiRoamingSettingEnabledBad() { return false; } 1457 1458 public void setEnabledBad(boolean enabled) {} 1459 public boolean getEnabledBad() { return false; } 1460 } 1461 """ 1462 ) 1463 ) 1464 ) 1465 } 1466 1467 @Test Check banned collectionsnull1468 fun `Check banned collections`() { 1469 check( 1470 apiLint = "", // enabled 1471 expectedIssues = """ 1472 src/android/pkg/MyClass.java:6: error: Parameter type is concrete collection (`java.util.HashMap`); must be higher-level interface [ConcreteCollection] [See https://s.android.com/api-guidelines#classes-collections] 1473 src/android/pkg/MyClass.java:10: error: Return type is concrete collection (`java.util.Vector`); must be higher-level interface [ConcreteCollection] [See https://s.android.com/api-guidelines#classes-collections] 1474 src/android/pkg/MyClass.java:10: error: Parameter type is concrete collection (`java.util.LinkedList`); must be higher-level interface [ConcreteCollection] [See https://s.android.com/api-guidelines#classes-collections] 1475 """, 1476 expectedFail = DefaultLintErrorMessage, 1477 sourceFiles = arrayOf( 1478 java( 1479 """ 1480 package android.pkg; 1481 1482 import androidx.annotation.NonNull; 1483 1484 public class MyClass { 1485 public MyClass(@NonNull java.util.HashMap<String,String> map1, 1486 @NonNull java.util.Map<String,String> map2) { 1487 } 1488 @NonNull 1489 public java.util.Vector<String> getList(@NonNull java.util.LinkedList<String> list) { 1490 throw new RuntimeException(); 1491 } 1492 } 1493 """ 1494 ), 1495 androidxNonNullSource 1496 ) 1497 ) 1498 } 1499 1500 @Test Check nullable collectionsnull1501 fun `Check nullable collections`() { 1502 check( 1503 apiLint = "", // enabled 1504 expectedIssues = """ 1505 src/android/pkg/MySubClass.java:5: warning: Public class android.pkg.MySubClass stripped of unavailable superclass android.pkg.MyHiddenInterface [HiddenSuperclass] 1506 src/android/pkg/MyCallback.java:4: warning: Type of parameter list in android.pkg.MyCallback.onFoo(java.util.List<java.lang.String> list) is a nullable collection (`java.util.List`); must be non-null [NullableCollection] [See https://s.android.com/api-guidelines#methods-prefer-non-null-collections] 1507 src/android/pkg/MyClass.java:9: warning: Return type of method android.pkg.MyClass.getList(java.util.List<java.lang.String>) is a nullable collection (`java.util.List`); must be non-null [NullableCollection] [See https://s.android.com/api-guidelines#methods-prefer-non-null-collections] 1508 src/android/pkg/MyClass.java:13: warning: Type of field android.pkg.MyClass.STRINGS is a nullable collection (`java.lang.String[]`); must be non-null [NullableCollection] [See https://s.android.com/api-guidelines#methods-prefer-non-null-collections] 1509 src/android/pkg/MySubClass.java:14: warning: Return type of method android.pkg.MySubClass.getOtherList(java.util.List<java.lang.String>) is a nullable collection (`java.util.List`); must be non-null [NullableCollection] [See https://s.android.com/api-guidelines#methods-prefer-non-null-collections] 1510 """, 1511 sourceFiles = arrayOf( 1512 java( 1513 """ 1514 package android.pkg; 1515 1516 import androidx.annotation.Nullable; 1517 1518 public class MyClass { 1519 public MyClass() { } 1520 1521 @Nullable 1522 public java.util.List<String> getList(@Nullable java.util.List<String> list) { 1523 return null; 1524 } 1525 @Nullable 1526 public static final String[] STRINGS = null; 1527 1528 /** @deprecated don't use this. */ 1529 @Deprecated 1530 @Nullable 1531 public String[] ignoredBecauseDeprecated(@Nullable String[] ignored) { 1532 return null; 1533 } 1534 1535 protected MyClass() { 1536 } 1537 } 1538 """ 1539 ), 1540 java( 1541 """ 1542 package android.pkg; 1543 1544 import androidx.annotation.Nullable; 1545 1546 /** @hide */ 1547 public interface MyHiddenInterface { 1548 @Nullable 1549 java.util.List<String> getOtherList(@Nullable java.util.List<String> list); 1550 } 1551 """ 1552 ), 1553 java( 1554 """ 1555 package android.pkg; 1556 1557 import androidx.annotation.Nullable; 1558 1559 public class MySubClass extends MyClass implements MyHiddenInterface { 1560 @Nullable 1561 public java.util.List<String> getList(@Nullable java.util.List<String> list) { 1562 // Ignored because it has the same nullability as its super method 1563 return null; 1564 } 1565 1566 @Override 1567 @Nullable 1568 public java.util.List<String> getOtherList(@Nullable java.util.List<String> list) { 1569 // Reported because the super method is hidden. 1570 return null; 1571 } 1572 } 1573 """ 1574 ), 1575 java( 1576 """ 1577 package android.pkg; 1578 1579 public class MyCallback { 1580 public void onFoo(@Nullable java.util.List<String> list) { 1581 } 1582 } 1583 """ 1584 ), 1585 androidxNullableSource 1586 ) 1587 ) 1588 } 1589 1590 @Test Check non-overlapping flagsnull1591 fun `Check non-overlapping flags`() { 1592 check( 1593 apiLint = "", // enabled 1594 expectedIssues = """ 1595 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] [See https://s.android.com/api-guidelines#overlapping-constants] 1596 """, 1597 sourceFiles = arrayOf( 1598 java( 1599 """ 1600 package android.accounts; 1601 1602 public class OverlappingFlags { 1603 private OverlappingFlags() { } 1604 public static final int DRAG_FLAG_GLOBAL_PREFIX_URI_PERMISSION = 128; // 0x80 1605 public static final int DRAG_FLAG_GLOBAL_URI_READ = 1; // 0x1 1606 public static final int DRAG_FLAG_GLOBAL_URI_WRITE = 2; // 0x2 1607 public static final int DRAG_FLAG_OPAQUE = 512; // 0x200 1608 public static final int SYSTEM_UI_FLAG_FULLSCREEN = 4; // 0x4 1609 public static final int SYSTEM_UI_FLAG_HIDE_NAVIGATION = 2; // 0x2 1610 public static final int SYSTEM_UI_FLAG_IMMERSIVE = 2048; // 0x800 1611 public static final int SYSTEM_UI_FLAG_IMMERSIVE_STICKY = 4096; // 0x1000 1612 public static final int SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN = 1024; // 0x400 1613 public static final int SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION = 512; // 0x200 1614 public static final int SYSTEM_UI_FLAG_LAYOUT_STABLE = 256; // 0x100 1615 public static final int SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR = 16; // 0x10 1616 1617 public static final int TEST1_FLAG_FIRST = 1; 1618 public static final int TEST1_FLAG_SECOND = 3; 1619 public static final int TEST2_FLAG_FIRST = 5; 1620 } 1621 """ 1622 ) 1623 ) 1624 ) 1625 } 1626 1627 @Test Check exception related issuesnull1628 fun `Check exception related issues`() { 1629 check( 1630 extraArguments = arrayOf( 1631 ARG_API_LINT, 1632 // Conflicting advice: 1633 ARG_HIDE, "BannedThrow" 1634 ), 1635 expectedIssues = """ 1636 src/android/pkg/MyClass.java:6: error: Methods must not throw generic exceptions (`java.lang.Exception`) [GenericException] [See https://s.android.com/api-guidelines#appropriate-exception] 1637 src/android/pkg/MyClass.java:7: error: Methods must not throw generic exceptions (`java.lang.Throwable`) [GenericException] [See https://s.android.com/api-guidelines#appropriate-exception] 1638 src/android/pkg/MyClass.java:8: error: Methods must not throw generic exceptions (`java.lang.Error`) [GenericException] [See https://s.android.com/api-guidelines#appropriate-exception] 1639 src/android/pkg/MyClass.java:11: error: Methods calling system APIs should rethrow `RemoteException` as `RuntimeException` (but do not list it in the throws clause) [RethrowRemoteException] [See https://s.android.com/api-guidelines#appropriate-exception] 1640 """, 1641 expectedFail = DefaultLintErrorMessage, 1642 sourceFiles = arrayOf( 1643 java( 1644 """ 1645 package android.pkg; 1646 import android.os.RemoteException; 1647 1648 @SuppressWarnings("RedundantThrows") 1649 public class MyClass { 1650 public void method1() throws Exception { } 1651 public void method2() throws Throwable { } 1652 public void method3() throws Error { } 1653 public void method4() throws IllegalArgumentException { } 1654 public void method4() throws NullPointerException { } 1655 public void method5() throws RemoteException { } 1656 public void ok(int p) throws NullPointerException { } 1657 } 1658 """ 1659 ) 1660 ) 1661 ) 1662 } 1663 1664 @Test Check no mentions of Google in APIsnull1665 fun `Check no mentions of Google in APIs`() { 1666 check( 1667 apiLint = "", // enabled 1668 expectedIssues = """ 1669 src/android/pkg/MyClass.java:4: error: Must never reference Google (`MyGoogleService`) [MentionsGoogle] [See https://s.android.com/api-guidelines#mentions-google] 1670 src/android/pkg/MyClass.java:5: error: Must never reference Google (`callGoogle`) [MentionsGoogle] [See https://s.android.com/api-guidelines#mentions-google] 1671 """, 1672 expectedFail = DefaultLintErrorMessage, 1673 sourceFiles = arrayOf( 1674 java( 1675 """ 1676 package android.pkg; 1677 1678 public class MyClass { 1679 public static class MyGoogleService { 1680 public void callGoogle() { } 1681 } 1682 } 1683 """ 1684 ) 1685 ) 1686 ) 1687 } 1688 1689 @Test Check no usages of heavy BitSetnull1690 fun `Check no usages of heavy BitSet`() { 1691 check( 1692 apiLint = "", // enabled 1693 expectedIssues = """ 1694 src/android/pkg/MyClass.java:9: error: Type must not be heavy BitSet (method android.pkg.MyClass.reverse(java.util.BitSet)) [HeavyBitSet] [See https://s.android.com/api-guidelines#avoid-bitset] 1695 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] [See https://s.android.com/api-guidelines#avoid-bitset] 1696 src/android/pkg/MyClass.java:7: error: Type must not be heavy BitSet (field android.pkg.MyClass.bitset) [HeavyBitSet] [See https://s.android.com/api-guidelines#avoid-bitset] 1697 """, 1698 expectedFail = DefaultLintErrorMessage, 1699 sourceFiles = arrayOf( 1700 java( 1701 """ 1702 package android.pkg; 1703 import androidx.annotation.Nullable; 1704 import java.util.BitSet; 1705 1706 public class MyClass { 1707 @Nullable 1708 public final BitSet bitset; 1709 @Nullable 1710 public BitSet reverse(@Nullable BitSet bitset) { return null; } 1711 } 1712 """ 1713 ), 1714 androidxNullableSource 1715 ) 1716 ) 1717 } 1718 1719 @Test Check Manager related issuesnull1720 fun `Check Manager related issues`() { 1721 check( 1722 apiLint = "", // enabled 1723 expectedIssues = """ 1724 src/android/pkg/MyFirstManager.java:6: error: Managers must always be obtained from Context; no direct constructors [ManagerConstructor] 1725 src/android/pkg/MyFirstManager.java:9: error: Managers must always be obtained from Context (`get`) [ManagerLookup] 1726 """, 1727 expectedFail = DefaultLintErrorMessage, 1728 sourceFiles = arrayOf( 1729 java( 1730 """ 1731 package android.pkg; 1732 1733 import androidx.annotation.Nullable; 1734 1735 public class MyFirstManager { 1736 public MyFirstManager() { 1737 } 1738 @Nullable 1739 public MyFirstManager get() { return null; } 1740 @Nullable 1741 public MySecondManager ok() { return null; } 1742 } 1743 """ 1744 ), 1745 java( 1746 """ 1747 package android.pkg; 1748 1749 public class MySecondManager { 1750 private MySecondManager() { 1751 } 1752 } 1753 """ 1754 ), 1755 androidxNullableSource 1756 ) 1757 ) 1758 } 1759 1760 @Test Check boxed typesnull1761 fun `Check boxed types`() { 1762 check( 1763 apiLint = "", // enabled 1764 expectedIssues = """ 1765 src/test/pkg/KotlinClass.kt:4: error: Must avoid boxed primitives (`java.lang.Double`) [AutoBoxing] [See https://s.android.com/api-guidelines#auto-boxing] 1766 src/test/pkg/KotlinClass.kt:6: error: Must avoid boxed primitives (`java.lang.Boolean`) [AutoBoxing] [See https://s.android.com/api-guidelines#auto-boxing] 1767 src/test/pkg/MyClass.java:9: error: Must avoid boxed primitives (`java.lang.Long`) [AutoBoxing] [See https://s.android.com/api-guidelines#auto-boxing] 1768 src/test/pkg/MyClass.java:12: error: Must avoid boxed primitives (`java.lang.Short`) [AutoBoxing] [See https://s.android.com/api-guidelines#auto-boxing] 1769 src/test/pkg/MyClass.java:12: error: Must avoid boxed primitives (`java.lang.Double`) [AutoBoxing] [See https://s.android.com/api-guidelines#auto-boxing] 1770 src/test/pkg/MyClass.java:14: error: Must avoid boxed primitives (`java.lang.Boolean`) [AutoBoxing] [See https://s.android.com/api-guidelines#auto-boxing] 1771 src/test/pkg/MyClass.java:7: error: Must avoid boxed primitives (`java.lang.Integer`) [AutoBoxing] [See https://s.android.com/api-guidelines#auto-boxing] 1772 """, 1773 expectedFail = DefaultLintErrorMessage, 1774 sourceFiles = arrayOf( 1775 java( 1776 """ 1777 package test.pkg; 1778 1779 import androidx.annotation.Nullable; 1780 1781 public class MyClass { 1782 @Nullable 1783 public final Integer integer1; 1784 public final int integer2; 1785 public MyClass(@Nullable Long l) { 1786 } 1787 @Nullable 1788 public Short getDouble(@Nullable Double l) { return null; } 1789 @Nullable 1790 public Boolean getBoolean() { return null; } 1791 } 1792 """ 1793 ), 1794 kotlin( 1795 """ 1796 package test.pkg 1797 class KotlinClass { 1798 fun getIntegerOk(): Double { TODO() } 1799 fun getIntegerBad(): Double? { TODO() } 1800 fun getBooleanOk(): Boolean { TODO() } 1801 fun getBooleanBad(): Boolean? { TODO() } 1802 } 1803 """ 1804 ), 1805 androidxNullableSource 1806 ) 1807 ) 1808 } 1809 1810 @Test Check static utilitiesnull1811 fun `Check static utilities`() { 1812 check( 1813 apiLint = "", // enabled 1814 expectedIssues = """ 1815 src/android/pkg/MyUtils1.java:3: error: Fully-static utility classes must not have constructor [StaticUtils] 1816 src/android/pkg/MyUtils2.java:3: error: Fully-static utility classes must not have constructor [StaticUtils] 1817 """, 1818 expectedFail = DefaultLintErrorMessage, 1819 sourceFiles = arrayOf( 1820 java( 1821 """ 1822 package android.pkg; 1823 1824 public class MyUtils1 { 1825 // implicit constructor 1826 public static void foo() { } 1827 } 1828 """ 1829 ), 1830 java( 1831 """ 1832 package android.pkg; 1833 1834 public class MyUtils2 { 1835 public MyUtils2() { } 1836 public static void foo() { } 1837 } 1838 """ 1839 ), 1840 java( 1841 """ 1842 package android.pkg; 1843 1844 public class MyUtils3 { 1845 private MyUtils3() { } 1846 public static void foo() { } 1847 } 1848 """ 1849 ), 1850 java( 1851 """ 1852 package android.pkg; 1853 1854 public class MyUtils4 { 1855 // OK: instance method 1856 public void foo() { } 1857 } 1858 """ 1859 ), 1860 java( 1861 """ 1862 package android.pkg; 1863 1864 public class MyUtils5 { 1865 // OK: instance field 1866 public final int foo = 42; 1867 public static void foo() { } 1868 } 1869 """ 1870 ) 1871 ) 1872 ) 1873 } 1874 1875 @Test Check context firstnull1876 fun `Check context first`() { 1877 check( 1878 apiLint = "", // enabled 1879 expectedIssues = """ 1880 src/android/pkg/MyClass.java:11: error: Context is distinct, so it must be the first argument (method `wrong`) [ContextFirst] 1881 src/android/pkg/MyClass.java:12: error: ContentResolver is distinct, so it must be the first argument (method `wrong`) [ContextFirst] 1882 """, 1883 expectedFail = DefaultLintErrorMessage, 1884 sourceFiles = arrayOf( 1885 java( 1886 """ 1887 package android.pkg; 1888 import android.content.Context; 1889 import android.content.ContentResolver; 1890 import androidx.annotation.Nullable; 1891 1892 public class MyClass { 1893 public MyClass(@Nullable Context context1, @Nullable Context context2) { 1894 } 1895 public void ok(@Nullable ContentResolver resolver, int i) { } 1896 public void ok(@Nullable Context context, int i) { } 1897 public void wrong(int i, @Nullable Context context) { } 1898 public void wrong(int i, @Nullable ContentResolver resolver) { } 1899 } 1900 """ 1901 ), 1902 androidxNullableSource 1903 ) 1904 ) 1905 } 1906 1907 @Test Check listener lastnull1908 fun `Check listener last`() { 1909 check( 1910 extraArguments = arrayOf(ARG_API_LINT, ARG_HIDE, "ExecutorRegistration"), 1911 expectedIssues = """ 1912 src/android/pkg/MyClass.java:7: warning: Listeners should always be at end of argument list (method `MyClass`) [ListenerLast] [See https://s.android.com/api-guidelines#placement-of-sam-parameters] 1913 src/android/pkg/MyClass.java:10: warning: Listeners should always be at end of argument list (method `wrong`) [ListenerLast] [See https://s.android.com/api-guidelines#placement-of-sam-parameters] 1914 """, 1915 sourceFiles = arrayOf( 1916 java( 1917 """ 1918 package android.pkg; 1919 import android.pkg.MyCallback; 1920 import android.content.Context; 1921 import androidx.annotation.Nullable; 1922 1923 public class MyClass { 1924 public MyClass(@Nullable MyCallback listener, int i) { 1925 } 1926 public void ok(@Nullable Context context, int i, @Nullable MyCallback listener) { } 1927 public void wrong(@Nullable MyCallback listener, int i) { } 1928 } 1929 """ 1930 ), 1931 java( 1932 """ 1933 package android.pkg; 1934 1935 @SuppressWarnings("WeakerAccess") 1936 public abstract class MyCallback { 1937 } 1938 """ 1939 ), 1940 androidxNullableSource 1941 ) 1942 ) 1943 } 1944 1945 @Test Check overloaded argumentsnull1946 fun `Check overloaded arguments`() { 1947 // TODO: This check is not yet hooked up 1948 check( 1949 apiLint = "", // enabled 1950 expectedIssues = """ 1951 """, 1952 sourceFiles = arrayOf( 1953 java( 1954 """ 1955 package android.pkg; 1956 1957 public class MyClass { 1958 private MyClass() { 1959 } 1960 1961 public void name1() { } 1962 public void name1(int i) { } 1963 public void name1(int i, int j) { } 1964 public void name1(int i, int j, int k) { } 1965 public void name1(int i, int j, int k, float f) { } 1966 1967 public void name2(int i) { } 1968 public void name2(int i, int j) { } 1969 public void name2(int i, float j, float k) { } 1970 public void name2(int i, int j, int k, float f) { } 1971 public void name2(int i, float f, int j) { } 1972 1973 public void name3() { } 1974 public void name3(int i) { } 1975 public void name3(int i, int j) { } 1976 public void name3(int i, float j, int k) { } 1977 1978 public void name4(int i, int j, float f, long z) { } 1979 public void name4(double d, int i, int j, float f) { } 1980 } 1981 """ 1982 ) 1983 ) 1984 ) 1985 } 1986 1987 @Test Check Callback Handlersnull1988 fun `Check Callback Handlers`() { 1989 check( 1990 apiLint = "", // enabled 1991 expectedIssues = """ 1992 src/android/pkg/MyClass.java:16: warning: Registration methods should have overload that accepts delivery Executor: `registerWrongCallback` [ExecutorRegistration] [See https://s.android.com/api-guidelines#callbacks-listener] 1993 src/android/pkg/MyClass.java:6: warning: Registration methods should have overload that accepts delivery Executor: `MyClass` [ExecutorRegistration] [See https://s.android.com/api-guidelines#callbacks-listener] 1994 """, 1995 sourceFiles = arrayOf( 1996 java( 1997 """ 1998 package android.pkg; 1999 2000 import androidx.annotation.Nullable; 2001 2002 public class MyClass { 2003 public MyClass(@Nullable MyCallback callback) { 2004 } 2005 2006 public void registerStreamEventCallback(@Nullable MyCallback callback); 2007 public void unregisterStreamEventCallback(@Nullable MyCallback callback); 2008 public void registerStreamEventCallback(@Nullable java.util.concurrent.Executor executor, 2009 @Nullable MyCallback callback); 2010 public void unregisterStreamEventCallback(@Nullable java.util.concurrent.Executor executor, 2011 @Nullable MyCallback callback); 2012 2013 public void registerWrongCallback(@Nullable MyCallback callback); 2014 public void unregisterWrongCallback(@Nullable MyCallback callback); 2015 } 2016 """ 2017 ), 2018 java( 2019 """ 2020 package android.graphics; 2021 2022 import android.pkg.MyCallback; 2023 import androidx.annotation.Nullable; 2024 2025 public class MyUiClass { 2026 public void registerWrongCallback(@Nullable MyCallback callback); 2027 public void unregisterWrongCallback(@Nullable MyCallback callback); 2028 } 2029 """ 2030 ), 2031 java( 2032 """ 2033 package android.pkg; 2034 2035 @SuppressWarnings("WeakerAccess") 2036 public abstract class MyCallback { 2037 } 2038 """ 2039 ), 2040 androidxNullableSource 2041 ) 2042 ) 2043 } 2044 2045 @Test Check resource namesnull2046 fun `Check resource names`() { 2047 check( 2048 apiLint = "", // enabled 2049 expectedIssues = """ 2050 src/android/R.java:11: error: Expected resource name in `android.R.id` to be in the `fooBarBaz` style, was `wrong_style` [ResourceValueFieldName] 2051 src/android/R.java:17: error: Expected config name to be in the `config_fooBarBaz` style, was `config_wrong_config_style` [ConfigFieldName] 2052 src/android/R.java:20: error: Expected resource name in `android.R.layout` to be in the `foo_bar_baz` style, was `wrongNameStyle` [ResourceFieldName] 2053 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] 2054 """, 2055 expectedFail = DefaultLintErrorMessage, 2056 sourceFiles = arrayOf( 2057 java( 2058 """ 2059 package android; 2060 2061 public final class R { 2062 public static final class id { 2063 public static final int text = 7000; 2064 public static final int config_fooBar = 7001; 2065 public static final int layout_fooBar = 7002; 2066 public static final int state_foo = 7003; 2067 public static final int foo = 7004; 2068 public static final int fooBar = 7005; 2069 public static final int wrong_style = 7006; 2070 } 2071 public static final class layout { 2072 public static final int text = 7000; 2073 public static final int config_fooBar = 7001; 2074 public static final int config_foo = 7002; 2075 public static final int config_wrong_config_style = 7003; 2076 2077 public static final int ok_name_style = 7004; 2078 public static final int wrongNameStyle = 7005; 2079 } 2080 public static final class style { 2081 public static final int TextAppearance_Compat_Notification = 0x7f0c00ec; 2082 public static final int TextAppearance_Compat_Notification_Info = 0x7f0c00ed; 2083 public static final int TextAppearance_Compat_Notification_Line2 = 0x7f0c00ef; 2084 public static final int TextAppearance_Compat_Notification_Time = 0x7f0c00f2; 2085 public static final int TextAppearance_Compat_Notification_Title = 0x7f0c00f4; 2086 public static final int Widget_Compat_NotificationActionContainer = 0x7f0c015d; 2087 public static final int Widget_Compat_NotificationActionText = 0x7f0c015e; 2088 2089 public static final int wrong_style_name = 7000; 2090 } 2091 } 2092 """ 2093 ) 2094 ) 2095 ) 2096 } 2097 2098 @Test Check filesnull2099 fun `Check files`() { 2100 check( 2101 apiLint = "", // enabled 2102 expectedIssues = """ 2103 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] 2104 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] 2105 """, 2106 sourceFiles = arrayOf( 2107 java( 2108 """ 2109 package android.pkg; 2110 import android.content.Context; 2111 import android.content.ContentResolver; 2112 import androidx.annotation.Nullable; 2113 import java.io.File; 2114 import java.io.InputStream; 2115 2116 public class CheckFiles { 2117 public CheckFiles(@Nullable Context context, @Nullable File file) { 2118 } 2119 public void ok(int i, @Nullable File file) { } 2120 public void ok(int i, @Nullable InputStream stream) { } 2121 public void error(int i, @Nullable File file) { } 2122 } 2123 """ 2124 ), 2125 androidxNullableSource 2126 ) 2127 ) 2128 } 2129 2130 @Test Check parcelable listsnull2131 fun `Check parcelable lists`() { 2132 check( 2133 apiLint = "", // enabled 2134 expectedIssues = """ 2135 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] 2136 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] 2137 """, 2138 sourceFiles = arrayOf( 2139 java( 2140 """ 2141 package android.pkg; 2142 import android.content.Context; 2143 import android.content.ContentResolver; 2144 import androidx.annotation.Nullable; 2145 import java.io.File; 2146 import java.io.InputStream; 2147 2148 public class CheckFiles { 2149 public CheckFiles(@Nullable Context context, @Nullable File file) { 2150 } 2151 public void ok(int i, @Nullable File file) { } 2152 public void ok(int i, @Nullable InputStream stream) { } 2153 public void error(int i, @Nullable File file) { } 2154 } 2155 """ 2156 ), 2157 androidxNullableSource 2158 ) 2159 ) 2160 } 2161 2162 @Test Check abstract innernull2163 fun `Check abstract inner`() { 2164 check( 2165 apiLint = "", // enabled 2166 expectedIssues = """ 2167 src/android/pkg/MyManager.java:9: warning: Abstract inner classes should be static to improve testability: class android.pkg.MyManager.MyInnerManager [AbstractInner] 2168 """, 2169 sourceFiles = arrayOf( 2170 java( 2171 """ 2172 package android.pkg; 2173 import android.content.Context; 2174 import android.content.ContentResolver; 2175 import java.io.File; 2176 import java.io.InputStream; 2177 2178 public abstract class MyManager { 2179 private MyManager() {} 2180 public abstract class MyInnerManager { 2181 private MyInnerManager() {} 2182 } 2183 public abstract static class MyOkManager { 2184 private MyOkManager() {} 2185 } 2186 } 2187 """ 2188 ) 2189 ) 2190 ) 2191 } 2192 2193 @Test Check for extending errorsnull2194 fun `Check for extending errors`() { 2195 check( 2196 apiLint = "", // enabled 2197 expectedIssues = """ 2198 src/android/pkg/MyClass.java:3: error: Trouble must be reported through an `Exception`, not an `Error` (`MyClass` extends `Error`) [ExtendsError] 2199 src/android/pkg/MySomething.java:3: error: Exceptions must be named `FooException`, was `MySomething` [ExceptionName] 2200 """, 2201 expectedFail = DefaultLintErrorMessage, 2202 sourceFiles = arrayOf( 2203 java( 2204 """ 2205 package android.pkg; 2206 2207 public class MyClass extends Error { 2208 } 2209 """ 2210 ), 2211 java( 2212 """ 2213 package android.pkg; 2214 2215 public class MySomething extends RuntimeException { 2216 } 2217 """ 2218 ) 2219 ) 2220 ) 2221 } 2222 2223 @Test Check units and method namesnull2224 fun `Check units and method names`() { 2225 check( 2226 extraArguments = arrayOf(ARG_API_LINT, ARG_HIDE, "NoByteOrShort"), 2227 expectedIssues = """ 2228 src/android/pkg/UnitNameTest.java:7: error: Expected method name units to be `Hours`, was `Hr` in `getErrorHr` [MethodNameUnits] [See https://s.android.com/api-guidelines#unit-names] 2229 src/android/pkg/UnitNameTest.java:8: error: Expected method name units to be `Nanos`, was `Ns` in `getErrorNs` [MethodNameUnits] [See https://s.android.com/api-guidelines#unit-names] 2230 src/android/pkg/UnitNameTest.java:9: error: Expected method name units to be `Bytes`, was `Byte` in `getErrorByte` [MethodNameUnits] [See https://s.android.com/api-guidelines#unit-names] 2231 src/android/pkg/UnitNameTest.java:14: error: Fractions must use floats, was `int` in `getErrorFraction` [FractionFloat] 2232 src/android/pkg/UnitNameTest.java:15: error: Fractions must use floats, was `int` in `setErrorFraction` [FractionFloat] 2233 src/android/pkg/UnitNameTest.java:19: error: Percentage must use ints, was `float` in `getErrorPercentage` [PercentageInt] 2234 src/android/pkg/UnitNameTest.java:20: error: Percentage must use ints, was `float` in `setErrorPercentage` [PercentageInt] 2235 src/android/pkg/UnitNameTest.java:22: error: Expected method name units to be `Bytes`, was `Byte` in `readSingleByte` [MethodNameUnits] [See https://s.android.com/api-guidelines#unit-names] 2236 """, 2237 expectedFail = DefaultLintErrorMessage, 2238 sourceFiles = arrayOf( 2239 androidxNonNullSource, 2240 java( 2241 """ 2242 package android.pkg; 2243 2244 import androidx.annotation.NonNull; 2245 2246 public class UnitNameTest { 2247 public int okay() { return 0; } 2248 public int getErrorHr() { return 0; } 2249 public int getErrorNs() { return 0; } 2250 public short getErrorByte() { return (short)0; } 2251 2252 public float getOkFraction() { return 0f; } 2253 public void setOkFraction(float f) { } 2254 public void setOkFraction(int n, int d) { } 2255 public int getErrorFraction() { return 0; } 2256 public void setErrorFraction(int i) { } 2257 2258 public int getOkPercentage() { return 0f; } 2259 public void setOkPercentage(int i) { } 2260 public float getErrorPercentage() { return 0f; } 2261 public void setErrorPercentage(float f) { } 2262 2263 public int readSingleByte() { return 0; } 2264 2265 public static final class UnitNameTestBuilder { 2266 public UnitNameTestBuilder() {} 2267 2268 @NonNull 2269 public UnitNameTest build() { return null; } 2270 2271 @NonNull 2272 public UnitNameTestBuilder setOkFraction(float f) { return this; } 2273 2274 @NonNull 2275 public UnitNameTestBuilder setOkPercentage(int i) { return this; } 2276 } 2277 } 2278 """ 2279 ) 2280 ) 2281 ) 2282 } 2283 2284 @Test Check closeablenull2285 fun `Check closeable`() { 2286 check( 2287 apiLint = "", // enabled 2288 expectedIssues = """ 2289 src/android/pkg/MyErrorClass1.java:3: warning: Classes that release resources (close()) should implement AutoClosable and CloseGuard: class android.pkg.MyErrorClass1 [NotCloseable] 2290 src/android/pkg/MyErrorClass2.java:3: warning: Classes that release resources (finalize(), shutdown()) should implement AutoClosable and CloseGuard: class android.pkg.MyErrorClass2 [NotCloseable] 2291 """, 2292 sourceFiles = arrayOf( 2293 java( 2294 """ 2295 package android.pkg; 2296 2297 public abstract class MyOkClass1 implements java.io.Closeable { 2298 public void close() {} 2299 } 2300 """ 2301 ), 2302 java( 2303 """ 2304 package android.pkg; 2305 2306 // Ok: indirectly implementing AutoCloseable 2307 public abstract class MyOkClass2 implements MyInterface { 2308 public void close() {} 2309 } 2310 """ 2311 ), 2312 java( 2313 """ 2314 package android.pkg; 2315 2316 public class MyInterface extends AutoCloseable { 2317 public void close() {} 2318 } 2319 """ 2320 ), 2321 java( 2322 """ 2323 package android.pkg; 2324 2325 public abstract class MyErrorClass1 { 2326 public void close() {} 2327 } 2328 """ 2329 ), 2330 java( 2331 """ 2332 package android.pkg; 2333 2334 public abstract class MyErrorClass2 { 2335 public void finalize() {} 2336 public void shutdown() {} 2337 } 2338 """ 2339 ) 2340 ) 2341 ) 2342 } 2343 2344 @Test Check closeable for minSdkVersion 19null2345 fun `Check closeable for minSdkVersion 19`() { 2346 check( 2347 apiLint = "", // enabled 2348 expectedIssues = """ 2349 src/android/pkg/MyErrorClass1.java:3: warning: Classes that release resources (close()) should implement AutoClosable and CloseGuard: class android.pkg.MyErrorClass1 [NotCloseable] 2350 src/android/pkg/MyErrorClass2.java:3: warning: Classes that release resources (finalize(), shutdown()) should implement AutoClosable and CloseGuard: class android.pkg.MyErrorClass2 [NotCloseable] 2351 """, 2352 manifest = """<?xml version="1.0" encoding="UTF-8"?> 2353 <manifest xmlns:android="http://schemas.android.com/apk/res/android"> 2354 <uses-sdk android:minSdkVersion="19" /> 2355 </manifest> 2356 """.trimIndent(), 2357 sourceFiles = arrayOf( 2358 java( 2359 """ 2360 package android.pkg; 2361 2362 public abstract class MyErrorClass1 { 2363 public void close() {} 2364 } 2365 """ 2366 ), 2367 java( 2368 """ 2369 package android.pkg; 2370 2371 public abstract class MyErrorClass2 { 2372 public void finalize() {} 2373 public void shutdown() {} 2374 } 2375 """ 2376 ) 2377 ) 2378 ) 2379 } 2380 2381 @Test Do not check closeable for minSdkVersion less than 19null2382 fun `Do not check closeable for minSdkVersion less than 19`() { 2383 check( 2384 apiLint = "", // enabled 2385 expectedIssues = "", 2386 manifest = """<?xml version="1.0" encoding="UTF-8"?> 2387 <manifest xmlns:android="http://schemas.android.com/apk/res/android"> 2388 <uses-sdk android:minSdkVersion="18" /> 2389 </manifest> 2390 """.trimIndent(), 2391 sourceFiles = arrayOf( 2392 java( 2393 """ 2394 package android.pkg; 2395 2396 public abstract class MyErrorClass1 { 2397 public void close() {} 2398 } 2399 """ 2400 ), 2401 java( 2402 """ 2403 package android.pkg; 2404 2405 public abstract class MyErrorClass2 { 2406 public void finalize() {} 2407 public void shutdown() {} 2408 } 2409 """ 2410 ) 2411 ) 2412 ) 2413 } 2414 2415 @Test Check ICU types for minSdkVersion 24null2416 fun `Check ICU types for minSdkVersion 24`() { 2417 check( 2418 apiLint = "", // enabled 2419 expectedIssues = """ 2420 src/android/pkg/MyErrorClass1.java:8: warning: Type `java.util.TimeZone` should be replaced with richer ICU type `android.icu.util.TimeZone` [UseIcu] 2421 """, 2422 manifest = """<?xml version="1.0" encoding="UTF-8"?> 2423 <manifest xmlns:android="http://schemas.android.com/apk/res/android"> 2424 <uses-sdk android:minSdkVersion="24" /> 2425 </manifest> 2426 """.trimIndent(), 2427 sourceFiles = arrayOf( 2428 java( 2429 """ 2430 package android.pkg; 2431 2432 import android.annotation.NonNull; 2433 import java.util.TimeZone; 2434 2435 public abstract class MyErrorClass1 { 2436 @NonNull 2437 public TimeZone getDefaultTimeZone() { 2438 return TimeZone.getDefault(); 2439 } 2440 } 2441 """ 2442 ), 2443 nonNullSource 2444 ) 2445 ) 2446 } 2447 2448 @Test Do not check ICU types for minSdkVersion less than 24null2449 fun `Do not check ICU types for minSdkVersion less than 24`() { 2450 check( 2451 apiLint = "", // enabled 2452 expectedIssues = "", 2453 manifest = """<?xml version="1.0" encoding="UTF-8"?> 2454 <manifest xmlns:android="http://schemas.android.com/apk/res/android"> 2455 <uses-sdk android:minSdkVersion="23" /> 2456 </manifest> 2457 """.trimIndent(), 2458 sourceFiles = arrayOf( 2459 java( 2460 """ 2461 package android.pkg; 2462 2463 import android.annotation.NonNull; 2464 import java.util.TimeZone; 2465 2466 public abstract class MyErrorClass1 { 2467 @NonNull 2468 public TimeZone getDefaultTimeZone() { 2469 return TimeZone.getDefault(); 2470 } 2471 } 2472 """ 2473 ), 2474 nonNullSource 2475 ) 2476 ) 2477 } 2478 2479 @Test Check Kotlin keywordsnull2480 fun `Check Kotlin keywords`() { 2481 check( 2482 apiLint = "", // enabled 2483 expectedIssues = """ 2484 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] 2485 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] 2486 """, 2487 expectedFail = DefaultLintErrorMessage, 2488 sourceFiles = arrayOf( 2489 java( 2490 """ 2491 package android.pkg; 2492 2493 public class KotlinKeywordTest { 2494 public void okay(); 2495 public final int okay = 0; 2496 2497 public void fun() {} // error 2498 public final int as = 0; // error 2499 } 2500 """ 2501 ), 2502 androidxNonNullSource, 2503 androidxNullableSource 2504 ) 2505 ) 2506 } 2507 2508 @Test Check Kotlin operatorsnull2509 fun `Check Kotlin operators`() { 2510 check( 2511 apiLint = "", // enabled 2512 expectedIssues = """ 2513 src/android/pkg/KotlinOperatorTest.java:6: info: Method can be invoked with an indexing operator from Kotlin: `get` (this is usually desirable; just make sure it makes sense for this type of object) [KotlinOperator] 2514 src/android/pkg/KotlinOperatorTest.java:7: info: Method can be invoked with an indexing operator from Kotlin: `set` (this is usually desirable; just make sure it makes sense for this type of object) [KotlinOperator] 2515 src/android/pkg/KotlinOperatorTest.java:8: info: Method can be invoked with function call syntax from Kotlin: `invoke` (this is usually desirable; just make sure it makes sense for this type of object) [KotlinOperator] 2516 src/android/pkg/KotlinOperatorTest.java:9: info: Method can be invoked as a binary operator from Kotlin: `plus` (this is usually desirable; just make sure it makes sense for this type of object) [KotlinOperator] 2517 src/android/pkg/KotlinOperatorTest.java:9: error: Only one of `plus` and `plusAssign` methods should be present for Kotlin [UniqueKotlinOperator] 2518 src/android/pkg/KotlinOperatorTest.java:10: info: Method can be invoked as a compound assignment operator from Kotlin: `plusAssign` (this is usually desirable; just make sure it makes sense for this type of object) [KotlinOperator] 2519 """, 2520 expectedFail = DefaultLintErrorMessage, 2521 sourceFiles = arrayOf( 2522 java( 2523 """ 2524 package android.pkg; 2525 2526 import androidx.annotation.Nullable; 2527 2528 public class KotlinOperatorTest { 2529 public int get(int i) { return i + 2; } 2530 public void set(int i, int j, int k) { } 2531 public void invoke(int i, int j, int k) { } 2532 public int plus(@Nullable JavaClass other) { return 0; } 2533 public void plusAssign(@Nullable JavaClass other) { } 2534 } 2535 """ 2536 ), 2537 androidxNullableSource 2538 ) 2539 ) 2540 } 2541 2542 @Test Return collections instead of arraysnull2543 fun `Return collections instead of arrays`() { 2544 check( 2545 extraArguments = arrayOf(ARG_API_LINT, ARG_HIDE, "AutoBoxing"), 2546 expectedIssues = """ 2547 src/android/pkg/ArrayTest.java:12: warning: Method should return Collection<Object> (or subclass) instead of raw array; was `java.lang.Object[]` [ArrayReturn] [See https://s.android.com/api-guidelines#methods-prefer-collection-over-array] 2548 src/android/pkg/ArrayTest.java:13: warning: Method parameter should be Collection<Number> (or subclass) instead of raw array; was `java.lang.Number[]` [ArrayReturn] [See https://s.android.com/api-guidelines#methods-prefer-collection-over-array] 2549 """, 2550 sourceFiles = arrayOf( 2551 java( 2552 """ 2553 package android.pkg; 2554 2555 import androidx.annotation.NonNull; 2556 2557 public class ArrayTest { 2558 @NonNull 2559 public int[] ok1() { throw new RuntimeException(); } 2560 @NonNull 2561 public String[] ok2() { throw new RuntimeException(); } 2562 public void ok3(@Nullable int[] i) { } 2563 @NonNull 2564 public Object[] error1() { throw new RuntimeException(); } 2565 public void error2(@NonNull Number[] i) { } 2566 public void ok(@NonNull Number... args) { } 2567 } 2568 """ 2569 ), 2570 kotlin( 2571 """ 2572 package test.pkg 2573 fun okMethod(vararg values: Integer, foo: Float, bar: Float) 2574 """ 2575 ), 2576 androidxNonNullSource 2577 ) 2578 ) 2579 } 2580 2581 @Test Check user handle namesnull2582 fun `Check user handle names`() { 2583 check( 2584 apiLint = "", // enabled 2585 expectedIssues = """ 2586 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] 2587 src/android/pkg/UserHandleTest.java:8: warning: Method taking UserHandle should be named `doFooAsUser` or `queryFooForUser`, was `error` [UserHandleName] 2588 """, 2589 sourceFiles = arrayOf( 2590 java( 2591 """ 2592 package android.pkg; 2593 import android.os.UserHandle; 2594 import androidx.annotation.Nullable; 2595 2596 public class UserHandleTest { 2597 public void doFooAsUser(int i, @Nullable UserHandle handle) {} //OK 2598 public void doFooForUser(int i, @Nullable UserHandle handle) {} //OK 2599 public void error(int i, @Nullable UserHandle handle) {} 2600 } 2601 """ 2602 ), 2603 java( 2604 """ 2605 package android.pkg; 2606 import android.os.UserHandle; 2607 import androidx.annotation.Nullable; 2608 2609 public class MyManager { 2610 private MyManager() { } 2611 public void error(int i, @Nullable UserHandle handle) {} 2612 } 2613 """ 2614 ), 2615 androidxNullableSource 2616 ) 2617 ) 2618 } 2619 2620 @Test Check parametersnull2621 fun `Check parameters`() { 2622 check( 2623 apiLint = "", // enabled 2624 expectedIssues = """ 2625 src/android/pkg/FooOptions.java:3: warning: Classes holding a set of parameters should be called `FooParams`, was `FooOptions` [UserHandleName] 2626 """, 2627 sourceFiles = arrayOf( 2628 java( 2629 """ 2630 package android.pkg; 2631 2632 public class FooOptions { 2633 } 2634 """ 2635 ), 2636 java( 2637 """ 2638 package android.app; 2639 2640 public class ActivityOptions { 2641 } 2642 """ 2643 ) 2644 ) 2645 ) 2646 } 2647 2648 @Test Check service namesnull2649 fun `Check service names`() { 2650 check( 2651 apiLint = "", // enabled 2652 expectedIssues = """ 2653 src/android/content/Context.java:11: error: Inconsistent service constant name; expected `SOMETHING_SERVICE`, was `OTHER_MANAGER` [ServiceName] 2654 src/android/content/Context.java:12: error: Inconsistent service constant name; expected `OTHER_SERVICE`, was `OTHER_MANAGER_SERVICE` [ServiceName] 2655 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] 2656 """, 2657 expectedFail = DefaultLintErrorMessage, 2658 sourceFiles = arrayOf( 2659 java( 2660 """ 2661 package android.content; 2662 2663 public class Context { 2664 private Context() { } 2665 // Good 2666 public static final String FOO_BAR_SERVICE = "foo_bar"; 2667 // Unrelated 2668 public static final int NON_STRING_SERVICE = 42; 2669 // Bad 2670 public static final String OTHER_SERVICE = "something"; 2671 public static final String OTHER_MANAGER = "something"; 2672 public static final String OTHER_MANAGER_SERVICE = "other_manager"; 2673 } 2674 """ 2675 ), 2676 java( 2677 """ 2678 package android.pkg; 2679 2680 // Unrelated 2681 public class ServiceNameTest { 2682 private ServiceNameTest() { } 2683 public static final String FOO_BAR_SERVICE = "foo_bar"; 2684 public static final String OTHER_SERVICE = "something"; 2685 public static final int NON_STRING_SERVICE = 42; 2686 public static final String BIND_SOME_SERVICE = "android.permission.BIND_SOME_SERVICE"; 2687 } 2688 """ 2689 ) 2690 ) 2691 ) 2692 } 2693 2694 @Test Check method name tensenull2695 fun `Check method name tense`() { 2696 check( 2697 apiLint = "", // enabled 2698 expectedIssues = """ 2699 src/android/pkg/MethodNameTest.java:6: warning: Unexpected tense; probably meant `enabled`, was `fooEnable` [MethodNameTense] 2700 src/android/pkg/MethodNameTest.java:7: warning: Unexpected tense; probably meant `enabled`, was `mustEnable` [MethodNameTense] 2701 """, 2702 sourceFiles = arrayOf( 2703 java( 2704 """ 2705 package android.pkg; 2706 2707 public class MethodNameTest { 2708 private MethodNameTest() { } 2709 public void enable() { } // ok, not Enable 2710 public void fooEnable() { } // warn 2711 public boolean mustEnable() { return true; } // warn 2712 public boolean isEnabled() { return true; } 2713 } 2714 """ 2715 ) 2716 ) 2717 ) 2718 } 2719 2720 @Test Check no clonenull2721 fun `Check no clone`() { 2722 check( 2723 apiLint = "", // enabled 2724 expectedIssues = """ 2725 src/android/pkg/CloneTest.java:8: error: Provide an explicit copy constructor instead of implementing `clone()` [NoClone] [See https://s.android.com/api-guidelines#avoid-clone] 2726 """, 2727 expectedFail = DefaultLintErrorMessage, 2728 sourceFiles = arrayOf( 2729 java( 2730 """ 2731 package android.pkg; 2732 2733 import androidx.annotation.Nullable; 2734 2735 public class CloneTest { 2736 public void clone(int i) { } // ok 2737 @NonNull 2738 public CloneTest clone() { return super.clone(); } // error 2739 } 2740 """ 2741 ), 2742 androidxNullableSource 2743 ) 2744 ) 2745 } 2746 2747 @Test Check ICU typesnull2748 fun `Check ICU types`() { 2749 check( 2750 apiLint = "", // enabled 2751 expectedIssues = """ 2752 src/android/pkg/IcuTest.java:6: warning: Type `java.util.TimeZone` should be replaced with richer ICU type `android.icu.util.TimeZone` [UseIcu] 2753 src/android/pkg/IcuTest.java:8: warning: Type `java.text.BreakIterator` should be replaced with richer ICU type `android.icu.text.BreakIterator` [UseIcu] 2754 src/android/pkg/IcuTest.java:8: warning: Type `java.text.Collator` should be replaced with richer ICU type `android.icu.text.Collator` [UseIcu] 2755 """, 2756 sourceFiles = arrayOf( 2757 java( 2758 """ 2759 package android.pkg; 2760 2761 import androidx.annotation.Nullable; 2762 2763 public abstract class IcuTest { 2764 public IcuTest(@Nullable java.util.TimeZone timeZone) { } 2765 @Nullable 2766 public abstract java.text.BreakIterator foo(@Nullable java.text.Collator collator); 2767 } 2768 """ 2769 ), 2770 androidxNullableSource 2771 ) 2772 ) 2773 } 2774 2775 @Test Check using parcel file descriptorsnull2776 fun `Check using parcel file descriptors`() { 2777 check( 2778 apiLint = "", // enabled 2779 expectedIssues = """ 2780 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] [See https://s.android.com/api-guidelines#prefer-parcelfiledescriptor] 2781 src/android/pkg/PdfTest.java:7: error: Must use ParcelFileDescriptor instead of FileDescriptor in method android.pkg.PdfTest.getFileDescriptor() [UseParcelFileDescriptor] [See https://s.android.com/api-guidelines#prefer-parcelfiledescriptor] 2782 """, 2783 expectedFail = DefaultLintErrorMessage, 2784 sourceFiles = arrayOf( 2785 java( 2786 """ 2787 package android.pkg; 2788 2789 import androidx.annotation.Nullable; 2790 2791 public abstract class PdfTest { 2792 public void error1(@Nullable java.io.FileDescriptor fd) { } 2793 public int getFileDescriptor() { return -1; } 2794 public void ok(@Nullable android.os.ParcelFileDescriptor fd) { } 2795 } 2796 """ 2797 ), 2798 java( 2799 """ 2800 package android.system; 2801 2802 public class Os { 2803 public void ok(@Nullable java.io.FileDescriptor fd) { } 2804 public int getFileDescriptor() { return -1; } 2805 } 2806 """ 2807 ), 2808 java( 2809 """ 2810 package android.yada; 2811 2812 import android.annotation.Nullable; 2813 2814 public class YadaService extends android.app.Service { 2815 @Override 2816 public final void dump(@Nullable java.io.FileDescriptor fd, @Nullable java.io.PrintWriter pw, @Nullable String[] args) { 2817 } 2818 } 2819 """ 2820 ), 2821 androidxNullableSource, 2822 nonNullSource 2823 ) 2824 ) 2825 } 2826 2827 @Test Check using bytes and shortsnull2828 fun `Check using bytes and shorts`() { 2829 check( 2830 apiLint = "", // enabled 2831 expectedIssues = """ 2832 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] [See https://s.android.com/api-guidelines#avoid-short-byte] 2833 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] [See https://s.android.com/api-guidelines#avoid-short-byte] 2834 """, 2835 sourceFiles = arrayOf( 2836 java( 2837 """ 2838 package android.pkg; 2839 2840 public abstract class ByteTest { 2841 public void error1(byte b) { } 2842 public void error2(short s) { } 2843 } 2844 """ 2845 ) 2846 ) 2847 ) 2848 } 2849 2850 @Test Check singleton constructorsnull2851 fun `Check singleton constructors`() { 2852 check( 2853 apiLint = "", // enabled 2854 expectedIssues = """ 2855 src/android/pkg/MySingleton.java:8: error: Singleton classes should use `getInstance()` methods: `MySingleton` [SingletonConstructor] [See https://s.android.com/api-guidelines#singleton-class] 2856 """, 2857 expectedFail = DefaultLintErrorMessage, 2858 sourceFiles = arrayOf( 2859 java( 2860 """ 2861 package android.pkg; 2862 2863 import androidx.annotation.Nullable; 2864 2865 public abstract class MySingleton { 2866 @Nullable 2867 public static MySingleton getMyInstance() { return null; } 2868 public MySingleton() { } 2869 public void foo() { } 2870 } 2871 """ 2872 ), 2873 java( 2874 """ 2875 package android.pkg; 2876 2877 import androidx.annotation.Nullable; 2878 2879 public abstract class MySingleton2 { 2880 @Nullable 2881 public static MySingleton2 getMyInstance() { return null; } 2882 private MySingleton2() { } // OK, private 2883 public void foo() { } 2884 } 2885 """ 2886 ), 2887 androidxNullableSource 2888 ) 2889 ) 2890 } 2891 2892 @Test Check forbidden super-classesnull2893 fun `Check forbidden super-classes`() { 2894 check( 2895 apiLint = "", // enabled 2896 expectedIssues = """ 2897 src/android/pkg/FirstActivity.java:2: error: FirstActivity should not extend `Activity`. Activity subclasses are impossible to compose. Expose a composable API instead. [ForbiddenSuperClass] 2898 src/android/pkg/IndirectActivity.java:2: error: IndirectActivity should not extend `Activity`. Activity subclasses are impossible to compose. Expose a composable API instead. [ForbiddenSuperClass] 2899 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] 2900 """, 2901 expectedFail = DefaultLintErrorMessage, 2902 sourceFiles = arrayOf( 2903 java( 2904 """ 2905 package android.pkg; 2906 public abstract class FirstActivity extends android.app.Activity { 2907 private FirstActivity() { } 2908 } 2909 """ 2910 ), 2911 java( 2912 """ 2913 package android.pkg; 2914 public abstract class IndirectActivity extends android.app.ListActivity { 2915 private IndirectActivity() { } 2916 } 2917 """ 2918 ), 2919 java( 2920 """ 2921 package android.pkg; 2922 public abstract class MyTask extends android.os.AsyncTask<String,String,String> { 2923 private MyTask() { } 2924 } 2925 """ 2926 ) 2927 ) 2928 ) 2929 } 2930 2931 @Test KotlinOperator check only applies when not using operator modifiernull2932 fun `KotlinOperator check only applies when not using operator modifier`() { 2933 check( 2934 apiLint = "", // enabled 2935 expectedIssues = """ 2936 src/android/pkg/A.kt:3: info: Note that adding the `operator` keyword would allow calling this method using operator syntax [KotlinOperator] 2937 src/android/pkg/Bar.kt:4: info: Note that adding the `operator` keyword would allow calling this method using operator syntax [KotlinOperator] 2938 src/android/pkg/Foo.java:8: info: Method can be invoked as a binary operator from Kotlin: `div` (this is usually desirable; just make sure it makes sense for this type of object) [KotlinOperator] 2939 """, 2940 sourceFiles = arrayOf( 2941 java( 2942 """ 2943 package android.pkg; 2944 2945 import androidx.annotation.Nullable; 2946 2947 public class Foo { 2948 private Foo() { } 2949 @Nullable 2950 public Foo div(int value) { } 2951 } 2952 """ 2953 ), 2954 kotlin( 2955 """ 2956 package android.pkg 2957 class Bar { 2958 operator fun div(value: Int): Bar { TODO() } 2959 fun plus(value: Int): Bar { TODO() } 2960 } 2961 """ 2962 ), 2963 kotlin( 2964 """ 2965 package android.pkg 2966 class FontFamily(val fonts: List<String>) : List<String> by fonts 2967 """ 2968 ), 2969 kotlin( 2970 """ 2971 package android.pkg 2972 class B: A() { 2973 override fun get(i: Int): A { 2974 return A() 2975 } 2976 } 2977 """ 2978 ), 2979 kotlin( 2980 """ 2981 package android.pkg 2982 open class A { 2983 open fun get(i: Int): A { 2984 return A() 2985 } 2986 } 2987 """ 2988 ), 2989 androidxNullableSource 2990 ) 2991 ) 2992 } 2993 2994 @Test Test fields, parameters and returns require nullabilitynull2995 fun `Test fields, parameters and returns require nullability`() { 2996 check( 2997 apiLint = "", // enabled 2998 extraArguments = arrayOf(ARG_API_LINT, ARG_HIDE, "AllUpper,StaticUtils,Enum"), 2999 expectedIssues = """ 3000 src/android/pkg/Foo.java:11: error: Missing nullability on parameter `name` in method `Foo` [MissingNullability] [See https://s.android.com/api-guidelines#annotations] 3001 src/android/pkg/Foo.java:12: error: Missing nullability on parameter `value` in method `setBadValue` [MissingNullability] [See https://s.android.com/api-guidelines#annotations] 3002 src/android/pkg/Foo.java:13: error: Missing nullability on method `getBadValue` return [MissingNullability] [See https://s.android.com/api-guidelines#annotations] 3003 src/android/pkg/Foo.java:20: error: Missing nullability on parameter `duration` in method `methodMissingParamAnnotations` [MissingNullability] [See https://s.android.com/api-guidelines#annotations] 3004 src/android/pkg/Foo.java:7: error: Missing nullability on field `badField` in class `class android.pkg.Foo` [MissingNullability] [See https://s.android.com/api-guidelines#annotations] 3005 """, 3006 expectedFail = DefaultLintErrorMessage, 3007 sourceFiles = arrayOf( 3008 java( 3009 """ 3010 package android.pkg; 3011 3012 import androidx.annotation.NonNull; 3013 import androidx.annotation.Nullable; 3014 3015 public class Foo<T> { 3016 public final Foo badField; 3017 @Nullable 3018 public final Foo goodField; 3019 3020 public Foo(String name, int number) { } 3021 public void setBadValue(Foo value) { } 3022 public Foo getBadValue(int number) { throw UnsupportedOperationExceptions(); } 3023 public void setGoodValue(@Nullable Foo value) { } 3024 public void setGoodIgnoredGenericValue(T value) { } 3025 @NonNull 3026 public Foo getGoodValue(int number) { throw UnsupportedOperationExceptions(); } 3027 3028 @NonNull 3029 public Foo methodMissingParamAnnotations(java.time.Duration duration) { 3030 throw UnsupportedOperationException(); 3031 } 3032 } 3033 """ 3034 ), 3035 java( 3036 """ 3037 package test.pkg; 3038 @SuppressWarnings("ALL") 3039 public enum Foo { 3040 A, B; 3041 } 3042 """ 3043 ), 3044 kotlin( 3045 """ 3046 package test.pkg 3047 enum class Language { 3048 KOTLIN, 3049 JAVA 3050 } 3051 """ 3052 ), 3053 kotlin( 3054 """ 3055 package android.pkg 3056 3057 object Bar 3058 3059 class FooBar { 3060 companion object 3061 } 3062 3063 class FooBarNamed { 3064 companion object Named 3065 } 3066 """ 3067 ), 3068 androidxNullableSource, 3069 androidxNonNullSource 3070 ) 3071 ) 3072 } 3073 3074 @Test Test equals, toString, non-null constants, enums and annotation members don't require nullabilitynull3075 fun `Test equals, toString, non-null constants, enums and annotation members don't require nullability`() { 3076 check( 3077 apiLint = "", // enabled 3078 expectedIssues = "", 3079 sourceFiles = arrayOf( 3080 java( 3081 """ 3082 package android.pkg; 3083 3084 import android.annotation.SuppressLint; 3085 3086 public class Foo<T> { 3087 public static final String FOO_CONSTANT = "test"; 3088 3089 public boolean equals(Object other) { 3090 return other == this; 3091 } 3092 3093 public int hashCode() { 3094 return 0; 3095 } 3096 3097 public String toString() { 3098 return "Foo"; 3099 } 3100 3101 @SuppressLint("Enum") 3102 public enum FooEnum { 3103 FOO, BAR 3104 } 3105 3106 public @interface FooAnnotation { 3107 String value() default ""; 3108 } 3109 } 3110 """ 3111 ), 3112 androidxNullableSource, 3113 androidxNonNullSource 3114 ) 3115 ) 3116 } 3117 3118 @Test 3119 fun `Nullability check for generic methods referencing parent type parameter`() { 3120 check( 3121 apiLint = "", // enabled 3122 expectedIssues = """ 3123 src/test/pkg/MyClass.java:14: error: Missing nullability on method `method4` return [MissingNullability] [See https://s.android.com/api-guidelines#annotations] 3124 src/test/pkg/MyClass.java:14: error: Missing nullability on parameter `input` in method `method4` [MissingNullability] [See https://s.android.com/api-guidelines#annotations] 3125 """, 3126 expectedFail = DefaultLintErrorMessage, 3127 sourceFiles = arrayOf( 3128 java( 3129 """ 3130 package test.pkg; 3131 3132 import androidx.annotation.NonNull; 3133 import androidx.annotation.Nullable; 3134 3135 public class MyClass extends HiddenParent<String> { 3136 public void method1() { } 3137 3138 @NonNull 3139 @Override 3140 public String method3(@Nullable String input) { return null; } 3141 3142 @Override 3143 public String method4(String input) { return null; } 3144 } 3145 """ 3146 ), 3147 java( 3148 """ 3149 package test.pkg; 3150 3151 class HiddenParent<T> { 3152 public T method2(T t) { } 3153 public T method3(T t) { } 3154 public T method4(T t) { } 3155 } 3156 """ 3157 ), 3158 androidxNullableSource, 3159 androidxNonNullSource 3160 ) 3161 ) 3162 } 3163 3164 @Test 3165 fun `No new setting keys`() { 3166 check( 3167 apiLint = "", // enabled 3168 extraArguments = arrayOf( 3169 ARG_ERROR, 3170 "NoSettingsProvider" 3171 ), 3172 expectedIssues = """ 3173 src/android/provider/Settings.java:9: error: New setting keys are not allowed (Field: BAD1); use getters/setters in relevant manager class [NoSettingsProvider] [See https://s.android.com/api-guidelines#no-settings-provider] 3174 src/android/provider/Settings.java:12: error: Bare field okay2 must be marked final, or moved behind accessors if mutable [MutableBareField] [See https://s.android.com/api-guidelines#mutable-bare-field] 3175 src/android/provider/Settings.java:17: error: New setting keys are not allowed (Field: BAD1); use getters/setters in relevant manager class [NoSettingsProvider] [See https://s.android.com/api-guidelines#no-settings-provider] 3176 src/android/provider/Settings.java:21: error: New setting keys are not allowed (Field: BAD1); use getters/setters in relevant manager class [NoSettingsProvider] [See https://s.android.com/api-guidelines#no-settings-provider] 3177 """, 3178 expectedFail = DefaultLintErrorMessage, 3179 sourceFiles = arrayOf( 3180 java( 3181 """ 3182 package android.provider; 3183 3184 import androidx.annotation.Nullable; 3185 3186 public class Settings { 3187 private Settings() { } 3188 public static class Global { 3189 private Global() { } 3190 public static final String BAD1 = ""; 3191 public final String okay1 = ""; 3192 @Nullable 3193 public static String okay2 = ""; 3194 public static final int OKAY3 = 0; 3195 } 3196 public static class Secure { 3197 private Secure() { } 3198 public static final String BAD1 = ""; 3199 } 3200 public static class System { 3201 private System() { } 3202 public static final String BAD1 = ""; 3203 } 3204 public static class Other { 3205 private Other() { } 3206 public static final String OKAY1 = ""; 3207 } 3208 } 3209 """ 3210 ), 3211 androidxNullableSource 3212 ) 3213 ) 3214 } 3215 3216 @Test 3217 fun `No issues for ignored packages`() { 3218 check( 3219 apiLint = """ 3220 package java.math { 3221 public class BigInteger { 3222 ctor public BigInteger(); 3223 } 3224 } 3225 """.trimIndent(), 3226 extraArguments = arrayOf( 3227 ARG_API_LINT_IGNORE_PREFIX, 3228 "java." 3229 ), 3230 expectedIssues = "", 3231 sourceFiles = arrayOf( 3232 java( 3233 """ 3234 package java.math; 3235 3236 public class BigInteger { 3237 public byte newMethod() { 3238 return 0; 3239 } 3240 } 3241 """ 3242 ) 3243 ) 3244 ) 3245 } 3246 3247 @Test 3248 fun `vararg use in annotations`() { 3249 check( 3250 apiLint = "", // enabled 3251 expectedIssues = "", 3252 sourceFiles = arrayOf( 3253 kotlin( 3254 """ 3255 package test.pkg 3256 3257 import kotlin.reflect.KClass 3258 3259 annotation class MyAnnotation( 3260 vararg val markerClass: KClass<out Annotation> 3261 ) 3262 """ 3263 ) 3264 ) 3265 ) 3266 } 3267 3268 @Test 3269 fun `Inherited interface constants`() { 3270 check( 3271 expectedIssues = "", 3272 expectedFail = "", 3273 apiLint = """ 3274 package javax.microedition.khronos.egl { 3275 public interface EGL { 3276 } 3277 public interface EGL10 extends javax.microedition.khronos.egl.EGL { 3278 field public static final int EGL_SUCCESS = 0; 3279 } 3280 public interface EGL11 extends javax.microedition.khronos.egl.EGL10 { 3281 field public static final int EGL_CONTEXT_LOST = 1; 3282 } 3283 public interface EGLDisplay { 3284 } 3285 } 3286 """, 3287 sourceFiles = arrayOf( 3288 java( 3289 """ 3290 package javax.microedition.khronos.egl; 3291 3292 public interface EGL { 3293 } 3294 """ 3295 ), 3296 java( 3297 """ 3298 package javax.microedition.khronos.egl; 3299 3300 public interface EGL10 extends EGL { 3301 EGLDisplay EGL_SUCCESS = new EGLImpl(); 3302 } 3303 """ 3304 ), 3305 java( 3306 """ 3307 package javax.microedition.khronos.egl; 3308 3309 public interface EGL11 extends EGL10 { 3310 int EGL_CONTEXT_LOST = 1; 3311 } 3312 """ 3313 ), 3314 java( 3315 """ 3316 package javax.microedition.khronos.egl; 3317 3318 public abstract class EGLDisplay { 3319 } 3320 """ 3321 ) 3322 ) 3323 ) 3324 } 3325 3326 @Test 3327 fun `Inherited interface constants inherited through parents into children`() { 3328 check( 3329 expectedIssues = "", 3330 expectedFail = "", 3331 apiLint = """ 3332 package android.provider { 3333 public static final class Settings.Global extends android.provider.Settings.NameValueTable { 3334 } 3335 public static class Settings.NameValueTable implements android.provider.BaseColumns { 3336 } 3337 public interface BaseColumns { 3338 field public static final String _ID = "_id"; 3339 } 3340 } 3341 """, 3342 sourceFiles = arrayOf( 3343 java( 3344 """ 3345 package android.provider; 3346 3347 public class Settings { 3348 private Settings() { } 3349 public static final class Global extends NameValueTable { 3350 } 3351 public static final class NameValueTable implements BaseColumns { 3352 } 3353 } 3354 """ 3355 ), 3356 java( 3357 """ 3358 package android.provider; 3359 3360 public interface BaseColumns { 3361 public static final String _ID = "_id"; 3362 } 3363 """ 3364 ) 3365 ), 3366 extraArguments = arrayOf("--error", "NoSettingsProvider") 3367 ) 3368 } 3369 3370 @Test 3371 fun `No warnings about nullability on private constructor getters`() { 3372 check( 3373 expectedIssues = "", 3374 apiLint = "", 3375 sourceFiles = arrayOf( 3376 kotlin( 3377 """ 3378 package test.pkg 3379 class MyClass private constructor( 3380 val myParameter: Set<Int> 3381 ) 3382 """ 3383 ) 3384 ) 3385 ) 3386 } 3387 3388 @Test 3389 fun `Methods returning ListenableFuture end with async`() { 3390 check( 3391 apiLint = "", // enabled 3392 expectedIssues = """ 3393 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] 3394 """, 3395 expectedFail = DefaultLintErrorMessage, 3396 sourceFiles = arrayOf( 3397 java( 3398 """ 3399 package android.pkg; 3400 3401 import androidx.annotation.Nullable; 3402 import com.google.common.util.concurrent.ListenableFuture; 3403 3404 public final class MyClass { 3405 public @Nullable ListenableFuture<String> bad() { return null; } 3406 public @Nullable ListenableFuture<String> goodAsync() { return null; } 3407 } 3408 """ 3409 ), 3410 java( 3411 """ 3412 package com.google.common.util.concurrent; 3413 public class ListenableFuture<T> { 3414 } 3415 """ 3416 ), 3417 androidxNullableSource 3418 ) 3419 ) 3420 } 3421 3422 @Test 3423 fun `Listener replaceable with OutcomeReceiver or ListenableFuture`() { 3424 check( 3425 apiLint = "", // enabled 3426 expectedIssues = """ 3427 src/android/pkg/Cases.java:7: error: Cases.BadCallback can be replaced with OutcomeReceiver<R,E> (platform) or suspend fun / ListenableFuture (AndroidX). [GenericCallbacks] [See https://s.android.com/api-guidelines#callbacks-sam] 3428 src/android/pkg/Cases.java:15: error: Cases.BadGenericListener can be replaced with OutcomeReceiver<R,E> (platform) or suspend fun / ListenableFuture (AndroidX). [GenericCallbacks] [See https://s.android.com/api-guidelines#callbacks-sam] 3429 src/android/pkg/Cases.java:11: error: Cases.BadListener can be replaced with OutcomeReceiver<R,E> (platform) or suspend fun / ListenableFuture (AndroidX). [GenericCallbacks] [See https://s.android.com/api-guidelines#callbacks-sam] 3430 """, 3431 expectedFail = DefaultLintErrorMessage, 3432 sourceFiles = arrayOf( 3433 java( 3434 """ 3435 package android.pkg; 3436 3437 import androidx.annotation.NonNull; 3438 import java.io.IOException; 3439 3440 public final class Cases { 3441 public class BadCallback { 3442 public abstract void onSuccess(@NonNull String result); 3443 public void onFailure(@NonNull Throwable error) {} 3444 } 3445 public interface BadListener { 3446 void onResult(@NonNull Object result); 3447 void onError(@NonNull IOException error); 3448 } 3449 public interface BadGenericListener<R, E extends Throwable> { 3450 void onResult(@NonNull R result); 3451 void onError(@NonNull E error); 3452 } 3453 public interface OkListener { 3454 void onResult(@NonNull Object result); 3455 void onError(@NonNull int error); 3456 } 3457 public interface Ok2Listener { 3458 void onResult(@NonNull int result); 3459 void onError(@NonNull Throwable error); 3460 } 3461 public interface Ok3Listener { 3462 void onSuccess(@NonNull String result); 3463 void onOtherThing(@NonNull String result); 3464 void onFailure(@NonNull Throwable error); 3465 } 3466 } 3467 """ 3468 ), 3469 androidxNonNullSource 3470 ) 3471 ) 3472 } 3473 3474 @Test 3475 fun `No warning on generic return type`() { 3476 check( 3477 expectedIssues = "", 3478 apiLint = "", 3479 sourceFiles = arrayOf( 3480 kotlin( 3481 """ 3482 package test.pkg 3483 class SimpleArrayMap<K, V> { 3484 override fun getOrDefault(key: K, defaultValue: V): V {} 3485 } 3486 """ 3487 ) 3488 ) 3489 ) 3490 } 3491 3492 @Test 3493 fun `No crash when setter start with numbers`() { 3494 check( 3495 expectedIssues = "", 3496 apiLint = "", 3497 sourceFiles = arrayOf( 3498 java( 3499 """ 3500 package test.pkg; 3501 import androidx.annotation.NonNull; 3502 public class Config { 3503 public boolean is80211mcSupported() { return true; } 3504 public static final class Builder { 3505 @NonNull 3506 public Builder set80211mcSupported(boolean supports80211mc) { 3507 } 3508 @NonNull 3509 public Config build() { 3510 return Config(); 3511 } 3512 } 3513 } 3514 """ 3515 ), 3516 androidxNonNullSource, 3517 ) 3518 ) 3519 } 3520 3521 @Test 3522 fun `No error for nullability on synthetic methods`() { 3523 check( 3524 expectedIssues = "", 3525 apiLint = "", 3526 sourceFiles = arrayOf( 3527 kotlin( 3528 """ 3529 package test.pkg 3530 class Foo { 3531 @JvmSynthetic 3532 fun bar(): String {} 3533 } 3534 """ 3535 ) 3536 ) 3537 ) 3538 } 3539 3540 @Test 3541 fun `Constructors return types don't require nullability`() { 3542 check( 3543 expectedIssues = "", 3544 apiLint = "", 3545 sourceFiles = arrayOf( 3546 java( 3547 """ 3548 package test.pkg; 3549 public class Foo() { 3550 // Doesn't require nullability 3551 public Foo(@NonNull String bar); 3552 // Requires nullability 3553 public @NonNull String baz(@NonNull String whatever); 3554 } 3555 """ 3556 ) 3557 ) 3558 ) 3559 } 3560 3561 @Test No nullability allowed on overrides of unannotated methods or parametersnull3562 fun `No nullability allowed on overrides of unannotated methods or parameters`() { 3563 check( 3564 expectedIssues = """ 3565 src/test/pkg/Foo.java:10: error: Invalid nullability on method `bar` return. Overrides of unannotated super method cannot be Nullable. [InvalidNullabilityOverride] [See https://s.android.com/api-guidelines#annotations-nullability-overrides] 3566 src/test/pkg/Foo.java:10: error: Invalid nullability on parameter `baz` in method `bar`. Parameters of overrides cannot be NonNull if the super parameter is unannotated. [InvalidNullabilityOverride] [See https://s.android.com/api-guidelines#annotations-nullability-overrides] 3567 src/test/pkg/Foo.java:5: error: Missing nullability on method `bar` return [MissingNullability] [See https://s.android.com/api-guidelines#annotations] 3568 src/test/pkg/Foo.java:5: error: Missing nullability on parameter `baz` in method `bar` [MissingNullability] [See https://s.android.com/api-guidelines#annotations] 3569 """, 3570 apiLint = "", 3571 expectedFail = DefaultLintErrorMessage, 3572 sourceFiles = arrayOf( 3573 java( 3574 """ 3575 package test.pkg; 3576 3577 public class Foo { 3578 // Not annotated 3579 public String bar(String baz); 3580 } 3581 // Not allowed to mark override method Nullable if parent is not annotated 3582 // Not allowed to mark override parameter NonNull if parent is not annotated 3583 public class Bar extends Foo { 3584 @Nullable @Override public String bar(@NonNull String baz); 3585 } 3586 """ 3587 ), 3588 androidxNullableSource, 3589 androidxNonNullSource 3590 ) 3591 ) 3592 } 3593 3594 @Test Override enforcement on kotlin sourced child classnull3595 fun `Override enforcement on kotlin sourced child class`() { 3596 3597 check( 3598 expectedIssues = """ 3599 src/test/pkg/Bar.kt:5: error: Invalid nullability on parameter `baz` in method `bar`. Parameters of overrides cannot be NonNull if the super parameter is unannotated. [InvalidNullabilityOverride] [See https://s.android.com/api-guidelines#annotations-nullability-overrides] 3600 src/test/pkg/Foo.java:5: error: Missing nullability on method `bar` return [MissingNullability] [See https://s.android.com/api-guidelines#annotations] 3601 src/test/pkg/Foo.java:5: error: Missing nullability on parameter `baz` in method `bar` [MissingNullability] [See https://s.android.com/api-guidelines#annotations] 3602 """, 3603 apiLint = "", 3604 expectedFail = DefaultLintErrorMessage, 3605 sourceFiles = arrayOf( 3606 java( 3607 """ 3608 package test.pkg; 3609 3610 public class Foo { 3611 // Not annotated 3612 public String bar(String baz); 3613 } 3614 """ 3615 ), 3616 kotlin( 3617 """ 3618 package test.pkg 3619 // Not allowed to mark override method Nullable if parent is not annotated 3620 // Not allowed to mark override parameter NonNull if parent is not annotated 3621 class Bar : Foo { 3622 override fun bar(baz: String): String 3623 } 3624 """ 3625 ), 3626 androidxNullableSource, 3627 androidxNonNullSource 3628 ) 3629 ) 3630 } 3631 3632 @Test Overrides of non-null methods cannot be nullablenull3633 fun `Overrides of non-null methods cannot be nullable`() { 3634 check( 3635 expectedIssues = """ 3636 src/test/pkg/Foo.java:9: error: Invalid nullability on method `bar` return. Overrides of NonNull methods cannot be Nullable. [InvalidNullabilityOverride] [See https://s.android.com/api-guidelines#annotations-nullability-overrides] 3637 """, 3638 apiLint = "", 3639 expectedFail = DefaultLintErrorMessage, 3640 sourceFiles = arrayOf( 3641 java( 3642 """ 3643 package test.pkg; 3644 3645 public class Foo { 3646 @NonNull public String bar(@Nullable String baz); 3647 } 3648 3649 // Not allowed to mark override method Nullable if parent is nonNull 3650 public class Bar extends Foo { 3651 @Nullable @Override public String bar(@Nullable String baz); 3652 } 3653 """ 3654 ), 3655 androidxNullableSource, 3656 androidxNonNullSource 3657 ) 3658 ) 3659 } 3660 3661 @Test Overrides of nullable parameters cannot be non-nullnull3662 fun `Overrides of nullable parameters cannot be non-null`() { 3663 check( 3664 expectedIssues = """ 3665 src/test/pkg/Foo.java:10: error: Invalid nullability on parameter `baz` in method `bar`. Parameters of overrides cannot be NonNull if super parameter is Nullable. [InvalidNullabilityOverride] [See https://s.android.com/api-guidelines#annotations-nullability-overrides] 3666 """, 3667 apiLint = "", 3668 expectedFail = DefaultLintErrorMessage, 3669 sourceFiles = arrayOf( 3670 java( 3671 """ 3672 package test.pkg; 3673 3674 public class Foo { 3675 // Not annotated 3676 @NonNull public String bar(@Nullable String baz); 3677 } 3678 3679 // Not allowed to mark override parameter NonNull if parent is Nullable 3680 public class Bar extends Foo { 3681 @NonNull @Override public String bar(@NonNull String baz); 3682 } 3683 """ 3684 ), 3685 androidxNullableSource, 3686 androidxNonNullSource 3687 ) 3688 ) 3689 } 3690 3691 @Test Unchecked exceptions not allowednull3692 fun `Unchecked exceptions not allowed`() { 3693 check( 3694 expectedIssues = """ 3695 src/test/pkg/Foo.java:22: error: Methods must not throw unchecked exceptions [BannedThrow] 3696 src/test/pkg/Foo.java:23: error: Methods must not throw unchecked exceptions [BannedThrow] 3697 src/test/pkg/Foo.java:24: error: Methods must not throw unchecked exceptions [BannedThrow] 3698 src/test/pkg/Foo.java:25: error: Methods must not throw unchecked exceptions [BannedThrow] 3699 src/test/pkg/Foo.java:26: error: Methods must not throw unchecked exceptions [BannedThrow] 3700 src/test/pkg/Foo.java:27: error: Methods must not throw unchecked exceptions [BannedThrow] 3701 src/test/pkg/Foo.java:28: error: Methods must not throw unchecked exceptions [BannedThrow] 3702 src/test/pkg/Foo.java:29: error: Methods must not throw unchecked exceptions [BannedThrow] 3703 src/test/pkg/Foo.java:30: error: Methods must not throw unchecked exceptions [BannedThrow] 3704 src/test/pkg/Foo.java:31: error: Methods must not throw unchecked exceptions [BannedThrow] 3705 src/test/pkg/Foo.java:32: error: Methods must not throw unchecked exceptions [BannedThrow] 3706 src/test/pkg/Foo.java:33: error: Methods must not throw unchecked exceptions [BannedThrow] 3707 src/test/pkg/Foo.java:34: error: Methods must not throw unchecked exceptions [BannedThrow] 3708 src/test/pkg/Foo.java:35: error: Methods must not throw unchecked exceptions [BannedThrow] 3709 src/test/pkg/Foo.java:36: error: Methods must not throw unchecked exceptions [BannedThrow] 3710 src/test/pkg/Foo.java:37: error: Methods must not throw unchecked exceptions [BannedThrow] 3711 src/test/pkg/Foo.java:38: error: Methods must not throw unchecked exceptions [BannedThrow] 3712 src/test/pkg/Foo.java:39: error: Methods must not throw unchecked exceptions [BannedThrow] 3713 src/test/pkg/Foo.java:40: error: Methods must not throw unchecked exceptions [BannedThrow] 3714 src/test/pkg/Foo.java:41: error: Methods must not throw unchecked exceptions [BannedThrow] 3715 src/test/pkg/Foo.java:42: error: Methods must not throw unchecked exceptions [BannedThrow] 3716 src/test/pkg/Foo.java:43: error: Methods must not throw unchecked exceptions [BannedThrow] 3717 src/test/pkg/Foo.java:44: error: Methods must not throw unchecked exceptions [BannedThrow] 3718 src/test/pkg/Foo.java:45: error: Methods must not throw unchecked exceptions [BannedThrow] 3719 src/test/pkg/Foo.java:46: error: Methods must not throw unchecked exceptions [BannedThrow] 3720 src/test/pkg/Foo.java:47: error: Methods must not throw unchecked exceptions [BannedThrow] 3721 src/test/pkg/Foo.java:48: error: Methods must not throw unchecked exceptions [BannedThrow] 3722 src/test/pkg/Foo.java:49: error: Methods must not throw unchecked exceptions [BannedThrow] 3723 src/test/pkg/Foo.java:50: error: Methods must not throw unchecked exceptions [BannedThrow] 3724 src/test/pkg/Foo.java:51: error: Methods must not throw unchecked exceptions [BannedThrow] 3725 src/test/pkg/Foo.java:52: error: Methods must not throw unchecked exceptions [BannedThrow] 3726 src/test/pkg/Foo.java:53: error: Methods must not throw unchecked exceptions [BannedThrow] 3727 """, 3728 apiLint = "", 3729 expectedFail = DefaultLintErrorMessage, 3730 sourceFiles = arrayOf( 3731 java( 3732 """ 3733 package test.pkg; 3734 import java.lang.reflect.UndeclaredThrowableException; 3735 import java.lang.reflect.MalformedParametersException; 3736 import java.lang.reflect.MalformedParameterizedTypeException; 3737 import java.lang.invoke.WrongMethodTypeException; 3738 import java.lang.annotation.AnnotationTypeMismatchException; 3739 import java.lang.annotation.IncompleteAnnotationException; 3740 import java.util.MissingResourceException; 3741 import java.util.EmptyStackException; 3742 import java.util.concurrent.CompletionException; 3743 import java.util.concurrent.RejectedExecutionException; 3744 import java.util.IllformedLocaleException; 3745 import java.util.ConcurrentModificationException; 3746 import java.util.NoSuchElementException; 3747 import java.io.UncheckedIOException; 3748 import java.time.DateTimeException; 3749 import java.security.ProviderException; 3750 import java.nio.BufferUnderflowException; 3751 import java.nio.BufferOverflowException; 3752 public class Foo { 3753 // 32 errors 3754 public void a() throws NullPointerException; 3755 public void b() throws ClassCastException; 3756 public void c() throws IndexOutOfBoundsException; 3757 public void d() throws UndeclaredThrowableException; 3758 public void e() throws MalformedParametersException; 3759 public void f() throws MalformedParameterizedTypeException; 3760 public void g() throws WrongMethodTypeException; 3761 public void h() throws EnumConstantNotPresentException; 3762 public void i() throws IllegalMonitorStateException; 3763 public void j() throws SecurityException; 3764 public void k() throws UnsupportedOperationException; 3765 public void l() throws AnnotationTypeMismatchException; 3766 public void m() throws IncompleteAnnotationException; 3767 public void n() throws TypeNotPresentException; 3768 public void o() throws IllegalStateException; 3769 public void p() throws ArithmeticException; 3770 public void q() throws IllegalArgumentException; 3771 public void r() throws ArrayStoreException; 3772 public void s() throws NegativeArraySizeException; 3773 public void t() throws MissingResourceException; 3774 public void u() throws EmptyStackException; 3775 public void v() throws CompletionException; 3776 public void w() throws RejectedExecutionException; 3777 public void x() throws IllformedLocaleException; 3778 public void y() throws ConcurrentModificationException; 3779 public void z() throws NoSuchElementException; 3780 public void aa() throws UncheckedIOException; 3781 public void ab() throws DateTimeException; 3782 public void ac() throws ProviderException; 3783 public void ad() throws BufferUnderflowException; 3784 public void ae() throws BufferOverflowException; 3785 public void af() throws AssertionError; 3786 } 3787 """ 3788 ), 3789 ) 3790 ) 3791 } 3792 3793 @Test Nullability overrides in unbounded generics should be allowednull3794 fun `Nullability overrides in unbounded generics should be allowed`() { 3795 check( 3796 apiLint = "", 3797 sourceFiles = arrayOf( 3798 kotlin( 3799 """ 3800 package test.pkg; 3801 3802 interface Base<T> { 3803 fun method1(input: T): T 3804 } 3805 3806 class Subject1 : Base<String> { 3807 override fun method1(input: String): String { 3808 TODO() 3809 } 3810 } 3811 3812 class Subject2 : Base<String?> { 3813 override fun method1(input: String?): String? { 3814 TODO() 3815 } 3816 } 3817 """ 3818 ) 3819 ) 3820 ) 3821 } 3822 3823 @Test Nullability overrides in unbounded generics (Object to generic and back)null3824 fun `Nullability overrides in unbounded generics (Object to generic and back)`() { 3825 check( 3826 apiLint = "", 3827 sourceFiles = arrayOf( 3828 kotlin( 3829 """ 3830 package test.pkg 3831 3832 open class SimpleArrayMap<K, V> { 3833 open operator fun get(key: K): V? { 3834 TODO() 3835 } 3836 } 3837 """ 3838 ), 3839 java( 3840 """ 3841 package test.pkg; 3842 3843 import java.util.Map; 3844 3845 public class ArrayMap<K, V> extends SimpleArrayMap<K, V> implements Map<K, V> { 3846 @Override 3847 @Nullable 3848 public V get(@NonNull Object key) { 3849 return super.get((K) key); 3850 } 3851 } 3852 3853 """ 3854 ) 3855 ) 3856 ) 3857 } 3858 3859 @Test Nullability overrides in unbounded generics (one super method lacks nullness info)null3860 fun `Nullability overrides in unbounded generics (one super method lacks nullness info)`() { 3861 check( 3862 apiLint = "", 3863 sourceFiles = arrayOf( 3864 kotlin( 3865 """ 3866 package test.pkg 3867 3868 open class SimpleArrayMap<K, V> { 3869 open operator fun get(key: K): V? { 3870 TODO() 3871 } 3872 } 3873 """ 3874 ), 3875 java( 3876 """ 3877 package test.pkg; 3878 3879 import java.util.Map; 3880 3881 public class ArrayMap<K, V> extends SimpleArrayMap<K, V> implements Map<K, V> { 3882 @Override 3883 @Nullable 3884 public V get(@Nullable Object key) { 3885 return super.get((K) key); 3886 } 3887 } 3888 3889 """ 3890 ) 3891 ) 3892 ) 3893 } 3894 } 3895