1 /*
2  * Copyright 2024 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 androidx.build.lint
18 
19 import androidx.build.lint.Stubs.Companion.JetBrainsAnnotations
20 import com.android.tools.lint.checks.infrastructure.TestFile
21 import com.android.tools.lint.checks.infrastructure.TestMode
22 import org.junit.Test
23 import org.junit.runner.RunWith
24 import org.junit.runners.JUnit4
25 
26 @RunWith(JUnit4::class)
27 class JSpecifyNullnessMigrationTest :
28     AbstractLintDetectorTest(
29         useDetector = JSpecifyNullnessMigration(),
30         useIssues = listOf(JSpecifyNullnessMigration.ISSUE),
31         stubs = annotationStubs
32     ) {
33     @Test
Nullness annotation on array parameternull34     fun `Nullness annotation on array parameter`() {
35         val input =
36             java(
37                 """
38                 package test.pkg;
39                 import androidx.annotation.NonNull;
40                 public class Foo {
41                     public void foo(@NonNull String[] arr) {}
42                 }
43                 """
44                     .trimIndent()
45             )
46 
47         val expected =
48             """
49             src/test/pkg/Foo.java:4: Error: Switch nullness annotation to JSpecify [JSpecifyNullness]
50                 public void foo(@NonNull String[] arr) {}
51                                 ~~~~~~~~
52             1 errors, 0 warnings
53             """
54                 .trimIndent()
55 
56         val expectedFixDiffs =
57             """
58             Autofix for src/test/pkg/Foo.java line 4: Replace annotation:
59             @@ -4 +4
60             -     public void foo(@NonNull String[] arr) {}
61             +     public void foo(String @org.jspecify.annotations.NonNull [] arr) {}
62             """
63                 .trimIndent()
64 
65         runNullnessTest(input, expected, expectedFixDiffs)
66     }
67 
68     @Test
Nullness annotation on array method returnnull69     fun `Nullness annotation on array method return`() {
70         val input =
71             java(
72                 """
73                 package test.pkg;
74                 import androidx.annotation.Nullable;
75                 public class Foo {
76                     @Nullable
77                     public String[] foo() { return null; }
78                 }
79                 """
80                     .trimIndent(),
81             )
82 
83         val expected =
84             """
85             src/test/pkg/Foo.java:4: Error: Switch nullness annotation to JSpecify [JSpecifyNullness]
86                 @Nullable
87                 ~~~~~~~~~
88             1 errors, 0 warnings
89             """
90                 .trimIndent()
91 
92         val expectedFixDiffs =
93             """
94             Autofix for src/test/pkg/Foo.java line 4: Replace annotation:
95             @@ -4 +4
96             -     @Nullable
97             -     public String[] foo() { return null; }
98             +     public String @org.jspecify.annotations.Nullable [] foo() { return null; }
99             """
100                 .trimIndent()
101 
102         runNullnessTest(input, expected, expectedFixDiffs)
103     }
104 
105     @Test
Nullness annotation on array method return and array parameternull106     fun `Nullness annotation on array method return and array parameter`() {
107         val input =
108             java(
109                 """
110                 package test.pkg;
111                 import androidx.annotation.Nullable;
112                 public class Foo {
113                     @Nullable
114                     public String[] foo(@Nullable String[] arr) { return null; }
115                 }
116                 """
117                     .trimIndent()
118             )
119 
120         val expected =
121             """
122             src/test/pkg/Foo.java:4: Error: Switch nullness annotation to JSpecify [JSpecifyNullness]
123                 @Nullable
124                 ~~~~~~~~~
125             src/test/pkg/Foo.java:5: Error: Switch nullness annotation to JSpecify [JSpecifyNullness]
126                 public String[] foo(@Nullable String[] arr) { return null; }
127                                     ~~~~~~~~~
128             2 errors, 0 warnings
129             """
130                 .trimIndent()
131 
132         val expectedFixDiffs =
133             """
134             Autofix for src/test/pkg/Foo.java line 4: Replace annotation:
135             @@ -4 +4
136             -     @Nullable
137             -     public String[] foo(@Nullable String[] arr) { return null; }
138             +     public String @org.jspecify.annotations.Nullable [] foo(@Nullable String[] arr) { return null; }
139             Autofix for src/test/pkg/Foo.java line 5: Replace annotation:
140             @@ -5 +5
141             -     public String[] foo(@Nullable String[] arr) { return null; }
142             +     public String[] foo(String @org.jspecify.annotations.Nullable [] arr) { return null; }
143             """
144                 .trimIndent()
145 
146         runNullnessTest(input, expected, expectedFixDiffs)
147     }
148 
149     @Test
Nullness annotation on array fieldnull150     fun `Nullness annotation on array field`() {
151         val input =
152             java(
153                 """
154                 package test.pkg;
155                 import androidx.annotation.Nullable;
156                 public class Foo {
157                     @Nullable public String[] foo;
158                 }
159                 """
160                     .trimIndent()
161             )
162 
163         val expected =
164             """
165             src/test/pkg/Foo.java:4: Error: Switch nullness annotation to JSpecify [JSpecifyNullness]
166                 @Nullable public String[] foo;
167                 ~~~~~~~~~
168             1 errors, 0 warnings
169             """
170                 .trimIndent()
171 
172         val expectedFixDiffs =
173             """
174             Autofix for src/test/pkg/Foo.java line 4: Replace annotation:
175             @@ -4 +4
176             -     @Nullable public String[] foo;
177             +     public String @org.jspecify.annotations.Nullable [] foo;
178             """
179                 .trimIndent()
180 
181         runNullnessTest(input, expected, expectedFixDiffs)
182     }
183 
184     @Test
Nullness annotation on 2d arraynull185     fun `Nullness annotation on 2d array`() {
186         val input =
187             java(
188                 """
189                 package test.pkg;
190                 import androidx.annotation.Nullable;
191                 public class Foo {
192                     @Nullable public String[][] foo;
193                 }
194                 """
195                     .trimIndent()
196             )
197 
198         val expected =
199             """
200             src/test/pkg/Foo.java:4: Error: Switch nullness annotation to JSpecify [JSpecifyNullness]
201                 @Nullable public String[][] foo;
202                 ~~~~~~~~~
203             1 errors, 0 warnings
204             """
205                 .trimIndent()
206 
207         val expectedFixDiffs =
208             """
209             Autofix for src/test/pkg/Foo.java line 4: Replace annotation:
210             @@ -4 +4
211             -     @Nullable public String[][] foo;
212             +     public String @org.jspecify.annotations.Nullable [][] foo;
213             """
214                 .trimIndent()
215 
216         runNullnessTest(input, expected, expectedFixDiffs)
217     }
218 
219     @Test
Nullness annotation on varargsnull220     fun `Nullness annotation on varargs`() {
221         val input =
222             java(
223                 """
224                 package test.pkg;
225                 import androidx.annotation.NonNull;
226                 public class Foo {
227                     public void foo(@NonNull String... arr) {}
228                 }
229                 """
230                     .trimIndent(),
231             )
232 
233         val expected =
234             """
235             src/test/pkg/Foo.java:4: Error: Switch nullness annotation to JSpecify [JSpecifyNullness]
236                 public void foo(@NonNull String... arr) {}
237                                 ~~~~~~~~
238             1 errors, 0 warnings
239             """
240                 .trimIndent()
241 
242         val expectedFixDiffs =
243             """
244             Autofix for src/test/pkg/Foo.java line 4: Replace annotation:
245             @@ -4 +4
246             -     public void foo(@NonNull String... arr) {}
247             +     public void foo(String @org.jspecify.annotations.NonNull ... arr) {}
248             """
249                 .trimIndent()
250 
251         runNullnessTest(input, expected, expectedFixDiffs)
252     }
253 
254     @Test
Nullness annotation on array varargsnull255     fun `Nullness annotation on array varargs`() {
256         val input =
257             java(
258                 """
259                     package test.pkg;
260                     import androidx.annotation.NonNull;
261                     public class Foo {
262                         public void foo(@NonNull String[]... args) {}
263                     }
264                 """
265                     .trimIndent()
266             )
267 
268         val expected =
269             """
270                 src/test/pkg/Foo.java:4: Error: Switch nullness annotation to JSpecify [JSpecifyNullness]
271                     public void foo(@NonNull String[]... args) {}
272                                     ~~~~~~~~
273                 1 errors, 0 warnings
274             """
275                 .trimIndent()
276 
277         val expectedFixDiffs =
278             """
279                 Autofix for src/test/pkg/Foo.java line 4: Replace annotation:
280                 @@ -4 +4
281                 -     public void foo(@NonNull String[]... args) {}
282                 +     public void foo(String @org.jspecify.annotations.NonNull []... args) {}
283             """
284                 .trimIndent()
285 
286         runNullnessTest(input, expected, expectedFixDiffs)
287     }
288 
289     @Test
Nullness annotation on method return with array in commentsnull290     fun `Nullness annotation on method return with array in comments`() {
291         val input =
292             java(
293                 """
294                 package test.pkg;
295                 import androidx.annotation.Nullable;
296                 public class Foo {
297                    /**
298                     * @return A String[]
299                     */
300                     @Nullable
301                     public String[] foo() { return null; }
302                 }
303                 """
304                     .trimIndent(),
305             )
306 
307         val expected =
308             """
309             src/test/pkg/Foo.java:7: Error: Switch nullness annotation to JSpecify [JSpecifyNullness]
310                 @Nullable
311                 ~~~~~~~~~
312             1 errors, 0 warnings
313             """
314                 .trimIndent()
315 
316         val expectedFixDiffs =
317             """
318             Autofix for src/test/pkg/Foo.java line 7: Replace annotation:
319             @@ -7 +7
320             -     @Nullable
321             -     public String[] foo() { return null; }
322             +     public String @org.jspecify.annotations.Nullable [] foo() { return null; }
323             """
324                 .trimIndent()
325 
326         runNullnessTest(input, expected, expectedFixDiffs)
327     }
328 
329     @Test
Nullness annotation on method return with annotation in commentsnull330     fun `Nullness annotation on method return with annotation in comments`() {
331         val input =
332             java(
333                 """
334                 package test.pkg;
335                 import androidx.annotation.Nullable;
336                 public class Foo {
337                    /**
338                     * @return A @Nullable string array
339                     */
340                     @Nullable
341                     public String[] foo() { return null; }
342                 }
343                 """
344                     .trimIndent(),
345             )
346 
347         val expected =
348             """
349             src/test/pkg/Foo.java:7: Error: Switch nullness annotation to JSpecify [JSpecifyNullness]
350                 @Nullable
351                 ~~~~~~~~~
352             1 errors, 0 warnings
353             """
354                 .trimIndent()
355 
356         val expectedFixDiffs =
357             """
358             Autofix for src/test/pkg/Foo.java line 7: Replace annotation:
359             @@ -7 +7
360             -     @Nullable
361             -     public String[] foo() { return null; }
362             +     public String @org.jspecify.annotations.Nullable [] foo() { return null; }
363             """
364                 .trimIndent()
365 
366         runNullnessTest(input, expected, expectedFixDiffs)
367     }
368 
369     @Test
Nullness annotation removed from local variable declarationnull370     fun `Nullness annotation removed from local variable declaration`() {
371         val input =
372             java(
373                 """
374                     package test.pkg;
375                     import androidx.annotation.Nullable;
376                     public class Foo {
377                         public String foo() {
378                             @Nullable String str = null;
379                             return str;
380                         }
381                     }
382                 """
383                     .trimIndent()
384             )
385         val expected =
386             """
387             src/test/pkg/Foo.java:5: Error: Switch nullness annotation to JSpecify [JSpecifyNullness]
388                     @Nullable String str = null;
389                     ~~~~~~~~~
390             1 errors, 0 warnings
391             """
392                 .trimIndent()
393         val expectedFixDiffs =
394             """
395             Autofix for src/test/pkg/Foo.java line 5: Delete:
396             @@ -5 +5
397             -         @Nullable String str = null;
398             +         String str = null;
399             """
400                 .trimIndent()
401 
402         runNullnessTest(input, expected, expectedFixDiffs)
403     }
404 
405     @Test
Nullness annotation removed from void return typenull406     fun `Nullness annotation removed from void return type`() {
407         val input =
408             java(
409                 """
410                     package test.pkg;
411                     import androidx.annotation.Nullable;
412                     public class Foo {
413                         @Nullable
414                         public void foo() {}
415                     }
416                 """
417                     .trimIndent()
418             )
419 
420         val expected =
421             """
422                 src/test/pkg/Foo.java:4: Error: Switch nullness annotation to JSpecify [JSpecifyNullness]
423                     @Nullable
424                     ~~~~~~~~~
425                 1 errors, 0 warnings
426             """
427                 .trimIndent()
428 
429         val expectedFixDiffs =
430             """
431                 Autofix for src/test/pkg/Foo.java line 4: Delete:
432                 @@ -4 +4
433                 -     @Nullable
434             """
435                 .trimIndent()
436 
437         runNullnessTest(input, expected, expectedFixDiffs)
438     }
439 
440     @Test
Nullness annotation removed from primitive parameter typenull441     fun `Nullness annotation removed from primitive parameter type`() {
442         val input =
443             java(
444                 """
445                     package test.pkg;
446                     import androidx.annotation.Nullable;
447                     public class Foo {
448                         public void foo(@Nullable int i) {}
449                     }
450                 """
451                     .trimIndent()
452             )
453 
454         val expected =
455             """
456                 src/test/pkg/Foo.java:4: Error: Switch nullness annotation to JSpecify [JSpecifyNullness]
457                     public void foo(@Nullable int i) {}
458                                     ~~~~~~~~~
459                 1 errors, 0 warnings
460             """
461                 .trimIndent()
462 
463         val expectedFixDiffs =
464             """
465                 Autofix for src/test/pkg/Foo.java line 4: Delete:
466                 @@ -4 +4
467                 -     public void foo(@Nullable int i) {}
468                 +     public void foo(int i) {}
469             """
470                 .trimIndent()
471 
472         runNullnessTest(input, expected, expectedFixDiffs)
473     }
474 
475     @Test
Nullness annotation on class type parameternull476     fun `Nullness annotation on class type parameter`() {
477         val input =
478             java(
479                 """
480                 package test.pkg;
481                 import androidx.annotation.NonNull;
482                 public class Foo {
483                     public void foo(@NonNull Foo.InnerFoo arr) {}
484                     public class InnerFoo {}
485                 }
486                 """
487                     .trimIndent()
488             )
489 
490         val expected =
491             """
492             src/test/pkg/Foo.java:4: Error: Switch nullness annotation to JSpecify [JSpecifyNullness]
493                 public void foo(@NonNull Foo.InnerFoo arr) {}
494                                 ~~~~~~~~
495             1 errors, 0 warnings
496             """
497                 .trimIndent()
498 
499         val expectedFixDiffs =
500             """
501                 Autofix for src/test/pkg/Foo.java line 4: Replace annotation:
502                 @@ -4 +4
503                 -     public void foo(@NonNull Foo.InnerFoo arr) {}
504                 +     public void foo(Foo.@org.jspecify.annotations.NonNull InnerFoo arr) {}
505             """
506                 .trimIndent()
507 
508         runNullnessTest(input, expected, expectedFixDiffs)
509     }
510 
511     @Test
Nullness annotation on class type returnnull512     fun `Nullness annotation on class type return`() {
513         val input =
514             java(
515                 """
516                 package test.pkg;
517                 import androidx.annotation.Nullable;
518                 public class Foo {
519                     @Nullable
520                     public String foo() { return null; }
521                 }
522                 """
523                     .trimIndent()
524             )
525 
526         val expected =
527             """
528             src/test/pkg/Foo.java:4: Error: Switch nullness annotation to JSpecify [JSpecifyNullness]
529                 @Nullable
530                 ~~~~~~~~~
531             1 errors, 0 warnings
532             """
533                 .trimIndent()
534 
535         val expectedFixDiffs =
536             """
537                 Autofix for src/test/pkg/Foo.java line 4: Replace annotation:
538                 @@ -4 +4
539                 -     @Nullable
540                 -     public String foo() { return null; }
541                 +     public @org.jspecify.annotations.Nullable String foo() { return null; }
542             """
543                 .trimIndent()
544 
545         runNullnessTest(input, expected, expectedFixDiffs)
546     }
547 
548     @Test
Nullness annotation on class type paramnull549     fun `Nullness annotation on class type param`() {
550         val input =
551             java(
552                 """
553                 package test.pkg;
554                 import androidx.annotation.Nullable;
555                 public class Foo {
556                     public String foo(@Nullable String foo) { return null; }
557                 }
558                 """
559                     .trimIndent()
560             )
561 
562         val expected =
563             """
564             src/test/pkg/Foo.java:4: Error: Switch nullness annotation to JSpecify [JSpecifyNullness]
565                 public String foo(@Nullable String foo) { return null; }
566                                   ~~~~~~~~~
567             1 errors, 0 warnings
568             """
569                 .trimIndent()
570 
571         val expectedFixDiffs =
572             """
573                 Autofix for src/test/pkg/Foo.java line 4: Replace annotation:
574                 @@ -4 +4
575                 -     public String foo(@Nullable String foo) { return null; }
576                 +     public String foo(@org.jspecify.annotations.Nullable String foo) { return null; }
577             """
578                 .trimIndent()
579 
580         runNullnessTest(input, expected, expectedFixDiffs)
581     }
582 
583     @Test
Nullness annotation on class type param and returnnull584     fun `Nullness annotation on class type param and return`() {
585         val input =
586             java(
587                 """
588                 package test.pkg;
589                 import androidx.annotation.NonNull;
590                 import androidx.annotation.Nullable;
591                 public class Foo {
592                     @Nullable
593                     public String foo(@NonNull String foo) { return null; }
594                 }
595                 """
596                     .trimIndent()
597             )
598 
599         val expected =
600             """
601             src/test/pkg/Foo.java:5: Error: Switch nullness annotation to JSpecify [JSpecifyNullness]
602                 @Nullable
603                 ~~~~~~~~~
604             src/test/pkg/Foo.java:6: Error: Switch nullness annotation to JSpecify [JSpecifyNullness]
605                 public String foo(@NonNull String foo) { return null; }
606                                   ~~~~~~~~
607             2 errors, 0 warnings
608             """
609                 .trimIndent()
610 
611         val expectedFixDiffs =
612             """
613                 Autofix for src/test/pkg/Foo.java line 5: Replace annotation:
614                 @@ -5 +5
615                 -     @Nullable
616                 -     public String foo(@NonNull String foo) { return null; }
617                 +     public @org.jspecify.annotations.Nullable String foo(@NonNull String foo) { return null; }
618                 Autofix for src/test/pkg/Foo.java line 6: Replace annotation:
619                 @@ -6 +6
620                 -     public String foo(@NonNull String foo) { return null; }
621                 +     public String foo(@org.jspecify.annotations.NonNull String foo) { return null; }
622             """
623                 .trimIndent()
624 
625         runNullnessTest(input, expected, expectedFixDiffs)
626     }
627 
628     @Test
Nullness annotation on type parameter returnnull629     fun `Nullness annotation on type parameter return`() {
630         val input =
631             java(
632                 """
633                     package test.pkg;
634                     import androidx.annotation.Nullable;
635                     public class Foo {
636                         @Nullable
637                         public <T> T foo() {
638                             return null;
639                         }
640                     }
641                 """
642                     .trimIndent()
643             )
644 
645         val expected =
646             """
647                 src/test/pkg/Foo.java:4: Error: Switch nullness annotation to JSpecify [JSpecifyNullness]
648                     @Nullable
649                     ~~~~~~~~~
650                 1 errors, 0 warnings
651             """
652                 .trimIndent()
653         val expectedFixDiffs =
654             """
655                 Autofix for src/test/pkg/Foo.java line 4: Replace annotation:
656                 @@ -4 +4
657                 -     @Nullable
658                 -     public <T> T foo() {
659                 +     public <T> @org.jspecify.annotations.Nullable T foo() {
660             """
661                 .trimIndent()
662 
663         runNullnessTest(input, expected, expectedFixDiffs)
664     }
665 
666     @Test
Nullness annotation on inner type where outer type contains namenull667     fun `Nullness annotation on inner type where outer type contains name`() {
668         val input =
669             java(
670                 """
671                     package test.pkg;
672                     import androidx.annotation.Nullable;
673                     public class RecyclerView {
674                         public class Recycler {}
675                         @Nullable
676                         public RecyclerView.Recycler foo() {
677                             return null;
678                         }
679                     }
680                 """
681                     .trimIndent()
682             )
683 
684         val expected =
685             """
686                 src/test/pkg/RecyclerView.java:5: Error: Switch nullness annotation to JSpecify [JSpecifyNullness]
687                     @Nullable
688                     ~~~~~~~~~
689                 1 errors, 0 warnings
690             """
691                 .trimIndent()
692         val expectedFixDiffs =
693             """
694                 Autofix for src/test/pkg/RecyclerView.java line 5: Replace annotation:
695                 @@ -5 +5
696                 -     @Nullable
697                 -     public RecyclerView.Recycler foo() {
698                 +     public RecyclerView.@org.jspecify.annotations.Nullable Recycler foo() {
699             """
700                 .trimIndent()
701 
702         runNullnessTest(input, expected, expectedFixDiffs)
703     }
704 
705     @Test
Nullness annotation on parameterized typenull706     fun `Nullness annotation on parameterized type`() {
707         val input =
708             java(
709                 """
710                     package test.pkg;
711                     import androidx.annotation.Nullable;
712                     import java.util.List;
713                     public class Foo {
714                         @Nullable
715                         public List<String> foo() {
716                             return null;
717                         }
718                     }
719                 """
720                     .trimIndent()
721             )
722 
723         val expected =
724             """
725                 src/test/pkg/Foo.java:5: Error: Switch nullness annotation to JSpecify [JSpecifyNullness]
726                     @Nullable
727                     ~~~~~~~~~
728                 1 errors, 0 warnings
729             """
730                 .trimIndent()
731         val expectedFixDiffs =
732             """
733                 Autofix for src/test/pkg/Foo.java line 5: Replace annotation:
734                 @@ -5 +5
735                 -     @Nullable
736                 -     public List<String> foo() {
737                 +     public @org.jspecify.annotations.Nullable List<String> foo() {
738             """
739                 .trimIndent()
740 
741         runNullnessTest(input, expected, expectedFixDiffs)
742     }
743 
744     @Test
Nullness annotation on parameter with newline after typenull745     fun `Nullness annotation on parameter with newline after type`() {
746         val input =
747             java(
748                 """
749                     package test.pkg;
750                     import androidx.annotation.NonNull;
751                     public class Foo {
752                         public void foo(@NonNull String
753                             arg) {}
754                     }
755                 """
756                     .trimIndent()
757             )
758 
759         val expected =
760             """
761             src/test/pkg/Foo.java:4: Error: Switch nullness annotation to JSpecify [JSpecifyNullness]
762                 public void foo(@NonNull String
763                                 ~~~~~~~~
764             1 errors, 0 warnings
765             """
766                 .trimIndent()
767         // Import fix included in this one but not others because the annotation names are different
768         // (NotNull vs NonNull).
769         val expectedFixDiffs =
770             """
771             Autofix for src/test/pkg/Foo.java line 4: Replace annotation:
772             @@ -4 +4
773             -     public void foo(@NonNull String
774             +     public void foo(@org.jspecify.annotations.NonNull String
775             """
776                 .trimIndent()
777 
778         runNullnessTest(input, expected, expectedFixDiffs)
779     }
780 
781     @Test
Detection of Jetbrains nullability usagenull782     fun `Detection of Jetbrains nullability usage`() {
783         val source =
784             java(
785                 """
786                 import org.jetbrains.annotations.NotNull;
787                 import org.jetbrains.annotations.Nullable;
788 
789                 public class NullabilityAnnotationsJava {
790                     private void method1(@NotNull String arg) {
791                     }
792 
793                     private void method2(@Nullable String arg) {
794                     }
795                 }
796             """
797                     .trimIndent()
798             )
799 
800         val input = arrayOf(source, JetBrainsAnnotations)
801 
802         val expected =
803             """
804                 src/NullabilityAnnotationsJava.java:5: Error: Switch nullness annotation to JSpecify [JSpecifyNullness]
805                     private void method1(@NotNull String arg) {
806                                          ~~~~~~~~
807                 src/NullabilityAnnotationsJava.java:8: Error: Switch nullness annotation to JSpecify [JSpecifyNullness]
808                     private void method2(@Nullable String arg) {
809                                          ~~~~~~~~~
810                 2 errors, 0 warnings
811             """
812                 .trimIndent()
813 
814         val expectFixDiffs =
815             """
816                 Autofix for src/NullabilityAnnotationsJava.java line 5: Replace annotation:
817                 @@ -3 +3
818                 + import org.jspecify.annotations.NonNull;
819                 @@ -5 +6
820                 -     private void method1(@NotNull String arg) {
821                 +     private void method1(@NonNull String arg) {
822                 Autofix for src/NullabilityAnnotationsJava.java line 8: Replace annotation:
823                 @@ -8 +8
824                 -     private void method2(@Nullable String arg) {
825                 +     private void method2(@org.jspecify.annotations.Nullable String arg) {
826             """
827                 .trimIndent()
828 
829         check(*input).expect(expected).expectFixDiffs(expectFixDiffs)
830     }
831 
runNullnessTestnull832     private fun runNullnessTest(input: TestFile, expected: String, expectedFixDiffs: String) {
833         lint()
834             .files(*stubs, input)
835             // Skip WHITESPACE mode because an array suffix with whitespace in the middle "[ ]" will
836             // break the pattern matching, but is extremely unlikely to happen in practice.
837             // Skip FULLY_QUALIFIED mode because the type-use annotation positioning depends on
838             // whether types are fully qualified, so fixes will be different.
839             .skipTestModes(TestMode.WHITESPACE, TestMode.FULLY_QUALIFIED)
840             .run()
841             .expect(expected)
842             .expectFixDiffs(expectedFixDiffs)
843     }
844 
845     companion object {
846         val annotationStubs =
847             arrayOf(
848                 kotlin(
849                     """
850                         package androidx.annotation
851                         annotation class NonNull
852                     """
853                 ),
854                 kotlin(
855                     """
856                         package androidx.annotation
857                         annotation class Nullable
858                     """
859                 )
860             )
861     }
862 }
863