• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2023 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.propertyitem
18 
19 import com.android.tools.metalava.model.PrimitiveTypeItem
20 import com.android.tools.metalava.model.PropertyItem
21 import com.android.tools.metalava.model.testing.testTypeString
22 import com.android.tools.metalava.model.testsuite.BaseModelTest
23 import com.android.tools.metalava.testing.kotlin
24 import com.google.common.truth.Truth.assertThat
25 import kotlin.test.assertNull
26 import kotlin.test.assertTrue
27 import org.junit.Assert.assertEquals
28 import org.junit.Test
29 
30 /** Common tests for implementations of [PropertyItem]. */
31 class CommonPropertyItemTest : BaseModelTest() {
32 
33     @Test
Test access type parameter of outer classnull34     fun `Test access type parameter of outer class`() {
35         runCodebaseTest(
36             signature(
37                 """
38                     // Signature format: 2.0
39                     package test.pkg {
40                       public class Outer<O> {
41                       }
42                       public class Outer.Middle {
43                       }
44                       public abstract class Outer.Middle.Inner {
45                         property public abstract O property;
46                       }
47                     }
48                 """
49             ),
50             kotlin(
51                 """
52                     package test.pkg
53 
54                     class Outer<O> private constructor() {
55                         inner class Middle private constructor() {
56                             abstract inner class Inner private constructor() {
57                                 abstract val property: O
58                             }
59                         }
60                     }
61                 """
62             ),
63         ) {
64             val oTypeParameter = codebase.assertClass("test.pkg.Outer").typeParameterList.single()
65             val propertyType =
66                 codebase
67                     .assertClass("test.pkg.Outer.Middle.Inner")
68                     .assertProperty("property")
69                     .type()
70 
71             propertyType.assertReferencesTypeParameter(oTypeParameter)
72         }
73     }
74 
75     @Test
Test deprecated getter and setter by annotationnull76     fun `Test deprecated getter and setter by annotation`() {
77         runCodebaseTest(
78             kotlin(
79                 """
80                     package test.pkg
81 
82                     class Bar {
83                         private var fooImpl: String = ""
84                         @Deprecated("blah")
85                         var foo: String
86                             get() = fooImpl
87                             @Deprecated("blah")
88                             set(value) {fooImpl = value}
89                     }
90                 """
91             ),
92         ) {
93             val barClass = codebase.assertClass("test.pkg.Bar")
94             val property = barClass.assertProperty("foo")
95             val methods = barClass.methods()
96             val getter = methods.single { it.name() == "getFoo" }
97             val setter = methods.single { it.name() == "setFoo" }
98             assertEquals("property originallyDeprecated", true, property.originallyDeprecated)
99             assertEquals("getter originallyDeprecated", true, getter.originallyDeprecated)
100             assertEquals("setter originallyDeprecated", true, setter.originallyDeprecated)
101         }
102     }
103 
104     @Test
Test property delegate to Kotlin objectnull105     fun `Test property delegate to Kotlin object`() {
106         runCodebaseTest(
107             kotlin(
108                 """
109                     package test.pkg
110                     import kotlin.properties.ReadOnlyProperty
111                     import kotlin.reflect.KProperty
112                     class Foo {
113                         val field: String by object : ReadOnlyProperty<Foo, String> {
114                             fun getValue(thisRef: T, property: KProperty<*>) = "foo"
115                         }
116                     }
117                 """
118             ),
119             // No signature file as it does not care about field values that are not constant
120             // literals.
121         ) {
122             val fooClass = codebase.assertClass("test.pkg.Foo")
123             val fieldType = fooClass.fields().single().type()
124             fieldType.assertClassTypeItem {
125                 // The type of the field is NOT `String` (that is the type of the property). The
126                 // type of the field is the property delegate.
127                 assertThat(qualifiedName).isEqualTo("kotlin.properties.ReadOnlyProperty")
128             }
129 
130             val propertyType = fooClass.properties().single().type()
131             propertyType.assertClassTypeItem {
132                 assertThat(qualifiedName).isEqualTo("java.lang.String")
133             }
134         }
135     }
136 
137     @Test
Test property delegate to generic Kotlin objectnull138     fun `Test property delegate to generic Kotlin object`() {
139         runCodebaseTest(
140             kotlin(
141                 """
142                     package test.pkg
143                     val targetList: List<String?> = emptyList()
144                     class Foo {
145                         val delegatingList by ::targetList
146                     }
147                 """
148             ),
149         ) {
150             val fooClass = codebase.assertClass("test.pkg.Foo")
151             val fieldItem = fooClass.fields().single()
152             assertThat(fieldItem.name()).isEqualTo("delegatingList\$delegate")
153             val fieldType = fieldItem.type()
154             fieldType.assertClassTypeItem {
155                 assertThat(testTypeString(kotlinStyleNulls = true))
156                     .isEqualTo(
157                         "kotlin.reflect.KProperty0<? extends java.util.List<? extends java.lang.String?>>"
158                     )
159             }
160 
161             val propertyItem = fooClass.properties().single()
162             assertThat(propertyItem.name()).isEqualTo("delegatingList")
163             val propertyType = propertyItem.type()
164             propertyType.assertClassTypeItem {
165                 assertThat(testTypeString(kotlinStyleNulls = true))
166                     .isEqualTo("java.util.List<java.lang.String?>")
167             }
168         }
169     }
170 
171     @Test
Test property delegate to lambda Kotlin objectnull172     fun `Test property delegate to lambda Kotlin object`() {
173         runCodebaseTest(
174             kotlin(
175                 """
176                     package test.pkg
177                     val targetList: (Int, String?) -> Boolean = {}
178                     class Foo {
179                         val delegatingList by ::targetList
180                     }
181                 """
182             ),
183         ) {
184             val fooClass = codebase.assertClass("test.pkg.Foo")
185             val fieldItem = fooClass.fields().single()
186             assertThat(fieldItem.name()).isEqualTo("delegatingList\$delegate")
187             val fieldType = fieldItem.type()
188             fieldType.assertClassTypeItem {
189                 assertThat(testTypeString(kotlinStyleNulls = true))
190                     .isEqualTo(
191                         "kotlin.reflect.KProperty0<? extends kotlin.jvm.functions.Function2<? super java.lang.Integer,? super java.lang.String?,? extends java.lang.Boolean>>"
192                     )
193             }
194 
195             val propertyItem = fooClass.properties().single()
196             assertThat(propertyItem.name()).isEqualTo("delegatingList")
197             val propertyType = propertyItem.type()
198             propertyType.assertClassTypeItem {
199                 assertThat(testTypeString(kotlinStyleNulls = true))
200                     .isEqualTo(
201                         "kotlin.jvm.functions.Function2<java.lang.Integer,java.lang.String?,java.lang.Boolean>"
202                     )
203             }
204         }
205     }
206 
207     @Test
Test abstract property of non-null stringnull208     fun `Test abstract property of non-null string`() {
209         runCodebaseTest(
210             kotlin(
211                 """
212                     package test.pkg
213                     class Foo {
214                         val property: String
215                              get() = ""
216                     }
217                 """
218             ),
219         ) {
220             val fooClass = codebase.assertClass("test.pkg.Foo")
221             val propertyType = fooClass.properties().single().type()
222             propertyType.assertClassTypeItem {
223                 assertThat(testTypeString(kotlinStyleNulls = true)).isEqualTo("java.lang.String")
224             }
225 
226             val getter = fooClass.methods().single()
227             assertThat(getter.kotlinLikeDescription())
228                 .isEqualTo("fun getProperty(): java.lang.String")
229         }
230     }
231 
232     @Test
Test abstract property of nullable stringnull233     fun `Test abstract property of nullable string`() {
234         runCodebaseTest(
235             kotlin(
236                 """
237                     package test.pkg
238                     class Foo {
239                         val property: String?
240                              get() = null
241                     }
242                 """
243             ),
244         ) {
245             val fooClass = codebase.assertClass("test.pkg.Foo")
246             val propertyType = fooClass.properties().single().type()
247             propertyType.assertClassTypeItem {
248                 assertThat(testTypeString(kotlinStyleNulls = true)).isEqualTo("java.lang.String?")
249             }
250 
251             val getter = fooClass.methods().single()
252             assertThat(getter.kotlinLikeDescription())
253                 .isEqualTo("fun getProperty(): java.lang.String?")
254         }
255     }
256 
257     @Test
Test abstract property of list of non-null stringnull258     fun `Test abstract property of list of non-null string`() {
259         runCodebaseTest(
260             kotlin(
261                 """
262                     package test.pkg
263                     class Foo {
264                         val property: List<String>
265                              get() = emptyList()
266                     }
267                 """
268             ),
269         ) {
270             val fooClass = codebase.assertClass("test.pkg.Foo")
271             val propertyType = fooClass.properties().single().type()
272             propertyType.assertClassTypeItem {
273                 assertThat(testTypeString(kotlinStyleNulls = true))
274                     .isEqualTo("java.util.List<java.lang.String>")
275             }
276 
277             val getter = fooClass.methods().single()
278             assertThat(getter.kotlinLikeDescription())
279                 .isEqualTo("fun getProperty(): java.util.List<java.lang.String>")
280         }
281     }
282 
283     @Test
Test abstract property of list of nullable stringnull284     fun `Test abstract property of list of nullable string`() {
285         runCodebaseTest(
286             kotlin(
287                 """
288                     package test.pkg
289                     class Foo {
290                         val property: List<String?>
291                              get() = emptyList()
292                     }
293                 """
294             ),
295         ) {
296             val fooClass = codebase.assertClass("test.pkg.Foo")
297             val propertyType = fooClass.properties().single().type()
298             propertyType.assertClassTypeItem {
299                 assertThat(testTypeString(kotlinStyleNulls = true))
300                     .isEqualTo("java.util.List<java.lang.String?>")
301             }
302 
303             val getter = fooClass.methods().single()
304             assertThat(getter.kotlinLikeDescription())
305                 .isEqualTo("fun getProperty(): java.util.List<java.lang.String?>")
306         }
307     }
308 
309     @Test
Test abstract mutable property of non-null stringnull310     fun `Test abstract mutable property of non-null string`() {
311         runCodebaseTest(
312             kotlin(
313                 """
314                     package test.pkg
315                     class Foo {
316                         var property: String
317                             get() = ""
318                             set(value) {field = value}
319                     }
320                 """
321             ),
322         ) {
323             val fooClass = codebase.assertClass("test.pkg.Foo")
324             val propertyType = fooClass.properties().single().type()
325             propertyType.assertClassTypeItem {
326                 assertThat(testTypeString(kotlinStyleNulls = true)).isEqualTo("java.lang.String")
327             }
328 
329             val methods =
330                 fooClass.methods().map { it.kotlinLikeDescription() }.sorted().joinToString("\n")
331             assertThat(methods)
332                 .isEqualTo(
333                     """
334                         fun getProperty(): java.lang.String
335                         fun setProperty(value: java.lang.String): void
336                     """
337                         .trimIndent()
338                 )
339         }
340     }
341 
342     @Test
Test abstract mutable property of nullable stringnull343     fun `Test abstract mutable property of nullable string`() {
344         runCodebaseTest(
345             kotlin(
346                 """
347                     package test.pkg
348                     class Foo {
349                         var property: String?
350                             get() = null
351                             set(value) {field = value}
352                     }
353                 """
354             ),
355         ) {
356             val fooClass = codebase.assertClass("test.pkg.Foo")
357             val propertyType = fooClass.properties().single().type()
358             propertyType.assertClassTypeItem {
359                 assertThat(testTypeString(kotlinStyleNulls = true)).isEqualTo("java.lang.String?")
360             }
361 
362             val methods =
363                 fooClass.methods().map { it.kotlinLikeDescription() }.sorted().joinToString("\n")
364             assertThat(methods)
365                 .isEqualTo(
366                     """
367                         fun getProperty(): java.lang.String?
368                         fun setProperty(value: java.lang.String?): void
369                     """
370                         .trimIndent()
371                 )
372         }
373     }
374 
375     @Test
Test abstract mutable property of list of non-null stringnull376     fun `Test abstract mutable property of list of non-null string`() {
377         runCodebaseTest(
378             kotlin(
379                 """
380                     package test.pkg
381                     class Foo {
382                         var property: List<String>
383                             get() = emptyList()
384                             set(value) {field = value}
385                     }
386                 """
387             ),
388         ) {
389             val fooClass = codebase.assertClass("test.pkg.Foo")
390             val propertyType = fooClass.properties().single().type()
391             propertyType.assertClassTypeItem {
392                 assertThat(testTypeString(kotlinStyleNulls = true))
393                     .isEqualTo("java.util.List<java.lang.String>")
394             }
395 
396             val methods =
397                 fooClass.methods().map { it.kotlinLikeDescription() }.sorted().joinToString("\n")
398             assertThat(methods)
399                 .isEqualTo(
400                     """
401                         fun getProperty(): java.util.List<java.lang.String>
402                         fun setProperty(value: java.util.List<java.lang.String>): void
403                     """
404                         .trimIndent()
405                 )
406         }
407     }
408 
409     @Test
Test abstract mutable property of list of nullable stringnull410     fun `Test abstract mutable property of list of nullable string`() {
411         runCodebaseTest(
412             kotlin(
413                 """
414                     package test.pkg
415                     class Foo {
416                         var property: List<String?>
417                             get() = emptyList()
418                             set(value) {field = value}
419                     }
420                 """
421             ),
422         ) {
423             val fooClass = codebase.assertClass("test.pkg.Foo")
424             val propertyType = fooClass.properties().single().type()
425             propertyType.assertClassTypeItem {
426                 assertThat(testTypeString(kotlinStyleNulls = true))
427                     .isEqualTo("java.util.List<java.lang.String?>")
428             }
429 
430             val methods =
431                 fooClass.methods().map { it.kotlinLikeDescription() }.sorted().joinToString("\n")
432             assertThat(methods)
433                 .isEqualTo(
434                     """
435                         fun getProperty(): java.util.List<java.lang.String?>
436                         fun setProperty(value: java.util.List<java.lang.String?>): void
437                     """
438                         .trimIndent()
439                 )
440         }
441     }
442 
443     @Test
Test mutable non-null generic property overriding property exposing public setternull444     fun `Test mutable non-null generic property overriding property exposing public setter`() {
445         runCodebaseTest(
446             kotlin(
447                 """
448                     package test.pkg
449 
450                     abstract class Baz<T> {
451                         abstract var property: T
452                             internal set
453                     }
454 
455                     class Foo<T>(initialValue: T) : Baz<T> {
456                         override var property: T = initialValue
457                             public set
458                     }
459                 """
460             ),
461         ) {
462             val fooClass = codebase.assertClass("test.pkg.Foo")
463 
464             val methods =
465                 fooClass.methods().map { it.kotlinLikeDescription() }.sorted().joinToString("\n")
466             assertThat(methods)
467                 .isEqualTo(
468                     """
469                         fun getProperty(): T
470                         fun setProperty(<set-?>: T): void
471                     """
472                         .trimIndent()
473                 )
474         }
475     }
476 
477     @Test
Test mutable nullable generic property overriding property exposing public setternull478     fun `Test mutable nullable generic property overriding property exposing public setter`() {
479         runCodebaseTest(
480             kotlin(
481                 """
482                     package test.pkg
483 
484                     abstract class Baz<T> {
485                         abstract var property: T?
486                             internal set
487                     }
488 
489                     class Foo<T> : Baz<T> {
490                         override var property: T? = null
491                             public set
492                     }
493                 """
494             ),
495         ) {
496             val fooClass = codebase.assertClass("test.pkg.Foo")
497 
498             val methods =
499                 fooClass.methods().map { it.kotlinLikeDescription() }.sorted().joinToString("\n")
500             assertThat(methods)
501                 .isEqualTo(
502                     """
503                         fun getProperty(): T?
504                         fun setProperty(<set-?>: T?): void
505                     """
506                         .trimIndent()
507                 )
508         }
509     }
510 
511     @Test
Test mutable list of nullable property overriding property exposing public setternull512     fun `Test mutable list of nullable property overriding property exposing public setter`() {
513         runCodebaseTest(
514             kotlin(
515                 """
516                     package test.pkg
517 
518                     abstract class Baz {
519                         abstract var property: List<String?>
520                             internal set
521                     }
522 
523                     class Foo : Baz<T> {
524                         override var property: List<String?> = emptyList()
525                             public set
526                     }
527                 """
528             ),
529         ) {
530             val fooClass = codebase.assertClass("test.pkg.Foo")
531 
532             val methods =
533                 fooClass.methods().map { it.kotlinLikeDescription() }.sorted().joinToString("\n")
534             assertThat(methods)
535                 .isEqualTo(
536                     """
537                         fun getProperty(): java.util.List<java.lang.String?>
538                         fun setProperty(<set-?>: java.util.List<java.lang.String?>): void
539                     """
540                         .trimIndent()
541                 )
542         }
543     }
544 
545     @Test
Test companion propertynull546     fun `Test companion property`() {
547         runCodebaseTest(
548             kotlin(
549                 """
550                     package test.pkg
551                     class Foo {
552                         companion object {
553                             val value: Int = 0
554                             const val constant: Int = 1
555                             @JvmField val jvmField: Int = 2
556                         }
557                     }
558                 """
559             )
560         ) {
561             val foo = codebase.assertClass("test.pkg.Foo")
562             assertThat(foo.methods()).isEmpty()
563             assertThat(foo.properties()).isEmpty()
564             foo.assertField("constant")
565             foo.assertField("jvmField")
566 
567             val fooCompanion = codebase.assertClass("test.pkg.Foo.Companion")
568             assertThat(fooCompanion.fields()).isEmpty()
569             assertThat(fooCompanion.methods()).hasSize(1)
570             val valueGetterOnCompanion = fooCompanion.assertMethod("getValue", "")
571 
572             assertThat(fooCompanion.properties()).hasSize(3)
573             val constantPropertyOnCompanion = fooCompanion.assertProperty("constant")
574             val jvmPropertyOnCompanion = fooCompanion.assertProperty("jvmField")
575             val valuePropertyOnCompanion = fooCompanion.assertProperty("value")
576 
577             assertThat(jvmPropertyOnCompanion.getter).isNull()
578             assertThat(constantPropertyOnCompanion.getter).isNull()
579             assertThat(valuePropertyOnCompanion.getter).isEqualTo(valueGetterOnCompanion)
580         }
581     }
582 
583     @Test
Test top level propertiesnull584     fun `Test top level properties`() {
585         runCodebaseTest(
586             kotlin(
587                 """
588                     @file:JvmName("Foo")
589                     package test.pkg
590 
591                     var variable = 0
592 
593                     val valWithNoBackingField
594                         get() = 0
595 
596                     const val CONST = 0
597 
598                     @JvmField
599                     val jvmField = 0
600                 """
601             )
602         ) {
603             val fileFacadeClass = codebase.assertClass("test.pkg.Foo")
604             assertThat(fileFacadeClass.properties()).hasSize(4)
605 
606             // var property with getter, setter, and backing field
607             val variable = fileFacadeClass.assertProperty("variable")
608             assertThat(variable.getter).isNotNull()
609             assertThat(variable.setter).isNotNull()
610             assertThat(variable.backingField).isNotNull()
611             assertThat(variable.constructorParameter).isNull()
612 
613             // val property with getter, no setter or backing field
614             val valWithNoBackingField = fileFacadeClass.assertProperty("valWithNoBackingField")
615             assertThat(valWithNoBackingField.getter).isNotNull()
616             assertThat(valWithNoBackingField.setter).isNull()
617             assertThat(valWithNoBackingField.backingField).isNull()
618             assertThat(valWithNoBackingField.constructorParameter).isNull()
619 
620             // const val doesn't have accessors, but does have backing field
621             val constVal = fileFacadeClass.assertProperty("CONST")
622             assertThat(constVal.getter).isNull()
623             assertThat(constVal.setter).isNull()
624             assertThat(constVal.backingField).isNotNull()
625             assertThat(constVal.constructorParameter).isNull()
626 
627             // jvmfield val doesn't have accessors, but does have backing field
628             val jvmField = fileFacadeClass.assertProperty("jvmField")
629             assertThat(jvmField.getter).isNull()
630             assertThat(jvmField.setter).isNull()
631             assertThat(jvmField.backingField).isNotNull()
632             assertThat(jvmField.constructorParameter).isNull()
633         }
634     }
635 
636     @Test
Test top level extension propertiesnull637     fun `Test top level extension properties`() {
638         runCodebaseTest(
639             kotlin(
640                 """
641                     @file:JvmName("Foo")
642                     package test.pkg
643 
644                     var String.stringExtension
645                         get() = 0
646                         set(value) {}
647                 """
648             )
649         ) {
650             val fileFacadeClass = codebase.assertClass("test.pkg.Foo")
651             assertThat(fileFacadeClass.properties()).hasSize(1)
652 
653             // extension property has getter and setter, but no backing field
654             val stringExtension = fileFacadeClass.assertProperty("stringExtension")
655             assertThat(stringExtension.getter).isNotNull()
656             assertThat(stringExtension.setter).isNotNull()
657             assertThat(stringExtension.backingField).isNull()
658             assertThat(stringExtension.constructorParameter).isNull()
659         }
660     }
661 
662     @Test
Value class extension propertiesnull663     fun `Value class extension properties`() {
664         runCodebaseTest(
665             kotlin(
666                 """
667                     @file:JvmName("Foo")
668                     package test.pkg
669 
670                     value class IntValue(val value: Int)
671 
672                     var IntValue.valueClassExtension
673                         get() = 0
674                         set(value) {}
675                 """
676             )
677         ) {
678             val fileFacadeClass = codebase.assertClass("test.pkg.Foo")
679             assertThat(fileFacadeClass.properties()).hasSize(1)
680 
681             // extension property has getter and setter, but no backing field
682             val valueClassExtension = fileFacadeClass.assertProperty("valueClassExtension")
683             assertThat(valueClassExtension.getter).isNotNull()
684             assertThat(valueClassExtension.setter).isNotNull()
685             assertThat(valueClassExtension.backingField).isNull()
686             assertThat(valueClassExtension.constructorParameter).isNull()
687 
688             // the extension property receiver is a value class type, which gets mapped to its
689             // value type
690             valueClassExtension.receiver.assertPrimitiveTypeItem {
691                 assertEquals(kind, PrimitiveTypeItem.Primitive.INT)
692             }
693         }
694     }
695 
Test final modifier for propertiesnull696     fun `Test final modifier for properties`() {
697         runCodebaseTest(
698             kotlin(
699                 """
700                     package test.pkg
701                     class FinalClass {
702                         val propertyInFinalClass = 0
703                     }
704                 """
705             ),
706             kotlin(
707                 """
708                     package test.pkg
709                     open class OpenClass {
710                         val finalPropertyInOpenClass = 0
711                         open val openPropertyInOpenClass = 0
712                     }
713                 """
714             )
715         ) {
716             val finalClass = codebase.assertClass("test.pkg.FinalClass")
717             assertThat(finalClass.modifiers.isFinal()).isTrue()
718             // Properties in final classes are final, so using the modifier would be redundant.
719             assertThat(finalClass.assertProperty("propertyInFinalClass").modifiers.isFinal())
720                 .isFalse()
721 
722             val openClass = codebase.assertClass("test.pkg.OpenClass")
723             assertThat(openClass.modifiers.isFinal()).isFalse()
724             assertThat(openClass.assertProperty("finalPropertyInOpenClass").modifiers.isFinal())
725                 .isTrue()
726             assertThat(openClass.assertProperty("openPropertyInOpenClass").modifiers.isFinal())
727                 .isFalse()
728         }
729     }
730 
731     @Test
JvmStatic property in object is staticnull732     fun `JvmStatic property in object is static`() {
733         runCodebaseTest(
734             kotlin(
735                 """
736                     package test.pkg
737                     import kotlin.jvm.JvmStatic
738                     object Foo {
739                         @JvmStatic
740                         val jvmStaticProperty = 0
741                         val notStaticProperty = 0
742                     }
743                 """
744             )
745         ) {
746             val fooClass = codebase.assertClass("test.pkg.Foo")
747             val jvmStaticProperty = fooClass.assertProperty("jvmStaticProperty")
748             assertThat(jvmStaticProperty.modifiers.isStatic()).isTrue()
749             val notStaticProperty = fooClass.assertProperty("notStaticProperty")
750             assertThat(notStaticProperty.modifiers.isStatic()).isFalse()
751         }
752     }
753 
754     @Test
Test abstract and default modifier on propertiesnull755     fun `Test abstract and default modifier on properties`() {
756         runCodebaseTest(
757             kotlin(
758                 """
759                     package test.pkg
760 
761                     interface Interface {
762                         // interface properties cannot have initializers
763                         // if no getter is defined, the property is abstract
764                         val abstractVal: Int
765                         // getter is defined, the property is default
766                         val defaultVal: Int
767                             get() = 0
768 
769                         companion object {
770                             // this shouldn't be default because the immediate container is an
771                             // object, not an interface
772                             val interfaceCompanionValWithGetter: Int
773                                 get() = 0
774                         }
775                     }
776 
777                     abstract class AbstractClass {
778                         abstract val abstractVal: Int
779 
780                         val nonAbstractValWithInitializer: Int = 0
781                         val nonAbstractValWithGetter: Int
782                             get() = 0
783 
784                         // lateinit cannot be paired with abstract
785                         lateinit var nonAbstractLateinitVar: String
786 
787                         val notAbstractValInInitBlock: Int
788                         init {
789                             notAbstractValInInitBlock = 0
790                         }
791                     }
792                 """
793             )
794         ) {
795             val interfaceClass = codebase.assertClass("test.pkg.Interface")
796             val abstractInterfaceProperty = interfaceClass.assertProperty("abstractVal")
797             assertThat(abstractInterfaceProperty.modifiers.isAbstract()).isTrue()
798             assertThat(abstractInterfaceProperty.modifiers.isDefault()).isFalse()
799             val defaultInterfaceProperty = interfaceClass.assertProperty("defaultVal")
800             assertThat(defaultInterfaceProperty.modifiers.isAbstract()).isFalse()
801             assertThat(defaultInterfaceProperty.modifiers.isDefault()).isTrue()
802 
803             val interfaceCompanion = interfaceClass.nestedClasses().single()
804             val interfaceCompanionProperty =
805                 interfaceCompanion.assertProperty("interfaceCompanionValWithGetter")
806             assertThat(interfaceCompanionProperty.modifiers.isAbstract()).isFalse()
807             assertThat(interfaceCompanionProperty.modifiers.isDefault()).isFalse()
808 
809             val abstractClass = codebase.assertClass("test.pkg.AbstractClass")
810             val abstractClassProperty = abstractClass.assertProperty("abstractVal")
811             assertThat(abstractClassProperty.modifiers.isAbstract()).isTrue()
812             assertThat(abstractClassProperty.modifiers.isDefault()).isFalse()
813 
814             val nonAbstractPropertiesFromAbstractClass =
815                 listOf(
816                     abstractClass.assertProperty("nonAbstractValWithInitializer"),
817                     abstractClass.assertProperty("nonAbstractValWithGetter"),
818                     abstractClass.assertProperty("nonAbstractLateinitVar"),
819                     abstractClass.assertProperty("notAbstractValInInitBlock"),
820                 )
821 
822             for (property in nonAbstractPropertiesFromAbstractClass) {
823                 assertThat(property.modifiers.isAbstract()).isFalse()
824                 assertThat(property.modifiers.isDefault()).isFalse()
825             }
826         }
827     }
828 
829     @Test
Test property receiversnull830     fun `Test property receivers`() {
831         runCodebaseTest(
832             kotlin(
833                 """
834                     @file:JvmName("Foo")
835                     package test.pkg
836                     val noReceiverProperty = 0
837                     // Extension properties can't have backing fields, so they need defined getters
838                     val Int.intProperty
839                         get() = 0
840                     val String.stringProperty
841                         get() = 0
842                     val Array<String>.stringArrayProperty
843                         get() = 0
844                     val List<String>.stringListProperty
845                         get() = 0
846                 """
847             ),
848             // Skip getters in the signature file since they aren't important to the test
849             signature(
850                 """
851                     // Signature format: 5.0
852                     package test.pkg {
853                       public final class Foo {
854                         property public static int noReceiverProperty;
855                         property public static int int.intProperty;
856                         property public static int String.stringProperty;
857                         property public static int String[].stringArrayProperty;
858                         property public static int java.util.List<? extends String>.stringListProperty;
859                       }
860                     }
861                 """
862             ),
863             signature(
864                 """
865                     // Signature format: 5.0
866                     // - kotlin-name-type-order=yes
867                     package test.pkg {
868                       public final class Foo {
869                         property public static noReceiverProperty: int;
870                         property public static int.intProperty: int;
871                         property public static String.stringProperty: int;
872                         property public static String[].stringArrayProperty: int;
873                         property public static java.util.List<? extends String>.stringListProperty: int;
874                       }
875                     }
876                 """
877             )
878         ) {
879             val fooClass = codebase.assertClass("test.pkg.Foo")
880 
881             assertNull(fooClass.assertProperty("noReceiverProperty").receiver)
882 
883             fooClass.assertProperty("intProperty").receiver.assertPrimitiveTypeItem {
884                 assertEquals(kind, PrimitiveTypeItem.Primitive.INT)
885             }
886 
887             fooClass.assertProperty("stringProperty").receiver.assertClassTypeItem {
888                 assertTrue(isString())
889             }
890 
891             fooClass.assertProperty("stringArrayProperty").receiver.assertArrayTypeItem {
892                 componentType.assertClassTypeItem { assertTrue(isString()) }
893             }
894 
895             fooClass.assertProperty("stringListProperty").receiver.assertClassTypeItem {
896                 assertEquals(qualifiedName, "java.util.List")
897                 arguments.single().assertWildcardItem {
898                     extendsBound.assertClassTypeItem { assertTrue(isString()) }
899                 }
900             }
901         }
902     }
903 
904     @Test
Test property type parameters, receiver typesnull905     fun `Test property type parameters, receiver types`() {
906         runCodebaseTest(
907             kotlin(
908                 """
909                     @file:JvmName("Foo")
910                     package test.pkg
911                     val String.noTypeParameterProperty = 0
912                     // Property type parameters must be used in the receiver type
913                     // Extension properties can't have backing fields, so they need defined getters
914                     val <T> T.oneTypeParameterReceiver
915                         get() = 0
916                     val <T> List<T>.oneTypeParameterListReceiver
917                         get() = 0
918                     val <T : String> T.oneTypeParameterWithBoundsReceiver
919                         get() = 0
920                     val <T1, T2> Map<T1, T2>.twoTypeParameterMapReceiver
921                         get() = 0
922                     val <T1 : String, T2 : List<T1>> Map<T1, T2>.twoTypeParameterWithBoundsMapReceiver
923                         get() = 0
924                 """
925             ),
926             // Skip getters in the signature file since they aren't important to the test
927             signature(
928                 """
929                     // Signature format: 5.0
930                     package test.pkg {
931                       public final class Foo {
932                         property public static int String.noTypeParameterProperty;
933                         property public static <T> int T.oneTypeParameterReceiver;
934                         property public static <T> int java.util.List<? extends T>.oneTypeParameterListReceiver;
935                         property public static <T extends String> int T.oneTypeParameterWithBoundsReceiver;
936                         property public static <T1, T2> int java.util.Map<T1,? extends T2>.twoTypeParameterMapReceiver;
937                         property public static <T1 extends String, T2 extends java.util.List<? extends T1>> int java.util.Map<T1,? extends T2>.twoTypeParameterWithBoundsMapReceiver;
938                       }
939                     }
940                 """
941             ),
942             signature(
943                 """
944                     // Signature format: 5.0
945                     // - kotlin-name-type-order=yes
946                     package test.pkg {
947                       public final class Foo {
948                         property public static String.noTypeParameterProperty: int;
949                         property public static <T> T.oneTypeParameterReceiver: int;
950                         property public static <T> java.util.List<? extends T>.oneTypeParameterListReceiver: int;
951                         property public static <T extends String> T.oneTypeParameterWithBoundsReceiver: int;
952                         property public static <T1, T2> java.util.Map<T1,? extends T2>.twoTypeParameterMapReceiver: int;
953                         property public static <T1 extends String, T2 extends java.util.List<? extends T1>> java.util.Map<T1,? extends T2>.twoTypeParameterWithBoundsMapReceiver: int;
954                       }
955                     }
956                 """
957             ),
958         ) {
959             val fooClass = codebase.assertClass("test.pkg.Foo")
960 
961             val noTypeParameterProperty = fooClass.assertProperty("noTypeParameterProperty")
962             assertThat(noTypeParameterProperty.typeParameterList).isEmpty()
963 
964             // val <T> T.oneTypeParameterReceiver
965             val oneTypeParameterReceiver = fooClass.assertProperty("oneTypeParameterReceiver")
966             val oneTypeParameterReceiverT = oneTypeParameterReceiver.typeParameterList.single()
967             assertThat(oneTypeParameterReceiverT.name()).isEqualTo("T")
968             assertThat(oneTypeParameterReceiverT.typeBounds()).isEmpty()
969             assertThat(oneTypeParameterReceiverT.isReified()).isFalse()
970             oneTypeParameterReceiver.receiver.assertVariableTypeItem {
971                 assertEquals(asTypeParameter, oneTypeParameterReceiverT)
972             }
973 
974             // val <T> List<T>.oneTypeParameterListReceiver
975             val oneTypeParameterListReceiver =
976                 fooClass.assertProperty("oneTypeParameterListReceiver")
977             val oneTypeParameterListReceiverT =
978                 oneTypeParameterListReceiver.typeParameterList.single()
979             assertThat(oneTypeParameterListReceiverT.name()).isEqualTo("T")
980             assertThat(oneTypeParameterListReceiverT.typeBounds()).isEmpty()
981             assertThat(oneTypeParameterListReceiverT.isReified()).isFalse()
982             oneTypeParameterListReceiver.receiver.assertClassTypeItem {
983                 assertEquals(qualifiedName, "java.util.List")
984                 arguments.single().assertWildcardItem {
985                     extendsBound.assertVariableTypeItem {
986                         assertEquals(asTypeParameter, oneTypeParameterListReceiverT)
987                     }
988                 }
989             }
990 
991             // val <T : String> T.oneTypeParameterWithBoundsReceiver
992             val oneTypeParameterWithBoundsReceiver =
993                 fooClass.assertProperty("oneTypeParameterWithBoundsReceiver")
994             val oneTypeParameterWithBoundsReceiverT =
995                 oneTypeParameterWithBoundsReceiver.typeParameterList.single()
996             assertThat(oneTypeParameterWithBoundsReceiverT.name()).isEqualTo("T")
997             assertThat(oneTypeParameterWithBoundsReceiverT.typeBounds().single().isString())
998                 .isTrue()
999             assertThat(oneTypeParameterWithBoundsReceiverT.isReified()).isFalse()
1000             oneTypeParameterWithBoundsReceiver.receiver.assertVariableTypeItem {
1001                 assertEquals(asTypeParameter, oneTypeParameterReceiverT)
1002             }
1003 
1004             // val <T1, T2> Map<T1, T2>.twoTypeParameterMapReceiver
1005             val twoTypeParameterMapReceiver = fooClass.assertProperty("twoTypeParameterMapReceiver")
1006             val twoTypeParameterMapReceiverT1 = twoTypeParameterMapReceiver.typeParameterList[0]
1007             assertThat(twoTypeParameterMapReceiverT1.name()).isEqualTo("T1")
1008             assertThat(twoTypeParameterMapReceiverT1.typeBounds()).isEmpty()
1009             assertThat(twoTypeParameterMapReceiverT1.isReified()).isFalse()
1010             val twoTypeParameterMapReceiverT2 = twoTypeParameterMapReceiver.typeParameterList[1]
1011             assertThat(twoTypeParameterMapReceiverT2.name()).isEqualTo("T2")
1012             assertThat(twoTypeParameterMapReceiverT2.typeBounds()).isEmpty()
1013             assertThat(twoTypeParameterMapReceiverT2.isReified()).isFalse()
1014             twoTypeParameterMapReceiver.receiver.assertClassTypeItem {
1015                 assertEquals(qualifiedName, "java.util.Map")
1016                 assertEquals(arguments.size, 2)
1017                 arguments[0].assertVariableTypeItem {
1018                     assertEquals(asTypeParameter, twoTypeParameterMapReceiverT1)
1019                 }
1020                 arguments[1].assertWildcardItem {
1021                     extendsBound.assertVariableTypeItem {
1022                         assertEquals(asTypeParameter, twoTypeParameterMapReceiverT2)
1023                     }
1024                 }
1025             }
1026 
1027             // val <T1 : String, T2 : List<T1>> Map<T1, T2>.twoTypeParameterWithBoundsMapReceiver
1028             val twoTypeParameterWithBoundsMapReceiver =
1029                 fooClass.assertProperty("twoTypeParameterWithBoundsMapReceiver")
1030             val twoTypeParameterWithBoundsMapReceiverT1 =
1031                 twoTypeParameterWithBoundsMapReceiver.typeParameterList[0]
1032             assertThat(twoTypeParameterWithBoundsMapReceiverT1.name()).isEqualTo("T1")
1033             assertThat(twoTypeParameterWithBoundsMapReceiverT1.typeBounds().single().isString())
1034                 .isTrue()
1035             assertThat(twoTypeParameterWithBoundsMapReceiverT1.isReified()).isFalse()
1036             val twoTypeParameterWithBoundsMapReceiverT2 =
1037                 twoTypeParameterWithBoundsMapReceiver.typeParameterList[1]
1038             assertThat(twoTypeParameterWithBoundsMapReceiverT2.name()).isEqualTo("T2")
1039             twoTypeParameterWithBoundsMapReceiverT2.typeBounds().single().assertClassTypeItem {
1040                 assertEquals(qualifiedName, "java.util.List")
1041                 arguments.single().assertWildcardItem {
1042                     extendsBound.assertVariableTypeItem {
1043                         assertEquals(asTypeParameter, twoTypeParameterWithBoundsMapReceiverT1)
1044                     }
1045                 }
1046             }
1047             assertThat(twoTypeParameterWithBoundsMapReceiverT2.isReified()).isFalse()
1048             twoTypeParameterWithBoundsMapReceiver.receiver.assertClassTypeItem {
1049                 assertEquals(qualifiedName, "java.util.Map")
1050                 assertEquals(arguments.size, 2)
1051                 arguments[0].assertVariableTypeItem {
1052                     assertEquals(asTypeParameter, twoTypeParameterWithBoundsMapReceiverT1)
1053                 }
1054                 arguments[1].assertWildcardItem {
1055                     extendsBound.assertVariableTypeItem {
1056                         assertEquals(asTypeParameter, twoTypeParameterWithBoundsMapReceiverT2)
1057                     }
1058                 }
1059             }
1060         }
1061     }
1062 
1063     @Test
Test property type parameters, property typenull1064     fun `Test property type parameters, property type`() {
1065         runCodebaseTest(
1066             kotlin(
1067                 """
1068                     @file:JvmName("Foo")
1069                     package test.pkg
1070                     val <T> T.typeParameterExtension
1071                         get() = this
1072                 """
1073             ),
1074             signature(
1075                 """
1076                     // Signature format: 5.0
1077                     package test.pkg {
1078                       public final class Foo {
1079                         method public static <T> T getTypeParameterExtension(T);
1080                         property public static <T> T T.typeParameterExtension;
1081                       }
1082                     }
1083                 """
1084             ),
1085             signature(
1086                 """
1087                     // Signature format: 5.0
1088                     // - kotlin-name-type-order=yes
1089                     package test.pkg {
1090                       public final class Foo {
1091                         method public static <T> getTypeParameterExtension(receiver: T): T;
1092                         property public static <T> T.typeParameterExtension: T;
1093                       }
1094                     }
1095                 """
1096             ),
1097         ) {
1098             val fooClass = codebase.assertClass("test.pkg.Foo")
1099             // Verify that the type parameter list is also used for the property type
1100             val typeParameterExtension = fooClass.assertProperty("typeParameterExtension")
1101             val typeParameterExtensionT = typeParameterExtension.typeParameterList.single()
1102             assertThat(typeParameterExtensionT.name()).isEqualTo("T")
1103             assertThat(typeParameterExtensionT.typeBounds()).isEmpty()
1104             assertThat(typeParameterExtensionT.isReified()).isFalse()
1105             typeParameterExtension.type().assertVariableTypeItem {
1106                 assertEquals(asTypeParameter, typeParameterExtensionT)
1107             }
1108         }
1109     }
1110 }
1111