@@ -26,6 +26,8 @@ import com.android.tools.lint.detector.api.JavaContext
2626import com.android.tools.lint.detector.api.Scope
2727import com.android.tools.lint.detector.api.Severity
2828import com.android.tools.lint.detector.api.SourceCodeScanner
29+ import com.google.common.collect.HashMultimap
30+ import com.google.common.collect.Multimap
2931import com.intellij.psi.PsiMethod
3032import com.intellij.psi.PsiType
3133import com.intellij.psi.util.PsiUtil
@@ -48,6 +50,7 @@ import org.jetbrains.uast.visitor.AbstractUastVisitor
4850internal const val CUSTOM_SCOPE_KEY = " autodispose.typesWithScope"
4951internal const val LENIENT = " autodispose.lenient"
5052internal const val OVERRIDE_SCOPES = " autodispose.overrideScopes"
53+ internal const val KOTLIN_EXTENSION_FUNCTIONS = " autodispose.kotlinExtensionFunctions"
5154
5255/* *
5356 * Detector which checks if your stream subscriptions are handled by AutoDispose.
@@ -98,19 +101,24 @@ class AutoDisposeDetector : Detector(), SourceCodeScanner {
98101 private val REACTIVE_TYPES = setOf (OBSERVABLE , FLOWABLE , PARALLEL_FLOWABLE , SINGLE , MAYBE ,
99102 COMPLETABLE )
100103
104+ private val REACTIVE_SUBSCRIBE_METHOD_NAMES = setOf (" subscribe" , " subscribeWith" )
105+
101106 internal const val PROPERTY_FILE = " gradle.properties"
102107 }
103108
104109 // The scopes that are applicable for the lint check.
105110 // This includes the DEFAULT_SCOPES as well as any custom scopes
106111 // defined by the consumer.
107112 private lateinit var appliedScopes: Set <String >
113+ private lateinit var ktExtensionMethodToPackageMap: Multimap <String , String >
114+ private lateinit var appliedMethodNames: List <String >
108115
109116 private var lenient: Boolean = false
110117
111118 override fun beforeCheckRootProject (context : Context ) {
112119 var overrideScopes = false
113120 val scopes = mutableSetOf<String >()
121+ val ktExtensionMethodToPackageMap = HashMultimap .create<String , String >()
114122
115123 // Add the custom scopes defined in configuration.
116124 val props = Properties ()
@@ -125,6 +133,16 @@ class AutoDisposeDetector : Detector(), SourceCodeScanner {
125133 .toList()
126134 scopes.addAll(customScopes)
127135 }
136+ props.getProperty(KOTLIN_EXTENSION_FUNCTIONS )?.let { ktExtensionProperty ->
137+ ktExtensionProperty.split(" ," )
138+ .forEach {
139+ val arr = it.split(" #" , limit = 2 )
140+ if (arr.size >= 2 ) {
141+ val (packageName, methodName) = arr
142+ ktExtensionMethodToPackageMap.put(methodName, packageName)
143+ }
144+ }
145+ }
128146 props.getProperty(LENIENT )?.toBoolean()?.let {
129147 lenient = it
130148 }
@@ -136,10 +154,12 @@ class AutoDisposeDetector : Detector(), SourceCodeScanner {
136154 if (! overrideScopes) {
137155 scopes.addAll(DEFAULT_SCOPES )
138156 }
139- appliedScopes = scopes
157+ this .appliedScopes = scopes
158+ this .ktExtensionMethodToPackageMap = ktExtensionMethodToPackageMap
159+ this .appliedMethodNames = (REACTIVE_SUBSCRIBE_METHOD_NAMES + ktExtensionMethodToPackageMap.keySet()).toList()
140160 }
141161
142- override fun getApplicableMethodNames (): List <String > = listOf ( " subscribe " , " subscribeWith " )
162+ override fun getApplicableMethodNames (): List <String > = appliedMethodNames
143163
144164 override fun createUastHandler (context : JavaContext ): UElementHandler ? {
145165 return object : UElementHandler () {
@@ -257,7 +277,15 @@ class AutoDisposeDetector : Detector(), SourceCodeScanner {
257277 }
258278
259279 private fun isReactiveType (evaluator : JavaEvaluator , method : PsiMethod ): Boolean {
260- return REACTIVE_TYPES .any { evaluator.isMemberInClass(method, it) }
280+ return REACTIVE_SUBSCRIBE_METHOD_NAMES .contains(method.name) && REACTIVE_TYPES .any {
281+ evaluator.isMemberInClass(method, it)
282+ }
283+ }
284+
285+ private fun isKotlinExtension (evaluator : JavaEvaluator , method : PsiMethod ): Boolean {
286+ return ktExtensionMethodToPackageMap.get(method.name).any {
287+ evaluator.isMemberInClass(method, it)
288+ }
261289 }
262290
263291 /* *
@@ -295,7 +323,7 @@ class AutoDisposeDetector : Detector(), SourceCodeScanner {
295323 if (! getApplicableMethodNames().contains(method.name)) return
296324 val evaluator = context.evaluator
297325
298- if (isReactiveType(evaluator, method) &&
326+ if (( isReactiveType(evaluator, method) || isKotlinExtension(evaluator, method) ) &&
299327 isInScope(evaluator, node)
300328 ) {
301329 if (! lenient) {
0 commit comments