Introduce KSType.isFunctionType and KSType.isSuspendFunctionType

It would be cumbersome to implement in user space by looking up the
fully qualified names. Let's provide it in the API since compiler
already knows it.
diff --git a/api/src/main/kotlin/com/google/devtools/ksp/symbol/KSType.kt b/api/src/main/kotlin/com/google/devtools/ksp/symbol/KSType.kt
index 934d579..81eb6ee 100644
--- a/api/src/main/kotlin/com/google/devtools/ksp/symbol/KSType.kt
+++ b/api/src/main/kotlin/com/google/devtools/ksp/symbol/KSType.kt
@@ -95,4 +95,14 @@
      * True if the type is an error type, which means the type can't be resolved by compiler.
      */
     val isError: Boolean
-}
\ No newline at end of file
+
+    /**
+     * True if the type is a function type. Note that a suspend function will return false here.
+     */
+    val isFunctionType: Boolean
+
+    /**
+     * True if the type is a suspend function
+     */
+    val isSuspendFunctionType: Boolean
+}
diff --git a/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/processor/FunctionTypeProcessor.kt b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/processor/FunctionTypeProcessor.kt
new file mode 100644
index 0000000..a479226
--- /dev/null
+++ b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/processor/FunctionTypeProcessor.kt
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2020 Google LLC
+ * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
+ *
+ * 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 com.google.devtools.ksp.processor
+
+import com.google.devtools.ksp.processing.Resolver
+import com.google.devtools.ksp.symbol.*
+import com.google.devtools.ksp.visitor.KSTopDownVisitor
+
+open class FunctionTypeProcessor : AbstractTestProcessor() {
+    val results = mutableListOf<String>()
+    val typeCollector = TypeCollector()
+    val types = mutableSetOf<KSType>()
+
+    override fun process(resolver: Resolver): List<KSAnnotated> {
+        val files = resolver.getNewFiles()
+        val ignoredNames = mutableSetOf<String>()
+
+        files.forEach {
+            it.accept(
+                object : KSTopDownVisitor<Unit, Unit>() {
+                    override fun defaultHandler(node: KSNode, data: Unit) = Unit
+
+                    override fun visitPropertyDeclaration(property: KSPropertyDeclaration, data: Unit) {
+                        val type = property.type.resolve()
+                        val propertyName = property.simpleName.asString()
+                        val typeName = type.declaration.simpleName.asString()
+                        results.add("$propertyName: $typeName : ${type.isFunctionType}, ${type.isSuspendFunctionType}")
+                    }
+                }, Unit
+            )
+        }
+
+        return emptyList()
+    }
+
+    override fun toResult(): List<String> {
+        return results.sorted()
+    }
+
+}
diff --git a/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSErrorType.kt b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSErrorType.kt
index 24fb6a6..249eec1 100644
--- a/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSErrorType.kt
+++ b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSErrorType.kt
@@ -65,4 +65,8 @@
     override fun toString(): String {
         return "<ERROR TYPE>"
     }
-}
\ No newline at end of file
+
+    override val isFunctionType: Boolean = false
+
+    override val isSuspendFunctionType: Boolean = false
+}
diff --git a/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSTypeImpl.kt b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSTypeImpl.kt
index 4de3d4e..9f6d556 100644
--- a/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSTypeImpl.kt
+++ b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSTypeImpl.kt
@@ -23,6 +23,10 @@
 import com.google.devtools.ksp.symbol.impl.KSObjectCache
 import com.google.devtools.ksp.symbol.impl.binary.KSTypeArgumentDescriptorImpl
 import com.google.devtools.ksp.symbol.impl.replaceTypeArguments
+import org.jetbrains.kotlin.builtins.isFunctionType
+import org.jetbrains.kotlin.builtins.isKFunctionType
+import org.jetbrains.kotlin.builtins.isKSuspendFunctionType
+import org.jetbrains.kotlin.builtins.isSuspendFunctionType
 import org.jetbrains.kotlin.types.KotlinType
 import org.jetbrains.kotlin.types.getAbbreviation
 import org.jetbrains.kotlin.types.isError
@@ -102,6 +106,12 @@
     override fun hashCode(): Int = kotlinType.hashCode()
 
     override fun toString(): String = (kotlinType.getAbbreviation() ?: kotlinType).toString()
+
+    override val isFunctionType: Boolean
+        get() = kotlinType.isFunctionType || kotlinType.isKFunctionType
+
+    override val isSuspendFunctionType: Boolean
+        get() = kotlinType.isSuspendFunctionType || kotlinType.isKSuspendFunctionType
 }
 
 class IdKey<T>(private val k: T) {
diff --git a/compiler-plugin/src/test/java/com/google/devtools/ksp/test/KotlinKSPTestGenerated.java b/compiler-plugin/src/test/java/com/google/devtools/ksp/test/KotlinKSPTestGenerated.java
index df0ea8e..9931c3d 100644
--- a/compiler-plugin/src/test/java/com/google/devtools/ksp/test/KotlinKSPTestGenerated.java
+++ b/compiler-plugin/src/test/java/com/google/devtools/ksp/test/KotlinKSPTestGenerated.java
@@ -132,6 +132,11 @@
         runTest("testData/api/functionTypeAlias.kt");
     }
 
+    @TestMetadata("functionTypes.kt")
+    public void testFunctionTypes() throws Exception {
+        runTest("testData/api/functionTypes.kt");
+    }
+
     @TestMetadata("getPackage.kt")
     public void testGetPackage() throws Exception {
         runTest("testData/api/getPackage.kt");
diff --git a/compiler-plugin/testData/api/functionTypes.kt b/compiler-plugin/testData/api/functionTypes.kt
new file mode 100644
index 0000000..69f8366
--- /dev/null
+++ b/compiler-plugin/testData/api/functionTypes.kt
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2021 Google LLC
+ * Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
+ *
+ * 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.
+ */
+
+// WITH_RUNTIME
+// TEST PROCESSOR: FunctionTypeProcessor
+// EXPECTED:
+// a: Function0 : true, false
+// b: Function1 : true, false
+// c: Function0 : true, false
+// d: Function2 : true, false
+// e: KFunction0 : true, false
+// f: KSuspendFunction0 : false, true
+// g: KFunction1 : true, false
+// h: KSuspendFunction1 : false, true
+// i: Function1 : true, false
+// j: SuspendFunction1 : false, true
+// k: SuspendFunction0 : false, true
+// l: SuspendFunction1 : false, true
+// m: SuspendFunction0 : false, true
+// n: SuspendFunction2 : false, true
+// o: KFunction0 : true, false
+// p: KSuspendFunction0 : false, true
+// vbar: KSuspendFunction0 : false, true
+// vfoo: KFunction0 : true, false
+// END
+
+@file:Suppress("Boolean", "Byte", "Int", "Short", "Double", "Float", "Unit", "Suppress", "C")
+
+class C {
+    fun foo(): Boolean = true
+    suspend fun bar(): Int = 0
+    val vfoo = ::foo
+    val vbar = ::bar
+}
+
+fun foo() = Unit
+suspend fun bar() = Unit
+
+val a: () -> Unit = TODO()
+val b: (Int) -> Unit = TODO()
+val c: () -> Byte = TODO()
+val d: (Short, Float) -> Double = TODO()
+
+val e = C().vfoo
+val f = C().vbar
+val g = C::foo
+val h = C::bar
+
+val i: Int.() -> Boolean = TODO()
+val j: suspend Boolean.() -> Int = TODO()
+
+val k: suspend () -> Unit = TODO()
+val l: suspend (Int) -> Unit = TODO()
+val m: suspend () -> Byte = TODO()
+val n: suspend (Short, Float) -> Double = TODO()
+
+val o = ::foo
+val p = ::bar