Merge "Fix NonNullableMutableLiveData lint check" into androidx-master-dev
diff --git a/lifecycle/lifecycle-livedata-core-ktx-lint/build.gradle b/lifecycle/lifecycle-livedata-core-ktx-lint/build.gradle
index 2a1a5f5..3a7be4e 100644
--- a/lifecycle/lifecycle-livedata-core-ktx-lint/build.gradle
+++ b/lifecycle/lifecycle-livedata-core-ktx-lint/build.gradle
@@ -37,6 +37,8 @@
     }
     compileOnly KOTLIN_STDLIB
 
+    compileOnly LINT_CORE
+
     testImplementation KOTLIN_STDLIB
     testImplementation LINT_CORE
     testImplementation LINT_TESTS
diff --git a/lifecycle/lifecycle-livedata-core-ktx-lint/src/main/java/androidx/lifecycle/lint/NonNullableMutableLiveDataDetector.kt b/lifecycle/lifecycle-livedata-core-ktx-lint/src/main/java/androidx/lifecycle/lint/NonNullableMutableLiveDataDetector.kt
index 10a133f..2bb57da 100644
--- a/lifecycle/lifecycle-livedata-core-ktx-lint/src/main/java/androidx/lifecycle/lint/NonNullableMutableLiveDataDetector.kt
+++ b/lifecycle/lifecycle-livedata-core-ktx-lint/src/main/java/androidx/lifecycle/lint/NonNullableMutableLiveDataDetector.kt
@@ -16,6 +16,7 @@
 
 package androidx.lifecycle.lint
 
+import com.android.tools.lint.checks.DataFlowAnalyzer
 import com.android.tools.lint.detector.api.Category
 import com.android.tools.lint.detector.api.Detector
 import com.android.tools.lint.detector.api.Implementation
@@ -38,8 +39,10 @@
 import org.jetbrains.uast.UClass
 import org.jetbrains.uast.UElement
 import org.jetbrains.uast.UReferenceExpression
+import org.jetbrains.uast.getParentOfType
 import org.jetbrains.uast.getUastParentOfType
 import org.jetbrains.uast.isNullLiteral
+import org.jetbrains.uast.kotlin.KotlinUField
 import org.jetbrains.uast.kotlin.KotlinUSimpleReferenceExpression
 import org.jetbrains.uast.resolveToUElement
 
@@ -77,16 +80,38 @@
         if (!isKotlin(node.sourcePsi) || !context.evaluator.isMemberInSubClassOf(method,
                 "androidx.lifecycle.LiveData", false)) return
 
+        val fieldTypes = mutableListOf<KtTypeReference>()
+
+        val analyzer = node.getParentOfType<UClass>(UClass::class.java, true) ?: return
+        analyzer.accept(object : DataFlowAnalyzer(listOf(node)) {
+            override fun visitClass(node: UClass): Boolean {
+                for (element in node.uastDeclarations) {
+                    if (element is KotlinUField) {
+                        (element.sourcePsi?.children?.get(0) as? KtCallExpression)
+                            ?.typeArguments?.singleOrNull()?.typeReference?.let {
+                            fieldTypes.add(it)
+                        }
+                    }
+                }
+                return super.visitClass(node)
+            }
+        })
+
         val receiverType = node.receiverType as PsiClassType
-        val liveDataType = if (receiverType.hasParameters()) {
-            val receiver = (node.receiver as? KotlinUSimpleReferenceExpression)?.resolve() ?: return
-            val assignment = UastLintUtils.findLastAssignment(receiver as PsiVariable, node)
-                ?: return
-            val constructorExpression = assignment.sourcePsi as? KtCallExpression
-            constructorExpression?.typeArguments?.singleOrNull()?.typeReference
+        val liveDataType = if (fieldTypes.isNullOrEmpty()) {
+            if (receiverType.hasParameters()) {
+                val receiver =
+                    (node.receiver as? KotlinUSimpleReferenceExpression)?.resolve() ?: return
+                val assignment = UastLintUtils.findLastAssignment(receiver as PsiVariable, node)
+                    ?: return
+                val constructorExpression = assignment.sourcePsi as? KtCallExpression
+                constructorExpression?.typeArguments?.singleOrNull()?.typeReference
+            } else {
+                getTypeArg(receiverType)
+            } ?: return
         } else {
-            getTypeArg(receiverType)
-        } ?: return
+            fieldTypes[0]
+        }
 
         if (liveDataType.typeElement !is KtNullableType) {
             val fixes = mutableListOf<LintFix>()
diff --git a/lifecycle/lifecycle-livedata-core-ktx-lint/src/test/java/androidx/lifecycle/lint/NonNullableMutableLiveDataDetectorTest.kt b/lifecycle/lifecycle-livedata-core-ktx-lint/src/test/java/androidx/lifecycle/lint/NonNullableMutableLiveDataDetectorTest.kt
index 9094fd1..2f5cd03 100644
--- a/lifecycle/lifecycle-livedata-core-ktx-lint/src/test/java/androidx/lifecycle/lint/NonNullableMutableLiveDataDetectorTest.kt
+++ b/lifecycle/lifecycle-livedata-core-ktx-lint/src/test/java/androidx/lifecycle/lint/NonNullableMutableLiveDataDetectorTest.kt
@@ -128,6 +128,114 @@
     }
 
     @Test
+    fun nullLiteralFailField() {
+        check(
+            kotlin(
+                """
+                package com.example
+
+                import androidx.lifecycle.MutableLiveData
+
+                val liveDataField = MutableLiveData<Boolean>()
+
+                fun foo() {
+                    liveDataField.value = null
+                }
+            """
+            ).indented()
+        ).expect("""
+src/com/example/test.kt:8: Error: Cannot set non-nullable LiveData value to null [NullSafeMutableLiveData]
+    liveDataField.value = null
+                          ~~~~
+1 errors, 0 warnings
+        """)
+    }
+
+    @Test
+    fun nullLiteralFailMultipleFields() {
+        check(
+            kotlin(
+                """
+                package com.example
+
+                import androidx.lifecycle.MutableLiveData
+
+                val liveDataField = MutableLiveData<Boolean>()
+                val secondLiveDataField = MutableLiveData<String>()
+
+                fun foo() {
+                    liveDataField.value = null
+                    secondLiveDataField.value = null
+                }
+            """
+            ).indented()
+        ).expect("""
+src/com/example/test.kt:9: Error: Cannot set non-nullable LiveData value to null [NullSafeMutableLiveData]
+    liveDataField.value = null
+                          ~~~~
+src/com/example/test.kt:10: Error: Cannot set non-nullable LiveData value to null [NullSafeMutableLiveData]
+    secondLiveDataField.value = null
+                                ~~~~
+2 errors, 0 warnings
+        """)
+    }
+
+    @Test
+    fun nullLiteralFailFieldAndIgnore() {
+        check(
+            kotlin(
+                """
+                package com.example
+
+                import androidx.lifecycle.MutableLiveData
+
+                val liveDataField = MutableLiveData<Boolean>()
+                val ignoreThisField = ArrayList<String>(arrayListOf("a", "b"))
+
+                fun foo() {
+                    liveDataField.value = null
+                    ignoreThisField[0] = null
+                }
+            """
+            ).indented()
+        ).expect("""
+src/com/example/test.kt:9: Error: Cannot set non-nullable LiveData value to null [NullSafeMutableLiveData]
+    liveDataField.value = null
+                          ~~~~
+1 errors, 0 warnings
+        """)
+    }
+
+    @Test
+    fun nullLiteralFailFieldAndLocalVariable() {
+        check(
+            kotlin(
+                """
+                package com.example
+
+                import androidx.lifecycle.MutableLiveData
+
+                val liveDataField = MutableLiveData<Boolean>()
+
+                fun foo() {
+                    liveDataField.value = null
+                    val liveDataVariable = MutableLiveData<Boolean>()
+                    liveDataVariable.value = null
+                }
+            """
+            ).indented()
+        ).expect("""
+src/com/example/test.kt:8: Error: Cannot set non-nullable LiveData value to null [NullSafeMutableLiveData]
+    liveDataField.value = null
+                          ~~~~
+src/com/example/test.kt:10: Error: Cannot set non-nullable LiveData value to null [NullSafeMutableLiveData]
+    liveDataVariable.value = null
+                             ~~~~
+2 errors, 0 warnings
+        """)
+    }
+
+    @Test
     fun nullLiteralQuickFix() {
         check(
             kotlin("""