Skip to content

Commit

Permalink
Merge "Add thrownTypes to XExecutableElement" into androidx-main
Browse files Browse the repository at this point in the history
  • Loading branch information
Treehugger Robot authored and Gerrit Code Review committed Jul 12, 2021
2 parents 55dadd9 + 278f3ca commit 0399731
Show file tree
Hide file tree
Showing 7 changed files with 163 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
*/
package androidx.room.compiler.processing

import androidx.room.compiler.processing.javac.JavacExecutableElement
import androidx.room.compiler.processing.ksp.KspMethodElement
import androidx.room.compiler.processing.ksp.KspMethodType
import com.squareup.javapoet.ClassName
Expand Down Expand Up @@ -139,11 +138,8 @@ object MethodSpecHelper {
}
addAnnotation(Override::class.java)
varargs(executableElement.isVarArgs())
if (executableElement is JavacExecutableElement) {
// copy throws for java
executableElement.element.thrownTypes.forEach {
addException(TypeName.get(it))
}
executableElement.thrownTypes.forEach {
addException(it.typeName)
}
returns(resolvedType.returnType.typeName)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@ interface XExecutableElement : XHasModifiers, XElement {
* @see [isVarArgs]
*/
val parameters: List<XExecutableParameterElement>

/**
* The list of `Throwable`s that are declared in this executable's signature.
*/
val thrownTypes: List<XType>
/**
* Returns true if this method receives a vararg parameter.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package androidx.room.compiler.processing.javac

import androidx.room.compiler.processing.XExecutableElement
import androidx.room.compiler.processing.XHasModifiers
import androidx.room.compiler.processing.XNullability
import androidx.room.compiler.processing.javac.kotlin.KmExecutable
import androidx.room.compiler.processing.javac.kotlin.descriptor
import javax.lang.model.element.ExecutableElement
Expand Down Expand Up @@ -59,6 +60,16 @@ internal abstract class JavacExecutableElement(
return element.isVarArgs
}

override val thrownTypes by lazy {
element.thrownTypes.map {
env.wrap<JavacType>(
typeMirror = it,
kotlinType = null,
elementNullability = XNullability.UNKNOWN
)
}
}

companion object {
internal const val DEFAULT_IMPLS_CLASS_NAME = "DefaultImpls"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,10 @@ import androidx.room.compiler.processing.XAnnotated
import androidx.room.compiler.processing.XExecutableElement
import androidx.room.compiler.processing.XExecutableParameterElement
import androidx.room.compiler.processing.XHasModifiers
import androidx.room.compiler.processing.XType
import androidx.room.compiler.processing.ksp.KspAnnotated.UseSiteFilter.Companion.NO_USE_SITE
import androidx.room.compiler.processing.util.ISSUE_TRACKER_LINK
import com.google.devtools.ksp.KspExperimental
import com.google.devtools.ksp.isConstructor
import com.google.devtools.ksp.symbol.KSFunctionDeclaration
import com.google.devtools.ksp.symbol.Modifier
Expand Down Expand Up @@ -60,6 +62,16 @@ internal abstract class KspExecutableElement(
}
}

@OptIn(KspExperimental::class)
override val thrownTypes: List<XType> by lazy {
env.resolver.getJvmCheckedException(declaration).map {
env.wrap(
ksType = it,
allowPrimitives = false
)
}.toList()
}

override fun isVarArgs(): Boolean {
// in java, only the last argument can be a vararg so for suspend functions, it is never
// a vararg function. this would change if room generated kotlin code
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,16 @@ internal sealed class KspSyntheticPropertyMethodElement(
override val docComment: String?
get() = null

override val thrownTypes: List<XType>
get() {
// TODO replace with the Resolver method when it is available. This solution works only
// in sources.
// https://github.com/google/ksp/issues/505
return getAnnotation(Throws::class)
?.getAsTypeList("exceptionClasses")
?: emptyList()
}

final override fun asMemberOf(other: XType): XMethodType {
return KspSyntheticPropertyMethodType.create(
element = this,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -442,8 +442,9 @@ class MethodSpecHelperTest(
) { invocation ->
val (target, methods) = invocation.getOverrideTestTargets(ignoreInheritedMethods)
methods.forEachIndexed { index, method ->
if (invocation.isKsp && method.name == "throwsException") {
if (invocation.isKsp && method.name == "throwsException" && preCompiledCode) {
// TODO b/171572318
// https://github.com/google/ksp/issues/507
} else {
val subject = MethodSpecHelper.overridingWithFinalParams(
method,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,19 +20,24 @@ import androidx.room.compiler.processing.util.CONTINUATION_CLASS_NAME
import androidx.room.compiler.processing.util.Source
import androidx.room.compiler.processing.util.UNIT_CLASS_NAME
import androidx.room.compiler.processing.util.className
import androidx.room.compiler.processing.util.compileFiles
import androidx.room.compiler.processing.util.getDeclaredMethod
import androidx.room.compiler.processing.util.getMethod
import androidx.room.compiler.processing.util.getParameter
import androidx.room.compiler.processing.util.runProcessorTest
import androidx.room.compiler.processing.util.typeName
import com.google.common.truth.Truth
import com.google.common.truth.Truth.assertThat
import com.google.common.truth.Truth.assertWithMessage
import com.squareup.javapoet.ClassName
import com.squareup.javapoet.ParameterizedTypeName
import com.squareup.javapoet.TypeName
import com.squareup.javapoet.WildcardTypeName
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
import java.io.IOException
import java.lang.IllegalStateException

@RunWith(JUnit4::class)
class XExecutableElementTest {
Expand Down Expand Up @@ -604,6 +609,122 @@ class XExecutableElementTest {
genericToPrimitiveOverrides(asMemberOf = true)
}

@Test
fun thrownTypes() {
fun buildSources(pkg: String) = listOf(
Source.java(
"$pkg.JavaSubject",
"""
package $pkg;
import java.io.*;
public class JavaSubject {
public JavaSubject() throws IllegalArgumentException {}
public void multipleThrows() throws IOException, IllegalStateException {
}
}
""".trimIndent()
),
Source.kotlin(
"KotlinSubject.kt",
"""
package $pkg
import java.io.*
public class KotlinSubject {
@Throws(IllegalArgumentException::class)
constructor() {
}
@Throws(IOException::class, IllegalStateException::class)
fun multipleThrows() {
}
}
""".trimIndent()
),
Source.kotlin(
"AccessorThrows.kt",
"""
package $pkg
import java.io.*
public class KotlinAccessors {
@get:Throws(IllegalArgumentException::class)
val getterThrows: Int = 3
@set:Throws(IllegalStateException::class)
var setterThrows: Int = 3
@get:Throws(IOException::class)
@set:Throws(IllegalStateException::class, IllegalArgumentException::class)
var bothThrows: Int = 3
}
""".trimIndent()
)
)
runProcessorTest(
sources = buildSources("app"),
classpath = compileFiles(sources = buildSources("lib"))
) { invocation ->
fun collectExceptions(subject: XTypeElement): List<Pair<String, Set<TypeName>>> {
return (subject.getConstructors() + subject.getDeclaredMethods()).mapNotNull {
val throwTypes = it.thrownTypes
val name = if (it is XMethodElement) {
it.name
} else {
"<init>"
}
if (throwTypes.isEmpty()) {
null
} else {
name to throwTypes.map { it.typeName }.toSet()
}
}
}
// TODO
// add lib here once https://github.com/google/ksp/issues/507 is fixed
listOf("app").forEach { pkg ->
invocation.processingEnv.requireTypeElement("$pkg.KotlinSubject").let { subject ->
assertWithMessage(subject.qualifiedName).that(
collectExceptions(subject)
).containsExactly(
"<init>" to setOf(ClassName.get(IllegalArgumentException::class.java)),
"multipleThrows" to setOf(
ClassName.get(IOException::class.java),
ClassName.get(IllegalStateException::class.java)
)
)
}
invocation.processingEnv.requireTypeElement("$pkg.JavaSubject").let { subject ->
assertWithMessage(subject.qualifiedName).that(
collectExceptions(subject)
).containsExactly(
"<init>" to setOf(ClassName.get(IllegalArgumentException::class.java)),
"multipleThrows" to setOf(
ClassName.get(IOException::class.java),
ClassName.get(IllegalStateException::class.java)
)
)
}
invocation.processingEnv.requireTypeElement("$pkg.KotlinAccessors").let { subject ->
assertWithMessage(subject.qualifiedName).that(
collectExceptions(subject)
).containsExactly(
"getGetterThrows" to setOf(
ClassName.get(IllegalArgumentException::class.java)
),
"setSetterThrows" to setOf(
ClassName.get(IllegalStateException::class.java)
),
"getBothThrows" to setOf(
ClassName.get(IOException::class.java)
),
"setBothThrows" to setOf(
ClassName.get(IllegalStateException::class.java),
ClassName.get(IllegalArgumentException::class.java)
),
)
}
}
}
}

// see b/160258066
private fun genericToPrimitiveOverrides(asMemberOf: Boolean) {
val source = Source.kotlin(
Expand Down

0 comments on commit 0399731

Please sign in to comment.