Skip to content

Commit

Permalink
add annotation validation to validate API
Browse files Browse the repository at this point in the history
  • Loading branch information
neetopia committed Nov 20, 2020
1 parent ad3144e commit 96a7e1f
Show file tree
Hide file tree
Showing 4 changed files with 54 additions and 32 deletions.
4 changes: 2 additions & 2 deletions api/src/main/kotlin/com/google/devtools/ksp/utils.kt
Expand Up @@ -83,8 +83,8 @@ fun KSDeclaration.isLocal(): Boolean {
* Perform a validation on a given symbol to check if all interested types in symbols enclosed scope are valid, i.e. resolvable.
* @param predicate: A lambda for filtering interested symbols for performance purpose. Default checks all.
*/
fun KSNode.validate(predicate: (KSNode, KSNode) -> Boolean = { _, _-> true } ): Boolean {
return this.accept(KSValidateVisitor(predicate), Unit)
fun KSNode.validate(predicate: (KSNode?, KSNode) -> Boolean = { _, _-> true } ): Boolean {
return this.accept(KSValidateVisitor(predicate), null)
}

/**
Expand Down
Expand Up @@ -3,77 +3,88 @@ package com.google.devtools.ksp.visitor
import com.google.devtools.ksp.ExceptionMessage
import com.google.devtools.ksp.symbol.*

class KSValidateVisitor(private val predicate: (KSNode, KSNode) -> Boolean) : KSDefaultVisitor<Unit, Boolean>() {
private fun validateDeclarations(declarationContainer: KSDeclarationContainer): Boolean {
return !declarationContainer.declarations.any { predicate(declarationContainer, it) && !it.accept(this, Unit) }
class KSValidateVisitor(private val predicate: (KSNode?, KSNode) -> Boolean) : KSDefaultVisitor<KSNode?, Boolean>() {
private fun validateType(type: KSType): Boolean {
return !type.isError && !type.arguments.any { it.type?.accept(this, null) == false }
}

private fun validateTypeParameters(declaration: KSDeclaration): Boolean {
return !declaration.typeParameters.any { predicate(declaration, it) && !it.accept(this, Unit) }
override fun defaultHandler(node: KSNode, data: KSNode?): Boolean {
return true
}

private fun validateType(type: KSType): Boolean {
return !type.isError && !type.arguments.any { it.type?.accept(this, Unit) == false }
override fun visitDeclaration(declaration: KSDeclaration, data: KSNode?): Boolean {
if (!predicate(data, declaration)) {
return true
}
if (declaration.typeParameters.any { !it.accept(this, declaration) }) {
return false
}
return this.visitAnnotated(declaration, data)
}

override fun visitDeclarationContainer(declarationContainer: KSDeclarationContainer, data: KSNode?): Boolean {
return !predicate(data, declarationContainer) || declarationContainer.declarations.all { it.accept(this, declarationContainer) }
}

override fun visitTypeParameter(typeParameter: KSTypeParameter, data: KSNode?): Boolean {
return !predicate(data, typeParameter) || typeParameter.bounds.all{ it.accept(this, typeParameter) }
}

override fun defaultHandler(node: KSNode, data: Unit): Boolean {
throw IllegalStateException("unhandled validation condition, $ExceptionMessage")
override fun visitAnnotated(annotated: KSAnnotated, data: KSNode?): Boolean {
return !predicate(data, annotated) || annotated.annotations.all{ it.accept(this, annotated) }
}

override fun visitTypeReference(typeReference: KSTypeReference, data: Unit): Boolean {
override fun visitAnnotation(annotation: KSAnnotation, data: KSNode?): Boolean {
return !predicate(data, annotation) || annotation.annotationType.accept(this, annotation)
}

override fun visitTypeReference(typeReference: KSTypeReference, data: KSNode?): Boolean {
return validateType(typeReference.resolve())
}

override fun visitClassDeclaration(classDeclaration: KSClassDeclaration, data: Unit): Boolean {
if (!validateTypeParameters(classDeclaration)) {
override fun visitClassDeclaration(classDeclaration: KSClassDeclaration, data: KSNode?): Boolean {
if (classDeclaration.asStarProjectedType().isError) {
return false
}
if (classDeclaration.asStarProjectedType().isError) {
if (!classDeclaration.superTypes.all{ it.accept(this, classDeclaration) }) {
return false
}
if (classDeclaration.superTypes.any { predicate(classDeclaration, it) && !it.accept(this, Unit) }) {
if (!this.visitDeclaration(classDeclaration, data)) {
return false
}
if (!validateDeclarations(classDeclaration)) {
if (!this.visitDeclarationContainer(classDeclaration, data)) {
return false
}
return true
}

override fun visitFunctionDeclaration(function: KSFunctionDeclaration, data: Unit): Boolean {
override fun visitFunctionDeclaration(function: KSFunctionDeclaration, data: KSNode?): Boolean {
if (function.returnType != null && !(predicate(function, function.returnType!!) && function.returnType!!.accept(this, data))) {
return false
}
if (function.parameters.any { predicate(function, it) && !it.accept(this, Unit)}) {
if (!function.parameters.all{ it.accept(this, function) }) {
return false
}
if (!validateTypeParameters(function)) {
if (!this.visitDeclaration(function, data)) {
return false
}
if (!validateDeclarations(function)) {
if (!this.visitDeclarationContainer(function, data)) {
return false
}
return true
}

override fun visitPropertyDeclaration(property: KSPropertyDeclaration, data: Unit): Boolean {
override fun visitPropertyDeclaration(property: KSPropertyDeclaration, data: KSNode?): Boolean {
if (predicate(property, property.type) && property.type.resolve().isError) {
return false
}
if (!validateTypeParameters(property)) {
return false
}
return true
}

override fun visitTypeParameter(typeParameter: KSTypeParameter, data: Unit): Boolean {
if (typeParameter.bounds.any { predicate(typeParameter, it) && !it.accept(this, Unit) }) {
if (!this.visitDeclaration(property, data)) {
return false
}
return true
}

override fun visitValueParameter(valueParameter: KSValueParameter, data: Unit): Boolean {
return valueParameter.type?.accept(this, Unit) != false
override fun visitValueParameter(valueParameter: KSValueParameter, data: KSNode?): Boolean {
return valueParameter.type.accept(this, valueParameter)
}
}
Expand Up @@ -22,10 +22,12 @@ class ValidateProcessor : AbstractTestProcessor() {
val GoodClass = resolver.getClassDeclarationByName("GoodClass")!!
val C = resolver.getClassDeclarationByName("C")!!
val BadJavaClass = resolver.getClassDeclarationByName("BadJavaClass")!!
val ErrorAnnotationType = resolver.getClassDeclarationByName("ErrorAnnotationType")!!
validate(ErrorInMember)
ErrorInMember.declarations.map { validate(it) }
validate(GoodClass)
validate(C)
validate(BadJavaClass)
validate(ErrorAnnotationType)
}
}
9 changes: 9 additions & 0 deletions compiler-plugin/testData/api/validateTypes.kt
Expand Up @@ -7,15 +7,24 @@
// GoodClass valid
// C valid
// BadJavaClass invalid
// ErrorAnnotationType invalid
// END
// FILE: a.kt
annotation class Anno(val i: Int)

@Anno(1)
class ErrorInMember : C {
val goodProp: Int
fun errorFun(): NonExistType {

}
}

@NonExistAnnotation
class ErrorAnnotationType {
}

@Anno(1)
open class GoodClass {
val a: Int

Expand Down

0 comments on commit 96a7e1f

Please sign in to comment.