Conditionally error due to missing getter/setter based on binding scope.

When processing a POJO with binding scope to bind to query only, then
it is OK if the POJO has no setter or constructor. Likewise if a POJO
with binding scope to read from cursor, then it is OK to have no
getter. When the binding scope is two-way, then both getters and
setters are needed (or getter and matching constructor).

Bug: 138664463
Test: PojoProcessorTest
Change-Id: I1324f2e55174aa9999498666000b86591d05e970
diff --git a/room/compiler/src/main/kotlin/androidx/room/processor/PojoProcessor.kt b/room/compiler/src/main/kotlin/androidx/room/processor/PojoProcessor.kt
index efebfac..653ae86 100644
--- a/room/compiler/src/main/kotlin/androidx/room/processor/PojoProcessor.kt
+++ b/room/compiler/src/main/kotlin/androidx/room/processor/PojoProcessor.kt
@@ -707,7 +707,9 @@
                     context.logger.e(field.element,
                             ProcessorErrors.tooManyMatchingGetters(field, matching))
                 })
-        context.checker.check(success, field.element, CANNOT_FIND_GETTER_FOR_FIELD)
+        context.checker.check(
+            success || bindingScope == FieldProcessor.BindingScope.READ_FROM_CURSOR,
+            field.element, CANNOT_FIND_GETTER_FOR_FIELD)
     }
 
     private fun assignSetters(
@@ -756,7 +758,9 @@
                     context.logger.e(field.element,
                             ProcessorErrors.tooManyMatchingSetter(field, matching))
                 })
-        context.checker.check(success, field.element, CANNOT_FIND_SETTER_FOR_FIELD)
+        context.checker.check(
+            success || bindingScope == FieldProcessor.BindingScope.BIND_TO_STMT,
+            field.element, CANNOT_FIND_SETTER_FOR_FIELD)
     }
 
     /**
diff --git a/room/compiler/src/test/kotlin/androidx/room/processor/PojoProcessorTest.kt b/room/compiler/src/test/kotlin/androidx/room/processor/PojoProcessorTest.kt
index 4dd648a..106c34f 100644
--- a/room/compiler/src/test/kotlin/androidx/room/processor/PojoProcessorTest.kt
+++ b/room/compiler/src/test/kotlin/androidx/room/processor/PojoProcessorTest.kt
@@ -1482,6 +1482,132 @@
                 ProcessorErrors.missingIgnoredColumns(listOf("no_such_column")))
     }
 
+    @Test
+    fun noSetter_scopeBindStmt() {
+        simpleRun(
+            """
+                package foo.bar;
+                import androidx.room.*;
+                public class ${MY_POJO.simpleName()} {
+                    private String foo;
+                    private String bar;
+
+                    public String getFoo() { return foo; }
+                    public String getBar() { return bar; }
+                }
+                """.toJFO(MY_POJO.toString())) { invocation ->
+            PojoProcessor.createFor(context = invocation.context,
+                element = invocation.typeElement(MY_POJO.toString()),
+                bindingScope = FieldProcessor.BindingScope.BIND_TO_STMT,
+                parent = null).process()
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun noSetter_scopeTwoWay() {
+        simpleRun(
+            """
+                package foo.bar;
+                import androidx.room.*;
+                public class ${MY_POJO.simpleName()} {
+                    private String foo;
+                    private String bar;
+
+                    public String getFoo() { return foo; }
+                    public String getBar() { return bar; }
+                }
+                """.toJFO(MY_POJO.toString())) { invocation ->
+            PojoProcessor.createFor(context = invocation.context,
+                element = invocation.typeElement(MY_POJO.toString()),
+                bindingScope = FieldProcessor.BindingScope.TWO_WAY,
+                parent = null).process()
+        }.failsToCompile().withErrorContaining("Cannot find setter for field.")
+    }
+
+    @Test
+    fun noSetter_scopeReadFromCursor() {
+        simpleRun(
+            """
+                package foo.bar;
+                import androidx.room.*;
+                public class ${MY_POJO.simpleName()} {
+                    private String foo;
+                    private String bar;
+
+                    public String getFoo() { return foo; }
+                    public String getBar() { return bar; }
+                }
+                """.toJFO(MY_POJO.toString())) { invocation ->
+            PojoProcessor.createFor(context = invocation.context,
+                element = invocation.typeElement(MY_POJO.toString()),
+                bindingScope = FieldProcessor.BindingScope.READ_FROM_CURSOR,
+                parent = null).process()
+        }.failsToCompile().withErrorContaining("Cannot find setter for field.")
+    }
+
+    @Test
+    fun noGetter_scopeBindStmt() {
+        simpleRun(
+            """
+                package foo.bar;
+                import androidx.room.*;
+                public class ${MY_POJO.simpleName()} {
+                    private String foo;
+                    private String bar;
+
+                    public void setFoo(String foo) { this.foo = foo; }
+                    public void setBar(String bar) { this.bar = bar; }
+                }
+                """.toJFO(MY_POJO.toString())) { invocation ->
+            PojoProcessor.createFor(context = invocation.context,
+                element = invocation.typeElement(MY_POJO.toString()),
+                bindingScope = FieldProcessor.BindingScope.BIND_TO_STMT,
+                parent = null).process()
+        }.failsToCompile().withErrorContaining("Cannot find getter for field.")
+    }
+
+    @Test
+    fun noGetter_scopeTwoWay() {
+        simpleRun(
+            """
+                package foo.bar;
+                import androidx.room.*;
+                public class ${MY_POJO.simpleName()} {
+                    private String foo;
+                    private String bar;
+
+                    public void setFoo(String foo) { this.foo = foo; }
+                    public void setBar(String bar) { this.bar = bar; }
+                }
+                """.toJFO(MY_POJO.toString())) { invocation ->
+            PojoProcessor.createFor(context = invocation.context,
+                element = invocation.typeElement(MY_POJO.toString()),
+                bindingScope = FieldProcessor.BindingScope.TWO_WAY,
+                parent = null).process()
+        }.failsToCompile().withErrorContaining("Cannot find getter for field.")
+    }
+
+    @Test
+    fun noGetter_scopeReadCursor() {
+        simpleRun(
+            """
+                package foo.bar;
+                import androidx.room.*;
+                public class ${MY_POJO.simpleName()} {
+                    private String foo;
+                    private String bar;
+
+                    public void setFoo(String foo) { this.foo = foo; }
+                    public void setBar(String bar) { this.bar = bar; }
+                }
+                """.toJFO(MY_POJO.toString())) { invocation ->
+            PojoProcessor.createFor(context = invocation.context,
+                element = invocation.typeElement(MY_POJO.toString()),
+                bindingScope = FieldProcessor.BindingScope.READ_FROM_CURSOR,
+                parent = null).process()
+        }.compilesWithoutError()
+    }
+
     private fun singleRun(
         code: String,
         vararg jfos: JavaFileObject,
@@ -1523,7 +1649,7 @@
             handler.invoke(
                 PojoProcessor.createFor(context = invocation.context,
                         element = invocation.typeElement(MY_POJO.toString()),
-                        bindingScope = FieldProcessor.BindingScope.READ_FROM_CURSOR,
+                        bindingScope = FieldProcessor.BindingScope.TWO_WAY,
                         parent = null).process(),
                 invocation
             )
diff --git a/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/dao/BooksDao.kt b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/dao/BooksDao.kt
index 6cadb2f..8dae07b0 100644
--- a/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/dao/BooksDao.kt
+++ b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/dao/BooksDao.kt
@@ -33,6 +33,7 @@
 import androidx.room.integration.kotlintestapp.vo.BookWithPublisher
 import androidx.room.integration.kotlintestapp.vo.DateConverter
 import androidx.room.integration.kotlintestapp.vo.Lang
+import androidx.room.integration.kotlintestapp.vo.MiniBook
 import androidx.room.integration.kotlintestapp.vo.Publisher
 import androidx.room.integration.kotlintestapp.vo.PublisherWithBookSales
 import androidx.room.integration.kotlintestapp.vo.PublisherWithBooks
@@ -116,6 +117,9 @@
     @Insert
     fun addBooks(vararg books: Book)
 
+    @Insert(entity = Book::class)
+    fun addMiniBook(miniBook: MiniBook)
+
     @Insert
     fun addBookAuthors(vararg bookAuthors: BookAuthor)
 
diff --git a/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/vo/Book.kt b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/vo/Book.kt
index 6d7b7a7..21292d5 100644
--- a/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/vo/Book.kt
+++ b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/vo/Book.kt
@@ -16,6 +16,7 @@
 
 package androidx.room.integration.kotlintestapp.vo
 
+import androidx.room.ColumnInfo
 import androidx.room.Entity
 import androidx.room.ForeignKey
 import androidx.room.PrimaryKey
@@ -30,7 +31,9 @@
     @PrimaryKey val bookId: String,
     val title: String,
     val bookPublisherId: String,
+    @ColumnInfo(defaultValue = "0")
     @field:TypeConverters(Lang::class)
     val languages: Set<Lang>,
+    @ColumnInfo(defaultValue = "0")
     val salesCnt: Int
 )
diff --git a/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/vo/MiniBook.kt b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/vo/MiniBook.kt
new file mode 100644
index 0000000..42d013a
--- /dev/null
+++ b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/vo/MiniBook.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.room.integration.kotlintestapp.vo
+
+import androidx.room.PrimaryKey
+
+data class MiniBook(
+    @PrimaryKey val bookId: String,
+    val title: String,
+    val bookPublisherId: String
+)
\ No newline at end of file