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