1 /*
<lambda>null2  * Copyright (C) 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 com.android.tools.metalava.model.testsuite.fielditem
18 
19 import com.android.tools.metalava.model.FieldItem
20 import com.android.tools.metalava.model.testsuite.BaseModelTest
21 import com.android.tools.metalava.model.testsuite.assertHasNonNullNullability
22 import com.android.tools.metalava.model.testsuite.assertHasNullableNullability
23 import com.android.tools.metalava.model.testsuite.runNullabilityTest
24 import com.android.tools.metalava.testing.KnownSourceFiles
25 import com.android.tools.metalava.testing.java
26 import com.android.tools.metalava.testing.kotlin
27 import com.google.common.truth.Truth.assertWithMessage
28 import java.io.PrintWriter
29 import java.io.StringWriter
30 import kotlin.test.assertEquals
31 import org.junit.Test
32 
33 /** Common tests for implementations of [FieldItem]. */
34 class CommonFieldItemTest : BaseModelTest() {
35 
36     @Test
37     fun `Test access type parameter of outer class`() {
38         runCodebaseTest(
39             signature(
40                 """
41                     // Signature format: 2.0
42                     package test.pkg {
43                       public class Outer<O> {
44                       }
45                       public class Outer.Middle {
46                       }
47                       public class Outer.Middle.Inner {
48                         field public O field;
49                       }
50                     }
51                 """
52             ),
53             java(
54                 """
55                     package test.pkg;
56 
57                     public class Outer<O> {
58                         private Outer() {}
59 
60                         public class Middle {
61                             private Middle() {}
62                             public class Inner {
63                                 private Inner() {}
64                                 public O field;
65                             }
66                         }
67                     }
68                 """
69             ),
70         ) {
71             val oTypeParameter = codebase.assertClass("test.pkg.Outer").typeParameterList.single()
72             val fieldType =
73                 codebase.assertClass("test.pkg.Outer.Middle.Inner").assertField("field").type()
74 
75             fieldType.assertReferencesTypeParameter(oTypeParameter)
76         }
77     }
78 
79     @Test
80     fun `Test implicit nullability of enum constant`() {
81         runNullabilityTest(
82             java(
83                 """
84                     package test.pkg;
85 
86                     public enum Foo {
87                         ENUM1
88                     }
89                 """
90             ),
91             signature(
92                 """
93                     // Signature format: 2.0
94                     package test.pkg {
95                       public enum Foo {
96                         enum_constant public test.pkg.Foo ENUM1;
97                       }
98                     }
99                 """
100             ),
101             kotlin(
102                 """
103                     package test.pkg
104 
105                     enum class Foo {
106                         ENUM1
107                     }
108                 """
109             ),
110             signature(
111                 """
112                     // Signature format: 5.0
113                     package test.pkg {
114                       public enum Foo {
115                         enum_constant public test.pkg.Foo ENUM1;
116                       }
117                     }
118                 """
119             ),
120         ) {
121             val enumConstant = codebase.assertClass("test.pkg.Foo").fields().single()
122 
123             // Annotations should not be added as it is implicitly non-null.
124             enumConstant.type().assertHasNonNullNullability(false)
125         }
126     }
127 
128     @Test
129     fun `Test implicit nullability of static final String`() {
130         runNullabilityTest(
131             java(
132                 """
133                     package test.pkg;
134 
135                     public class Foo {
136                         public static final String CONST = "CONST";
137                     }
138                 """
139             ),
140             signature(
141                 """
142                     // Signature format: 2.0
143                     package test.pkg {
144                       public class Foo {
145                         field public static final String CONST = "CONST";
146                       }
147                     }
148                 """
149             ),
150             kotlin(
151                 """
152                     package test.pkg
153 
154                     class Foo {
155                         companion object {
156                             const val CONST = "CONST"
157                         }
158                     }
159                 """
160             ),
161             signature(
162                 """
163                     // Signature format: 5.0
164                     package test.pkg {
165                       public class Foo {
166                         field public static final String CONST = "CONST";
167                       }
168                     }
169                 """
170             ),
171         ) {
172             val stringConstant = codebase.assertClass("test.pkg.Foo").assertField("CONST")
173 
174             stringConstant.type().assertHasNonNullNullability(false)
175         }
176     }
177 
178     @Test
179     fun `Test implicit nullability of companion object`() {
180         runCodebaseTest(
181             // Only Kotlin has companion objects.
182             kotlin(
183                 """
184                     package test.pkg
185 
186                     class Foo {
187                         companion object {
188                         }
189                     }
190                 """
191             ),
192         ) {
193             val companionObject = codebase.assertClass("test.pkg.Foo").fields().single()
194 
195             companionObject.type().assertHasNonNullNullability(false)
196         }
197     }
198 
199     @Test
200     fun `Test nullability of field annotated with @NonNull or kotlin equivalent`() {
201         runNullabilityTest(
202             java(
203                 """
204                     package test.pkg;
205                     import libcore.util.NonNull;
206 
207                     public class Foo {
208                         @NonNull
209                         public String field;
210                     }
211                 """
212             ),
213             signature(
214                 """
215                     // Signature format: 2.0
216                     package test.pkg {
217                       public class Foo {
218                         field @NonNull public String field;
219                       }
220                     }
221                 """
222             ),
223             kotlin(
224                 """
225                     package test.pkg
226 
227                     class Foo {
228                         var field: String = ""
229                     }
230                 """
231             ),
232             signature(
233                 """
234                     // Signature format: 5.0
235                     package test.pkg {
236                       public class Foo {
237                         field public String field;
238                       }
239                     }
240                 """
241             ),
242         ) {
243             val field = codebase.assertClass("test.pkg.Foo").assertField("field")
244 
245             // Do not check the annotation as type use annotations are ambiguous in signature files
246             // that do not specify `kotlin-name-type-order=yes`
247             field.type().assertHasNonNullNullability()
248         }
249     }
250 
251     @Test
252     fun `Test nullability of field annotated with @not-type-use-NonNull`() {
253         runCodebaseTest(
254             inputSet(
255                 KnownSourceFiles.notTypeUseNonNullSource,
256                 java(
257                     """
258                         package test.pkg;
259                         import java.util.Map;
260                         import not.type.use.NonNull;
261 
262                         public class Foo<T> {
263                             @NonNull public String field1;
264                             @NonNull public String[] field2;
265                             @NonNull public String[][] field3;
266                             @NonNull public T field4;
267                             @NonNull public Map.Entry<T, String> field5;
268                         }
269                     """
270                 ),
271             ),
272             inputSet(
273                 signature(
274                     """
275                         // Signature format: 2.0
276                         package test.pkg {
277                           public class Foo<T> {
278                             field @NonNull public String field1;
279                             field @NonNull public String[] field2;
280                             field @NonNull public String[][] field3;
281                             field @NonNull public T field4;
282                             field @NonNull public java.util.Map.Entry<T, String> field5;
283                           }
284                         }
285                     """
286                 ),
287             ),
288             // Kotlin does not care about different nullability annotations.
289         ) {
290             val expectedTypes =
291                 mapOf(
292                     "field1" to "java.lang.String",
293                     "field2" to "java.lang.String![]",
294                     "field3" to "java.lang.String![]![]",
295                     "field4" to "T",
296                     "field5" to "java.util.Map.Entry<T!,java.lang.String!>",
297                 )
298             for (field in codebase.assertClass("test.pkg.Foo").fields()) {
299                 val name = field.name()
300                 val expectedType = expectedTypes[name]!!
301                 // Compare the kotlin style format of the field to ensure that only the outermost
302                 // type is affected by the not-type-use nullability annotation.
303                 assertWithMessage(name)
304                     .that(field.type().toTypeString(kotlinStyleNulls = true))
305                     .isEqualTo(expectedType)
306             }
307         }
308     }
309 
310     @Test
311     fun `Test nullability of field annotated with @not-type-use-Nullable`() {
312         runCodebaseTest(
313             inputSet(
314                 KnownSourceFiles.notTypeUseNullableSource,
315                 java(
316                     """
317                         package test.pkg;
318                         import java.util.Map;
319                         import not.type.use.Nullable;
320 
321                         public class Foo<T> {
322                             @Nullable public String field1;
323                             @Nullable public String[] field2;
324                             @Nullable public String[][] field3;
325                             @Nullable public T field4;
326                             @Nullable public Map.Entry<T, String> field5;
327                         }
328                     """
329                 ),
330             ),
331             inputSet(
332                 signature(
333                     """
334                         // Signature format: 2.0
335                         package test.pkg {
336                           public class Foo<T> {
337                             field @Nullable public String field1;
338                             field @Nullable public String[] field2;
339                             field @Nullable public String[][] field3;
340                             field @Nullable public T field4;
341                             field @Nullable public java.util.Map.Entry<T, String> field5;
342                           }
343                         }
344                     """
345                 ),
346             ),
347             // Kotlin does not care about different nullability annotations.
348         ) {
349             val expectedTypes =
350                 mapOf(
351                     "field1" to "java.lang.String?",
352                     "field2" to "java.lang.String![]?",
353                     "field3" to "java.lang.String![]![]?",
354                     "field4" to "T?",
355                     "field5" to "java.util.Map.Entry<T!,java.lang.String!>?",
356                 )
357             for (field in codebase.assertClass("test.pkg.Foo").fields()) {
358                 val name = field.name()
359                 val expectedType = expectedTypes[name]!!
360                 // Compare the kotlin style format of the field to ensure that only the outermost
361                 // type is affected by the not-type-use nullability annotation.
362                 assertWithMessage(name)
363                     .that(field.type().toTypeString(kotlinStyleNulls = true))
364                     .isEqualTo(expectedType)
365             }
366         }
367     }
368 
369     @Test
370     fun `Test nullability of non-null field annotated with @Nullable or kotlin equivalent`() {
371         runNullabilityTest(
372             java(
373                 """
374                     package test.pkg;
375                     import not.type.use.Nullable;
376 
377                     public class Foo {
378                         @Nullable
379                         public static final String CONST = "CONST";
380                     }
381                 """
382             ),
383             signature(
384                 """
385                     // Signature format: 2.0
386                     package test.pkg {
387                       public class Foo {
388                         field @Nullable public static final String CONST = "CONST";
389                       }
390                     }
391                 """
392             ),
393             kotlin(
394                 """
395                     package test.pkg
396 
397                     class Foo {
398                         companion object {
399                             const val CONST: String? = "CONST"
400                         }
401                     }
402                 """
403             ),
404             signature(
405                 """
406                     // Signature format: 5.0
407                     package test.pkg {
408                       public class Foo {
409                         field public static final String? CONST = "CONST";
410                       }
411                     }
412                 """
413             ),
414         ) {
415             val stringConstant = codebase.assertClass("test.pkg.Foo").assertField("CONST")
416 
417             // Do not check the annotation as type use annotations are ambiguous in signature files
418             // that do not specify `kotlin-name-type-order=yes`
419             stringConstant.type().assertHasNullableNullability()
420         }
421     }
422 
423     @Test
424     fun `Test implicit nullability of constant field initialized from @NonNull method`() {
425         runCodebaseTest(
426             inputSet(
427                 KnownSourceFiles.nonNullSource,
428                 java(
429                     """
430                         package test.pkg;
431                         import android.annotation.NonNull;
432 
433                         public class Foo {
434                             public static final String CONST = method();
435                             @NonNull
436                             private static String method() {return "CONST";}
437                         }
438                     """
439                 ),
440             ),
441             inputSet(
442                 kotlin(
443                     """
444                         package test.pkg
445 
446                         class Foo {
447                             companion object {
448                                 const val CONST = method()
449                                 private fun method() = "CONST"
450                             }
451                         }
452                     """
453                 ),
454             ),
455         ) {
456             val stringConstant = codebase.assertClass("test.pkg.Foo").assertField("CONST")
457 
458             stringConstant.type().assertHasNonNullNullability(false)
459         }
460     }
461 
462     @Test
463     fun `Test handling of Float MIN_NORMAL`() {
464         runCodebaseTest(
465             signature(
466                 """
467                     // Signature format: 2.0
468                     package test.pkg {
469                       public class Test {
470                         field public static final float MIN_NORMAL1 = 1.17549435E-38f;
471                         field public static final float MIN_NORMAL2 = 1.1754944E-38f;
472                         field public static final float MIN_NORMAL3 = 0x1.0p-126f;
473                       }
474                     }
475                 """
476             ),
477             java(
478                 """
479                     package test.pkg;
480 
481                     public class Test {
482                         private Test() {}
483 
484                         public static final float MIN_NORMAL1 = 1.17549435E-38f;
485                         public static final float MIN_NORMAL2 = 1.1754944E-38f;
486                         public static final float IN_NORMAL3 = 0x1.0p-126f;
487                     }
488                 """
489             ),
490         ) {
491             val testClass = codebase.assertClass("test.pkg.Test")
492 
493             val minNormalBits = java.lang.Float.MIN_NORMAL.toBits()
494             for (field in testClass.fields()) {
495                 val value = field.initialValue(true) as Float
496                 val valueBits = value.toBits()
497                 assertEquals(
498                     minNormalBits,
499                     valueBits,
500                     message =
501                         "field ${field.name()} - expected ${Integer.toHexString(minNormalBits)}, found ${Integer.toHexString(valueBits)}"
502                 )
503 
504                 val written =
505                     StringWriter()
506                         .apply {
507                             PrintWriter(this).use { out -> field.writeValueWithSemicolon(out) }
508                         }
509                         .toString()
510 
511                 assertEquals(" = 1.17549435E-38f;", written, message = "field ${field.name()}")
512             }
513         }
514     }
515 }
516