117565118: Suspended functions are not tracked

Change-Id: I2b7454a28dccf3ef07f7620e9c30e4e112045434
Fixes: 117565118
Test: Unit tests included
diff --git a/src/main/java/com/android/tools/metalava/doclava1/ApiFile.java b/src/main/java/com/android/tools/metalava/doclava1/ApiFile.java
index a8e11dd..f676af4 100644
--- a/src/main/java/com/android/tools/metalava/doclava1/ApiFile.java
+++ b/src/main/java/com/android/tools/metalava/doclava1/ApiFile.java
@@ -561,6 +561,10 @@
                     modifiers.setInline(true);
                     token = tokenizer.requireToken();
                     break;
+                case "suspend":
+                    modifiers.setSuspend(true);
+                    token = tokenizer.requireToken();
+                    break;
                 case "vararg":
                     modifiers.setVarArg(true);
                     token = tokenizer.requireToken();
diff --git a/src/main/java/com/android/tools/metalava/model/DefaultModifierList.kt b/src/main/java/com/android/tools/metalava/model/DefaultModifierList.kt
index 941b7d4..65ebd48 100644
--- a/src/main/java/com/android/tools/metalava/model/DefaultModifierList.kt
+++ b/src/main/java/com/android/tools/metalava/model/DefaultModifierList.kt
@@ -117,6 +117,10 @@
         return isSet(INFIX)
     }
 
+    override fun isSuspend(): Boolean {
+        return isSet(SUSPEND)
+    }
+
     override fun isOperator(): Boolean {
         return isSet(OPERATOR)
     }
@@ -201,6 +205,10 @@
         set(DEPRECATED, deprecated)
     }
 
+    fun setSuspend(suspend: Boolean) {
+        set(SUSPEND, suspend)
+    }
+
     override fun addAnnotation(annotation: AnnotationItem) {
         if (annotations == null) {
             annotations = mutableListOf()
@@ -268,6 +276,7 @@
         const val INFIX = 1 shl 16
         const val OPERATOR = 1 shl 17
         const val INLINE = 1 shl 18
+        const val SUSPEND = 1 shl 19
 
         private fun bit(modifier: String): Int {
             return when (modifier) {
@@ -290,6 +299,7 @@
                 "infix" -> INFIX
                 "operator" -> OPERATOR
                 "inline" -> INLINE
+                "suspend" -> SUSPEND
                 else -> error("Unsupported modifier $modifier")
             }
         }
@@ -300,6 +310,6 @@
          */
         private const val EQUIVALENCE_MASK = PUBLIC or PROTECTED or PRIVATE or STATIC or ABSTRACT or
             FINAL or TRANSIENT or VOLATILE or SYNCHRONIZED or DEPRECATED or VARARG or
-            SEALED or INTERNAL or INFIX or OPERATOR
+            SEALED or INTERNAL or INFIX or OPERATOR or SUSPEND
     }
 }
\ No newline at end of file
diff --git a/src/main/java/com/android/tools/metalava/model/ModifierList.kt b/src/main/java/com/android/tools/metalava/model/ModifierList.kt
index 7c5c1ef..46074fc 100644
--- a/src/main/java/com/android/tools/metalava/model/ModifierList.kt
+++ b/src/main/java/com/android/tools/metalava/model/ModifierList.kt
@@ -53,6 +53,7 @@
 
     fun isInternal(): Boolean = false
     fun isInfix(): Boolean = false
+    fun isSuspend(): Boolean = false
     fun isOperator(): Boolean = false
     fun isInline(): Boolean = false
     fun isEmpty(): Boolean
@@ -312,6 +313,10 @@
                     writer.write("sealed ")
                 }
 
+                if (list.isSuspend()) {
+                    writer.write("suspend ")
+                }
+
                 if (list.isInline()) {
                     writer.write("inline ")
                 }
@@ -405,6 +410,10 @@
                     writer.write("sealed ")
                 }
 
+                if (list.isSuspend()) {
+                    writer.write("suspend ")
+                }
+
                 if (list.isInline()) {
                     writer.write("inline ")
                 }
diff --git a/src/main/java/com/android/tools/metalava/model/psi/PsiModifierItem.kt b/src/main/java/com/android/tools/metalava/model/psi/PsiModifierItem.kt
index c847b8b..015b470 100644
--- a/src/main/java/com/android/tools/metalava/model/psi/PsiModifierItem.kt
+++ b/src/main/java/com/android/tools/metalava/model/psi/PsiModifierItem.kt
@@ -128,6 +128,13 @@
                             }
                         }
                     }
+                    if (ktModifierList.hasModifier(KtTokens.SUSPEND_KEYWORD)) {
+                        flags = flags or SUSPEND
+
+                        // Workaround for b/117565118:
+                        // Switch back from private to public
+                        flags = (flags and PRIVATE.inv()) or PUBLIC
+                    }
                 }
             }
 
diff --git a/src/test/java/com/android/tools/metalava/ApiFileTest.kt b/src/test/java/com/android/tools/metalava/ApiFileTest.kt
index fcc53ea..be7ddbe 100644
--- a/src/test/java/com/android/tools/metalava/ApiFileTest.kt
+++ b/src/test/java/com/android/tools/metalava/ApiFileTest.kt
@@ -463,6 +463,29 @@
     }
 
     @Test
+    fun `Suspend functions`() {
+        check(
+            sourceFiles = *arrayOf(
+                kotlin(
+                    """
+                    package test.pkg
+                    suspend inline fun hello() { }
+                    """
+                )
+            ),
+            api = """
+                package test.pkg {
+                  public final class TestKt {
+                    ctor public TestKt();
+                    method public static suspend inline java.lang.Object hello(kotlin.coroutines.experimental.Continuation<? super kotlin.Unit> p);
+                  }
+                }
+                """,
+            checkDoclava1 = false /* doesn't support Kotlin... */
+        )
+    }
+
+    @Test
     fun `Propagate Platform types in Kotlin`() {
         check(
             compatibilityMode = false,
diff --git a/src/test/java/com/android/tools/metalava/ApiFromTextTest.kt b/src/test/java/com/android/tools/metalava/ApiFromTextTest.kt
index c8af838..f056ef8 100644
--- a/src/test/java/com/android/tools/metalava/ApiFromTextTest.kt
+++ b/src/test/java/com/android/tools/metalava/ApiFromTextTest.kt
@@ -730,4 +730,22 @@
             api = source
         )
     }
+
+    @Test
+    fun `Suspended methods`() {
+        val source = """
+                package test.pkg {
+                  public final class TestKt {
+                    ctor public TestKt();
+                    method public static suspend inline java.lang.Object hello(kotlin.coroutines.experimental.Continuation<? super kotlin.Unit> p);
+                  }
+                }
+                """
+
+        check(
+            compatibilityMode = true,
+            signatureSource = source,
+            api = source
+        )
+    }
 }
\ No newline at end of file