xref: /aosp_15_r20/external/dagger2/javatests/dagger/lint/DaggerKotlinIssueDetectorTest.kt (revision f585d8a307d0621d6060bd7e80091fdcbf94fe27)
1*f585d8a3SJacky Wang /*
2*f585d8a3SJacky Wang  * Copyright (C) 2020 The Dagger Authors.
3*f585d8a3SJacky Wang  *
4*f585d8a3SJacky Wang  * Licensed under the Apache License, Version 2.0 (the "License");
5*f585d8a3SJacky Wang  * you may not use this file except in compliance with the License.
6*f585d8a3SJacky Wang  * You may obtain a copy of the License at
7*f585d8a3SJacky Wang  *
8*f585d8a3SJacky Wang  * http://www.apache.org/licenses/LICENSE-2.0
9*f585d8a3SJacky Wang  *
10*f585d8a3SJacky Wang  * Unless required by applicable law or agreed to in writing, software
11*f585d8a3SJacky Wang  * distributed under the License is distributed on an "AS IS" BASIS,
12*f585d8a3SJacky Wang  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*f585d8a3SJacky Wang  * See the License for the specific language governing permissions and
14*f585d8a3SJacky Wang  * limitations under the License.
15*f585d8a3SJacky Wang  */
16*f585d8a3SJacky Wang package dagger.lint
17*f585d8a3SJacky Wang 
18*f585d8a3SJacky Wang import com.android.tools.lint.checks.infrastructure.LintDetectorTest
19*f585d8a3SJacky Wang import com.android.tools.lint.checks.infrastructure.TestMode
20*f585d8a3SJacky Wang import com.android.tools.lint.detector.api.Detector
21*f585d8a3SJacky Wang import com.android.tools.lint.detector.api.Issue
22*f585d8a3SJacky Wang import org.junit.Test
23*f585d8a3SJacky Wang import org.junit.runner.RunWith
24*f585d8a3SJacky Wang import org.junit.runners.JUnit4
25*f585d8a3SJacky Wang 
26*f585d8a3SJacky Wang @Suppress("UnstableApiUsage")
27*f585d8a3SJacky Wang @RunWith(JUnit4::class)
28*f585d8a3SJacky Wang class DaggerKotlinIssueDetectorTest : LintDetectorTest() {
29*f585d8a3SJacky Wang 
30*f585d8a3SJacky Wang   private companion object {
31*f585d8a3SJacky Wang     private val javaxInjectStubs = kotlin(
32*f585d8a3SJacky Wang       """
33*f585d8a3SJacky Wang         package javax.inject
34*f585d8a3SJacky Wang 
35*f585d8a3SJacky Wang         annotation class Inject
36*f585d8a3SJacky Wang         annotation class Qualifier
37*f585d8a3SJacky Wang       """
38*f585d8a3SJacky Wang     ).indented()
39*f585d8a3SJacky Wang 
40*f585d8a3SJacky Wang     private val daggerStubs = kotlin(
41*f585d8a3SJacky Wang       """
42*f585d8a3SJacky Wang         package dagger
43*f585d8a3SJacky Wang 
44*f585d8a3SJacky Wang         annotation class Provides
45*f585d8a3SJacky Wang         annotation class Module
46*f585d8a3SJacky Wang       """
47*f585d8a3SJacky Wang     ).indented()
48*f585d8a3SJacky Wang 
49*f585d8a3SJacky Wang     // For some reason in Bazel the stdlib dependency on the classpath isn't visible to the
50*f585d8a3SJacky Wang     // LintTestTask, so we just include it ourselves here for now.
51*f585d8a3SJacky Wang     private val jvmStaticStubs = kotlin(
52*f585d8a3SJacky Wang       """
53*f585d8a3SJacky Wang         package kotlin.jvm
54*f585d8a3SJacky Wang 
55*f585d8a3SJacky Wang         annotation class JvmStatic
56*f585d8a3SJacky Wang       """
57*f585d8a3SJacky Wang     ).indented()
58*f585d8a3SJacky Wang   }
59*f585d8a3SJacky Wang 
getDetectornull60*f585d8a3SJacky Wang   override fun getDetector(): Detector = DaggerKotlinIssueDetector()
61*f585d8a3SJacky Wang 
62*f585d8a3SJacky Wang   override fun getIssues(): List<Issue> = DaggerKotlinIssueDetector.issues
63*f585d8a3SJacky Wang 
64*f585d8a3SJacky Wang   @Test
65*f585d8a3SJacky Wang   fun simpleSmokeTestForQualifiersAndProviders() {
66*f585d8a3SJacky Wang     lint()
67*f585d8a3SJacky Wang       .allowMissingSdk()
68*f585d8a3SJacky Wang       .files(
69*f585d8a3SJacky Wang         javaxInjectStubs,
70*f585d8a3SJacky Wang         daggerStubs,
71*f585d8a3SJacky Wang         jvmStaticStubs,
72*f585d8a3SJacky Wang         kotlin(
73*f585d8a3SJacky Wang           """
74*f585d8a3SJacky Wang           package foo
75*f585d8a3SJacky Wang           import javax.inject.Inject
76*f585d8a3SJacky Wang           import javax.inject.Qualifier
77*f585d8a3SJacky Wang           import kotlin.jvm.JvmStatic
78*f585d8a3SJacky Wang           import dagger.Provides
79*f585d8a3SJacky Wang           import dagger.Module
80*f585d8a3SJacky Wang 
81*f585d8a3SJacky Wang           @Qualifier
82*f585d8a3SJacky Wang           annotation class MyQualifier
83*f585d8a3SJacky Wang 
84*f585d8a3SJacky Wang           class InjectedTest {
85*f585d8a3SJacky Wang             // This should fail because of `:field`
86*f585d8a3SJacky Wang             @Inject
87*f585d8a3SJacky Wang             @field:MyQualifier
88*f585d8a3SJacky Wang             lateinit var prop: String
89*f585d8a3SJacky Wang 
90*f585d8a3SJacky Wang             // This is fine!
91*f585d8a3SJacky Wang             @Inject
92*f585d8a3SJacky Wang             @MyQualifier
93*f585d8a3SJacky Wang             lateinit var prop2: String
94*f585d8a3SJacky Wang           }
95*f585d8a3SJacky Wang 
96*f585d8a3SJacky Wang           @Module
97*f585d8a3SJacky Wang           object ObjectModule {
98*f585d8a3SJacky Wang             // This should fail because it uses `@JvmStatic`
99*f585d8a3SJacky Wang             @JvmStatic
100*f585d8a3SJacky Wang             @Provides
101*f585d8a3SJacky Wang             fun provideFoo(): String {
102*f585d8a3SJacky Wang 
103*f585d8a3SJacky Wang             }
104*f585d8a3SJacky Wang 
105*f585d8a3SJacky Wang             // This is fine!
106*f585d8a3SJacky Wang             @Provides
107*f585d8a3SJacky Wang             fun provideBar(): String {
108*f585d8a3SJacky Wang 
109*f585d8a3SJacky Wang             }
110*f585d8a3SJacky Wang           }
111*f585d8a3SJacky Wang 
112*f585d8a3SJacky Wang           @Module
113*f585d8a3SJacky Wang           class ClassModule {
114*f585d8a3SJacky Wang             companion object {
115*f585d8a3SJacky Wang               // This should fail because the companion object is part of ClassModule, so this is unnecessary.
116*f585d8a3SJacky Wang               @JvmStatic
117*f585d8a3SJacky Wang               @Provides
118*f585d8a3SJacky Wang               fun provideBaz(): String {
119*f585d8a3SJacky Wang 
120*f585d8a3SJacky Wang               }
121*f585d8a3SJacky Wang             }
122*f585d8a3SJacky Wang           }
123*f585d8a3SJacky Wang 
124*f585d8a3SJacky Wang           @Module
125*f585d8a3SJacky Wang           class ClassModuleQualified {
126*f585d8a3SJacky Wang             companion object {
127*f585d8a3SJacky Wang               // This should fail because the companion object is part of ClassModule, so this is unnecessary.
128*f585d8a3SJacky Wang               // This specifically tests a fully qualified annotation
129*f585d8a3SJacky Wang               @kotlin.jvm.JvmStatic
130*f585d8a3SJacky Wang               @Provides
131*f585d8a3SJacky Wang               fun provideBaz(): String {
132*f585d8a3SJacky Wang 
133*f585d8a3SJacky Wang               }
134*f585d8a3SJacky Wang             }
135*f585d8a3SJacky Wang           }
136*f585d8a3SJacky Wang 
137*f585d8a3SJacky Wang           @Module
138*f585d8a3SJacky Wang           class ClassModule2 {
139*f585d8a3SJacky Wang             // This should fail because the companion object is part of ClassModule
140*f585d8a3SJacky Wang             @Module
141*f585d8a3SJacky Wang             companion object {
142*f585d8a3SJacky Wang               @Provides
143*f585d8a3SJacky Wang               fun provideBaz(): String {
144*f585d8a3SJacky Wang 
145*f585d8a3SJacky Wang               }
146*f585d8a3SJacky Wang             }
147*f585d8a3SJacky Wang           }
148*f585d8a3SJacky Wang 
149*f585d8a3SJacky Wang           @Module
150*f585d8a3SJacky Wang           class ClassModule2Qualified {
151*f585d8a3SJacky Wang             // This should fail because the companion object is part of ClassModule
152*f585d8a3SJacky Wang             // This specifically tests a fully qualified annotation
153*f585d8a3SJacky Wang             @dagger.Module
154*f585d8a3SJacky Wang             companion object {
155*f585d8a3SJacky Wang               @Provides
156*f585d8a3SJacky Wang               fun provideBaz(): String {
157*f585d8a3SJacky Wang 
158*f585d8a3SJacky Wang               }
159*f585d8a3SJacky Wang             }
160*f585d8a3SJacky Wang           }
161*f585d8a3SJacky Wang 
162*f585d8a3SJacky Wang           // This is correct as of Dagger 2.26!
163*f585d8a3SJacky Wang           @Module
164*f585d8a3SJacky Wang           class ClassModule3 {
165*f585d8a3SJacky Wang             companion object {
166*f585d8a3SJacky Wang               @Provides
167*f585d8a3SJacky Wang               fun provideBaz(): String {
168*f585d8a3SJacky Wang 
169*f585d8a3SJacky Wang               }
170*f585d8a3SJacky Wang             }
171*f585d8a3SJacky Wang           }
172*f585d8a3SJacky Wang 
173*f585d8a3SJacky Wang           class ClassModule4 {
174*f585d8a3SJacky Wang             // This is should fail because this should be extracted to a standalone object.
175*f585d8a3SJacky Wang             @Module
176*f585d8a3SJacky Wang             companion object {
177*f585d8a3SJacky Wang               @Provides
178*f585d8a3SJacky Wang               fun provideBaz(): String {
179*f585d8a3SJacky Wang 
180*f585d8a3SJacky Wang               }
181*f585d8a3SJacky Wang             }
182*f585d8a3SJacky Wang           }
183*f585d8a3SJacky Wang         """
184*f585d8a3SJacky Wang         ).indented()
185*f585d8a3SJacky Wang       )
186*f585d8a3SJacky Wang       .allowCompilationErrors(false)
187*f585d8a3SJacky Wang       // Unlikely that @JvmStatic would be aliased, so skipping these modes.
188*f585d8a3SJacky Wang       .skipTestModes(TestMode.TYPE_ALIAS, TestMode.IMPORT_ALIAS)
189*f585d8a3SJacky Wang       .run()
190*f585d8a3SJacky Wang       .expect(
191*f585d8a3SJacky Wang         """
192*f585d8a3SJacky Wang         src/foo/MyQualifier.kt:14: Warning: Redundant 'field:' used for Dagger qualifier annotation. [FieldSiteTargetOnQualifierAnnotation]
193*f585d8a3SJacky Wang           @field:MyQualifier
194*f585d8a3SJacky Wang           ~~~~~~~~~~~~~~~~~~
195*f585d8a3SJacky Wang         src/foo/MyQualifier.kt:26: Warning: @JvmStatic used for @Provides function in an object class [JvmStaticProvidesInObjectDetector]
196*f585d8a3SJacky Wang           @JvmStatic
197*f585d8a3SJacky Wang           ~~~~~~~~~~
198*f585d8a3SJacky Wang         src/foo/MyQualifier.kt:43: Warning: @JvmStatic used for @Provides function in an object class [JvmStaticProvidesInObjectDetector]
199*f585d8a3SJacky Wang             @JvmStatic
200*f585d8a3SJacky Wang             ~~~~~~~~~~
201*f585d8a3SJacky Wang         src/foo/MyQualifier.kt:56: Warning: @JvmStatic used for @Provides function in an object class [JvmStaticProvidesInObjectDetector]
202*f585d8a3SJacky Wang             @kotlin.jvm.JvmStatic
203*f585d8a3SJacky Wang             ~~~~~~~~~~~~~~~~~~~~~
204*f585d8a3SJacky Wang         src/foo/MyQualifier.kt:66: Warning: Module companion objects should not be annotated with @Module. [ModuleCompanionObjects]
205*f585d8a3SJacky Wang           // This should fail because the companion object is part of ClassModule
206*f585d8a3SJacky Wang           ^
207*f585d8a3SJacky Wang         src/foo/MyQualifier.kt:78: Warning: Module companion objects should not be annotated with @Module. [ModuleCompanionObjects]
208*f585d8a3SJacky Wang           // This should fail because the companion object is part of ClassModule
209*f585d8a3SJacky Wang           ^
210*f585d8a3SJacky Wang         src/foo/MyQualifier.kt:101: Warning: Module companion objects should not be annotated with @Module. [ModuleCompanionObjects]
211*f585d8a3SJacky Wang           // This is should fail because this should be extracted to a standalone object.
212*f585d8a3SJacky Wang           ^
213*f585d8a3SJacky Wang         0 errors, 7 warnings
214*f585d8a3SJacky Wang         """.trimIndent()
215*f585d8a3SJacky Wang       )
216*f585d8a3SJacky Wang       .expectFixDiffs(
217*f585d8a3SJacky Wang         """
218*f585d8a3SJacky Wang         Fix for src/foo/MyQualifier.kt line 14: Remove 'field:':
219*f585d8a3SJacky Wang         @@ -14 +14
220*f585d8a3SJacky Wang         -   @field:MyQualifier
221*f585d8a3SJacky Wang         +   @MyQualifier
222*f585d8a3SJacky Wang         Fix for src/foo/MyQualifier.kt line 26: Remove @JvmStatic:
223*f585d8a3SJacky Wang         @@ -26 +26
224*f585d8a3SJacky Wang         -   @JvmStatic
225*f585d8a3SJacky Wang         +
226*f585d8a3SJacky Wang         Fix for src/foo/MyQualifier.kt line 43: Remove @JvmStatic:
227*f585d8a3SJacky Wang         @@ -43 +43
228*f585d8a3SJacky Wang         -     @JvmStatic
229*f585d8a3SJacky Wang         +
230*f585d8a3SJacky Wang         Fix for src/foo/MyQualifier.kt line 56: Remove @JvmStatic:
231*f585d8a3SJacky Wang         @@ -56 +56
232*f585d8a3SJacky Wang         -     @kotlin.jvm.JvmStatic
233*f585d8a3SJacky Wang         +
234*f585d8a3SJacky Wang         Fix for src/foo/MyQualifier.kt line 66: Remove @Module:
235*f585d8a3SJacky Wang         @@ -67 +67
236*f585d8a3SJacky Wang         -   @Module
237*f585d8a3SJacky Wang         +
238*f585d8a3SJacky Wang         Fix for src/foo/MyQualifier.kt line 78: Remove @Module:
239*f585d8a3SJacky Wang         @@ -80 +80
240*f585d8a3SJacky Wang         -   @dagger.Module
241*f585d8a3SJacky Wang         +
242*f585d8a3SJacky Wang         Fix for src/foo/MyQualifier.kt line 101: Remove @Module:
243*f585d8a3SJacky Wang         @@ -102 +102
244*f585d8a3SJacky Wang         -   @Module
245*f585d8a3SJacky Wang         +
246*f585d8a3SJacky Wang         """.trimIndent()
247*f585d8a3SJacky Wang       )
248*f585d8a3SJacky Wang   }
249*f585d8a3SJacky Wang }
250