Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bring kategory-annotations to the kategory repo #259

Merged
merged 1 commit into from
Sep 8, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 14 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,8 @@ buildscript {
gradleVersionsPluginVersion = '0.15.0'
javaVersion = JavaVersion.VERSION_1_7
kotlinTestVersion = '2.0.5'
kotlinVersion = '1.1.4'
kotlinVersion = '1.1.4-3'
kotlinxCoroutinesVersion = '0.18'
kategoryAnnotationsVersion = '0.3.7'
kotlinxCollectionsImmutableVersion = '0.1'
}

Expand Down Expand Up @@ -98,6 +97,19 @@ subprojects { project ->
vcsUrl = 'https://github.com/kategory/kategory.git'
}
}

compileKotlin.kotlinOptions.freeCompilerArgs += ["-Xskip-runtime-version-check"]

test {
testLogging {
events "passed", "skipped", "failed", "standardOut", "standardError"
}
}

build.dependsOn ':detekt'

sourceCompatibility = javaVersion
targetCompatibility = javaVersion
}

task wrapper(type: Wrapper) {
Expand Down
13 changes: 10 additions & 3 deletions detekt.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ potential-bugs:
active: false
UnsafeCast:
active: false
UnsafeCallOnNullableType:
active: false

exceptions:
active: true
Expand Down Expand Up @@ -78,7 +80,7 @@ formatting:
active: true
autoCorrect: true
OptionalUnit:
active: true
active: false
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ideally we'd need the opposite option: flagging places where Unit isn't there. For us explicitness is key and we've been bitten by returns that should be Unit instead before.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, If detekt rules can be user created we can also provide some to encourage a more functional style.

autoCorrect: false
ExpressionBodySyntax:
active: true
Expand All @@ -87,6 +89,11 @@ formatting:
active: false
autoCorrect: false

performance:
SpreadOperator:
active: false
autoCorrect: false

style:
active: true
WildcardImport:
Expand All @@ -100,9 +107,9 @@ style:
comments:
active: true
CommentOverPrivateMethod:
active: true
active: false
CommentOverPrivateProperty:
active: true
active: false
UndocumentedPublicClass:
active: false
UndocumentedPublicFunction:
Expand Down
1 change: 1 addition & 0 deletions kategory-annotations-processor/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/build
20 changes: 20 additions & 0 deletions kategory-annotations-processor/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import org.gradle.internal.jvm.Jvm

apply plugin: 'kotlin'
apply plugin: 'kotlin-kapt'

dependencies {
compile "org.jetbrains.kotlin:kotlin-stdlib-jre8:$kotlinVersion"
compile project(':kategory-annotations')
compile 'com.squareup:kotlinpoet:0.4.0'
compile 'me.eugeniomarletti:kotlin-metadata:1.1.0'
compileOnly 'com.google.auto.service:auto-service:1.0-rc3'
kapt 'com.google.auto.service:auto-service:1.0-rc3'

testCompile group: 'junit', name: 'junit', version: '4.12'
testCompile "com.google.testing.compile:compile-testing:0.6"
testCompile fileTree(dir: './src/test/libs', includes: ['*.jar'])
testCompile files(Jvm.current().getToolsJar())
}

apply from: rootProject.file('gradle/gradle-mvn-push.gradle')
4 changes: 4 additions & 0 deletions kategory-annotations-processor/gradle.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Maven publishing configuration
POM_NAME=Kategory Annotations Compile Time
POM_ARTIFACT_ID=kategory-annotations-processor
POM_PACKAGING=jar
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package kategory.common

typealias Type = String
typealias Package = String
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package kategory.common.messager

import me.eugeniomarletti.kotlin.processing.KotlinProcessingUtils
import javax.lang.model.element.AnnotationMirror
import javax.lang.model.element.AnnotationValue
import javax.lang.model.element.Element
import javax.tools.Diagnostic.Kind.ERROR
import javax.tools.Diagnostic.Kind.MANDATORY_WARNING
import javax.tools.Diagnostic.Kind.NOTE
import javax.tools.Diagnostic.Kind.WARNING

fun KotlinProcessingUtils.log(message: CharSequence,
element: Element? = null,
annotationMirror: AnnotationMirror? = null,
annotationValue: AnnotationValue? = null
) = messager.printMessage(NOTE, message, element, annotationMirror, annotationValue)

fun KotlinProcessingUtils.logW(message: CharSequence,
element: Element? = null,
annotationMirror: AnnotationMirror? = null,
annotationValue: AnnotationValue? = null
) = messager.printMessage(WARNING, message, element, annotationMirror, annotationValue)

fun KotlinProcessingUtils.logMW(message: CharSequence,
element: Element? = null,
annotationMirror: AnnotationMirror? = null,
annotationValue: AnnotationValue? = null
) = messager.printMessage(MANDATORY_WARNING, message, element, annotationMirror, annotationValue)

fun KotlinProcessingUtils.logE(message: CharSequence,
element: Element? = null,
annotationMirror: AnnotationMirror? = null,
annotationValue: AnnotationValue? = null
) = messager.printMessage(ERROR, message, element, annotationMirror, annotationValue)
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package kategory.common.utils

import kategory.common.messager.logE
import me.eugeniomarletti.kotlin.processing.KotlinAbstractProcessor
import javax.annotation.processing.RoundEnvironment
import javax.lang.model.element.Element
import javax.lang.model.element.TypeElement
import me.eugeniomarletti.kotlin.metadata.kaptGeneratedOption
import java.io.File

class KnownException(message: String, val element: Element?) : RuntimeException(message) {
override val message: String get() = super.message as String
operator fun component1() = message
operator fun component2() = element
}

abstract class AbstractProcessor : KotlinAbstractProcessor(), ProcessorUtils {

val generatedDir: File? get() = options[kaptGeneratedOption]?.let(::File)

override final fun process(annotations: Set<TypeElement>, roundEnv: RoundEnvironment): Boolean {
if (!roundEnv.errorRaised()) {
try {
onProcess(annotations, roundEnv)
}
catch (e: KnownException) {
logE(e.message, e.element)
}
}
return false
}

protected abstract fun onProcess(annotations: Set<TypeElement>, roundEnv: RoundEnvironment)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package kategory.common.utils

import org.jetbrains.kotlin.serialization.ClassData
import org.jetbrains.kotlin.serialization.PackageData
import org.jetbrains.kotlin.serialization.ProtoBuf.Constructor
import org.jetbrains.kotlin.serialization.ProtoBuf.Function
import org.jetbrains.kotlin.serialization.ProtoBuf.Property
import org.jetbrains.kotlin.serialization.ProtoBuf.TypeParameter
import org.jetbrains.kotlin.serialization.deserialization.NameResolver

sealed class ClassOrPackageDataWrapper {
abstract val `package`: String
abstract val nameResolver: NameResolver
abstract val constructorList: List<Constructor>
abstract val functionList: List<Function>
abstract val propertyList: List<Property>
abstract val typeParameters: List<TypeParameter>
abstract fun getTypeParameter(typeParameterIndex: Int): TypeParameter?

class Package(
override val nameResolver: NameResolver,
val packageProto: org.jetbrains.kotlin.serialization.ProtoBuf.Package,
override val `package`: String
) : ClassOrPackageDataWrapper() {
override val constructorList: List<Constructor> get() = emptyList()
override val functionList: List<Function> get() = packageProto.functionList
override val propertyList: List<Property> get() = packageProto.propertyList
override val typeParameters: List<TypeParameter> = emptyList()
override fun getTypeParameter(typeParameterIndex: Int): TypeParameter? = null
}

class Class(
override val nameResolver: NameResolver,
val classProto: org.jetbrains.kotlin.serialization.ProtoBuf.Class,
override val `package`: String
) : ClassOrPackageDataWrapper() {
override val constructorList: List<Constructor> get() = classProto.constructorList
override val functionList: List<Function> get() = classProto.functionList
override val propertyList: List<Property> get() = classProto.propertyList
override val typeParameters: List<TypeParameter> = classProto.typeParameterList
override fun getTypeParameter(typeParameterIndex: Int): TypeParameter? = classProto.getTypeParameter(typeParameterIndex)
}
}

fun ClassData.asClassOrPackageDataWrapper(`package`: String) =
ClassOrPackageDataWrapper.Class(nameResolver, classProto, `package`)

fun PackageData.asClassOrPackageDataWrapper(`package`: String) =
ClassOrPackageDataWrapper.Package(nameResolver, packageProto, `package`)
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package kategory.common.utils

import kategory.implicits.implicitAnnotationName
import me.eugeniomarletti.kotlin.metadata.KotlinClassMetadata
import me.eugeniomarletti.kotlin.metadata.KotlinMetadata
import me.eugeniomarletti.kotlin.metadata.KotlinMetadataUtils
import me.eugeniomarletti.kotlin.metadata.KotlinPackageMetadata
import me.eugeniomarletti.kotlin.metadata.getJvmMethodSignature
import me.eugeniomarletti.kotlin.metadata.kotlinMetadata
import me.eugeniomarletti.kotlin.metadata.kotlinPropertyAnnotationsFunPostfix
import org.jetbrains.kotlin.serialization.ProtoBuf
import javax.lang.model.element.Element
import javax.lang.model.element.ExecutableElement
import javax.lang.model.element.TypeElement
import javax.lang.model.element.VariableElement

interface ProcessorUtils : KotlinMetadataUtils {

fun KotlinMetadata.asClassOrPackageDataWrapper(classElement: TypeElement): ClassOrPackageDataWrapper? {
val `package` = elementUtils.getPackageOf(classElement).toString()
return when (this) {
is KotlinClassMetadata -> data.asClassOrPackageDataWrapper(`package`)
is KotlinPackageMetadata -> data.asClassOrPackageDataWrapper(`package`)
else -> null
}
}

fun getClassOrPackageDataWrapper(classElement: TypeElement): ClassOrPackageDataWrapper {
val metadata = classElement.kotlinMetadata ?: knownError("These annotations can only be used in Kotlin")
return metadata.asClassOrPackageDataWrapper(classElement) ?: knownError("This annotation can't be used on this element")
}

fun ClassOrPackageDataWrapper.getFunction(methodElement: ExecutableElement) =
methodElement.jvmMethodSignature.let { methodSignature ->
functionList
.firstOrNull { methodSignature == it.getJvmMethodSignature(nameResolver) }
?: knownError("Can't find annotated method $methodSignature")
}
}

fun knownError(message: String, element: Element? = null): Nothing = throw KnownException(message, element)

fun String.plusIfNotBlank(
postfix: String = "",
prefix: String = ""
) = if (this.isNotBlank()) prefix + this + postfix else this

val String.escapedClassName
get() = split('/', '.').joinToString("`.`").plusIfNotBlank(prefix = "`", postfix = "`")

val ProtoBuf.Class.Kind.isCompanionOrObject get() = when (this) {
ProtoBuf.Class.Kind.OBJECT,
ProtoBuf.Class.Kind.COMPANION_OBJECT -> true
else -> false
}

fun ClassOrPackageDataWrapper.getParameter(function: ProtoBuf.Function, parameterElement: VariableElement) =
parameterElement.simpleName.toString().let { parameterName ->
function.valueParameterList
.firstOrNull { parameterName == nameResolver.getString(it.name) }
?: knownError("Can't find annotated parameter $parameterName in ${function.getJvmMethodSignature(nameResolver)}")
}

fun ClassOrPackageDataWrapper.getPropertyOrNull(methodElement: ExecutableElement) =
methodElement.simpleName.toString()
.takeIf { it.endsWith(kotlinPropertyAnnotationsFunPostfix) }
?.substringBefore(kotlinPropertyAnnotationsFunPostfix)
?.let { propertyName -> propertyList.firstOrNull { propertyName == nameResolver.getString(it.name) } }

fun ProtoBuf.Type.extractFullName(
classData: ClassOrPackageDataWrapper,
outputTypeAlias: Boolean = true,
failOnGeneric: Boolean = true
): String {
val nameResolver = classData.nameResolver

if (failOnGeneric && !hasClassName()) knownError("Generic $implicitAnnotationName types are not yet supported")

val name = when {
hasTypeParameter() -> classData.getTypeParameter(typeParameter)!!.name
hasTypeParameterName() -> typeParameterName
outputTypeAlias && hasAbbreviatedType() -> abbreviatedType.typeAliasName
else -> className
}.let { nameResolver.getString(it).escapedClassName }

val argumentList = if (outputTypeAlias && hasAbbreviatedType()) abbreviatedType.argumentList else argumentList
val arguments = argumentList
.takeIf { it.isNotEmpty() }
?.joinToString(prefix = "<", postfix = ">") {
when {
it.hasType() -> it.type.extractFullName(classData, outputTypeAlias, failOnGeneric)
!failOnGeneric -> "*"
else -> knownError("Wildcard $implicitAnnotationName types are not yet supported")
}
}
?: ""

val nullability = if (nullable) "?" else ""

return name + arguments + nullability
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package kategory.derive

import kategory.common.utils.ClassOrPackageDataWrapper
import javax.lang.model.element.TypeElement

class AnnotatedDeriving(
val classElement: TypeElement,
val classOrPackageProto: ClassOrPackageDataWrapper,
val companionClassProto: ClassOrPackageDataWrapper,
val derivingTypeclasses: List<ClassOrPackageDataWrapper>,
val typeclassSuperTypes: Map<ClassOrPackageDataWrapper.Class, List<ClassOrPackageDataWrapper>>)
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package kategory.derive

import kategory.deriving

val derivingAnnotationKClass = deriving::class
val derivingAnnotationClass = derivingAnnotationKClass.java
val derivingAnnotationName = "@" + derivingAnnotationClass.simpleName
Loading