diff --git a/CHANGELOG.md b/CHANGELOG.md index cc13e777..11af69a0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,21 @@ +# 0.4.0-alpha06 +* Add support for Kotlin 2.1.0 + +# 0.4.0-alpha05 +* Set JvmVersion to 1.8 + +# 0.4.0-alpha04 +* Update Dagger version + +# 0.4.0-alpha03 +* Update XProcessing version + +# 0.4.0-alpha02 +* Upgrade JavaPoet, KotlinPoet, & XProcessing + +# 0.4.0-alpha01 +* Initial support for KSP + # 0.3.8 * Throw CannotResolveType error when compiler cannot resolve it. diff --git a/ast/build.gradle b/ast/build.gradle index 800c2361..b1f92cb5 100644 --- a/ast/build.gradle +++ b/ast/build.gradle @@ -1,12 +1,8 @@ plugins { id 'org.jetbrains.kotlin.jvm' - id 'org.jetbrains.dokka' + id 'com.vanniktech.maven.publish' } -apply plugin: 'kotlin-kapt' - -sourceCompatibility = 1.8 - dependencies { implementation deps.kotlin.stdlib implementation deps.dagger @@ -14,5 +10,3 @@ dependencies { testImplementation deps.test.junit testImplementation deps.test.truth } - -apply plugin: 'com.vanniktech.maven.publish' diff --git a/ast/src/main/kotlin/motif/ast/IrAnnotated.kt b/ast/src/main/kotlin/motif/ast/IrAnnotated.kt index 436077fd..e05ba649 100644 --- a/ast/src/main/kotlin/motif/ast/IrAnnotated.kt +++ b/ast/src/main/kotlin/motif/ast/IrAnnotated.kt @@ -21,11 +21,8 @@ interface IrAnnotated { val annotations: List - fun hasAnnotation(annotationClass: KClass): Boolean { - return annotations.any { it.matchesClass(annotationClass) } - } + fun hasAnnotation(annotationClass: KClass): Boolean = + annotations.any { it.matchesClass(annotationClass) } - fun isNullable(): Boolean { - return annotations.any { it.className?.endsWith("Nullable") == true } - } + fun isNullable(): Boolean = annotations.any { it.className?.endsWith("Nullable") == true } } diff --git a/ast/src/main/kotlin/motif/ast/IrAnnotation.kt b/ast/src/main/kotlin/motif/ast/IrAnnotation.kt index acb46285..934869d4 100644 --- a/ast/src/main/kotlin/motif/ast/IrAnnotation.kt +++ b/ast/src/main/kotlin/motif/ast/IrAnnotation.kt @@ -25,5 +25,7 @@ interface IrAnnotation : IrEquivalence { val members: List + val annotationValueMap: Map + fun matchesClass(annotationClass: KClass): Boolean } diff --git a/ast/src/main/kotlin/motif/ast/IrClass.kt b/ast/src/main/kotlin/motif/ast/IrClass.kt index cb2f8c62..815a3e4a 100644 --- a/ast/src/main/kotlin/motif/ast/IrClass.kt +++ b/ast/src/main/kotlin/motif/ast/IrClass.kt @@ -34,16 +34,13 @@ interface IrClass : IrAnnotated, IrHasModifiers { val simpleName: String get() = type.simpleName - fun hasNonDefaultConstructor(): Boolean { - return constructors.any { it.hasParameters() } - } + fun hasNonDefaultConstructor(): Boolean = constructors.any { it.hasParameters() } - fun annotatedInnerClass(annotationClass: KClass): IrClass? { - return nestedClasses.find { it.hasAnnotation(annotationClass) } - } + fun annotatedInnerClass(annotationClass: KClass): IrClass? = + nestedClasses.find { it.hasAnnotation(annotationClass) } enum class Kind { CLASS, - INTERFACE + INTERFACE, } } diff --git a/ast/src/main/kotlin/motif/ast/IrHasModifiers.kt b/ast/src/main/kotlin/motif/ast/IrHasModifiers.kt index c8edf7c4..efa54737 100644 --- a/ast/src/main/kotlin/motif/ast/IrHasModifiers.kt +++ b/ast/src/main/kotlin/motif/ast/IrHasModifiers.kt @@ -19,19 +19,11 @@ interface IrHasModifiers { val modifiers: Set - fun isStatic(): Boolean { - return IrModifier.STATIC in modifiers - } + fun isStatic(): Boolean = IrModifier.STATIC in modifiers - fun isPrivate(): Boolean { - return IrModifier.PRIVATE in modifiers - } + fun isPrivate(): Boolean = IrModifier.PRIVATE in modifiers - fun isPublic(): Boolean { - return IrModifier.PUBLIC in modifiers - } + fun isPublic(): Boolean = IrModifier.PUBLIC in modifiers - fun isAbstract(): Boolean { - return IrModifier.ABSTRACT in modifiers - } + fun isAbstract(): Boolean = IrModifier.ABSTRACT in modifiers } diff --git a/ast/src/main/kotlin/motif/ast/IrMethod.kt b/ast/src/main/kotlin/motif/ast/IrMethod.kt index 0b6190b0..c782493c 100644 --- a/ast/src/main/kotlin/motif/ast/IrMethod.kt +++ b/ast/src/main/kotlin/motif/ast/IrMethod.kt @@ -21,11 +21,7 @@ interface IrMethod : IrAnnotated, IrHasModifiers { val name: String val isConstructor: Boolean - fun hasParameters(): Boolean { - return parameters.isNotEmpty() - } + fun hasParameters(): Boolean = parameters.isNotEmpty() - fun isVoid(): Boolean { - return returnType.isVoid - } + fun isVoid(): Boolean = returnType.isVoid } diff --git a/ast/src/main/kotlin/motif/ast/IrModifier.kt b/ast/src/main/kotlin/motif/ast/IrModifier.kt index 2c53089d..021d1553 100644 --- a/ast/src/main/kotlin/motif/ast/IrModifier.kt +++ b/ast/src/main/kotlin/motif/ast/IrModifier.kt @@ -30,5 +30,5 @@ enum class IrModifier { VOLATILE, DEFAULT, OPEN, - TRANSITIVE + TRANSITIVE, } diff --git a/ast/src/main/kotlin/motif/ast/IrType.kt b/ast/src/main/kotlin/motif/ast/IrType.kt index 81d3af18..ed10d798 100644 --- a/ast/src/main/kotlin/motif/ast/IrType.kt +++ b/ast/src/main/kotlin/motif/ast/IrType.kt @@ -25,6 +25,7 @@ interface IrType : IrEquivalence { get() = simpleName(qualifiedName) fun resolveClass(): IrClass? + fun isAssignableTo(type: IrType): Boolean } diff --git a/ast/src/test/kotlin/motif/ast/SimpleNameTest.kt b/ast/src/test/kotlin/motif/ast/SimpleNameTest.kt index 8345554b..675ff38d 100644 --- a/ast/src/test/kotlin/motif/ast/SimpleNameTest.kt +++ b/ast/src/test/kotlin/motif/ast/SimpleNameTest.kt @@ -33,13 +33,12 @@ class SimpleNameTest(private val qualifiedName: String, private val expectedSimp "java.util.List" to "List", "java.util.Map" to "Map", "java.util.Map" to - "Map") + "Map", + ) @JvmStatic @Parameterized.Parameters(name = "{0}") - fun data(): Collection> { - return tests.map { (key, value) -> arrayOf(key, value) } - } + fun data(): Collection> = tests.map { (key, value) -> arrayOf(key, value) } } @Test diff --git a/build.gradle b/build.gradle index 955d67ff..3b03f127 100644 --- a/build.gradle +++ b/build.gradle @@ -1,3 +1,5 @@ +import org.jetbrains.dokka.gradle.DokkaTaskPartial + // https://github.com/gradle/gradle/issues/4848 gradle.startParameter.configureOnDemand = false @@ -11,6 +13,7 @@ buildscript { } dependencies { classpath deps.build.gradlePlugins.android + classpath deps.build.gradlePlugins.intellij classpath deps.build.gradlePlugins.kotlin classpath deps.build.gradlePlugins.ksp classpath deps.build.gradlePlugins.dokka @@ -39,6 +42,12 @@ subprojects { useTarget(deps.autoCommon) } else if (requested.group == "org.jetbrains.kotlinx" && requested.name == "kotlinx-metadata") { useTarget(deps.kotlinxMetadata) + } else if (requested.group == "com.google.devtools.ksp") { + useVersion(deps.versions.ksp) + } else if (requested.group == "androidx.room") { + if (requested.name != "room-compiler-processing" && requested.name != "room-compiler-processing-testing") { + useVersion(deps.versions.room) + } } } } @@ -56,33 +65,65 @@ subprojects { } boolean isKotlinLibrary = project.plugins.hasPlugin("org.jetbrains.kotlin.jvm") || project.plugins.hasPlugin("org.jetbrains.kotlin.android") if (isKotlinLibrary) { + java { + toolchain.languageVersion.set(JavaLanguageVersion.of(11)) + } + kotlin { + jvmToolchain(11) + } it.tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).configureEach { kotlinOptions { freeCompilerArgs += extraKotlincArgs } } } + + boolean isAndroidLibraryOrApp = project.plugins.hasPlugin("org.jetbrains.kotlin.android") + if (isAndroidLibraryOrApp) { + android { + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + } + } + + if (isKotlinLibrary && isAndroidLibraryOrApp) { + android { + kotlinOptions { + jvmTarget = "1.8" + } + } + } } if (project.path != ":tests") { apply plugin: 'com.diffplug.spotless' spotless { format 'misc', { - target '**/*.md', '**/.gitignore' + target '*.md', '.gitignore' trimTrailingWhitespace() endWithNewline() } kotlin { - target "**/*.kt" - ktlint(deps.versions.ktlint).userData(['indent_size': '2', 'continuation_indent_size': '2']) + target "src/**/*.kt" + ktlint(deps.versions.ktlint).editorConfigOverride([ + "indent_size": "2", + "continuation_indent_size": "4" + ]) + suppressLintsFor { + step = 'ktlint' + shortCode = 'standard:function-naming' + } ktfmt(deps.versions.ktfmt) licenseHeaderFile rootProject.file('config/spotless/copyright.kt') trimTrailingWhitespace() endWithNewline() } java { - target "**/*.java" + target "src/**/*.java" googleJavaFormat(deps.versions.gjf) licenseHeaderFile rootProject.file('config/spotless/copyright.java') removeUnusedImports() @@ -90,10 +131,19 @@ subprojects { endWithNewline() } groovyGradle { - target '**/*.gradle' + target '*.gradle' trimTrailingWhitespace() endWithNewline() } } } + + apply plugin: 'org.jetbrains.dokka' + if (!project.name.contains("samples") && !project.name.contains("tests")) { + tasks.withType(DokkaTaskPartial).configureEach { + outputDirectory.set(new File(buildDir, "docs/partial")) + moduleName.set(project.property("POM_ARTIFACT_ID").toString()) + moduleVersion.set(project.property("VERSION_NAME").toString()) + } + } } diff --git a/compiler/ast/build.gradle b/compiler/ast/build.gradle index df0669ad..dcf40949 100644 --- a/compiler/ast/build.gradle +++ b/compiler/ast/build.gradle @@ -1,13 +1,8 @@ plugins { id 'org.jetbrains.kotlin.jvm' - id 'org.jetbrains.dokka' + id 'com.vanniktech.maven.publish' } -apply plugin: 'kotlin-kapt' - -sourceCompatibility = 1.8 - - dependencies { implementation deps.kotlin.stdlib implementation deps.autoCommon @@ -22,5 +17,3 @@ dependencies { testImplementation deps.test.compileTesting testImplementation deps.test.roomCompilerProcessingTesting } - -apply plugin: 'com.vanniktech.maven.publish' diff --git a/compiler/ast/src/main/kotlin/com/uber/xprocessing/ext/XAnnotation.kt b/compiler/ast/src/main/kotlin/com/uber/xprocessing/ext/XAnnotation.kt index 04dbf2a2..19982c74 100644 --- a/compiler/ast/src/main/kotlin/com/uber/xprocessing/ext/XAnnotation.kt +++ b/compiler/ast/src/main/kotlin/com/uber/xprocessing/ext/XAnnotation.kt @@ -28,25 +28,23 @@ import com.google.auto.common.AnnotationMirrors * Used to find equivalence of two XAnnotation since AnnotationMirrors.equivalence() only applies to * the Javac backend. */ -fun XAnnotation.isEquivalent(other: XAnnotation, env: XProcessingEnv): Boolean { - return if (env.backend == XProcessingEnv.Backend.JAVAC) { - val key = AnnotationMirrors.equivalence().wrap(this.toJavac()) - val otherKey = AnnotationMirrors.equivalence().wrap(other.toJavac()) - key == otherKey - } else { - (type.isEquivalent(other.type, env) && - annotationValues.size == other.annotationValues.size && - annotationValues.zip(other.annotationValues).all { (lhs, rhs) -> lhs.isEquivalent(rhs) }) - } -} +fun XAnnotation.isEquivalent(other: XAnnotation, env: XProcessingEnv): Boolean = + if (env.backend == XProcessingEnv.Backend.JAVAC) { + val key = AnnotationMirrors.equivalence().wrap(this.toJavac()) + val otherKey = AnnotationMirrors.equivalence().wrap(other.toJavac()) + key == otherKey + } else { + (type.isEquivalent(other.type, env) && + annotationValues.size == other.annotationValues.size && + annotationValues.zip(other.annotationValues).all { (lhs, rhs) -> lhs.isEquivalent(rhs) }) + } /** * Used to find equivalence of two XAnnotation since AnnotationMirrors.equivalence() only applies to * the Javac backend. */ -fun XAnnotationValue.isEquivalent(other: XAnnotationValue): Boolean { - return this.name == other.name && this.value == other.value -} +fun XAnnotationValue.isEquivalent(other: XAnnotationValue): Boolean = + this.name == other.name && this.value == other.value /** Cleans up differences in the toString() methods between the JAVAC and KSP backends. */ fun XAnnotation.toPrettyString(): String { @@ -55,7 +53,9 @@ fun XAnnotation.toPrettyString(): String { .map { if (it.name == "value") { "\"${it.value}\"" - } else "${it.name} = ${it.value}" + } else { + "${it.name} = ${it.value}" + } } .joinToString(", ") val annotationValuesList = diff --git a/compiler/ast/src/main/kotlin/com/uber/xprocessing/ext/XElement.kt b/compiler/ast/src/main/kotlin/com/uber/xprocessing/ext/XElement.kt index c9a5dbc6..e8e98677 100644 --- a/compiler/ast/src/main/kotlin/com/uber/xprocessing/ext/XElement.kt +++ b/compiler/ast/src/main/kotlin/com/uber/xprocessing/ext/XElement.kt @@ -33,13 +33,10 @@ val XElement.modifiers: List if (isFinal()) modifiers += Modifier.FINAL if (isTransient()) modifiers += Modifier.TRANSIENT return@let modifiers - } - ?: emptyList() + } ?: emptyList() } val XElement.modifierNames: List get() = modifiers.map { it.name } -fun XHasModifiers.isPackagePrivate(): Boolean { - return !isPrivate() && !isProtected() && !isPublic() -} +fun XHasModifiers.isPackagePrivate(): Boolean = !isPrivate() && !isProtected() && !isPublic() diff --git a/compiler/ast/src/main/kotlin/com/uber/xprocessing/ext/XOverrides.kt b/compiler/ast/src/main/kotlin/com/uber/xprocessing/ext/XOverrides.kt index 799da720..bb7c9abd 100644 --- a/compiler/ast/src/main/kotlin/com/uber/xprocessing/ext/XOverrides.kt +++ b/compiler/ast/src/main/kotlin/com/uber/xprocessing/ext/XOverrides.kt @@ -44,7 +44,11 @@ object XOverrides { return when (env.backend) { XProcessingEnv.Backend.JAVAC -> { MoreElements.overrides( - overrider.toJavac(), overridden.toJavac(), inType.toJavac(), env.toJavac().typeUtils) + overrider.toJavac(), + overridden.toJavac(), + inType.toJavac(), + env.toJavac().typeUtils, + ) false } XProcessingEnv.Backend.KSP -> { @@ -105,8 +109,7 @@ object XOverrides { // one that declares the same method, and check that we haven't reached mA. We compare // the enclosing elements rather than the methods themselves for the reason described // at the start of the method. - var inherited: XMethodElement? = null - (inherited != null && overridden.enclosingElement != inherited.enclosingElement) + false } else if (overriddenType?.isInterface() == true) { // ...overrides from C another method mI declared in interface I. We've already checked // the conditions (assuming that the only alternative to mI being abstract or default is @@ -121,8 +124,7 @@ object XOverrides { // to `overriddenType` (or the several paths if there are several) and apply similar logic // to methodFromSuperclasses above. if (overrider.isAbstract()) { - var inherited: XMethodElement? = null - (inherited != null && overridden.enclosingElement != inherited.enclosingElement) + false } else { true } @@ -143,7 +145,8 @@ enum class Visibility { DEFAULT, PROTECTED, INTERNAL, - PUBLIC; + PUBLIC, + ; companion object { fun of(element: XMethodElement) = diff --git a/compiler/ast/src/main/kotlin/com/uber/xprocessing/ext/XProcessingEnv.kt b/compiler/ast/src/main/kotlin/com/uber/xprocessing/ext/XProcessingEnv.kt index fe56acd8..da74c5ab 100644 --- a/compiler/ast/src/main/kotlin/com/uber/xprocessing/ext/XProcessingEnv.kt +++ b/compiler/ast/src/main/kotlin/com/uber/xprocessing/ext/XProcessingEnv.kt @@ -25,14 +25,12 @@ val XProcessingEnv.typeUtils get() = XTypeUtils /** Provides access to KSP's resolver since XProcessing does not give us access */ -fun XProcessingEnv.resolver(): Resolver? { - return if (backend == XProcessingEnv.Backend.KSP) { - Class.forName("androidx.room.compiler.processing.ksp.KspProcessingEnv") - ?.getDeclaredField("_resolver") - ?.apply { isAccessible = true } - ?.get(this) as - Resolver? - } else { - null - } -} +fun XProcessingEnv.resolver(): Resolver? = + if (backend == XProcessingEnv.Backend.KSP) { + Class.forName("androidx.room.compiler.processing.ksp.KspProcessingEnv") + ?.getDeclaredField("_resolver") + ?.apply { isAccessible = true } + ?.get(this) as Resolver? + } else { + null + } diff --git a/compiler/ast/src/main/kotlin/com/uber/xprocessing/ext/XType.kt b/compiler/ast/src/main/kotlin/com/uber/xprocessing/ext/XType.kt index 261291b9..86a7044c 100644 --- a/compiler/ast/src/main/kotlin/com/uber/xprocessing/ext/XType.kt +++ b/compiler/ast/src/main/kotlin/com/uber/xprocessing/ext/XType.kt @@ -95,20 +95,18 @@ fun TypeName.containsWildcardType(): Boolean { } /** Returns a TypeName with any Wildcard types recursively removed. */ -fun TypeName.removeWildcardType(): TypeName { - return when (this) { - is WildcardTypeName -> this.upperBounds.first() ?: TypeName.OBJECT - is ParameterizedTypeName -> { - val args = this.typeArguments.map { it.removeWildcardType() }.toTypedArray() - ParameterizedTypeName.get(this.rawType, *args) +fun TypeName.removeWildcardType(): TypeName = + when (this) { + is WildcardTypeName -> this.upperBounds.first() ?: TypeName.OBJECT + is ParameterizedTypeName -> { + val args = this.typeArguments.map { it.removeWildcardType() }.toTypedArray() + ParameterizedTypeName.get(this.rawType, *args) + } + else -> this } - else -> this - } -} -fun TypeName.removeWildcardTypeIfContains(): TypeName { - return if (this.containsWildcardType()) this.removeWildcardType() else this -} +fun TypeName.removeWildcardTypeIfContains(): TypeName = + if (this.containsWildcardType()) this.removeWildcardType() else this /** * This adds extra handling to Java TypeNames so that when it is based on an XType that has type @@ -116,7 +114,7 @@ fun TypeName.removeWildcardTypeIfContains(): TypeName { * with Kotlin types. */ fun com.squareup.javapoet.TypeName.withRawTypeFix( - env: XProcessingEnv + env: XProcessingEnv, ): com.squareup.javapoet.TypeName { if (env.backend == XProcessingEnv.Backend.JAVAC) { return when (this) { @@ -128,8 +126,7 @@ fun com.squareup.javapoet.TypeName.withRawTypeFix( val mirror = env.findType(this.toString()) if (mirror != null && mirror.typeArguments.isNotEmpty() && "<" !in this.toString()) { val args = - mirror - .typeArguments + mirror.typeArguments .map { WildcardTypeName.subtypeOf(com.squareup.javapoet.TypeName.OBJECT) } .toTypedArray() ParameterizedTypeName.get(this, *args) @@ -147,42 +144,40 @@ fun com.squareup.javapoet.TypeName.withRawTypeFix( * Used to find equivalence of two XType since MoreTypes.equivalence() only applies to the Javac * backend. */ -fun XType.isEquivalent(other: XType, env: XProcessingEnv): Boolean { - return when (env.backend) { - XProcessingEnv.Backend.JAVAC -> { - val key = MoreTypes.equivalence().wrap(this.toJavac()) - val otherKey = MoreTypes.equivalence().wrap(other.toJavac()) - key == otherKey - } - XProcessingEnv.Backend.KSP -> { - if ("NonExistentClass" in typeName.toString() || - "NonExistentClass" in other.typeName.toString()) { - this.qualifiedName(env) == other.qualifiedName(env) - } else { - this.typeName.removeWildcardTypeIfContains() == - other.typeName.removeWildcardTypeIfContains() +fun XType.isEquivalent(other: XType, env: XProcessingEnv): Boolean = + when (env.backend) { + XProcessingEnv.Backend.JAVAC -> { + val key = MoreTypes.equivalence().wrap(this.toJavac()) + val otherKey = MoreTypes.equivalence().wrap(other.toJavac()) + key == otherKey + } + XProcessingEnv.Backend.KSP -> { + if ("NonExistentClass" in typeName.toString() || + "NonExistentClass" in other.typeName.toString()) { + this.qualifiedName(env) == other.qualifiedName(env) + } else { + this.typeName.removeWildcardTypeIfContains() == + other.typeName.removeWildcardTypeIfContains() + } } } - } -} /** * Used to find the hash of an XType since MoreTypes.equivalence().hashCode() only applies to the * Javac backend. */ -fun XType.hash(): Int { - return try { - MoreTypes.equivalence().wrap(this.toJavac()).hashCode() - } catch (t: Throwable) { - if (typeArguments.any { it.typeName is WildcardTypeName && it.typeName.toString() != "?" }) { - extendsBoundOrSelf().typeName.toString().hashCode() - } else if (this.hasCollectionType()) { - typeName.removeWildcardTypeIfContains().toString().hashCode() - } else { - hashCode() +fun XType.hash(): Int = + try { + MoreTypes.equivalence().wrap(this.toJavac()).hashCode() + } catch (t: Throwable) { + if (typeArguments.any { it.typeName is WildcardTypeName && it.typeName.toString() != "?" }) { + extendsBoundOrSelf().typeName.toString().hashCode() + } else if (this.hasCollectionType()) { + typeName.removeWildcardTypeIfContains().toString().hashCode() + } else { + hashCode() + } } - } -} fun XType.isInternal(): Boolean { val ksType = @@ -190,8 +185,7 @@ fun XType.isInternal(): Boolean { Class.forName("androidx.room.compiler.processing.ksp.KspType") ?.getDeclaredField("ksType") ?.apply { isAccessible = true } - ?.get(this) as? - KSType + ?.get(this) as? KSType } catch (throwable: Throwable) { null } @@ -206,15 +200,14 @@ fun KSType.isRaw(): Boolean { return toString().startsWith("raw ") } -fun XType.makeNonNullByDefault(): XType { - return try { - if (nullability == XNullability.UNKNOWN) makeNonNullable() else this - } catch (e: IllegalStateException) { - // Workaround for tests since we can't call XType#nullibility for types - // created with TypeMirror#toXProcessing(XProcessingEnv) - this - } -} +fun XType.makeNonNullByDefault(): XType = + try { + if (nullability == XNullability.UNKNOWN) makeNonNullable() else this + } catch (e: IllegalStateException) { + // Workaround for tests since we can't call XType#nullibility for types + // created with TypeMirror#toXProcessing(XProcessingEnv) + this + } @OptIn(KspExperimental::class) fun XType.mapToJavaType(env: XProcessingEnv): XType { @@ -225,12 +218,12 @@ fun XType.mapToJavaType(env: XProcessingEnv): XType { env.resolver() ?.getJavaClassByName(toKS().declaration.qualifiedName?.asString().orEmpty()) ?.toXProcessing(env) - ?.type - ?: return this + ?.type ?: return this } else { val rawTypeName = resolver.mapKotlinNameToJava( - typeElement?.type?.toKS()?.declaration?.qualifiedName?.asString().orEmpty()) + typeElement?.type?.toKS()?.declaration?.qualifiedName?.asString().orEmpty(), + ) val rawType = env.resolver()?.getJavaClassByName(rawTypeName)?.toXProcessing(env) ?: return this val types = @@ -238,7 +231,8 @@ fun XType.mapToJavaType(env: XProcessingEnv): XType { .map { val typeArgName = resolver.mapKotlinNameToJava( - it.toKS().declaration.qualifiedName?.asString().orEmpty()) + it.toKS().declaration.qualifiedName?.asString().orEmpty(), + ) env.resolver()?.getJavaClassByName(typeArgName)?.toXProcessing(env)?.type ?: return this } @@ -289,32 +283,25 @@ internal fun Resolver.mapJavaNameToKotlin(qualifiedName: String): String { return mapJavaNameToKotlin(ksName)?.asString() ?: qualifiedName } -fun XType.isDeclaredType(): Boolean { - return typeElement?.let { - when { - it.isClass() -> true - it.isInterface() -> true - it.isEnum() -> true - it.isAnnotationClass() -> true - it.isKotlinObject() -> true - else -> false - } - } - ?: false -} +fun XType.isDeclaredType(): Boolean = + typeElement?.let { + when { + it.isClass() -> true + it.isInterface() -> true + it.isEnum() -> true + it.isAnnotationClass() -> true + it.isKotlinObject() -> true + else -> false + } + } ?: false -fun XType.isEnum(): Boolean { - return typeElement?.let { it.isEnum() } ?: false -} +fun XType.isEnum(): Boolean = typeElement?.isEnum() ?: false -fun XType.isPrimitive(): Boolean { - return typeName.isPrimitive -} +fun XType.isPrimitive(): Boolean = typeName.isPrimitive -private fun XType.hasCollectionType(): Boolean { - return this.typeElement?.name.orEmpty() in collectionTypes || - typeArguments.any { it.hasCollectionType() } -} +private fun XType.hasCollectionType(): Boolean = + this.typeElement?.name.orEmpty() in collectionTypes || + typeArguments.any { it.hasCollectionType() } private val collectionTypes = setOf("MutableList", "MutableSet", "MutableCollection", "List", "Set", "Collection") diff --git a/compiler/ast/src/main/kotlin/com/uber/xprocessing/ext/XTypeElement.kt b/compiler/ast/src/main/kotlin/com/uber/xprocessing/ext/XTypeElement.kt index ee132563..bb2a28f2 100644 --- a/compiler/ast/src/main/kotlin/com/uber/xprocessing/ext/XTypeElement.kt +++ b/compiler/ast/src/main/kotlin/com/uber/xprocessing/ext/XTypeElement.kt @@ -35,7 +35,7 @@ import motif.ast.compiler.CompilerClass */ fun XTypeElement.getLocalAndInheritedMethods( env: XProcessingEnv, - useMoreElements: Boolean = true + useMoreElements: Boolean = true, ): List { val nonPrivateInstanceMethods = this.getAllNonPrivateInstanceMethods() @@ -71,14 +71,13 @@ fun XTypeElement.getLocalAndInheritedMethods( /** Return where or not this XTypeElement was from an XType defined in Kotlin */ @OptIn(KotlinPoetMetadataPreview::class) -fun XTypeElement?.isKotlinSource(env: XProcessingEnv): Boolean { - return when (env.backend) { - XProcessingEnv.Backend.JAVAC -> - try { - this?.toJavac()?.toKmClass() != null - } catch (e: Throwable) { - false - } - XProcessingEnv.Backend.KSP -> true - } -} +fun XTypeElement?.isKotlinSource(env: XProcessingEnv): Boolean = + when (env.backend) { + XProcessingEnv.Backend.JAVAC -> + try { + this?.toJavac()?.toKmClass() != null + } catch (e: Throwable) { + false + } + XProcessingEnv.Backend.KSP -> true + } diff --git a/compiler/ast/src/main/kotlin/motif/ast/compiler/CompilerAnnotation.kt b/compiler/ast/src/main/kotlin/motif/ast/compiler/CompilerAnnotation.kt index d6f15339..ff3f2c6b 100644 --- a/compiler/ast/src/main/kotlin/motif/ast/compiler/CompilerAnnotation.kt +++ b/compiler/ast/src/main/kotlin/motif/ast/compiler/CompilerAnnotation.kt @@ -37,20 +37,23 @@ class CompilerAnnotation(val env: XProcessingEnv, val mirror: XAnnotation) : IrA override val type: IrType = CompilerType(env, mirror.type) + override val annotationValueMap: Map + get() = mirror.annotationValues.associate { it.name to it.value } + override val members: List by lazy { val annotationMethods = mirror.type.typeElement?.getDeclaredMethods().orEmpty() mirror.annotationValues.map { annotationValue -> val executableElement = annotationMethods.firstOrNull { it.jvmName == annotationValue.name } ?: throw IllegalStateException( - "No matching annotations for ${annotationValue.name} in ${mirror.annotationValues.map { it.name }.joinToString(separator = ", ")}") + "No matching annotations for ${annotationValue.name} in ${mirror.annotationValues.map { it.name }.joinToString(separator = ", ")}", + ) CompilerMethod(env, mirror.type, executableElement.executableType, executableElement) } } - override fun matchesClass(annotationClass: KClass): Boolean { - return annotationClass.java.name == className - } + override fun matchesClass(annotationClass: KClass): Boolean = + annotationClass.java.name == className override fun equals(other: Any?): Boolean { if (this === other) return true @@ -63,11 +66,7 @@ class CompilerAnnotation(val env: XProcessingEnv, val mirror: XAnnotation) : IrA return true } - override fun hashCode(): Int { - return pretty.hashCode() - } + override fun hashCode(): Int = pretty.hashCode() - override fun toString(): String { - return pretty - } + override fun toString(): String = pretty } diff --git a/compiler/ast/src/main/kotlin/motif/ast/compiler/CompilerClass.kt b/compiler/ast/src/main/kotlin/motif/ast/compiler/CompilerClass.kt index 61a824b8..0f9ed2f5 100644 --- a/compiler/ast/src/main/kotlin/motif/ast/compiler/CompilerClass.kt +++ b/compiler/ast/src/main/kotlin/motif/ast/compiler/CompilerClass.kt @@ -90,7 +90,8 @@ class CompilerClass(override val env: XProcessingEnv, val declaredType: XType) : typeElement.getEnclosedTypeElements().map { typeElement -> if (typeElement.type.isError()) { throw IllegalStateException( - "Could not resolve type for nested class: ${typeElement.qualifiedName}") + "Could not resolve type for nested class: ${typeElement.qualifiedName}", + ) } CompilerClass(env, typeElement.type) } diff --git a/compiler/ast/src/main/kotlin/motif/ast/compiler/CompilerField.kt b/compiler/ast/src/main/kotlin/motif/ast/compiler/CompilerField.kt index 2d3e6e90..7a4fb55f 100644 --- a/compiler/ast/src/main/kotlin/motif/ast/compiler/CompilerField.kt +++ b/compiler/ast/src/main/kotlin/motif/ast/compiler/CompilerField.kt @@ -25,7 +25,7 @@ import motif.ast.IrType @OptIn(ExperimentalProcessingApi::class) class CompilerField( override val env: XProcessingEnv, - private val variableElement: XVariableElement + private val variableElement: XVariableElement, ) : IrUtil, IrField { override val type: IrType by lazy { CompilerType(env, variableElement.type) } diff --git a/compiler/ast/src/main/kotlin/motif/ast/compiler/CompilerMethod.kt b/compiler/ast/src/main/kotlin/motif/ast/compiler/CompilerMethod.kt index 7fa0af81..7aae4d64 100644 --- a/compiler/ast/src/main/kotlin/motif/ast/compiler/CompilerMethod.kt +++ b/compiler/ast/src/main/kotlin/motif/ast/compiler/CompilerMethod.kt @@ -38,7 +38,7 @@ class CompilerMethod( override val env: XProcessingEnv, val owner: XType, val type: XExecutableType, - val element: XExecutableElement + val element: XExecutableElement, ) : IrMethod, IrUtil { override val name: String = @@ -47,7 +47,8 @@ class CompilerMethod( is XConstructorElement -> "" else -> throw IllegalStateException( - "Could not find name for unknown XExecutableElement kind: ${element.kindName()}") + "Could not find name for unknown XExecutableElement kind: ${element.kindName()}", + ) } override val isConstructor: Boolean = element.isConstructor() @@ -75,19 +76,18 @@ class CompilerMethod( returnType } - override fun isVoid(): Boolean { - return when (env.backend) { - XProcessingEnv.Backend.JAVAC -> returnType.isVoid - XProcessingEnv.Backend.KSP -> { - val returnTypeMirror = (returnType as CompilerType).mirror - if (returnTypeMirror.isError()) { - element.toKS().returnType == null - } else { - returnTypeMirror.isVoid() + override fun isVoid(): Boolean = + when (env.backend) { + XProcessingEnv.Backend.JAVAC -> returnType.isVoid + XProcessingEnv.Backend.KSP -> { + val returnTypeMirror = (returnType as CompilerType).mirror + if (returnTypeMirror.isError()) { + element.toKS().returnType == null + } else { + returnTypeMirror.isVoid() + } } } - } - } val isSynthetic by lazy { env.backend == XProcessingEnv.Backend.KSP && "synthetic" in element.executableType.toString() diff --git a/compiler/ast/src/main/kotlin/motif/ast/compiler/CompilerMethodParameter.kt b/compiler/ast/src/main/kotlin/motif/ast/compiler/CompilerMethodParameter.kt index cef0db8b..541c4f88 100644 --- a/compiler/ast/src/main/kotlin/motif/ast/compiler/CompilerMethodParameter.kt +++ b/compiler/ast/src/main/kotlin/motif/ast/compiler/CompilerMethodParameter.kt @@ -27,7 +27,7 @@ import motif.ast.IrType class CompilerMethodParameter( override val env: XProcessingEnv, val element: XVariableElement, - val typeMirror: XType + val typeMirror: XType, ) : IrUtil, IrParameter { override val type: IrType by lazy { diff --git a/compiler/ast/src/main/kotlin/motif/ast/compiler/CompilerType.kt b/compiler/ast/src/main/kotlin/motif/ast/compiler/CompilerType.kt index 8245e55c..55503b44 100644 --- a/compiler/ast/src/main/kotlin/motif/ast/compiler/CompilerType.kt +++ b/compiler/ast/src/main/kotlin/motif/ast/compiler/CompilerType.kt @@ -37,9 +37,7 @@ class CompilerType(private val env: XProcessingEnv, mirror: XType) : IrType { val mirror = mirror.makeNonNullByDefault() - fun isInterface(): Boolean { - return IrClass.Kind.INTERFACE == resolveClass()?.kind - } + fun isInterface(): Boolean = IrClass.Kind.INTERFACE == resolveClass()?.kind override val qualifiedName: String by lazy { mirror.qualifiedName(env) } @@ -47,9 +45,8 @@ class CompilerType(private val env: XProcessingEnv, mirror: XType) : IrType { override val isPrimitive: Boolean by lazy { mirror.isPrimitive() } - override fun resolveClass(): IrClass? { - return if (!mirror.isDeclaredType()) null else CompilerClass(env, mirror) - } + override fun resolveClass(): IrClass? = + if (!mirror.isDeclaredType()) null else CompilerClass(env, mirror) override fun isAssignableTo(type: IrType): Boolean { val baseMirror = (type as CompilerType).mirror @@ -109,19 +106,11 @@ class CompilerType(private val env: XProcessingEnv, mirror: XType) : IrType { return true } - override fun hashCode(): Int { - return mirror.hash() - } + override fun hashCode(): Int = mirror.hash() - override fun toString(): String { - return mirror.toString() - } + override fun toString(): String = mirror.toString() - fun mapToJavaType(): CompilerType { - return CompilerType(env, mirror.mapToJavaType(env)) - } + fun mapToJavaType(): CompilerType = CompilerType(env, mirror.mapToJavaType(env)) - fun mapToKotlinType(): CompilerType { - return CompilerType(env, mirror.mapToKotlinType(env)) - } + fun mapToKotlinType(): CompilerType = CompilerType(env, mirror.mapToKotlinType(env)) } diff --git a/compiler/ast/src/main/kotlin/motif/ast/compiler/IrUtil.kt b/compiler/ast/src/main/kotlin/motif/ast/compiler/IrUtil.kt index 51f4d41f..00d00ec3 100644 --- a/compiler/ast/src/main/kotlin/motif/ast/compiler/IrUtil.kt +++ b/compiler/ast/src/main/kotlin/motif/ast/compiler/IrUtil.kt @@ -27,11 +27,8 @@ interface IrUtil { val env: XProcessingEnv - fun XElement.irModifiers(): Set { - return modifierNames.map { IrModifier.valueOf(it) }.toSet() - } + fun XElement.irModifiers(): Set = modifierNames.map { IrModifier.valueOf(it) }.toSet() - fun XElement.irAnnotations(): List { - return getAllAnnotations().map { CompilerAnnotation(env, it) } - } + fun XElement.irAnnotations(): List = + getAllAnnotations().map { CompilerAnnotation(env, it) } } diff --git a/compiler/ast/src/test/kotlin/motif/ast/compiler/CompilerClassTest.kt b/compiler/ast/src/test/kotlin/motif/ast/compiler/CompilerClassTest.kt index 9977edbb..15004478 100644 --- a/compiler/ast/src/test/kotlin/motif/ast/compiler/CompilerClassTest.kt +++ b/compiler/ast/src/test/kotlin/motif/ast/compiler/CompilerClassTest.kt @@ -31,22 +31,23 @@ import org.junit.runners.Parameterized @OptIn(ExperimentalProcessingApi::class) class XCompilerClassTest( private val processorType: ProcessorType, - private val srcLang: SourceLanguage + private val srcLang: SourceLanguage, ) { companion object { @JvmStatic @Parameterized.Parameters(name = "{0}_{1}") - fun data(): Collection> { - return cartesianProduct( - ProcessorType.values().toSortedSet(), SourceLanguage.values().toSortedSet()) - .filterNot { (proc, _) -> proc == ProcessorType.KSP } // Investigate this after ksp#1086 - .filterNot { (proc, srcLang) -> - proc == ProcessorType.AP && srcLang == SourceLanguage.KOTLIN - } - .map { it.toTypedArray() as Array } - .toList() - } + fun data(): Collection> = + cartesianProduct( + ProcessorType.values().toSortedSet(), + SourceLanguage.values().toSortedSet(), + ) + .filterNot { (proc, _) -> proc == ProcessorType.KSP } // Investigate this after ksp#1086 + .filterNot { (proc, srcLang) -> + proc == ProcessorType.AP && srcLang == SourceLanguage.KOTLIN + } + .map { it.toTypedArray() as Array } + .toList() } @Test @@ -59,8 +60,9 @@ class XCompilerClassTest( abstract class Bar {} class Foo extends Bar {} - """.trimIndent()) { - fooClass -> + """ + .trimIndent(), + ) { fooClass -> val superClass = fooClass.supertypes.single().resolveClass() as CompilerClass assertThat(superClass.typeArguments.map { it.qualifiedName }) .containsExactly("java.lang.String") @@ -79,8 +81,9 @@ class XCompilerClassTest( class Bar extends Baz {} class Foo extends Bar {} - """.trimIndent()) { - fooClass -> + """ + .trimIndent(), + ) { fooClass -> val barClass = fooClass.supertypes.single().resolveClass() as CompilerClass val bazClass = barClass.supertypes.single().resolveClass() as CompilerClass assertThat(bazClass.typeArguments.map { it.qualifiedName }) @@ -98,8 +101,9 @@ class XCompilerClassTest( class Bar {} class Foo extends Bar {} - """.trimIndent()) { - fooClass -> + """ + .trimIndent(), + ) { fooClass -> val superClass = fooClass.supertypes.single().resolveClass() as CompilerClass assertThat(superClass.typeArguments).isEmpty() } @@ -108,12 +112,14 @@ class XCompilerClassTest( @Test fun testObjectSupertype() { createClass( - "test.Foo", """ + "test.Foo", + """ package test; class Foo {} - """.trimIndent()) { - fooClass -> + """ + .trimIndent(), + ) { fooClass -> val objectClass = fooClass.supertypes.single().resolveClass()!! assertThat(objectClass.qualifiedName).isEqualTo(listOf("java.lang.Object").forLang().first()) assertThat(objectClass.supertypes).isEmpty() @@ -130,15 +136,17 @@ class XCompilerClassTest( interface Bar {} class Foo implements Bar {} - """.trimIndent()) { - fooClass -> + """ + .trimIndent(), + ) { fooClass -> val superTypes = fooClass.supertypes.map { it.qualifiedName } assertThat(superTypes) .containsExactly( *listOf("java.lang.Object", "test.Bar") .forLang() .filter { it != "kotlin.Any" } - .toTypedArray()) + .toTypedArray(), + ) } } @@ -154,29 +162,31 @@ class XCompilerClassTest( interface Baz {} class Foo implements Bar, Baz {} - """.trimIndent()) { - fooClass -> + """ + .trimIndent(), + ) { fooClass -> val superTypes = fooClass.supertypes.map { it.qualifiedName } assertThat(superTypes) .containsExactly( *listOf("java.lang.Object", "test.Bar", "test.Baz") .forLang() .filter { it != "kotlin.Any" } - .toTypedArray()) + .toTypedArray(), + ) } } private fun createClass( qualifiedName: String, @Language("JAVA") text: String, - assertions: (CompilerClass) -> Unit + assertions: (CompilerClass) -> Unit, ) { val pkg = - text.lines() + text + .lines() .firstOrNull { it.startsWith("package ") } ?.substringAfter(" ") - ?.substringBefore(";") - ?: "test" + ?.substringBefore(";") ?: "test" val srcFiles = text.split("\n{2,}".toRegex()).filterNot { it.startsWith("package") } val sources = when (srcLang) { @@ -196,7 +206,7 @@ class XCompilerClassTest( private fun process( invocation: XTestInvocation, qualifiedName: String, - assertions: (CompilerClass) -> Unit + assertions: (CompilerClass) -> Unit, ): CompilerClass { val env = invocation.processingEnv val typeElement = @@ -220,12 +230,14 @@ class XCompilerClassTest( .filter { it != name && it in src } .map { "import $pkg.$it;" } .joinToString("\n") - val code = """ + val code = + """ package $pkg; $imports $src - """.trimIndent() + """ + .trimIndent() return@map Source.java("$pkg.$name", code) } } @@ -256,41 +268,40 @@ class XCompilerClassTest( $imports ${if (hasParentClass) src.replace(" {}", "() {}") else src} - """.trimIndent() + """ + .trimIndent() return@map Source.kotlin("$pkg/$name.kt", code) } } - private fun String.toSourceFor(): String { - return if (srcLang == SourceLanguage.KOTLIN) { - this - } else { - this.replace("open ", "") - } - } - - private fun List.forLang(): Array { - return if (srcLang == SourceLanguage.KOTLIN) { - map { - when (it) { - "java.lang.Object" -> "kotlin.Any" - "java.lang.String" -> "kotlin.String" - else -> it + private fun String.toSourceFor(): String = + if (srcLang == SourceLanguage.KOTLIN) { + this + } else { + this.replace("open ", "") + } + + private fun List.forLang(): Array = + if (srcLang == SourceLanguage.KOTLIN) { + map { + when (it) { + "java.lang.Object" -> "kotlin.Any" + "java.lang.String" -> "kotlin.String" + else -> it + } } + } else { + this } - } else { - this - } - .toTypedArray() - } + .toTypedArray() } enum class ProcessorType { AP, - KSP + KSP, } enum class SourceLanguage { JAVA, - KOTLIN + KOTLIN, } diff --git a/compiler/build.gradle b/compiler/build.gradle index 768ca945..fb845b28 100644 --- a/compiler/build.gradle +++ b/compiler/build.gradle @@ -1,10 +1,8 @@ plugins { id 'org.jetbrains.kotlin.jvm' - id 'org.jetbrains.dokka' + id 'com.vanniktech.maven.publish' } -sourceCompatibility = 1.8 - dependencies { implementation deps.kotlin.stdlib implementation deps.kotlin.reflection @@ -35,5 +33,3 @@ dependencies { test { inputs.files(file("$rootDir/tests/src")) } - -apply plugin: 'com.vanniktech.maven.publish' diff --git a/compiler/ksp/build.gradle b/compiler/ksp/build.gradle index 660d1c69..a4e54977 100644 --- a/compiler/ksp/build.gradle +++ b/compiler/ksp/build.gradle @@ -1,9 +1,8 @@ plugins { id 'org.jetbrains.kotlin.jvm' + id 'com.vanniktech.maven.publish' } -sourceCompatibility = 1.8 - dependencies { implementation deps.kotlin.stdlib implementation deps.kotlin.reflection @@ -27,5 +26,3 @@ dependencies { testImplementation deps.test.compileTesting testImplementation deps.test.compileTestingKotlin } - -apply plugin: 'com.vanniktech.maven.publish' diff --git a/compiler/ksp/src/main/kotlin/motif/compiler/ksp/MessageWatcher.kt b/compiler/ksp/src/main/kotlin/motif/compiler/ksp/MessageWatcher.kt deleted file mode 100644 index 7e05dd80..00000000 --- a/compiler/ksp/src/main/kotlin/motif/compiler/ksp/MessageWatcher.kt +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (c) 2022 Uber Technologies, Inc. - * - * 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 motif.compiler.ksp - -import androidx.room.compiler.processing.XAnnotation -import androidx.room.compiler.processing.XAnnotationValue -import androidx.room.compiler.processing.XElement -import androidx.room.compiler.processing.XMessager -import com.google.devtools.ksp.KspErrorThrower -import javax.tools.Diagnostic - -/** - * A message watcher that re-throws exceptions from the KSP devtools package to work around an issue - * where exceptions are swallowed. - * - * https://github.com/google/ksp/issues/974 - */ -internal class MessageWatcher : XMessager() { - override fun onPrintMessage( - kind: Diagnostic.Kind, - msg: String, - element: XElement?, - annotation: XAnnotation?, - annotationValue: XAnnotationValue? - ) { - if (kind == Diagnostic.Kind.ERROR) { - KspErrorThrower.rethrowKspError("\n$msg") - } - } -} diff --git a/compiler/ksp/src/main/kotlin/motif/compiler/ksp/MotifSymbolProcessorProvider.kt b/compiler/ksp/src/main/kotlin/motif/compiler/ksp/MotifSymbolProcessorProvider.kt index bae006a2..c6c395fe 100644 --- a/compiler/ksp/src/main/kotlin/motif/compiler/ksp/MotifSymbolProcessorProvider.kt +++ b/compiler/ksp/src/main/kotlin/motif/compiler/ksp/MotifSymbolProcessorProvider.kt @@ -27,21 +27,19 @@ import motif.core.ResolvedGraph @AutoService(SymbolProcessorProvider::class) class MotifSymbolProcessorProvider( - private val config: XProcessingEnvConfig = XProcessingEnvConfig.DEFAULT + private val config: XProcessingEnvConfig = XProcessingEnvConfig.DEFAULT, ) : SymbolProcessorProvider { lateinit var graph: ResolvedGraph - override fun create(environment: SymbolProcessorEnvironment): SymbolProcessor { - return MotifSymbolProcessor(environment, config) - } + override fun create(environment: SymbolProcessorEnvironment): SymbolProcessor = + MotifSymbolProcessor(environment, config) @OptIn(ExperimentalProcessingApi::class) private inner class MotifSymbolProcessor( environment: SymbolProcessorEnvironment, - config: XProcessingEnvConfig + config: XProcessingEnvConfig, ) : KspBasicAnnotationProcessor(environment, config) { - override fun processingSteps() = - listOf(MotifProcessingStep(graphSetter = { graph = it }, messageWatcher = MessageWatcher())) + override fun processingSteps() = listOf(MotifProcessingStep(graphSetter = { graph = it })) } } diff --git a/compiler/src/main/kotlin/motif/compiler/CodeGenerator.kt b/compiler/src/main/kotlin/motif/compiler/CodeGenerator.kt index 522afd55..517675bf 100644 --- a/compiler/src/main/kotlin/motif/compiler/CodeGenerator.kt +++ b/compiler/src/main/kotlin/motif/compiler/CodeGenerator.kt @@ -29,8 +29,9 @@ object CodeGenerator { } else if (mode == OutputMode.KOTLIN) { if (env.backend == XProcessingEnv.Backend.JAVAC && kaptKotlinGeneratedDir == null) { throw IllegalStateException( - "-A$OPTION_MODE=${OutputMode.KOTLIN.name.toLowerCase()} " + - "requires -A$OPTION_KAPT_KOTLIN_GENERATED to be set.") + "-A$OPTION_MODE=${OutputMode.KOTLIN.name.lowercase()} " + + "requires -A$OPTION_KAPT_KOTLIN_GENERATED to be set.", + ) } generateKotlin(env, graph, kaptKotlinGeneratedDir) } else { @@ -46,29 +47,27 @@ object CodeGenerator { } } - private fun generateJava(env: XProcessingEnv, graph: ResolvedGraph): List { - return ScopeImplFactory.create(env, graph) - .map { scopeImpl -> JavaCodeGenerator.generate(scopeImpl) } - .onEach { javaFile -> javaFile.writeTo(env.filer) } - .map { "${it.packageName}.${it.typeSpec.name}" } - } + private fun generateJava(env: XProcessingEnv, graph: ResolvedGraph): List = + ScopeImplFactory.create(env, graph) + .map { scopeImpl -> JavaCodeGenerator.generate(scopeImpl) } + .onEach { javaFile -> javaFile.writeTo(env.filer) } + .map { "${it.packageName}.${it.typeSpec.name}" } private fun generateKotlin( env: XProcessingEnv, graph: ResolvedGraph, - kaptKotlinGeneratedDir: String? = null - ): List { - return ScopeImplFactory.create(env, graph) - .map { scopeImpl -> KotlinCodeGenerator.generate(scopeImpl) } - .onEach { fileSpec -> - if (kaptKotlinGeneratedDir != null) { - fileSpec.writeTo(File(kaptKotlinGeneratedDir)) - } else { - fileSpec.writeTo(env.filer) + kaptKotlinGeneratedDir: String? = null, + ): List = + ScopeImplFactory.create(env, graph) + .map { scopeImpl -> KotlinCodeGenerator.generate(scopeImpl) } + .onEach { fileSpec -> + if (kaptKotlinGeneratedDir != null) { + fileSpec.writeTo(File(kaptKotlinGeneratedDir)) + } else { + fileSpec.writeTo(env.filer) + } } - } - .map { "${it.packageName}.${it.name}" } - } + .map { "${it.packageName}.${it.name}" } } enum class OutputMode { diff --git a/compiler/src/main/kotlin/motif/compiler/JavaCodeGenerator.kt b/compiler/src/main/kotlin/motif/compiler/JavaCodeGenerator.kt index 72d5eaf6..e8464fc8 100644 --- a/compiler/src/main/kotlin/motif/compiler/JavaCodeGenerator.kt +++ b/compiler/src/main/kotlin/motif/compiler/JavaCodeGenerator.kt @@ -24,6 +24,7 @@ import com.squareup.javapoet.JavaFile import com.squareup.javapoet.MethodSpec import com.squareup.javapoet.ParameterSpec import com.squareup.javapoet.TypeSpec +import com.squareup.kotlinpoet.javapoet.KotlinPoetJavaPoetPreview import com.uber.xprocessing.ext.isKotlinSource import com.uber.xprocessing.ext.withRawTypeFix import javax.lang.model.element.Modifier @@ -37,95 +38,94 @@ object JavaCodeGenerator { return JavaFile.builder(scopeImpl.className.j.packageName(), typeSpec).build() } - private fun ScopeImpl.spec(): TypeSpec { - return TypeSpec.classBuilder(className.j) - .apply { - addAnnotation(scopeImplAnnotation.spec()) - addModifiers(Modifier.PUBLIC) - addSuperinterface(superClassName.j) - objectsField?.let { addField(it.spec()) } - addField(dependenciesField.spec()) - cacheFields.forEach { addField(it.spec()) } - addMethod(constructor.spec()) - alternateConstructor?.let { addMethod(it.spec()) } - accessMethodImpls.forEach { addMethod(it.spec()) } - childMethodImpls.forEach { addMethod(it.spec()) } - addMethod(scopeProviderMethod.spec()) - factoryProviderMethods.forEach { addMethods(it.specs()) } - dependencyProviderMethods.forEach { addMethod(it.spec()) } - dependencies?.let { addType(it.spec()) } - objectsImpl?.let { addType(it.spec()) } - } - .build() - } - - private fun ScopeImplAnnotation.spec(): AnnotationSpec { - return AnnotationSpec.builder(motif.ScopeImpl::class.java) - .apply { - if (children.isEmpty()) { - addMember("children", "{}") - } else { - children.forEach { child -> addMember("children", "\$T.class", child.j) } + private fun ScopeImpl.spec(): TypeSpec = + TypeSpec.classBuilder(className.j) + .apply { + addAnnotation(scopeImplAnnotation.spec()) + addModifiers(Modifier.PUBLIC) + addSuperinterface(superClassName.j) + objectsField?.let { addField(it.spec()) } + addField(dependenciesField.spec()) + cacheFields.forEach { addField(it.spec(useNullFieldInitialization)) } + addMethod(constructor.spec()) + alternateConstructor?.let { addMethod(it.spec()) } + accessMethodImpls.forEach { addMethod(it.spec()) } + childMethodImpls.forEach { addMethod(it.spec()) } + addMethod(scopeProviderMethod.spec()) + factoryProviderMethods.forEach { addMethods(it.specs(useNullFieldInitialization)) } + dependencyProviderMethods.forEach { addMethod(it.spec()) } + dependencies?.let { addType(it.spec()) } + objectsImpl?.let { addType(it.spec()) } } - addMember("scope", "\$T.class", scopeClassName.j) - addMember("dependencies", "\$T.class", dependenciesClassName.j) - } - .build() - } - - private fun ObjectsField.spec(): FieldSpec { - return FieldSpec.builder(objectsClassName.j, name, Modifier.PRIVATE, Modifier.FINAL) - .initializer("new \$T()", objectsImplClassName.j) - .build() - } - - private fun DependenciesField.spec(): FieldSpec { - return FieldSpec.builder(dependenciesClassName.j, name, Modifier.PRIVATE, Modifier.FINAL) - .build() - } - - private fun CacheField.spec(): FieldSpec { - return FieldSpec.builder(Object::class.java, name, Modifier.PRIVATE, Modifier.VOLATILE) - .initializer("\$T.NONE", None::class.java) - .build() - } - - private fun Constructor.spec(): MethodSpec { - return MethodSpec.constructorBuilder() - .addModifiers(Modifier.PUBLIC) - .addParameter(dependenciesClassName.j, dependenciesParameterName) - .addStatement("this.\$N = \$N", dependenciesFieldName, dependenciesParameterName) - .build() - } + .build() - private fun AlternateConstructor.spec(): MethodSpec { - return MethodSpec.constructorBuilder() - .addModifiers(Modifier.PUBLIC) - .addStatement("this(new \$T() {})", dependenciesClassName.j) - .build() - } - - private fun AccessMethodImpl.spec(): MethodSpec { - return MethodSpec.overriding( - overriddenMethod.element.toJavac(), - overriddenMethod.owner.toJavac() as DeclaredType, - env.toJavac().typeUtils) - .addStatement("return \$N()", providerMethodName) - .build() - } - - private fun ChildMethodImpl.spec(): MethodSpec { - return MethodSpec.methodBuilder(childMethodName) - .apply { - addAnnotation(Override::class.java) - addModifiers(Modifier.PUBLIC) - returns(childClassName.j) - this@spec.parameters.forEach { addParameter(it.spec()) } - addStatement("return new \$T(\$L)", childImplClassName.j, childDependenciesImpl.spec()) - } - .build() - } + private fun ScopeImplAnnotation.spec(): AnnotationSpec = + AnnotationSpec.builder(motif.ScopeImpl::class.java) + .apply { + if (children.isEmpty()) { + addMember("children", "{}") + } else { + children.forEach { child -> addMember("children", "\$T.class", child.j) } + } + addMember("scope", "\$T.class", scopeClassName.j) + addMember("dependencies", "\$T.class", dependenciesClassName.j) + } + .build() + + private fun ObjectsField.spec(): FieldSpec = + FieldSpec.builder(objectsClassName.j, name, Modifier.PRIVATE, Modifier.FINAL) + .initializer("new \$T()", objectsImplClassName.j) + .build() + + private fun DependenciesField.spec(): FieldSpec = + FieldSpec.builder(dependenciesClassName.j, name, Modifier.PRIVATE, Modifier.FINAL).build() + + private fun CacheField.spec(useNullFieldInitialization: Boolean): FieldSpec { + return if(useNullFieldInitialization) { + FieldSpec.builder(Object::class.java, name, Modifier.PRIVATE, Modifier.VOLATILE) + .build() + } else { + FieldSpec.builder(Object::class.java, name, Modifier.PRIVATE, Modifier.VOLATILE) + .initializer("\$T.NONE", None::class.java) + .build() + } + } + + + private fun Constructor.spec(): MethodSpec = + MethodSpec.constructorBuilder() + .addModifiers(Modifier.PUBLIC) + .addParameter(dependenciesClassName.j, dependenciesParameterName) + .addStatement("this.\$N = \$N", dependenciesFieldName, dependenciesParameterName) + .build() + + private fun AlternateConstructor.spec(): MethodSpec = + MethodSpec.constructorBuilder() + .addModifiers(Modifier.PUBLIC) + .addStatement("this(new \$T() {})", dependenciesClassName.j) + .build() + + private fun AccessMethodImpl.spec(): MethodSpec = + MethodSpec.overriding( + overriddenMethod.element.toJavac(), + overriddenMethod.owner.toJavac() as DeclaredType, + env.toJavac().typeUtils, + ) + .addStatement("return \$N()", providerMethodName) + .build() + + private fun ChildMethodImpl.spec(): MethodSpec = + MethodSpec.methodBuilder(childMethodName) + .apply { + addAnnotation(Override::class.java) + addModifiers(Modifier.PUBLIC) + returns(childClassName.j) + this@spec.parameters.forEach { addParameter(it.spec()) } + addStatement("return new \$T(\$L)", childImplClassName.j, childDependenciesImpl.spec()) + } + .build() + @OptIn(KotlinPoetJavaPoetPreview::class) private fun ChildDependenciesImpl.spec(): TypeSpec { val isKotlinDepInterface = env.findTypeElement(childDependenciesClassName.j).isKotlinSource(env) return TypeSpec.anonymousClassBuilder("") @@ -138,140 +138,141 @@ object JavaCodeGenerator { private fun ChildDependencyMethodImpl.spec( env: XProcessingEnv, - isKotlinDependenciesInterface: Boolean - ): MethodSpec { - return MethodSpec.methodBuilder(name) - .addAnnotation(Override::class.java) - .addModifiers(Modifier.PUBLIC) - .returns( - if (isKotlinDependenciesInterface) { - returnTypeName.j.withRawTypeFix(env) - } else { - returnTypeName.j - }) - .addStatement(returnExpression.spec()) - .build() - } - - private fun ChildDependencyMethodImpl.ReturnExpression.spec(): CodeBlock { - return when (this) { - is ChildDependencyMethodImpl.ReturnExpression.Parameter -> spec() - is ChildDependencyMethodImpl.ReturnExpression.Provider -> spec() - } - } - - private fun ChildDependencyMethodImpl.ReturnExpression.Parameter.spec(): CodeBlock { - return CodeBlock.of("return \$N", parameterName) - } - - private fun ChildDependencyMethodImpl.ReturnExpression.Provider.spec(): CodeBlock { - return CodeBlock.of("return \$T.this.\$N()", scopeImplName.j, providerName) - } - - private fun ChildMethodImplParameter.spec(): ParameterSpec { - return ParameterSpec.builder(typeName.j, name, Modifier.FINAL).build() - } - - private fun ScopeProviderMethod.spec(): MethodSpec { - return MethodSpec.methodBuilder(name) - .returns(scopeClassName.j) - .addStatement("return this") - .build() - } - - private fun FactoryProviderMethod.specs(): List { + isKotlinDependenciesInterface: Boolean, + ): MethodSpec = + MethodSpec.methodBuilder(name) + .addAnnotation(Override::class.java) + .addModifiers(Modifier.PUBLIC) + .returns( + if (isKotlinDependenciesInterface) { + returnTypeName.j.withRawTypeFix(env) + } else { + returnTypeName.j + }, + ) + .addStatement(returnExpression.spec()) + .build() + + private fun ChildDependencyMethodImpl.ReturnExpression.spec(): CodeBlock = + when (this) { + is ChildDependencyMethodImpl.ReturnExpression.Parameter -> spec() + is ChildDependencyMethodImpl.ReturnExpression.Provider -> spec() + } + + private fun ChildDependencyMethodImpl.ReturnExpression.Parameter.spec(): CodeBlock = + CodeBlock.of("return \$N", parameterName) + + private fun ChildDependencyMethodImpl.ReturnExpression.Provider.spec(): CodeBlock = + CodeBlock.of("return \$T.this.\$N()", scopeImplName.j, providerName) + + private fun ChildMethodImplParameter.spec(): ParameterSpec = + ParameterSpec.builder(typeName.j, name, Modifier.FINAL).build() + + private fun ScopeProviderMethod.spec(): MethodSpec = + MethodSpec.methodBuilder(name).returns(scopeClassName.j).addStatement("return this").build() + + private fun FactoryProviderMethod.specs(useNullFieldInitialization : Boolean): List { val primarySpec = - MethodSpec.methodBuilder(name).returns(returnTypeName.j).addStatement(body.spec()).build() + MethodSpec.methodBuilder(name).returns(returnTypeName.j).addStatement(body.spec(useNullFieldInitialization)).build() val spreadSpecs = spreadProviderMethods.map { it.spec() } return listOf(primarySpec) + spreadSpecs } - private fun FactoryProviderMethodBody.spec(): CodeBlock { - return when (this) { - is FactoryProviderMethodBody.Cached -> spec() - is FactoryProviderMethodBody.Uncached -> spec() - } - } - - private fun FactoryProviderMethodBody.Cached.spec(): CodeBlock { - return CodeBlock.builder() - .beginControlFlow("if (\$N == \$T.NONE)", cacheFieldName, None::class.java) - .beginControlFlow("synchronized (this)") - .beginControlFlow("if (\$N == \$T.NONE)", cacheFieldName, None::class.java) - .add("\$N = \$L;", cacheFieldName, instantiation.spec()) - .endControlFlow() - .endControlFlow() - .endControlFlow() - .add("return (\$T) \$N", returnTypeName.j, cacheFieldName) - .build() - } - - private fun FactoryProviderMethodBody.Uncached.spec(): CodeBlock { - return CodeBlock.of("return \$L", instantiation.spec()) - } - - private fun FactoryProviderInstantiation.spec(): CodeBlock { - return when (this) { - is FactoryProviderInstantiation.Basic -> spec() - is FactoryProviderInstantiation.Constructor -> spec() - is FactoryProviderInstantiation.Binds -> spec() - } - } - - private fun FactoryProviderInstantiation.Basic.spec(): CodeBlock { - return if (isStatic) { - CodeBlock.of("\$T.\$N\$L", objectsClassName.j, factoryMethodName, callProviders.spec()) - } else { - CodeBlock.of("\$N.\$N\$L", objectsFieldName, factoryMethodName, callProviders.spec()) - } - } - - private fun FactoryProviderInstantiation.Constructor.spec(): CodeBlock { - return CodeBlock.of("new \$T\$L", returnTypeName.j, callProviders.spec()) - } - - private fun FactoryProviderInstantiation.Binds.spec(): CodeBlock { - return CodeBlock.of("\$N()", providerMethodName) - } + private fun FactoryProviderMethodBody.spec(useNullFieldInitialization: Boolean): CodeBlock = + when (this) { + is FactoryProviderMethodBody.Cached -> spec(useNullFieldInitialization) + is FactoryProviderMethodBody.Uncached -> spec() + } + + private fun FactoryProviderMethodBody.Cached.spec(useNullFieldInitialization : Boolean): CodeBlock { + if(useNullFieldInitialization) { + val localFieldName = "_$cacheFieldName" + return CodeBlock.builder() + // Using a local variable reduces atomic read overhead + .add("Object $localFieldName = \$N;\n", cacheFieldName) + .beginControlFlow("if (\$N == null)", localFieldName) + .beginControlFlow("synchronized (this)") + .beginControlFlow("if (\$N == null)", cacheFieldName) + .add("\$N = \$L;\n", localFieldName, instantiation.spec()) + .beginControlFlow("if (\$N == null)", localFieldName) + .add("throw new \$T(\$S);\n", NullPointerException::class.java, "Factory method cannot return null") + .endControlFlow() + .add("\$N = \$L;\n", cacheFieldName, localFieldName) + .endControlFlow() + .endControlFlow() + .endControlFlow() + .add("return (\$T) \$N", returnTypeName.j, localFieldName) + .build() + } + return CodeBlock.builder() + .beginControlFlow("if (\$N == \$T.NONE)", cacheFieldName, None::class.java) + .beginControlFlow("synchronized (this)") + .beginControlFlow("if (\$N == \$T.NONE)", cacheFieldName, None::class.java) + .add("\$N = \$L;", cacheFieldName, instantiation.spec()) + .endControlFlow() + .endControlFlow() + .endControlFlow() + .add("return (\$T) \$N", returnTypeName.j, cacheFieldName) + .build() + } + + private fun FactoryProviderMethodBody.Uncached.spec(): CodeBlock = + CodeBlock.of("return \$L", instantiation.spec()) + + private fun FactoryProviderInstantiation.spec(): CodeBlock = + when (this) { + is FactoryProviderInstantiation.Basic -> spec() + is FactoryProviderInstantiation.Constructor -> spec() + is FactoryProviderInstantiation.Binds -> spec() + } + + private fun FactoryProviderInstantiation.Basic.spec(): CodeBlock = + if (isStatic) { + CodeBlock.of("\$T.\$N\$L", objectsClassName.j, factoryMethodName, callProviders.spec()) + } else { + CodeBlock.of("\$N.\$N\$L", objectsFieldName, factoryMethodName, callProviders.spec()) + } + + private fun FactoryProviderInstantiation.Constructor.spec(): CodeBlock = + CodeBlock.of("new \$T\$L", returnTypeName.j, callProviders.spec()) + + private fun FactoryProviderInstantiation.Binds.spec(): CodeBlock = + CodeBlock.of("\$N()", providerMethodName) private fun CallProviders.spec(): String { val callString = providerMethodNames.joinToString { "$it()" } return "($callString)" } - private fun SpreadProviderMethod.spec(): MethodSpec { - return MethodSpec.methodBuilder(name) - .returns(returnTypeName.j) - .addStatement("return \$N().\$N()", sourceProviderMethodName, spreadMethodName) - .build() - } + private fun SpreadProviderMethod.spec(): MethodSpec = + MethodSpec.methodBuilder(name) + .returns(returnTypeName.j) + .addStatement("return \$N().\$N()", sourceProviderMethodName, spreadMethodName) + .build() - private fun DependencyProviderMethod.spec(): MethodSpec { - return MethodSpec.methodBuilder(name) - .returns(returnTypeName.j) - .addStatement("return \$N.\$N()", dependenciesFieldName, dependencyMethodName) - .build() - } - - private fun Dependencies.spec(): TypeSpec { - return TypeSpec.interfaceBuilder(className.j) - .apply { - addModifiers(Modifier.PUBLIC) - methods.forEach { addMethod(it.spec()) } - } - .build() - } + private fun DependencyProviderMethod.spec(): MethodSpec = + MethodSpec.methodBuilder(name) + .returns(returnTypeName.j) + .addStatement("return \$N.\$N()", dependenciesFieldName, dependencyMethodName) + .build() - private fun DependencyMethod.spec(): MethodSpec { - return MethodSpec.methodBuilder(name) - .apply { - qualifier?.let { addAnnotation(it.spec()) } - addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT) - returns(returnTypeName.j) - addJavadoc(javaDoc.spec()) - } - .build() - } + private fun Dependencies.spec(): TypeSpec = + TypeSpec.interfaceBuilder(className.j) + .apply { + addModifiers(Modifier.PUBLIC) + methods.forEach { addMethod(it.spec()) } + } + .build() + + private fun DependencyMethod.spec(): MethodSpec = + MethodSpec.methodBuilder(name) + .apply { + qualifier?.let { addAnnotation(it.spec()) } + addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT) + returns(returnTypeName.j) + addJavadoc(javaDoc.spec()) + } + .build() private fun Qualifier.spec(): AnnotationSpec { val className = @@ -286,41 +287,39 @@ object JavaCodeGenerator { .build() } - private fun DependencyMethodJavaDoc.spec(): CodeBlock { - return CodeBlock.builder() - .apply { - add("
    \nRequested from:\n") - requestedFrom.forEach { add(it.spec()) } - add("
\n") - } - .build() - } + private fun DependencyMethodJavaDoc.spec(): CodeBlock = + CodeBlock.builder() + .apply { + add("
    \nRequested from:\n") + requestedFrom.forEach { add(it.spec()) } + add("
\n") + } + .build() private fun JavaDocMethodLink.spec(): CodeBlock { val parameterTypeString = parameterTypes.joinToString() return CodeBlock.of("
  • {@link \$L#\$N(\$L)}
  • \n", owner, methodName, parameterTypeString) } - private fun ObjectsImpl.spec(): TypeSpec { - return TypeSpec.classBuilder(className.j) - .apply { - addModifiers(Modifier.PRIVATE, Modifier.STATIC) - if (isInterface) { - addSuperinterface(superClassName.j) - } else { - superclass(superClassName.j) + private fun ObjectsImpl.spec(): TypeSpec = + TypeSpec.classBuilder(className.j) + .apply { + addModifiers(Modifier.PRIVATE, Modifier.STATIC) + if (isInterface) { + addSuperinterface(superClassName.j) + } else { + superclass(superClassName.j) + } + abstractMethods.forEach { addMethod(it.spec()) } } - abstractMethods.forEach { addMethod(it.spec()) } - } - .build() - } - - private fun ObjectsAbstractMethod.spec(): MethodSpec { - return MethodSpec.overriding( - overriddenMethod.element.toJavac(), - overriddenMethod.owner.toJavac() as DeclaredType, - env.toJavac().typeUtils) - .addStatement("throw new \$T()", UnsupportedOperationException::class.java) - .build() - } + .build() + + private fun ObjectsAbstractMethod.spec(): MethodSpec = + MethodSpec.overriding( + overriddenMethod.element.toJavac(), + overriddenMethod.owner.toJavac() as DeclaredType, + env.toJavac().typeUtils, + ) + .addStatement("throw new \$T()", UnsupportedOperationException::class.java) + .build() } diff --git a/compiler/src/main/kotlin/motif/compiler/KotlinCodeGenerator.kt b/compiler/src/main/kotlin/motif/compiler/KotlinCodeGenerator.kt index 7dbc87e8..a7eacc56 100644 --- a/compiler/src/main/kotlin/motif/compiler/KotlinCodeGenerator.kt +++ b/compiler/src/main/kotlin/motif/compiler/KotlinCodeGenerator.kt @@ -27,6 +27,7 @@ import com.squareup.kotlinpoet.ParameterizedTypeName import com.squareup.kotlinpoet.PropertySpec import com.squareup.kotlinpoet.TypeName import com.squareup.kotlinpoet.TypeSpec +import com.squareup.kotlinpoet.asTypeName import com.squareup.kotlinpoet.javapoet.KotlinPoetJavaPoetPreview import com.squareup.kotlinpoet.javapoet.toKClassName import motif.internal.None @@ -39,92 +40,94 @@ object KotlinCodeGenerator { return FileSpec.get(scopeImpl.className.kt.packageName, typeSpec) } - private fun ScopeImpl.spec(): TypeSpec { - return TypeSpec.classBuilder(className.kt) - .apply { - addAnnotation(suppressAnnotationSpec("REDUNDANT_PROJECTION", "UNCHECKED_CAST")) - addAnnotation(scopeImplAnnotation.spec()) - addModifiers(if (internalScope) KModifier.INTERNAL else KModifier.PUBLIC) - addSuperinterface(superClassName.kt) - objectsField?.let { addProperty(it.spec()) } - addProperty(dependenciesField.spec()) - cacheFields.forEach { addProperty(it.spec()) } - primaryConstructor(constructor.spec()) - alternateConstructor?.let { addFunction(it.spec()) } - accessMethodImpls.filter { !it.overriddenMethod.isSynthetic }.forEach { - addFunction(it.spec()) - } - accessMethodImpls.filter { it.overriddenMethod.isSynthetic }.forEach { - addProperty(it.propSpec()) + private fun ScopeImpl.spec(): TypeSpec = + TypeSpec.classBuilder(className.kt) + .apply { + addAnnotation(suppressAnnotationSpec("REDUNDANT_PROJECTION", "UNCHECKED_CAST")) + addAnnotation(scopeImplAnnotation.spec()) + addModifiers(if (internalScope) KModifier.INTERNAL else KModifier.PUBLIC) + addSuperinterface(superClassName.kt) + objectsField?.let { addProperty(it.spec()) } + addProperty(dependenciesField.spec()) + cacheFields.forEach { addProperty(it.spec(useNullFieldInitialization)) } + primaryConstructor(constructor.spec()) + alternateConstructor?.let { addFunction(it.spec()) } + accessMethodImpls + .filter { !it.overriddenMethod.isSynthetic } + .forEach { addFunction(it.spec()) } + accessMethodImpls + .filter { it.overriddenMethod.isSynthetic } + .forEach { addProperty(it.propSpec()) } + childMethodImpls.forEach { addFunction(it.spec()) } + addFunction(scopeProviderMethod.spec()) + factoryProviderMethods.forEach { addFunctions(it.specs(useNullFieldInitialization)) } + dependencyProviderMethods.forEach { addFunction(it.spec()) } + dependencies?.let { addType(it.spec()) } + objectsImpl?.let { addType(it.spec()) } } - childMethodImpls.forEach { addFunction(it.spec()) } - addFunction(scopeProviderMethod.spec()) - factoryProviderMethods.forEach { addFunctions(it.specs()) } - dependencyProviderMethods.forEach { addFunction(it.spec()) } - dependencies?.let { addType(it.spec()) } - objectsImpl?.let { addType(it.spec()) } - } - .build() - } + .build() - private fun ScopeImplAnnotation.spec(): AnnotationSpec { - return AnnotationSpec.builder(motif.ScopeImpl::class) - .apply { - addMember( - CodeBlock.builder() - .apply { - add("children = [") - children.forEachIndexed { i, child -> - val prefix = if (i == 0) "" else ", " - add("%L%T::class", prefix, child.kt) + private fun ScopeImplAnnotation.spec(): AnnotationSpec = + AnnotationSpec.builder(motif.ScopeImpl::class) + .apply { + addMember( + CodeBlock.builder() + .apply { + add("children = [") + children.forEachIndexed { i, child -> + val prefix = if (i == 0) "" else ", " + add("%L%T::class", prefix, child.kt) + } + add("]") } - add("]") - } - .build()) - addMember("scope = %T::class", scopeClassName.kt) - addMember("dependencies = %T::class", dependenciesClassName.kt) - } - .build() - } - - private fun ObjectsField.spec(): PropertySpec { - return PropertySpec.builder(name, objectsClassName.kt, KModifier.PRIVATE, KModifier.FINAL) - .initializer("%T()", objectsImplClassName.kt) - .build() - } + .build(), + ) + addMember("scope = %T::class", scopeClassName.kt) + addMember("dependencies = %T::class", dependenciesClassName.kt) + } + .build() - private fun DependenciesField.spec(): PropertySpec { - return PropertySpec.builder(name, dependenciesClassName.kt, KModifier.PRIVATE) - .initializer(name) - .build() - } + private fun ObjectsField.spec(): PropertySpec = + PropertySpec.builder(name, objectsClassName.kt, KModifier.PRIVATE, KModifier.FINAL) + .initializer("%T()", objectsImplClassName.kt) + .build() - private fun CacheField.spec(): PropertySpec { - return PropertySpec.builder(name, Any::class, KModifier.PRIVATE) - .mutable(true) - .addAnnotation(Volatile::class) - .initializer("%T.NONE", None::class) - .build() - } + private fun DependenciesField.spec(): PropertySpec = + PropertySpec.builder(name, dependenciesClassName.kt, KModifier.PRIVATE) + .initializer(name) + .build() - private fun Constructor.spec(): FunSpec { - return FunSpec.constructorBuilder() - .addParameter(dependenciesParameterName, dependenciesClassName.kt) - .build() - } + private fun CacheField.spec(useNullFieldInitialization: Boolean): PropertySpec { + return if(useNullFieldInitialization) { + PropertySpec.builder(name, Any::class.asTypeName().copy(true), KModifier.PRIVATE) + .mutable(true) + .addAnnotation(Volatile::class) + .initializer("null") + .build() + } else { + PropertySpec.builder(name, Any::class, KModifier.PRIVATE) + .mutable(true) + .addAnnotation(Volatile::class) + .initializer("%T.NONE", None::class) + .build() + } + } + + private fun Constructor.spec(): FunSpec = + FunSpec.constructorBuilder() + .addParameter(dependenciesParameterName, dependenciesClassName.kt) + .build() - private fun AlternateConstructor.spec(): FunSpec { - return FunSpec.constructorBuilder() - .addModifiers(KModifier.PUBLIC) - .callThisConstructor(CodeBlock.of("object : %T {}", dependenciesClassName.kt)) - .build() - } + private fun AlternateConstructor.spec(): FunSpec = + FunSpec.constructorBuilder() + .addModifiers(KModifier.PUBLIC) + .callThisConstructor(CodeBlock.of("object : %T {}", dependenciesClassName.kt)) + .build() - private fun AccessMethodImpl.spec(): FunSpec { - return XFunSpec.overriding(overriddenMethod.element, overriddenMethod.owner, env) - .addStatement("return %N()", providerMethodName) - .build() - } + private fun AccessMethodImpl.spec(): FunSpec = + XFunSpec.overriding(overriddenMethod.element, overriddenMethod.owner, env) + .addStatement("return %N()", providerMethodName) + .build() private fun AccessMethodImpl.propSpec(): PropertySpec { val propName = @@ -136,7 +139,9 @@ object KotlinCodeGenerator { } } return PropertySpec.builder( - propName, ClassName.bestGuess(overriddenMethod.returnType.qualifiedName)) + propName, + ClassName.bestGuess(overriddenMethod.returnType.qualifiedName), + ) .addModifiers(KModifier.OVERRIDE) .initializer("%N()", providerMethodName) .build() @@ -154,109 +159,115 @@ object KotlinCodeGenerator { .build() } - private fun ChildDependenciesImpl.spec(): TypeSpec { - return TypeSpec.anonymousClassBuilder() - .apply { - if (isAbstractClass) { - superclass(childDependenciesClassName.kt) - } else { - addSuperinterface(childDependenciesClassName.kt) + private fun ChildDependenciesImpl.spec(): TypeSpec = + TypeSpec.anonymousClassBuilder() + .apply { + if (isAbstractClass) { + superclass(childDependenciesClassName.kt) + } else { + addSuperinterface(childDependenciesClassName.kt) + } + methods.forEach { addFunction(it.spec()) } } - methods.forEach { addFunction(it.spec()) } - } - .build() - } + .build() - private fun ChildDependencyMethodImpl.spec(): FunSpec { - return FunSpec.builder(name) - .addModifiers(KModifier.PUBLIC, KModifier.OVERRIDE) - .returns(returnTypeName.kt) - .addCode(returnExpression.spec()) - .build() - } + private fun ChildDependencyMethodImpl.spec(): FunSpec = + FunSpec.builder(name) + .addModifiers(KModifier.PUBLIC, KModifier.OVERRIDE) + .returns(returnTypeName.kt) + .addCode(returnExpression.spec()) + .build() - private fun ChildDependencyMethodImpl.ReturnExpression.spec(): CodeBlock { - return when (this) { - is ChildDependencyMethodImpl.ReturnExpression.Parameter -> spec() - is ChildDependencyMethodImpl.ReturnExpression.Provider -> spec() - } - } + private fun ChildDependencyMethodImpl.ReturnExpression.spec(): CodeBlock = + when (this) { + is ChildDependencyMethodImpl.ReturnExpression.Parameter -> spec() + is ChildDependencyMethodImpl.ReturnExpression.Provider -> spec() + } - private fun ChildDependencyMethodImpl.ReturnExpression.Parameter.spec(): CodeBlock { - return CodeBlock.of("return %N", parameterName) - } + private fun ChildDependencyMethodImpl.ReturnExpression.Parameter.spec(): CodeBlock = + CodeBlock.of("return %N", parameterName) - private fun ChildDependencyMethodImpl.ReturnExpression.Provider.spec(): CodeBlock { - return CodeBlock.of("return this@%T.%N()", scopeImplName.kt, providerName) - } + private fun ChildDependencyMethodImpl.ReturnExpression.Provider.spec(): CodeBlock = + CodeBlock.of("return this@%T.%N()", scopeImplName.kt, providerName) - private fun ChildMethodImplParameter.spec(): ParameterSpec { - return ParameterSpec.builder(name, typeName.kt).build() - } + private fun ChildMethodImplParameter.spec(): ParameterSpec = + ParameterSpec.builder(name, typeName.kt).build() - private fun ScopeProviderMethod.spec(): FunSpec { - return FunSpec.builder(name) - .apply { - if (isInternal) { - addModifiers(KModifier.INTERNAL) + private fun ScopeProviderMethod.spec(): FunSpec = + FunSpec.builder(name) + .apply { + if (isInternal) { + addModifiers(KModifier.INTERNAL) + } } - } - .returns(scopeClassName.kt) - .addStatement("return this") - .build() - } + .returns(scopeClassName.kt) + .addStatement("return this") + .build() - private fun FactoryProviderMethod.specs(): List { + private fun FactoryProviderMethod.specs(useNullFieldInitialization : Boolean): List { val primarySpec = FunSpec.builder(name) .addModifiers(KModifier.INTERNAL) .returns(returnTypeName.reloadedForTypeArgs(env)) - .addCode(body.spec()) + .addCode(body.spec(useNullFieldInitialization)) .build() val spreadSpecs = spreadProviderMethods.map { it.spec() } return listOf(primarySpec) + spreadSpecs } - private fun FactoryProviderMethodBody.spec(): CodeBlock { - return when (this) { - is FactoryProviderMethodBody.Cached -> spec() - is FactoryProviderMethodBody.Uncached -> spec() - } + private fun FactoryProviderMethodBody.spec(useNullFieldInitialization : Boolean): CodeBlock = + when (this) { + is FactoryProviderMethodBody.Cached -> spec(useNullFieldInitialization) + is FactoryProviderMethodBody.Uncached -> spec() + } + + private fun FactoryProviderMethodBody.Cached.spec(useNullFieldInitialization : Boolean): CodeBlock { + if(useNullFieldInitialization) { + val localFieldName = "_$cacheFieldName" + val codeBlockBuilder = CodeBlock.builder() + // Using a local variable reduces atomic read overhead + .addStatement("var $localFieldName = %N;\n", cacheFieldName) + .beginControlFlow("if (%N == null)", localFieldName) + .beginControlFlow("synchronized (this)") + .beginControlFlow("if (%N == null)", cacheFieldName) + .addStatement("%N = %L", localFieldName, instantiation.spec()) + .addStatement("%N = %N", cacheFieldName, localFieldName) + .endControlFlow() + .endControlFlow() + .endControlFlow() + return codeBlockBuilder + .add("return ( %N as %T )", localFieldName, returnTypeName.reloadedForTypeArgs(env)) + .build() + } + return CodeBlock.builder() + .beginControlFlow("if (%N == %T.NONE)", cacheFieldName, None::class) + .beginControlFlow("synchronized (this)") + .beginControlFlow("if (%N == %T.NONE)", cacheFieldName, None::class) + .addStatement("%N=%L", cacheFieldName, instantiation.spec()) + .endControlFlow() + .endControlFlow() + .endControlFlow() + .add("return ( %N as %T )", cacheFieldName, returnTypeName.reloadedForTypeArgs(env)) + .build() } - private fun FactoryProviderMethodBody.Cached.spec(): CodeBlock { - return CodeBlock.builder() - .beginControlFlow("if (%N == %T.NONE)", cacheFieldName, None::class) - .beginControlFlow("synchronized (this)") - .beginControlFlow("if (%N == %T.NONE)", cacheFieldName, None::class) - .addStatement("%N=%L", cacheFieldName, instantiation.spec()) - .endControlFlow() - .endControlFlow() - .endControlFlow() - .add("return ( %N as %T )", cacheFieldName, returnTypeName.reloadedForTypeArgs(env)) - .build() - } + private fun motif.compiler.TypeName.reloadedForTypeArgs(env: XProcessingEnv): TypeName = + if (kt is ParameterizedTypeName) { + kt + } else { + // ensures that type arguments get loaded + KotlinTypeWorkaround.javaToKotlinType(env.requireType(j)) + } - private fun motif.compiler.TypeName.reloadedForTypeArgs(env: XProcessingEnv): TypeName { - return if (kt is ParameterizedTypeName) { - kt - } else { - // ensures that type arguments get loaded - KotlinTypeWorkaround.javaToKotlinType(env.requireType(j)) - } - } + private fun FactoryProviderMethodBody.Uncached.spec(): CodeBlock = + CodeBlock.of("return %L", instantiation.spec()) - private fun FactoryProviderMethodBody.Uncached.spec(): CodeBlock { - return CodeBlock.of("return %L", instantiation.spec()) - } - - private fun FactoryProviderInstantiation.spec(): CodeBlock { - return when (this) { - is FactoryProviderInstantiation.Basic -> spec() - is FactoryProviderInstantiation.Constructor -> spec() - is FactoryProviderInstantiation.Binds -> spec() - } - } + private fun FactoryProviderInstantiation.spec(): CodeBlock = + when (this) { + is FactoryProviderInstantiation.Basic -> spec() + is FactoryProviderInstantiation.Constructor -> spec() + is FactoryProviderInstantiation.Binds -> spec() + } private fun FactoryProviderInstantiation.Basic.spec(): CodeBlock { val methodName = factoryMethodName.substringBeforeLast('$') @@ -267,39 +278,35 @@ object KotlinCodeGenerator { } } - private fun FactoryProviderInstantiation.Constructor.spec(): CodeBlock { - return CodeBlock.of("%T%L", returnTypeName.kt, callProviders.spec()) - } + private fun FactoryProviderInstantiation.Constructor.spec(): CodeBlock = + CodeBlock.of("%T%L", returnTypeName.kt, callProviders.spec()) - private fun FactoryProviderInstantiation.Binds.spec(): CodeBlock { - return CodeBlock.of("%N()", providerMethodName) - } + private fun FactoryProviderInstantiation.Binds.spec(): CodeBlock = + CodeBlock.of("%N()", providerMethodName) private fun CallProviders.spec(): String { val callString = providerMethodNames.joinToString { "$it()" } return "($callString)" } - private fun SpreadProviderMethod.spec(): FunSpec { - return FunSpec.builder(name) - .apply { - returns(returnTypeName.kt) - if (isStatic) { - addStatement("return %T.%N()", sourceTypeName.kt, spreadMethodName) - } else { - addStatement("return %N().%N()", sourceProviderMethodName, spreadMethodName) + private fun SpreadProviderMethod.spec(): FunSpec = + FunSpec.builder(name) + .apply { + returns(returnTypeName.kt) + if (isStatic) { + addStatement("return %T.%N()", sourceTypeName.kt, spreadMethodName) + } else { + addStatement("return %N().%N()", sourceProviderMethodName, spreadMethodName) + } } - } - .build() - } + .build() - private fun DependencyProviderMethod.spec(): FunSpec { - return FunSpec.builder(name) - .addModifiers(KModifier.INTERNAL) - .returns(returnTypeName.kt) - .addStatement("return %N.%N()", dependenciesFieldName, dependencyMethodName) - .build() - } + private fun DependencyProviderMethod.spec(): FunSpec = + FunSpec.builder(name) + .addModifiers(KModifier.INTERNAL) + .returns(returnTypeName.kt) + .addStatement("return %N.%N()", dependenciesFieldName, dependencyMethodName) + .build() private fun Dependencies.spec(): TypeSpec { val typeSpecBuilder = @@ -311,17 +318,16 @@ object KotlinCodeGenerator { return typeSpecBuilder.apply { methods.forEach { addFunction(it.spec()) } }.build() } - private fun DependencyMethod.spec(): FunSpec { - return FunSpec.builder(name) - .apply { - qualifier?.let { addAnnotation(it.spec()) } - addModifiers(if (internal) KModifier.INTERNAL else KModifier.PUBLIC) - addModifiers(KModifier.ABSTRACT) - returns(returnTypeName.kt) - addKdoc(javaDoc.spec()) - } - .build() - } + private fun DependencyMethod.spec(): FunSpec = + FunSpec.builder(name) + .apply { + qualifier?.let { addAnnotation(it.spec()) } + addModifiers(if (internal) KModifier.INTERNAL else KModifier.PUBLIC) + addModifiers(KModifier.ABSTRACT) + returns(returnTypeName.kt) + addKdoc(javaDoc.spec()) + } + .build() private fun Qualifier.spec(): AnnotationSpec { val className = @@ -336,39 +342,34 @@ object KotlinCodeGenerator { .build() } - private fun DependencyMethodJavaDoc.spec(): CodeBlock { - return CodeBlock.builder() - .apply { - add("\nRequested from:\n") - requestedFrom.forEach { add(it.spec()) } - add("\n") - } - .build() - } - - private fun JavaDocMethodLink.spec(): CodeBlock { - return CodeBlock.of("* [%L.%N]\n", owner, methodName) - } + private fun DependencyMethodJavaDoc.spec(): CodeBlock = + CodeBlock.builder() + .apply { + add("\nRequested from:\n") + requestedFrom.forEach { add(it.spec()) } + add("\n") + } + .build() - private fun ObjectsImpl.spec(): TypeSpec { - return TypeSpec.classBuilder(className.kt) - .apply { - addModifiers(KModifier.PRIVATE) - if (isInterface) { - addSuperinterface(superClassName.kt) - } else { - superclass(superClassName.kt) + private fun JavaDocMethodLink.spec(): CodeBlock = CodeBlock.of("* [%L.%N]\n", owner, methodName) + + private fun ObjectsImpl.spec(): TypeSpec = + TypeSpec.classBuilder(className.kt) + .apply { + addModifiers(KModifier.PRIVATE) + if (isInterface) { + addSuperinterface(superClassName.kt) + } else { + superclass(superClassName.kt) + } + abstractMethods.forEach { addFunction(it.spec()) } } - abstractMethods.forEach { addFunction(it.spec()) } - } - .build() - } + .build() - private fun ObjectsAbstractMethod.spec(): FunSpec { - return XFunSpec.overriding(overriddenMethod.element, overriddenMethod.owner, env) - .addStatement("throw %T()", UnsupportedOperationException::class) - .build() - } + private fun ObjectsAbstractMethod.spec(): FunSpec = + XFunSpec.overriding(overriddenMethod.element, overriddenMethod.owner, env) + .addStatement("throw %T()", UnsupportedOperationException::class) + .build() private fun suppressAnnotationSpec(vararg names: String): AnnotationSpec = AnnotationSpec.builder(Suppress::class.java) diff --git a/compiler/src/main/kotlin/motif/compiler/KotlinTypeWorkaround.kt b/compiler/src/main/kotlin/motif/compiler/KotlinTypeWorkaround.kt index a51d06fd..94fe75ea 100644 --- a/compiler/src/main/kotlin/motif/compiler/KotlinTypeWorkaround.kt +++ b/compiler/src/main/kotlin/motif/compiler/KotlinTypeWorkaround.kt @@ -33,51 +33,45 @@ import kotlin.reflect.jvm.internal.impl.name.FqName @OptIn(KotlinPoetJavaPoetPreview::class) object KotlinTypeWorkaround { - fun javaToKotlinType(mirror: XType): TypeName { - return javaToKotlinType(asTypeName(mirror), mirror.getProcessingEnv()) - } + fun javaToKotlinType(mirror: XType): TypeName = + javaToKotlinType(asTypeName(mirror), mirror.getProcessingEnv()) fun javaToKotlinType( className: com.squareup.kotlinpoet.ClassName, - env: XProcessingEnv? - ): TypeName { - return javaToKotlinType(className as TypeName, env) - } + env: XProcessingEnv?, + ): TypeName = javaToKotlinType(className as TypeName, env) - private fun asTypeName(mirror: XType): TypeName { - return mirror.typeName.toKTypeName() - } + private fun asTypeName(mirror: XType): TypeName = mirror.typeName.toKTypeName() /** https://github.com/square/kotlinpoet/issues/236#issuecomment-437961476 */ - private fun javaToKotlinType(typeName: TypeName, env: XProcessingEnv?): TypeName { - return when (typeName) { - is ParameterizedTypeName -> - (javaToKotlinType(typeName.rawType, null) as com.squareup.kotlinpoet.ClassName) - .parameterizedBy( - *typeName.typeArguments.map { javaToKotlinType(it, env) }.toTypedArray()) - is WildcardTypeName -> - when { - typeName.inTypes.isNotEmpty() -> - WildcardTypeName.consumerOf(javaToKotlinType(typeName.inTypes.single(), env)) - typeName.outTypes.isNotEmpty() -> - WildcardTypeName.producerOf(javaToKotlinType(typeName.outTypes.single(), env)) - else -> throw IllegalStateException() + private fun javaToKotlinType(typeName: TypeName, env: XProcessingEnv?): TypeName = + when (typeName) { + is ParameterizedTypeName -> + (javaToKotlinType(typeName.rawType, null) as com.squareup.kotlinpoet.ClassName) + .parameterizedBy( + *typeName.typeArguments.map { javaToKotlinType(it, env) }.toTypedArray(), + ) + is WildcardTypeName -> + when { + typeName.inTypes.isNotEmpty() -> + WildcardTypeName.consumerOf(javaToKotlinType(typeName.inTypes.single(), env)) + typeName.outTypes.isNotEmpty() -> + WildcardTypeName.producerOf(javaToKotlinType(typeName.outTypes.single(), env)) + else -> throw IllegalStateException() + } + is TypeVariableName -> STAR + else -> { + val className = + JavaToKotlinClassMap.INSTANCE.mapJavaToKotlin(FqName(typeName.toString())) + ?.asSingleFqName() + ?.asString() + if (className == null) { + typeName.withRawTypeFix(env) + } else { + com.squareup.kotlinpoet.ClassName.bestGuess(className) } - is TypeVariableName -> STAR - else -> { - val className = - JavaToKotlinClassMap.INSTANCE - .mapJavaToKotlin(FqName(typeName.toString())) - ?.asSingleFqName() - ?.asString() - if (className == null) { - typeName.withRawTypeFix(env) - } else { - com.squareup.kotlinpoet.ClassName.bestGuess(className) } } - } - } private fun TypeName.withRawTypeFix(env: XProcessingEnv?): TypeName { if (env?.backend == XProcessingEnv.Backend.KSP && this is com.squareup.kotlinpoet.ClassName) { diff --git a/compiler/src/main/kotlin/motif/compiler/MotifProcessingStep.kt b/compiler/src/main/kotlin/motif/compiler/MotifProcessingStep.kt index 37724906..5e5f40a5 100644 --- a/compiler/src/main/kotlin/motif/compiler/MotifProcessingStep.kt +++ b/compiler/src/main/kotlin/motif/compiler/MotifProcessingStep.kt @@ -43,7 +43,7 @@ class MotifProcessingStep( override fun process( env: XProcessingEnv, elementsByAnnotation: Map>, - isLastRound: Boolean + isLastRound: Boolean, ): Set { messageWatcher?.let { env.messager.addMessageWatcher(messageWatcher) } @@ -83,7 +83,7 @@ class MotifProcessingStep( val mode: OutputMode? = try { - OutputMode.valueOf(env.options[OPTION_MODE]?.toUpperCase() ?: "") + OutputMode.valueOf(env.options[OPTION_MODE]?.uppercase() ?: "") } catch (ignore: IllegalArgumentException) { if (env.backend == XProcessingEnv.Backend.KSP) OutputMode.KOTLIN else null } @@ -98,7 +98,9 @@ class MotifProcessingStep( Expected: ${initialScopeNames.sorted().joinToString(", ")} Created: ${createdScopeNames.sorted().joinToString(", ")} Missing: ${missingScopeNames.sorted().joinToString(", ")} - """.trimIndent()) + """ + .trimIndent(), + ) return emptySet() } diff --git a/compiler/src/main/kotlin/motif/compiler/Names.kt b/compiler/src/main/kotlin/motif/compiler/Names.kt index 49fac078..6935beaa 100644 --- a/compiler/src/main/kotlin/motif/compiler/Names.kt +++ b/compiler/src/main/kotlin/motif/compiler/Names.kt @@ -25,11 +25,13 @@ class NameScope(blacklist: Iterable = emptySet()) { private val names = UniqueNameSet(blacklist) - fun name(type: Type): String { - return names.unique( - Names.safeName( - (type.type as CompilerType).mirror, (type.qualifier as? CompilerAnnotation)?.mirror)) - } + fun name(type: Type): String = + names.unique( + Names.safeName( + (type.type as CompilerType).mirror, + (type.qualifier as? CompilerAnnotation)?.mirror, + ), + ) } private class UniqueNameSet(blacklist: Iterable) { @@ -46,29 +48,25 @@ private class UniqueNameSet(blacklist: Iterable) { } } -class Names { - - companion object { +object Names { - @JvmStatic - fun safeName(typeMirror: XType, annotation: XAnnotation?): String { - var name = XNameVisitor.visit(typeMirror) - val annotationString = annotationString(annotation) - name = "$annotationString$name".decapitalize() - if (name in KEYWORDS) { - name += "_" - } - return name + @JvmStatic + fun safeName(typeMirror: XType, annotation: XAnnotation?): String { + var name = XNameVisitor.visit(typeMirror) + val annotationString = annotationString(annotation) + name = "$annotationString$name".decapitalize() + if (name in KEYWORDS) { + name += "_" } + return name + } - private fun annotationString(annotation: XAnnotation?): String { - return if (annotation?.qualifiedName == "javax.inject.Named") { + private fun annotationString(annotation: XAnnotation?): String = + if (annotation?.qualifiedName == "javax.inject.Named") { annotation.getAnnotationValue("value").value.toString() } else { annotation?.type?.typeElement?.name.orEmpty() } - } - } } private val KEYWORDS = @@ -122,4 +120,5 @@ private val KEYWORDS = "float", "native", "super", - "while") + "while", + ) diff --git a/compiler/src/main/kotlin/motif/compiler/Processor.kt b/compiler/src/main/kotlin/motif/compiler/Processor.kt index 7b71a265..07d136f8 100644 --- a/compiler/src/main/kotlin/motif/compiler/Processor.kt +++ b/compiler/src/main/kotlin/motif/compiler/Processor.kt @@ -26,15 +26,10 @@ const val OPTION_MODE = "motif.mode" class Processor : JavacBasicAnnotationProcessor() { lateinit var graph: ResolvedGraph - override fun getSupportedSourceVersion(): SourceVersion { - return SourceVersion.latestSupported() - } + override fun getSupportedSourceVersion(): SourceVersion = SourceVersion.latestSupported() - override fun processingSteps(): Iterable { - return listOf(MotifProcessingStep(graphSetter = { graph = it })) - } + override fun processingSteps(): Iterable = + listOf(MotifProcessingStep(graphSetter = { graph = it })) - override fun getSupportedOptions(): Set { - return setOf(OPTION_MODE, OPTION_KAPT_KOTLIN_GENERATED) - } + override fun getSupportedOptions(): Set = setOf(OPTION_MODE, OPTION_KAPT_KOTLIN_GENERATED) } diff --git a/compiler/src/main/kotlin/motif/compiler/ScopeImpl.kt b/compiler/src/main/kotlin/motif/compiler/ScopeImpl.kt index 854ee459..499e9c0b 100644 --- a/compiler/src/main/kotlin/motif/compiler/ScopeImpl.kt +++ b/compiler/src/main/kotlin/motif/compiler/ScopeImpl.kt @@ -18,6 +18,7 @@ package motif.compiler import androidx.room.compiler.processing.XProcessingEnv import androidx.room.compiler.processing.XType import androidx.room.compiler.processing.compat.XConverters.getProcessingEnv +import androidx.room.compiler.processing.compat.XConverters.toJavac import androidx.room.compiler.processing.compat.XConverters.toKS import com.squareup.javapoet.ParameterizedTypeName import com.squareup.javapoet.WildcardTypeName @@ -34,6 +35,7 @@ import motif.ast.compiler.CompilerMethod * implementations. */ class ScopeImpl( + val useNullFieldInitialization: Boolean, val className: ClassName, val superClassName: ClassName, val internalScope: Boolean, @@ -49,7 +51,7 @@ class ScopeImpl( val factoryProviderMethods: List, val dependencyProviderMethods: List, val objectsImpl: ObjectsImpl?, - val dependencies: Dependencies? + val dependencies: Dependencies?, ) /** @@ -64,7 +66,7 @@ class ScopeImpl( class ScopeImplAnnotation( val children: List, val scopeClassName: ClassName, - val dependenciesClassName: ClassName + val dependenciesClassName: ClassName, ) /** @@ -75,7 +77,7 @@ class ScopeImplAnnotation( class ObjectsField( val objectsClassName: ClassName, val objectsImplClassName: ClassName, - val name: String + val name: String, ) /** @@ -102,7 +104,7 @@ class CacheField(val name: String) class Constructor( val dependenciesClassName: ClassName, val dependenciesParameterName: String, - val dependenciesFieldName: String + val dependenciesFieldName: String, ) /** @@ -126,7 +128,7 @@ class AccessMethodImpl( // Required work around https://github.com/square/javapoet/issues/656 val env: XProcessingEnv, val overriddenMethod: CompilerMethod, - val providerMethodName: String + val providerMethodName: String, ) /** @@ -142,7 +144,7 @@ class ChildMethodImpl( val childImplClassName: ClassName, val childMethodName: String, val parameters: List, - val childDependenciesImpl: ChildDependenciesImpl + val childDependenciesImpl: ChildDependenciesImpl, ) /** @@ -164,7 +166,7 @@ class ChildDependenciesImpl( val childDependenciesClassName: ClassName, val methods: List, val isAbstractClass: Boolean, - val env: XProcessingEnv + val env: XProcessingEnv, ) /** @@ -179,7 +181,7 @@ class ChildDependencyMethodImpl( val name: String, val returnTypeName: TypeName, val returnExpression: ReturnExpression, - val isInternal: Boolean + val isInternal: Boolean, ) { sealed class ReturnExpression { @@ -221,7 +223,7 @@ class DependencyProviderMethod( val returnTypeName: TypeName, val dependenciesFieldName: String, val dependencyMethodName: String, - val env: XProcessingEnv + val env: XProcessingEnv, ) /** @@ -238,7 +240,7 @@ class FactoryProviderMethod( val returnTypeName: TypeName, val body: FactoryProviderMethodBody, val spreadProviderMethods: List, - val env: XProcessingEnv + val env: XProcessingEnv, ) sealed class FactoryProviderMethodBody { @@ -259,7 +261,7 @@ sealed class FactoryProviderMethodBody { val cacheFieldName: String, val returnTypeName: TypeName, val instantiation: FactoryProviderInstantiation, - val env: XProcessingEnv + val env: XProcessingEnv, ) : FactoryProviderMethodBody() /** @@ -288,7 +290,7 @@ sealed class FactoryProviderInstantiation { val objectsClassName: ClassName, val isStatic: Boolean, val factoryMethodName: String, - val callProviders: CallProviders + val callProviders: CallProviders, ) : FactoryProviderInstantiation() /** @@ -327,7 +329,7 @@ class SpreadProviderMethod( val returnTypeName: TypeName, val sourceTypeName: TypeName, val sourceProviderMethodName: String, - val spreadMethodName: String + val spreadMethodName: String, ) /** @@ -351,7 +353,7 @@ class DependencyMethod( val returnTypeName: TypeName, val qualifier: Qualifier?, val javaDoc: DependencyMethodJavaDoc, - val internal: Boolean + val internal: Boolean, ) /** @@ -379,7 +381,7 @@ class DependencyMethodJavaDoc(val requestedFrom: List) class JavaDocMethodLink( val owner: String, val methodName: String, - val parameterTypes: List + val parameterTypes: List, ) /** @@ -394,7 +396,7 @@ class ObjectsImpl( val className: ClassName, val superClassName: ClassName, val isInterface: Boolean, - val abstractMethods: List + val abstractMethods: List, ) /** @@ -408,7 +410,7 @@ class ObjectsImpl( class ObjectsAbstractMethod( // Required work around https://github.com/square/javapoet/issues/656 val env: XProcessingEnv, - val overriddenMethod: CompilerMethod + val overriddenMethod: CompilerMethod, ) class TypeName private constructor(private val mirror: XType) { @@ -420,10 +422,18 @@ class TypeName private constructor(private val mirror: XType) { jTypeName is com.squareup.javapoet.ClassName) { ParameterizedTypeName.get( jTypeName, - *mirror - .typeArguments + *mirror.typeArguments .map { WildcardTypeName.subtypeOf(Object::class.java) } - .toTypedArray()) + .toTypedArray(), + ) + } else if (mirror.typeArguments.isNotEmpty() && + "<" in jTypeName.toString() && + "$" in jTypeName.toString()) { + // Work around issue where JavaPoet returns a TypeName with a '$' for a Kotlin inner class + ParameterizedTypeName.get( + com.squareup.javapoet.ClassName.get(mirror.typeElement?.toJavac()), + *mirror.typeArguments.map { it.typeName }.toTypedArray(), + ) } else { jTypeName } @@ -456,25 +466,27 @@ class TypeName private constructor(private val mirror: XType) { companion object { - fun get(mirror: XType): TypeName { - return TypeName(mirror) - } + fun get(mirror: XType): TypeName = TypeName(mirror) } } class ClassName -private constructor(val j: com.squareup.javapoet.ClassName, val env: XProcessingEnv?) { +private constructor( + val j: com.squareup.javapoet.ClassName, + val env: XProcessingEnv?, +) { val kt: com.squareup.kotlinpoet.ClassName by lazy { val className = com.squareup.kotlinpoet.ClassName( - j.packageName(), j.simpleNames().first(), *j.simpleNames().drop(1).toTypedArray()) + j.packageName(), + j.simpleNames().first(), + *j.simpleNames().drop(1).toTypedArray(), + ) KotlinTypeWorkaround.javaToKotlinType(className, env) as com.squareup.kotlinpoet.ClassName } - fun nestedClass(name: String): ClassName { - return ClassName(j.nestedClass(name), env) - } + fun nestedClass(name: String): ClassName = ClassName(j.nestedClass(name), env) companion object { @@ -483,8 +495,7 @@ private constructor(val j: com.squareup.javapoet.ClassName, val env: XProcessing return ClassName(j, null) } - fun get(j: com.squareup.javapoet.TypeName, env: XProcessingEnv): ClassName { - return ClassName(j as com.squareup.javapoet.ClassName, env) - } + fun get(j: com.squareup.javapoet.TypeName, env: XProcessingEnv): ClassName = + ClassName(j as com.squareup.javapoet.ClassName, env) } } diff --git a/compiler/src/main/kotlin/motif/compiler/ScopeImplFactory.kt b/compiler/src/main/kotlin/motif/compiler/ScopeImplFactory.kt index 12b71b9c..95a6cbad 100644 --- a/compiler/src/main/kotlin/motif/compiler/ScopeImplFactory.kt +++ b/compiler/src/main/kotlin/motif/compiler/ScopeImplFactory.kt @@ -38,7 +38,10 @@ import motif.models.Spread import motif.models.Type class ScopeImplFactory -private constructor(private val env: XProcessingEnv, private val graph: ResolvedGraph) { +private constructor( + private val env: XProcessingEnv, + private val graph: ResolvedGraph, +) { private val scopeImplClassNames = mutableMapOf() private val dependenciesClassNames = mutableMapOf() @@ -64,7 +67,9 @@ private constructor(private val env: XProcessingEnv, private val graph: Resolved fun create(): ScopeImpl { val isInternal = (scope.clazz as? CompilerClass)?.isInternal() ?: false - return ScopeImpl( + return ScopeImpl( + (scope.clazz.annotations.find { + it.className == motif.Scope::class.java.name }!!.annotationValueMap[SCOPE_ANNOTATION_FIELD_USE_NULL] as? Boolean) ?: false, scope.implClassName, scope.typeName, isInternal, @@ -80,7 +85,8 @@ private constructor(private val env: XProcessingEnv, private val graph: Resolved factoryProviderMethods(), dependencyProviderMethods(), objectsImpl(), - dependencies()) + dependencies(), + ) } private fun scopeImplAnnotation(): ScopeImplAnnotation { @@ -94,56 +100,53 @@ private constructor(private val env: XProcessingEnv, private val graph: Resolved return ObjectsField(objectsClassName, objectsImplClassName, OBJECTS_FIELD_NAME) } - private fun dependenciesField(): DependenciesField { - return DependenciesField(scope.dependenciesClassName, DEPENDENCIES_FIELD_NAME) - } + private fun dependenciesField(): DependenciesField = + DependenciesField(scope.dependenciesClassName, DEPENDENCIES_FIELD_NAME) - private fun cacheFields(): List { - return scope.factoryMethods.filter { it.isCached }.map { factoryMethod -> - CacheField(getCacheFieldName(factoryMethod.returnType.type)) - } - } + private fun cacheFields(): List = + scope.factoryMethods + .filter { it.isCached } + .map { factoryMethod -> CacheField(getCacheFieldName(factoryMethod.returnType.type)) } - private fun constructor(): Constructor { - return Constructor(scope.dependenciesClassName, "dependencies", DEPENDENCIES_FIELD_NAME) - } + private fun constructor(): Constructor = + Constructor(scope.dependenciesClassName, "dependencies", DEPENDENCIES_FIELD_NAME) private fun alternateConstructor(): AlternateConstructor? { - if (getDependencyMethodData(scope).isNotEmpty()) { + if (getDependencyMethodData(scope).isNotEmpty() || + !scope.dependencies?.methods.isNullOrEmpty()) { return null } return AlternateConstructor(scope.dependenciesClassName) } - private fun accessMethodImpls(): List { - return scope.accessMethods.map { accessMethod -> - AccessMethodImpl( - env, - accessMethod.method as CompilerMethod, - getProviderMethodName(accessMethod.returnType)) - } - } - - private fun childMethodImpls(): List { - return graph.getChildEdges(scope).map(this::childMethodImpl) - } + private fun accessMethodImpls(): List = + scope.accessMethods.map { accessMethod -> + AccessMethodImpl( + env, + accessMethod.method as CompilerMethod, + getProviderMethodName(accessMethod.returnType), + ) + } - private fun childMethodImpl(childEdge: ScopeEdge): ChildMethodImpl { + private fun childMethodImpls(): List = + graph.getChildEdges(scope).map(this::childMethodImpl) - return ChildMethodImpl( - childEdge.child.typeName, - childEdge.child.implClassName, - childEdge.method.method.name, - childEdge.method.parameters.map(this::childMethodImplParameter), - childDependenciesImpl(childEdge)) - } + private fun childMethodImpl(childEdge: ScopeEdge): ChildMethodImpl = + ChildMethodImpl( + childEdge.child.typeName, + childEdge.child.implClassName, + childEdge.method.method.name, + childEdge.method.parameters.map(this::childMethodImplParameter), + childDependenciesImpl(childEdge), + ) private fun childMethodImplParameter( - childMethodParameter: ChildMethod.Parameter - ): ChildMethodImplParameter { - return ChildMethodImplParameter( - childMethodParameter.parameter.type.typeName, childMethodParameter.parameter.name) - } + childMethodParameter: ChildMethod.Parameter, + ): ChildMethodImplParameter = + ChildMethodImplParameter( + childMethodParameter.parameter.type.typeName, + childMethodParameter.parameter.name, + ) private fun childDependenciesImpl(childEdge: ScopeEdge): ChildDependenciesImpl { val parameters: Map = @@ -154,24 +157,34 @@ private constructor(private val env: XProcessingEnv, private val graph: Resolved } val isAbstractClass = dependencyMethodImpls.any { it.isInternal } return ChildDependenciesImpl( - childEdge.child.dependenciesClassName, dependencyMethodImpls, isAbstractClass, env) + childEdge.child.dependenciesClassName, + dependencyMethodImpls, + isAbstractClass, + env, + ) } private fun childDependencyMethodImpl( parameters: Map, - methodData: DependencyMethodData + methodData: DependencyMethodData, ): ChildDependencyMethodImpl { val parameter = parameters[methodData.returnType] val returnExpression = if (parameter == null) { ChildDependencyMethodImpl.ReturnExpression.Provider( - scope.implClassName, getProviderMethodName(methodData.returnType)) + scope.implClassName, + getProviderMethodName(methodData.returnType), + ) } else { ChildDependencyMethodImpl.ReturnExpression.Parameter(parameter.parameter.name) } val isInternal = (methodData.returnType.type as? CompilerType)?.isInternal() ?: false return ChildDependencyMethodImpl( - methodData.name, methodData.returnTypeName, returnExpression, isInternal) + methodData.name, + methodData.returnTypeName, + returnExpression, + isInternal, + ) } private fun scopeProviderMethod(): ScopeProviderMethod { @@ -180,19 +193,19 @@ private constructor(private val env: XProcessingEnv, private val graph: Resolved return ScopeProviderMethod(name, scope.typeName, isInternal) } - private fun factoryProviderMethods(): List { - return scope.factoryMethods.map { factoryMethod -> - val returnType = factoryMethod.returnType.type - val spreadProviderMethods = - factoryMethod.spread?.let { spreadProviderMethods(it) } ?: emptyList() - FactoryProviderMethod( - getProviderMethodName(returnType), - returnType.type.typeName, - factoryProviderMethodBody(factoryMethod), - spreadProviderMethods, - env) - } - } + private fun factoryProviderMethods(): List = + scope.factoryMethods.map { factoryMethod -> + val returnType = factoryMethod.returnType.type + val spreadProviderMethods = + factoryMethod.spread?.let { spreadProviderMethods(it) } ?: emptyList() + FactoryProviderMethod( + getProviderMethodName(returnType), + returnType.type.typeName, + factoryProviderMethodBody(factoryMethod), + spreadProviderMethods, + env, + ) + } private fun factoryProviderMethodBody(factoryMethod: FactoryMethod): FactoryProviderMethodBody { val instantiation = @@ -206,48 +219,50 @@ private constructor(private val env: XProcessingEnv, private val graph: Resolved getCacheFieldName(factoryMethod.returnType.type), factoryMethod.returnType.type.type.typeName, instantiation, - env) + env, + ) } else { FactoryProviderMethodBody.Uncached(instantiation) } } - private fun spreadProviderMethods(spread: Spread): List { - return spread.methods.map { method -> - SpreadProviderMethod( - getProviderMethodName(method.returnType), - method.method.isStatic(), - method.returnType.type.typeName, - method.sourceType.type.typeName, - getProviderMethodName(method.sourceType), - method.name) - } - } + private fun spreadProviderMethods(spread: Spread): List = + spread.methods.map { method -> + SpreadProviderMethod( + getProviderMethodName(method.returnType), + method.method.isStatic(), + method.returnType.type.typeName, + method.sourceType.type.typeName, + getProviderMethodName(method.sourceType), + method.name, + ) + } private fun basicInstantiation( - factoryMethod: BasicFactoryMethod - ): FactoryProviderInstantiation.Basic { - return FactoryProviderInstantiation.Basic( - OBJECTS_FIELD_NAME, - factoryMethod.objects.clazz.typeName, - factoryMethod.isStatic, - factoryMethod.name, - callProviders(factoryMethod)) - } + factoryMethod: BasicFactoryMethod, + ): FactoryProviderInstantiation.Basic = + FactoryProviderInstantiation.Basic( + OBJECTS_FIELD_NAME, + factoryMethod.objects.clazz.typeName, + factoryMethod.isStatic, + factoryMethod.name, + callProviders(factoryMethod), + ) private fun constructorInstantiation( - factoryMethod: ConstructorFactoryMethod - ): FactoryProviderInstantiation.Constructor { - return FactoryProviderInstantiation.Constructor( - factoryMethod.returnType.type.type.typeName, callProviders(factoryMethod)) - } + factoryMethod: ConstructorFactoryMethod, + ): FactoryProviderInstantiation.Constructor = + FactoryProviderInstantiation.Constructor( + factoryMethod.returnType.type.type.typeName, + callProviders(factoryMethod), + ) private fun bindsInstantiation( - factoryMethod: BindsFactoryMethod - ): FactoryProviderInstantiation.Binds { - return FactoryProviderInstantiation.Binds( - getProviderMethodName(factoryMethod.parameters.single().type)) - } + factoryMethod: BindsFactoryMethod, + ): FactoryProviderInstantiation.Binds = + FactoryProviderInstantiation.Binds( + getProviderMethodName(factoryMethod.parameters.single().type), + ) private fun callProviders(factoryMethod: FactoryMethod): CallProviders { val names = @@ -255,29 +270,30 @@ private constructor(private val env: XProcessingEnv, private val graph: Resolved return CallProviders(names) } - private fun dependencyProviderMethods(): List { - return getDependencyMethodData(scope).map { methodData -> - DependencyProviderMethod( - getProviderMethodName(methodData.returnType), - methodData.returnTypeName, - DEPENDENCIES_FIELD_NAME, - methodData.name, - env) - } - } + private fun dependencyProviderMethods(): List = + getDependencyMethodData(scope).map { methodData -> + DependencyProviderMethod( + getProviderMethodName(methodData.returnType), + methodData.returnTypeName, + DEPENDENCIES_FIELD_NAME, + methodData.name, + env, + ) + } private fun objectsImpl(): ObjectsImpl? { val objects = scope.objects ?: return null val objectsClassName = scope.objectsClassName ?: return null val abstractMethods = - objects.factoryMethods.filter { it.method.isAbstract() }.map { - ObjectsAbstractMethod(env, it.method as CompilerMethod) - } + objects.factoryMethods + .filter { it.method.isAbstract() } + .map { ObjectsAbstractMethod(env, it.method as CompilerMethod) } return ObjectsImpl( scope.objectsImplClassName, objectsClassName, objects.clazz.kind == IrClass.Kind.INTERFACE, - abstractMethods) + abstractMethods, + ) } private fun dependencies(): Dependencies? { @@ -296,7 +312,8 @@ private constructor(private val env: XProcessingEnv, private val graph: Resolved methodData.returnTypeName, qualifier, javaDoc(methodData), - isInternal) + isInternal, + ) } return Dependencies(scope.dependenciesClassName, methods) } @@ -312,8 +329,11 @@ private constructor(private val env: XProcessingEnv, private val graph: Resolved val owner = removeGenerics(ownerType.qualifiedName) val methodName = - if (callerMethod.isConstructor) ownerType.simpleName.substringBefore('<') - else callerMethod.name + if (callerMethod.isConstructor) { + ownerType.simpleName.substringBefore('<') + } else { + callerMethod.name + } val paramList = callerMethod.parameters.map { removeGenerics(it.type.qualifiedName) } JavaDocMethodLink(owner, methodName, paramList) @@ -321,9 +341,7 @@ private constructor(private val env: XProcessingEnv, private val graph: Resolved return DependencyMethodJavaDoc(requestedFrom) } - private fun removeGenerics(name: String): String { - return name.takeWhile { it != '<' } - } + private fun removeGenerics(name: String): String = name.takeWhile { it != '<' } private fun getProviderMethodName(type: Type): String { // val key = getTypeOrMappedType(type, providerMethodNames.keys) @@ -353,12 +371,11 @@ private constructor(private val env: XProcessingEnv, private val graph: Resolved val name: String, val returnTypeName: TypeName, val returnType: Type, - val sinks: List + val sinks: List, ) - private fun getDependencyMethodData(scope: Scope): List { - return dependencyMethods.computeIfAbsent(scope) { createDependencyMethods(scope) } - } + private fun getDependencyMethodData(scope: Scope): List = + dependencyMethods.computeIfAbsent(scope) { createDependencyMethods(scope) } private fun createDependencyMethods(scope: Scope): List { val nameScope = NameScope() @@ -414,9 +431,10 @@ private constructor(private val env: XProcessingEnv, private val graph: Resolved private const val OBJECTS_FIELD_NAME = "objects" private const val DEPENDENCIES_FIELD_NAME = "dependencies" + private const val SCOPE_ANNOTATION_FIELD_USE_NULL = "useNullFieldInitialization" - fun create(env: XProcessingEnv, graph: ResolvedGraph): List { - return ScopeImplFactory(env, graph).create() - } + + fun create(env: XProcessingEnv, graph: ResolvedGraph): List = + ScopeImplFactory(env, graph).create() } } diff --git a/compiler/src/main/kotlin/motif/compiler/XFunSpec.kt b/compiler/src/main/kotlin/motif/compiler/XFunSpec.kt index 22547fca..62afad73 100644 --- a/compiler/src/main/kotlin/motif/compiler/XFunSpec.kt +++ b/compiler/src/main/kotlin/motif/compiler/XFunSpec.kt @@ -24,18 +24,17 @@ import com.squareup.kotlinpoet.FunSpec import com.squareup.kotlinpoet.KModifier import com.squareup.kotlinpoet.ParameterSpec import com.squareup.kotlinpoet.javapoet.KotlinPoetJavaPoetPreview -import com.squareup.kotlinpoet.javapoet.toKTypeName import com.uber.xprocessing.ext.modifiers import javax.lang.model.element.Modifier import motif.compiler.KotlinTypeWorkaround.javaToKotlinType -@OptIn(KotlinPoetJavaPoetPreview::class) object XFunSpec { /** Copied from [FunSpec.overriding] and modified to leverage [javaToKotlinType]& XProcessing. */ + @OptIn(KotlinPoetJavaPoetPreview::class) fun overriding( executableElement: XExecutableElement, enclosing: XType, - env: XProcessingEnv + env: XProcessingEnv, ): FunSpec.Builder { val methodElement = (executableElement as? XMethodElement) @@ -50,28 +49,21 @@ object XFunSpec { env.requireType(method.returnType.typeName) } - val builder = overriding(methodElement) - builder.returns(javaToKotlinType(returnType)) - - var i = 0 - val size = builder.parameters.size - val resolvedParameterTypes = method.parameterTypes - while (i < size) { - val parameter = builder.parameters[i] - val type = javaToKotlinType(resolvedParameterTypes[i]) - builder.parameters[i] = parameter.toBuilder(parameter.name, type).build() - i++ - } - - return builder + return overriding(methodElement, method.parameterTypes).returns(javaToKotlinType(returnType)) } - private fun overriding(method: XMethodElement): FunSpec.Builder { + private fun overriding( + method: XMethodElement, + resolvedParameterTypes: List, + ): FunSpec.Builder { var modifiers: Set = method.modifiers.toMutableSet() require( Modifier.PRIVATE !in modifiers && Modifier.FINAL !in modifiers && - Modifier.STATIC !in modifiers) { "cannot override method with modifiers: $modifiers" } + Modifier.STATIC !in modifiers, + ) { + "cannot override method with modifiers: $modifiers" + } val methodName = method.name val funBuilder = FunSpec.builder(methodName) @@ -84,15 +76,17 @@ object XFunSpec { // TODO: Unsupported until XProcessing is updated /* - method as XParam - .map { it.asType() as TypeVariable } - .map { it.asTypeVariableName() } - .forEach { funBuilder.addTypeVariable(it) } - */ + method as XParam + .map { it.asType() as TypeVariable } + .map { it.asTypeVariableName() } + .forEach { funBuilder.addTypeVariable(it) } + */ - method.parameters.forEach { + method.parameters.forEachIndexed { index, parameter -> funBuilder.addParameter( - ParameterSpec.builder(it.name, it.type.typeName.toKTypeName()).build()) + ParameterSpec.builder(parameter.name, javaToKotlinType(resolvedParameterTypes[index])) + .build(), + ) } if (method.isVarArgs()) { funBuilder.parameters[funBuilder.parameters.lastIndex] = @@ -104,7 +98,8 @@ object XFunSpec { funBuilder.addAnnotation( AnnotationSpec.builder(Throws::class) .addMember(throwsValueString, *method.thrownTypes.toTypedArray()) - .build()) + .build(), + ) } return funBuilder diff --git a/compiler/src/main/kotlin/motif/compiler/XNameVisitor.kt b/compiler/src/main/kotlin/motif/compiler/XNameVisitor.kt index ff65cb7a..f540e9a2 100644 --- a/compiler/src/main/kotlin/motif/compiler/XNameVisitor.kt +++ b/compiler/src/main/kotlin/motif/compiler/XNameVisitor.kt @@ -33,36 +33,34 @@ import com.uber.xprocessing.ext.isPrimitive object XNameVisitor { - fun visit(t: XType): String { - return when { - t.isVoid() -> visitNoType(t) - t.isError() && - t.typeElement?.qualifiedName.orEmpty().let { "ERROR" in it || "NonExistent" in it } -> - visitError(t) - t.isArray() -> visitArray(t) - t.isWildcard() -> visitWildcard(t) - t.isDeclaredType() -> visitDeclared(t) - t.isPrimitive() -> visitPrimitive(t) - t.isEnum() -> visitDeclared(t) - t.isKotlinUnit() -> visitDeclared(t) - t.isTypeVariable() -> visitTypeVariable(t) - else -> visitNoType(t) - } - } - - private fun visitPrimitive(t: XType): String { - return when (t.typeName) { - TypeName.BOOLEAN -> "Boolean" - TypeName.BYTE -> "Byte" - TypeName.SHORT -> "Short" - TypeName.INT -> "Integer" - TypeName.LONG -> "Long" - TypeName.CHAR -> "Character" - TypeName.FLOAT -> "Float" - TypeName.DOUBLE -> "Double" - else -> throw IllegalStateException() - } - } + fun visit(t: XType): String = + when { + t.isVoid() -> visitNoType(t) + t.isError() && + t.typeElement?.qualifiedName.orEmpty().let { "ERROR" in it || "NonExistent" in it } -> + visitError(t) + t.isArray() -> visitArray(t) + t.isWildcard() -> visitWildcard(t) + t.isDeclaredType() -> visitDeclared(t) + t.isPrimitive() -> visitPrimitive(t) + t.isEnum() -> visitDeclared(t) + t.isKotlinUnit() -> visitDeclared(t) + t.isTypeVariable() -> visitTypeVariable(t) + else -> visitNoType(t) + } + + private fun visitPrimitive(t: XType): String = + when (t.typeName) { + TypeName.BOOLEAN -> "Boolean" + TypeName.BYTE -> "Byte" + TypeName.SHORT -> "Short" + TypeName.INT -> "Integer" + TypeName.LONG -> "Long" + TypeName.CHAR -> "Character" + TypeName.FLOAT -> "Float" + TypeName.DOUBLE -> "Double" + else -> throw IllegalStateException() + } private fun visitDeclared(t: XType, p: Void? = null): String { t.typeElement?.kindName() @@ -105,13 +103,10 @@ object XNameVisitor { return "$typeArgumentString$rawString" } - private fun visitArray(t: XArrayType, p: Void? = null): String { - return visit(t.componentType) + "Array" - } + private fun visitArray(t: XArrayType, p: Void? = null): String = visit(t.componentType) + "Array" - private fun visitTypeVariable(t: XType, p: Void? = null): String { - return t.typeName.toString().capitalize() - } + private fun visitTypeVariable(t: XType, p: Void? = null): String = + t.typeName.toString().capitalize() private fun visitWildcard(t: XType, p: Void? = null): String { if (t.typeName.toString() == "?" || t.typeName.toString() == "*") { @@ -119,28 +114,20 @@ object XNameVisitor { } return t.extendsBound()?.let { return visit(it) - } - ?: "" + } ?: "" } - private fun visitNoType(t: XType): String { - return if (t.isVoid()) "Void" else defaultAction(t) - } + private fun visitNoType(t: XType): String = if (t.isVoid()) "Void" else defaultAction(t) - private fun visitError(t: XType, p: Void? = null): String { - throw IllegalStateException( - "Could not generate name for ErrorType: $t. Check your code for missing imports or typos.") - } + private fun visitError(t: XType, p: Void? = null): String = + throw IllegalStateException( + "Could not generate name for ErrorType: $t. Check your code for missing imports or typos.", + ) - private fun defaultAction(t: XType?): String { - throw IllegalArgumentException("Unexpected type mirror: $t") - } + private fun defaultAction(t: XType?): String = + throw IllegalArgumentException("Unexpected type mirror: $t") } -private fun XType.isWildcard(): Boolean { - return typeName is WildcardTypeName -} +private fun XType.isWildcard(): Boolean = typeName is WildcardTypeName -private fun XType.isTypeVariable(): Boolean { - return typeName is TypeVariableName -} +private fun XType.isTypeVariable(): Boolean = typeName is TypeVariableName diff --git a/compiler/src/test/java/license/LicenseTest.kt b/compiler/src/test/java/license/LicenseTest.kt index 71e460d4..60528139 100644 --- a/compiler/src/test/java/license/LicenseTest.kt +++ b/compiler/src/test/java/license/LicenseTest.kt @@ -42,7 +42,8 @@ class LicenseTest { * See the License for the specific language governing permissions and * limitations under the License. */ - """.trimIndent() + """ + .trimIndent() @Test fun test() { diff --git a/compiler/src/test/java/motif/compiler/NamesTest.kt b/compiler/src/test/java/motif/compiler/NamesTest.kt index 448f1b3b..64ba38d3 100644 --- a/compiler/src/test/java/motif/compiler/NamesTest.kt +++ b/compiler/src/test/java/motif/compiler/NamesTest.kt @@ -24,27 +24,28 @@ import com.google.common.collect.Sets.cartesianProduct import com.google.common.truth.Truth.assertThat import com.squareup.javapoet.ClassName import javax.inject.Qualifier -import motif.compiler.Names.Companion.safeName +import motif.compiler.Names.safeName import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.Parameterized @RunWith(Parameterized::class) @OptIn(ExperimentalProcessingApi::class) -class XNamesTest(private val processorType: ProcessorType, private val srcLang: SourceLanguage) { +class NamesTest(private val processorType: ProcessorType, private val srcLang: SourceLanguage) { companion object { @JvmStatic @Parameterized.Parameters(name = "{0}_{1}") - fun data(): Collection> { - return cartesianProduct( - ProcessorType.values().toSortedSet(), SourceLanguage.values().toSortedSet()) - .filterNot { (proc, srcLang) -> - proc == ProcessorType.AP && srcLang == SourceLanguage.KOTLIN - } - .map { it.toTypedArray() as Array } - .toList() - } + fun data(): Collection> = + cartesianProduct( + ProcessorType.values().toSortedSet(), + SourceLanguage.values().toSortedSet(), + ) + .filterNot { (proc, srcLang) -> + proc == ProcessorType.AP && srcLang == SourceLanguage.KOTLIN + } + .map { it.toTypedArray() as Array } + .toList() } @Test @@ -67,7 +68,8 @@ class XNamesTest(private val processorType: ProcessorType, private val srcLang: assertSafeName( "java.util.HashMap", "java.util.HashMap", - "stringIntegerHashMap") + "stringIntegerHashMap", + ) } @Test @@ -75,7 +77,8 @@ class XNamesTest(private val processorType: ProcessorType, private val srcLang: assertSafeName( "java.util.HashMap", "java.util.HashMap", - "stringIntegerHashMap") + "stringIntegerHashMap", + ) } @Test @@ -98,7 +101,8 @@ class XNamesTest(private val processorType: ProcessorType, private val srcLang: assertSafeName( "java.util.HashMap, Integer>", "java.util.HashMap, Integer>", - "stringIntegerHashMapIntegerHashMap") + "stringIntegerHashMapIntegerHashMap", + ) } @Test @@ -106,7 +110,8 @@ class XNamesTest(private val processorType: ProcessorType, private val srcLang: assertSafeName( "java.util.Map.Entry", "java.util.Map.Entry", - "stringIntegerMapEntry") + "stringIntegerMapEntry", + ) } @Test @@ -147,7 +152,7 @@ class XNamesTest(private val processorType: ProcessorType, private val srcLang: private fun compile( classString: String, qualifierString: String, - assertion: (XTestInvocation, String) -> Unit + assertion: (XTestInvocation, String) -> Unit, ) { when (processorType) { ProcessorType.AP -> { @@ -236,7 +241,7 @@ class XNamesTest(private val processorType: ProcessorType, private val srcLang: javaClassString: String, ktClassString: String, expectedSafeName: String, - qualifierString: String = "" + qualifierString: String = "", ) { val assertion = { invocation: XTestInvocation, safeName: String -> invocation.assertCompilationResult { diff --git a/compiler/src/test/java/motif/compiler/ProGuard.kt b/compiler/src/test/java/motif/compiler/ProGuard.kt index 0878d5e3..732c7d67 100644 --- a/compiler/src/test/java/motif/compiler/ProGuard.kt +++ b/compiler/src/test/java/motif/compiler/ProGuard.kt @@ -32,20 +32,20 @@ object ProGuard { private val classPathFiles: List by lazy { listOf( - Scope::class, - Truth::class, - Inject::class, - Nullable::class, - Component::class, - Unit::class, - NotNull::class) + Scope::class, + Truth::class, + Inject::class, + Nullable::class, + Component::class, + Unit::class, + NotNull::class, + ) .map { libraryPath(it) } } @JvmStatic - fun run(externalClassesDirs: List, classesDir: File, proguardFile: File): File { - return run(externalClassesDirs, listOf(classesDir), proguardFile) - } + fun run(externalClassesDirs: List, classesDir: File, proguardFile: File): File = + run(externalClassesDirs, listOf(classesDir), proguardFile) @JvmStatic fun run(externalClassesDirs: List, classesDirs: List, proguardFile: File): File { @@ -76,7 +76,6 @@ object ProGuard { return outputJar } - private fun libraryPath(clazz: KClass<*>): File { - return File(clazz.java.protectionDomain.codeSource.location.toURI()) - } + private fun libraryPath(clazz: KClass<*>): File = + File(clazz.java.protectionDomain.codeSource.location.toURI()) } diff --git a/compiler/src/test/java/motif/compiler/TestHarness.kt b/compiler/src/test/java/motif/compiler/TestHarness.kt index 0eabbeee..a2d7e328 100644 --- a/compiler/src/test/java/motif/compiler/TestHarness.kt +++ b/compiler/src/test/java/motif/compiler/TestHarness.kt @@ -80,7 +80,10 @@ class TestHarness( val testCaseDirs = TEST_CASE_ROOT.listFiles { file: File -> isTestDir(file) } val combos = cartesianProduct( - ProcessorType.values().toList(), OutputMode.values().toList(), testCaseDirs.toList()) + ProcessorType.values().toList(), + OutputMode.values().toList(), + testCaseDirs.toList(), + ) return combos .filterNot { (_, mode, dir) -> mode == OutputMode.KOTLIN && (dir as File).resolve("SKIP_KOTLIN").exists() @@ -90,7 +93,7 @@ class TestHarness( // We don't generate Java from KSP proc == ProcessorType.KSP && mode == OutputMode.JAVA } - .filterNot { (proc, mode, dir, name) -> + .filterNot { (proc, _, _, name) -> // Can't run KSP on Java sources until fixed: https://github.com/google/ksp/issues/1086 proc == ProcessorType.KSP && "$name"[0] in setOf('T', 'E') } @@ -98,14 +101,12 @@ class TestHarness( testFilter(proc as ProcessorType, mode as OutputMode, dir as File, name as String) } .map { it.toTypedArray() as Array } - .toList() } - private fun isTestDir(file: File): Boolean { - return file.isDirectory && - file.listFiles().isNotEmpty() && - file.name.matches("^K?[TE].*".toRegex()) - } + private fun isTestDir(file: File): Boolean = + file.isDirectory && + file.listFiles().isNotEmpty() && + file.name.matches("^K?[TE].*".toRegex()) } @Test @@ -119,7 +120,7 @@ class TestHarness( ProcessorType.AP -> annotationProcessor?.graph ProcessorType.KSP -> symbolProcessorProvider?.graph } - ?: throw IllegalStateException("No graph found during processing") + checkNotNull(graph) { "No graph found during processing" } if (isErrorTest) { runErrorTest(result, graph) @@ -132,14 +133,15 @@ class TestHarness( result.outputClasspath.filterNot { "/ksp/" in it.absolutePath || "/kapt/" in it.absolutePath }, - proguardFile) + proguardFile, + ) val urls = (externalClassesDirs + proguardedClasses).map { it.toURI().toURL() }.toTypedArray() val classLoader: ClassLoader = URLClassLoader(urls, javaClass.classLoader) if (externalClassesDirs.isEmpty()) { URLClassLoader(arrayOf(proguardedClasses.toURI().toURL()), javaClass.classLoader) - } else {} + } val testClass = classLoader.loadClass(testClassName) if (testClass.getAnnotation(Ignore::class.java) == null) { @@ -159,7 +161,8 @@ class TestHarness( externalDir, if (shouldProcess) annotationProcessor else null, if (shouldProcess) symbolProcessorProvider else null, - emptyList()) + emptyList(), + ) assertSucceeded(externalResult) return externalResult.outputClasspath.filter { it.listFilesRecursively().isNotEmpty() } } @@ -171,9 +174,9 @@ class TestHarness( sourcesDir: File, annotationProcessor: javax.annotation.processing.Processor?, symbolProcessorProvider: SymbolProcessorProvider?, - classpath: List = emptyList() + classpath: List = emptyList(), ): TestCompilationResult { - val processorOptions = mapOf("motif.mode" to outputMode.name.toLowerCase()) + val processorOptions = mapOf("motif.mode" to outputMode.name.lowercase()) val sources = getFiles(sourcesDir).asSources() val annotationProcessors = annotationProcessor?.let { listOf(it, ComponentProcessor()) } ?: emptyList() @@ -185,10 +188,12 @@ class TestHarness( classpath = classpath, inheritClasspath = true, kaptProcessors = annotationProcessors, - symbolProcessorProviders = symbolProcessorProvider?.let { listOf(it) } - ?: emptyList(), + kotlincArguments = listOf("-language-version", "1.9", "-api-version", "1.9"), + symbolProcessorProviders = + symbolProcessorProvider?.let { listOf(it) } ?: emptyList(), processorOptions = processorOptions, - )) + ), + ) } private fun List.asSources(): List { @@ -203,13 +208,12 @@ class TestHarness( } } - private fun getFiles(dir: File): List { - return dir.walkTopDown() - .filter { - !it.isDirectory && it.extension in setOf("kt", "java") && it.name != "ScopeImpl.java" - } - .toList() - } + private fun getFiles(dir: File): List = + dir.walkTopDown() + .filter { + !it.isDirectory && it.extension in setOf("kt", "java") && it.name != "ScopeImpl.java" + } + .toList() private fun createProcessors(): Pair { val xProcConfig = XProcessingEnvConfig.DEFAULT.copy(false, true) @@ -250,7 +254,9 @@ class TestHarness( Error message has changed. The ERROR.txt file has been automatically updated by this test: 1. Verify that the changes are correct. 2. Commit the changes to source control. - """.trimIndent()) + """ + .trimIndent(), + ) .that(actualErrorString) .isEqualTo(expectedErrorString) } @@ -278,7 +284,9 @@ class TestHarness( Graph representation has changed. The GRAPH.txt file has been automatically updated by this test: 1. Verify that the changes are correct. 2. Commit the changes to source control. - """.trimIndent()) + """ + .trimIndent(), + ) .fail() } } @@ -301,18 +309,18 @@ class TestHarness( # # ######################################################################## - """.trimIndent() + """ + .trimIndent() return "$header\n$message\n" } @Throws(IOException::class) - private fun getExistingGraphString(): String { - return if (graphFile.exists()) { - com.google.common.io.Files.asCharSource(graphFile, Charset.defaultCharset()).read() - } else { - "" - } - } + private fun getExistingGraphString(): String = + if (graphFile.exists()) { + com.google.common.io.Files.asCharSource(graphFile, Charset.defaultCharset()).read() + } else { + "" + } private fun getActualErrorString(result: TestCompilationResult): String { val message = getMessage(result) @@ -331,7 +339,8 @@ class TestHarness( # # ######################################################################## - """.trimIndent() + """ + .trimIndent() return "$header\n$message\n" } @@ -343,28 +352,25 @@ class TestHarness( return "$header$resultMessage$footer".prependIndent(" ") } - private fun toCompilerMessage(message: String): String { - return message.trim().prependIndent(" ") - } + private fun toCompilerMessage(message: String): String = message.trim().prependIndent(" ") @Throws(IOException::class) - private fun getExistingErrorString(): String { - return if (errorFile.exists()) { - com.google.common.io.Files.asCharSource(errorFile, Charset.defaultCharset()).read() - } else { - "" - } - } + private fun getExistingErrorString(): String = + if (errorFile.exists()) { + com.google.common.io.Files.asCharSource(errorFile, Charset.defaultCharset()).read() + } else { + "" + } } internal fun File.listFilesRecursively() = walkTopDown().filter { it.isFile }.toList() -private fun TestCompilationResult.success() = success - -private fun TestCompilationResult.messages(): String { - return diagnostics[Diagnostic.Kind.ERROR] - .orEmpty() - .flatMap { it.msg.lines() } - .map { " $it" } - .joinToString(separator = "\n") -} +private fun TestCompilationResult.messages(): String = + diagnostics[Diagnostic.Kind.ERROR] + .orEmpty() + .flatMap { it.msg.lines() } + .joinToString( + separator = "\n", + ) { + " $it" + } diff --git a/compiler/src/test/java/motif/compiler/TestParameters.kt b/compiler/src/test/java/motif/compiler/TestParameters.kt index 6c93a515..d165c36f 100644 --- a/compiler/src/test/java/motif/compiler/TestParameters.kt +++ b/compiler/src/test/java/motif/compiler/TestParameters.kt @@ -17,10 +17,10 @@ package motif.compiler enum class ProcessorType { AP, - KSP + KSP, } enum class SourceLanguage { JAVA, - KOTLIN + KOTLIN, } diff --git a/core/build.gradle b/core/build.gradle index c1eba4b3..275159a5 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -1,14 +1,10 @@ plugins { id 'org.jetbrains.kotlin.jvm' - id 'org.jetbrains.dokka' + id 'com.vanniktech.maven.publish' } -sourceCompatibility = 1.8 - dependencies { api project(':models') implementation deps.kotlin.stdlib } - -apply plugin: 'com.vanniktech.maven.publish' diff --git a/core/src/main/kotlin/motif/core/Cycle.kt b/core/src/main/kotlin/motif/core/Cycle.kt index 0851a361..d4c2c62a 100644 --- a/core/src/main/kotlin/motif/core/Cycle.kt +++ b/core/src/main/kotlin/motif/core/Cycle.kt @@ -25,15 +25,14 @@ class Cycle(val path: List) { companion object { - fun find(items: Iterable, getChildren: (T) -> Iterable): Cycle? { - return CycleFinder(items, getChildren).find() - } + fun find(items: Iterable, getChildren: (T) -> Iterable): Cycle? = + CycleFinder(items, getChildren).find() } } private class CycleFinder( private val items: Iterable, - private val getChildren: (T) -> Iterable + private val getChildren: (T) -> Iterable, ) { fun find(): Cycle? { diff --git a/core/src/main/kotlin/motif/core/ProcessingError.kt b/core/src/main/kotlin/motif/core/ProcessingError.kt index 09f8e776..0bb336a0 100644 --- a/core/src/main/kotlin/motif/core/ProcessingError.kt +++ b/core/src/main/kotlin/motif/core/ProcessingError.kt @@ -34,5 +34,5 @@ class UnexposedSourceError(val source: Source, val sink: Sink) : ProcessingError class AlreadySatisfiedError( val scope: Scope, val source: Source, - val existingSources: List + val existingSources: List, ) : ProcessingError() diff --git a/core/src/main/kotlin/motif/core/ResolvedGraph.kt b/core/src/main/kotlin/motif/core/ResolvedGraph.kt index a630d2d6..e1c31a72 100644 --- a/core/src/main/kotlin/motif/core/ResolvedGraph.kt +++ b/core/src/main/kotlin/motif/core/ResolvedGraph.kt @@ -128,18 +128,31 @@ private class ErrorGraph(error: MotifError) : ResolvedGraph { override val roots = emptyList() override val scopes = emptyList() override val errors = listOf(error) + override fun getScope(scopeType: IrType) = null + override fun getChildEdges(scope: Scope) = emptyList() + override fun getParentEdges(scope: Scope) = emptyList() + override fun getChildUnsatisfied(scopeEdge: ScopeEdge) = emptyList() + override fun getUnsatisfied(scope: Scope) = emptyMap>() + override fun getSources(scope: Scope) = emptyList() + override fun getSinks(type: Type) = emptyList() + override fun getSinks(irType: IrType) = emptyList() + override fun getSources(irType: IrType) = emptyList() + override fun getSinks(scope: Scope) = emptyList() + override fun getProviders(sink: Sink) = emptyList() + override fun getConsumers(source: Source) = emptyList() + override fun getRequired(source: Source) = emptyList() } @@ -147,7 +160,7 @@ private class ValidResolvedGraph( private val scopeGraph: ScopeGraph, private val scopeStates: Map, private val childStates: Map, - private val graphState: State + private val graphState: State, ) : ResolvedGraph { private val scopeSinks = mutableMapOf>() diff --git a/core/src/main/kotlin/motif/core/ScopeGraph.kt b/core/src/main/kotlin/motif/core/ScopeGraph.kt index 0c03b581..623fb056 100644 --- a/core/src/main/kotlin/motif/core/ScopeGraph.kt +++ b/core/src/main/kotlin/motif/core/ScopeGraph.kt @@ -45,28 +45,21 @@ internal class ScopeGraph private constructor(val scopes: List) { val parsingErrors: List = scopes.filterIsInstance().map { it.parsingError } - fun getChildEdges(scope: Scope): List { - return childEdges[scope] - ?: throw NullPointerException("Scope not found: ${scope.qualifiedName}") - } + fun getChildEdges(scope: Scope): List = + childEdges[scope] ?: throw NullPointerException("Scope not found: ${scope.qualifiedName}") - fun getParentEdges(scope: Scope): List { - return parentEdges[scope] - ?: throw NullPointerException("Scope not found: ${scope.qualifiedName}") - } + fun getParentEdges(scope: Scope): List = + parentEdges[scope] ?: throw NullPointerException("Scope not found: ${scope.qualifiedName}") - fun getScope(scopeType: IrType): Scope? { - return scopeMap[scopeType] - } + fun getScope(scopeType: IrType): Scope? = scopeMap[scopeType] - private fun createChildren(scope: Scope): List { - return scope.childMethods.map { method -> - val childScope = - getScope(method.childScopeClass.type) - ?: throw IllegalStateException("Scope not found: ${scope.qualifiedName}") - ScopeEdge(scope, childScope, method) - } - } + private fun createChildren(scope: Scope): List = + scope.childMethods.map { method -> + val childScope = + getScope(method.childScopeClass.type) + ?: throw IllegalStateException("Scope not found: ${scope.qualifiedName}") + ScopeEdge(scope, childScope, method) + } private fun calculateCycle(): ScopeCycleError? { // Sort for stable tests @@ -78,8 +71,6 @@ internal class ScopeGraph private constructor(val scopes: List) { companion object { - fun create(scopes: List): ScopeGraph { - return ScopeGraph(scopes) - } + fun create(scopes: List): ScopeGraph = ScopeGraph(scopes) } } diff --git a/core/src/main/kotlin/motif/core/State.kt b/core/src/main/kotlin/motif/core/State.kt index 468d6a71..11a78a49 100644 --- a/core/src/main/kotlin/motif/core/State.kt +++ b/core/src/main/kotlin/motif/core/State.kt @@ -24,9 +24,7 @@ import motif.models.Type private typealias SetMultiMap = MutableMap> -private fun setMultiMap(): SetMultiMap { - return LinkedHashMap() -} +private fun setMultiMap(): SetMultiMap = LinkedHashMap() /** Carries state through the ResolvedGraph creation logic. */ internal class State( @@ -38,7 +36,7 @@ internal class State( val irTypeToSinks: SetMultiMap = setMultiMap(), val irTypeToSources: SetMultiMap = setMultiMap(), private val exposeNeeded: MutableSet = mutableSetOf(), - private val visibleSinks: SetMultiMap = setMultiMap() + private val visibleSinks: SetMultiMap = setMultiMap(), ) { val edges = LinkedHashMap>() @@ -95,10 +93,10 @@ internal class State( fun checkCycle() { Cycle.find(edges.keys) { source -> - edges.getOrDefault(source, emptyList()).flatMap { sink -> - sinkToSources.getOrDefault(sink, LinkedHashSet()) - } - } + edges.getOrDefault(source, emptyList()).flatMap { sink -> + sinkToSources.getOrDefault(sink, LinkedHashSet()) + } + } ?.let { cycle -> errors.add(DependencyCycleError(cycle.path)) } } @@ -106,18 +104,18 @@ internal class State( exposeNeeded.addAll(visibleSinks.keys) } - fun copy(): State { - return State( - sinkToSources.copy(), - sourceToSinks.copy(), - unsatisfied.toMutableSet(), - errors.toMutableList(), - sinks.copy(), - irTypeToSinks.copy(), - irTypeToSources.copy(), - exposeNeeded.toMutableSet(), - visibleSinks.copy()) - } + fun copy(): State = + State( + sinkToSources.copy(), + sourceToSinks.copy(), + unsatisfied.toMutableSet(), + errors.toMutableList(), + sinks.copy(), + irTypeToSinks.copy(), + irTypeToSources.copy(), + exposeNeeded.toMutableSet(), + visibleSinks.copy(), + ) private fun satisfy(sink: Sink, source: Source) { if (!visibleSinks.contains(sink)) return @@ -148,18 +146,18 @@ internal class State( companion object { - fun merge(states: List): State { - return State( - states.map { it.sinkToSources }.merge(), - states.map { it.sourceToSinks }.merge(), - states.map { it.unsatisfied }.merge(), - states.map { it.errors }.merge(), - states.map { it.sinks }.merge(), - states.map { it.irTypeToSinks }.merge(), - states.map { it.irTypeToSources }.merge(), - states.map { it.exposeNeeded }.merge(), - states.map { it.visibleSinks }.merge()) - } + fun merge(states: List): State = + State( + states.map { it.sinkToSources }.merge(), + states.map { it.sourceToSinks }.merge(), + states.map { it.unsatisfied }.merge(), + states.map { it.errors }.merge(), + states.map { it.sinks }.merge(), + states.map { it.irTypeToSinks }.merge(), + states.map { it.irTypeToSources }.merge(), + states.map { it.exposeNeeded }.merge(), + states.map { it.visibleSinks }.merge(), + ) } } diff --git a/errormessage/build.gradle b/errormessage/build.gradle index eda0a098..7a5b3d61 100644 --- a/errormessage/build.gradle +++ b/errormessage/build.gradle @@ -1,14 +1,9 @@ plugins { id 'org.jetbrains.kotlin.jvm' - id 'org.jetbrains.dokka' + id 'com.vanniktech.maven.publish' } -sourceCompatibility = 1.8 - - dependencies { implementation deps.kotlin.stdlib implementation project(':core') } - -apply plugin: 'com.vanniktech.maven.publish' diff --git a/errormessage/src/main/kotlin/motif/errormessage/AccessMethodParametersHandler.kt b/errormessage/src/main/kotlin/motif/errormessage/AccessMethodParametersHandler.kt index 686e6b24..0ad27475 100644 --- a/errormessage/src/main/kotlin/motif/errormessage/AccessMethodParametersHandler.kt +++ b/errormessage/src/main/kotlin/motif/errormessage/AccessMethodParametersHandler.kt @@ -31,6 +31,8 @@ internal class AccessMethodParametersHandler(private val error: AccessMethodPara Suggestions: * If this method was intended to be a child method, ensure that the return type is a Scope. - """.trimIndent()) + """ + .trimIndent(), + ) } } diff --git a/errormessage/src/main/kotlin/motif/errormessage/CannotResolveTypeHandler.kt b/errormessage/src/main/kotlin/motif/errormessage/CannotResolveTypeHandler.kt index a7c73440..11b2b081 100644 --- a/errormessage/src/main/kotlin/motif/errormessage/CannotResolveTypeHandler.kt +++ b/errormessage/src/main/kotlin/motif/errormessage/CannotResolveTypeHandler.kt @@ -31,6 +31,8 @@ internal class CannotResolveTypeHandler(private val error: CannotResolveType) : Suggestions: * Check if the module of ${error.type.qualifiedName} is provided as a dependency to the module where the parent scope of ${error.scope.qualifiedName} is defined. - """.trimIndent()) + """ + .trimIndent(), + ) } } diff --git a/errormessage/src/main/kotlin/motif/errormessage/DependencyMethodWithParametersHandler.kt b/errormessage/src/main/kotlin/motif/errormessage/DependencyMethodWithParametersHandler.kt index 0d89ea2a..8684eebb 100644 --- a/errormessage/src/main/kotlin/motif/errormessage/DependencyMethodWithParametersHandler.kt +++ b/errormessage/src/main/kotlin/motif/errormessage/DependencyMethodWithParametersHandler.kt @@ -18,7 +18,7 @@ package motif.errormessage import motif.models.DependencyMethodWithParameters internal class DependencyMethodWithParametersHandler( - private val error: DependencyMethodWithParameters + private val error: DependencyMethodWithParameters, ) : ErrorHandler { override val name = "DEPENDENCY METHOD PARAMETER" @@ -29,6 +29,8 @@ internal class DependencyMethodWithParametersHandler( Methods on dependencies interfaces must be parameterless: ${error.dependenciesClass.qualifiedName}.${error.method.name} - """.trimIndent()) + """ + .trimIndent(), + ) } } diff --git a/errormessage/src/main/kotlin/motif/errormessage/DuplicatedChildParameterSourceHandler.kt b/errormessage/src/main/kotlin/motif/errormessage/DuplicatedChildParameterSourceHandler.kt index ce93b62d..84b09ff6 100644 --- a/errormessage/src/main/kotlin/motif/errormessage/DuplicatedChildParameterSourceHandler.kt +++ b/errormessage/src/main/kotlin/motif/errormessage/DuplicatedChildParameterSourceHandler.kt @@ -18,7 +18,7 @@ package motif.errormessage import motif.models.DuplicatedChildParameterSource internal class DuplicatedChildParameterSourceHandler( - private val error: DuplicatedChildParameterSource + private val error: DuplicatedChildParameterSource, ) : ErrorHandler { override val name = "DUPLICATED CHILD PARAMETER SOURCE" @@ -29,7 +29,9 @@ internal class DuplicatedChildParameterSourceHandler( Multiple child method parameters of the same type: ${error.childScopeMethod.qualifiedName}(${highlightDuplicatedParameters()}) - """.trimIndent()) + """ + .trimIndent(), + ) } private fun highlightDuplicatedParameters(): String { diff --git a/errormessage/src/main/kotlin/motif/errormessage/DuplicatedDependenciesMethodHandler.kt b/errormessage/src/main/kotlin/motif/errormessage/DuplicatedDependenciesMethodHandler.kt index 3a40fd99..429a2afe 100644 --- a/errormessage/src/main/kotlin/motif/errormessage/DuplicatedDependenciesMethodHandler.kt +++ b/errormessage/src/main/kotlin/motif/errormessage/DuplicatedDependenciesMethodHandler.kt @@ -18,7 +18,7 @@ package motif.errormessage import motif.models.DuplicatedDependenciesMethod internal class DuplicatedDependenciesMethodHandler( - private val error: DuplicatedDependenciesMethod + private val error: DuplicatedDependenciesMethod, ) : ErrorHandler { override val name = "DUPLICATED DEPENDENCIES METHOD" @@ -32,6 +32,8 @@ internal class DuplicatedDependenciesMethodHandler( """ Suggestions: * Remove the redundant methods - """.trimIndent()) + """ + .trimIndent(), + ) } } diff --git a/errormessage/src/main/kotlin/motif/errormessage/ErrorHandler.kt b/errormessage/src/main/kotlin/motif/errormessage/ErrorHandler.kt index 7e005f11..c5351e67 100644 --- a/errormessage/src/main/kotlin/motif/errormessage/ErrorHandler.kt +++ b/errormessage/src/main/kotlin/motif/errormessage/ErrorHandler.kt @@ -56,44 +56,43 @@ internal interface ErrorHandler { companion object { - fun get(error: MotifError): ErrorHandler { - return when (error) { - is ParsingError -> - when (error) { - is ScopeMustBeAnInterface -> ScopeMustBeAnInterfaceHandler(error) - is VoidScopeMethod -> VoidScopeMethodHandler(error) - is AccessMethodParameters -> AccessMethodParametersHandler(error) - is ObjectsFieldFound -> ObjectsFieldFoundHandler(error) - is ObjectsConstructorFound -> ObjectsConstructorFoundHandler(error) - is VoidFactoryMethod -> VoidFactoryMethodHandler(error) - is NullableFactoryMethod -> NullableFactoryMethodHandler(error) - is NullableParameter -> NullableParameterHandler(error) - is NullableDynamicDependency -> NullableDynamicDependencyHandler(error) - is InvalidFactoryMethod -> InvalidFactoryMethodHandler(error) - is UnspreadableType -> UnspreadableTypeHandler(error) - is NoSuitableConstructor -> NoSuitableConstructorHandler(error) - is InjectAnnotationRequired -> InjectAnnotationRequiredHandler(error) - is NotAssignableBindsMethod -> NotAssignableBindsMethodHandler(error) - is VoidDependenciesMethod -> VoidDependenciesMethodHandler(error) - is DependencyMethodWithParameters -> DependencyMethodWithParametersHandler(error) - is NullableSpreadMethod -> NullableSpreadMethodHandler(error) - is InvalidQualifier -> InvalidQualifierHandler(error) - is DuplicatedChildParameterSource -> DuplicatedChildParameterSourceHandler(error) - is DuplicatedDependenciesMethod -> DuplicatedDependenciesMethodHandler(error) - is ScopeExtendsScope -> ScopeExtendsScopeMethodHandler(error) - is CannotResolveType -> CannotResolveTypeHandler(error) - } - is ProcessingError -> - when (error) { - is ScopeCycleError -> ScopeCycleHandler(error) - is UnsatisfiedDependencyError -> UnsatisfiedDependencyHandler(error) - is DependencyCycleError -> DependencyCycleHandler(error) - is UnexposedSourceError -> UnexposedSourceHandler(error) - is AlreadySatisfiedError -> AlreadySatisfiedHandler(error) - } - else -> throw IllegalStateException("Unknown error type: $${this::class.java.name}") - } - } + fun get(error: MotifError): ErrorHandler = + when (error) { + is ParsingError -> + when (error) { + is ScopeMustBeAnInterface -> ScopeMustBeAnInterfaceHandler(error) + is VoidScopeMethod -> VoidScopeMethodHandler(error) + is AccessMethodParameters -> AccessMethodParametersHandler(error) + is ObjectsFieldFound -> ObjectsFieldFoundHandler(error) + is ObjectsConstructorFound -> ObjectsConstructorFoundHandler(error) + is VoidFactoryMethod -> VoidFactoryMethodHandler(error) + is NullableFactoryMethod -> NullableFactoryMethodHandler(error) + is NullableParameter -> NullableParameterHandler(error) + is NullableDynamicDependency -> NullableDynamicDependencyHandler(error) + is InvalidFactoryMethod -> InvalidFactoryMethodHandler(error) + is UnspreadableType -> UnspreadableTypeHandler(error) + is NoSuitableConstructor -> NoSuitableConstructorHandler(error) + is InjectAnnotationRequired -> InjectAnnotationRequiredHandler(error) + is NotAssignableBindsMethod -> NotAssignableBindsMethodHandler(error) + is VoidDependenciesMethod -> VoidDependenciesMethodHandler(error) + is DependencyMethodWithParameters -> DependencyMethodWithParametersHandler(error) + is NullableSpreadMethod -> NullableSpreadMethodHandler(error) + is InvalidQualifier -> InvalidQualifierHandler(error) + is DuplicatedChildParameterSource -> DuplicatedChildParameterSourceHandler(error) + is DuplicatedDependenciesMethod -> DuplicatedDependenciesMethodHandler(error) + is ScopeExtendsScope -> ScopeExtendsScopeMethodHandler(error) + is CannotResolveType -> CannotResolveTypeHandler(error) + } + is ProcessingError -> + when (error) { + is ScopeCycleError -> ScopeCycleHandler(error) + is UnsatisfiedDependencyError -> UnsatisfiedDependencyHandler(error) + is DependencyCycleError -> DependencyCycleHandler(error) + is UnexposedSourceError -> UnexposedSourceHandler(error) + is AlreadySatisfiedError -> AlreadySatisfiedHandler(error) + } + else -> throw IllegalStateException("Unknown error type: $${this::class.java.name}") + } } } diff --git a/errormessage/src/main/kotlin/motif/errormessage/ErrorMessage.kt b/errormessage/src/main/kotlin/motif/errormessage/ErrorMessage.kt index e2c6883d..1b265bbb 100644 --- a/errormessage/src/main/kotlin/motif/errormessage/ErrorMessage.kt +++ b/errormessage/src/main/kotlin/motif/errormessage/ErrorMessage.kt @@ -30,16 +30,17 @@ class ErrorMessage(val name: String, val text: String) { ==================================== - """.trimIndent() + """ + .trimIndent() - val footer = """ + val footer = + """ ==================================== - """.trimIndent() + """ + .trimIndent() - fun toString(graph: ResolvedGraph): String { - return toString(graph.errors) - } + fun toString(graph: ResolvedGraph): String = toString(graph.errors) fun toString(errors: List): String { val content: String = diff --git a/errormessage/src/main/kotlin/motif/errormessage/InjectAnnotationRequiredHandler.kt b/errormessage/src/main/kotlin/motif/errormessage/InjectAnnotationRequiredHandler.kt index b074ad1f..1ef1d559 100644 --- a/errormessage/src/main/kotlin/motif/errormessage/InjectAnnotationRequiredHandler.kt +++ b/errormessage/src/main/kotlin/motif/errormessage/InjectAnnotationRequiredHandler.kt @@ -36,6 +36,8 @@ internal class InjectAnnotationRequiredHandler(private val error: InjectAnnotati Suggestions: * Annotation the desired constructor with @Inject. * Update the type to have only one constructor. - """.trimIndent()) + """ + .trimIndent(), + ) } } diff --git a/errormessage/src/main/kotlin/motif/errormessage/InvalidFactoryMethodHandler.kt b/errormessage/src/main/kotlin/motif/errormessage/InvalidFactoryMethodHandler.kt index bc67f2e9..6a2b2c84 100644 --- a/errormessage/src/main/kotlin/motif/errormessage/InvalidFactoryMethodHandler.kt +++ b/errormessage/src/main/kotlin/motif/errormessage/InvalidFactoryMethodHandler.kt @@ -27,6 +27,8 @@ internal class InvalidFactoryMethodHandler(private val error: InvalidFactoryMeth Factory method is invalid: ${error.objects.qualifiedName}.${error.method.name} - """.trimIndent()) + """ + .trimIndent(), + ) } } diff --git a/errormessage/src/main/kotlin/motif/errormessage/InvalidQualifierHandler.kt b/errormessage/src/main/kotlin/motif/errormessage/InvalidQualifierHandler.kt index 8f9e08ea..e42fd298 100644 --- a/errormessage/src/main/kotlin/motif/errormessage/InvalidQualifierHandler.kt +++ b/errormessage/src/main/kotlin/motif/errormessage/InvalidQualifierHandler.kt @@ -27,6 +27,8 @@ internal class InvalidQualifierHandler(private val error: InvalidQualifier) : Er Qualifier must define either no members or a single value member of type String: ${error.annotation.className} - """.trimIndent()) + """ + .trimIndent(), + ) } } diff --git a/errormessage/src/main/kotlin/motif/errormessage/NoSuitableConstructorHandler.kt b/errormessage/src/main/kotlin/motif/errormessage/NoSuitableConstructorHandler.kt index b9f13712..4f9c0229 100644 --- a/errormessage/src/main/kotlin/motif/errormessage/NoSuitableConstructorHandler.kt +++ b/errormessage/src/main/kotlin/motif/errormessage/NoSuitableConstructorHandler.kt @@ -32,6 +32,8 @@ internal class NoSuitableConstructorHandler(private val error: NoSuitableConstru [Factory Method] ${error.objects.qualifiedName}.${error.method.name} - """.trimIndent()) + """ + .trimIndent(), + ) } } diff --git a/errormessage/src/main/kotlin/motif/errormessage/NodeHandler.kt b/errormessage/src/main/kotlin/motif/errormessage/NodeHandler.kt index 7b6eed5e..56bd8ceb 100644 --- a/errormessage/src/main/kotlin/motif/errormessage/NodeHandler.kt +++ b/errormessage/src/main/kotlin/motif/errormessage/NodeHandler.kt @@ -25,44 +25,51 @@ import motif.models.SpreadSource object NodeHandler { - fun handle(node: Node): String { - return when (node) { - is ScopeSource -> { - """[SCOPE] - | TYPE: ${node.scope.qualifiedName}""".trimMargin() - } - is FactoryMethodSource -> { - """[FACTORY METHOD RETURN TYPE] + fun handle(node: Node): String = + when (node) { + is ScopeSource -> { + """[SCOPE] + | TYPE: ${node.scope.qualifiedName} + """ + .trimMargin() + } + is FactoryMethodSource -> { + """[FACTORY METHOD RETURN TYPE] | TYPE: ${node.factoryMethod.returnType.qualifiedName} - | METHOD: ${node.factoryMethod.qualifiedName}""".trimMargin() - } - is SpreadSource -> { - """[SPREAD METHOD] + | METHOD: ${node.factoryMethod.qualifiedName} + """ + .trimMargin() + } + is SpreadSource -> { + """[SPREAD METHOD] | TYPE: ${node.spreadMethod.returnType.qualifiedName} | METHOD: ${node.spreadMethod.spread.qualifiedName}.${node.spreadMethod.method.name} | FACTORY METHOD: ${node.spreadMethod.spread.factoryMethod.qualifiedName} - """.trimMargin() - } - is ChildParameterSource -> { - """[CHILD METHOD PARAMETER] + """ + .trimMargin() + } + is ChildParameterSource -> { + """[CHILD METHOD PARAMETER] | TYPE: ${node.parameter.type.qualifiedName} | METHOD: ${node.parameter.method.qualifiedName} | PARAMETER: ${node.parameter.parameter.name} - """.trimMargin() - } - is FactoryMethodSink -> { - """[FACTORY METHOD PARAMETER] + """ + .trimMargin() + } + is FactoryMethodSink -> { + """[FACTORY METHOD PARAMETER] | TYPE: ${node.parameter.type.qualifiedName} | METHOD: ${node.parameter.factoryMethod.qualifiedName} | PARAMETER: ${node.parameter.parameter.name} - """.trimMargin() - } - is AccessMethodSink -> { - """[ACCESS METHOD] + """ + .trimMargin() + } + is AccessMethodSink -> { + """[ACCESS METHOD] | TYPE: ${node.accessMethod.returnType.qualifiedName} | METHOD: ${node.accessMethod.qualifiedName} - """.trimMargin() + """ + .trimMargin() + } } - } - } } diff --git a/errormessage/src/main/kotlin/motif/errormessage/NotAssignableBindsMethodHandler.kt b/errormessage/src/main/kotlin/motif/errormessage/NotAssignableBindsMethodHandler.kt index 5b98fa37..f64881ef 100644 --- a/errormessage/src/main/kotlin/motif/errormessage/NotAssignableBindsMethodHandler.kt +++ b/errormessage/src/main/kotlin/motif/errormessage/NotAssignableBindsMethodHandler.kt @@ -35,6 +35,8 @@ internal class NotAssignableBindsMethodHandler(private val error: NotAssignableB [Parameter Type] ${error.parameterType.qualifiedName} - """.trimIndent()) + """ + .trimIndent(), + ) } } diff --git a/errormessage/src/main/kotlin/motif/errormessage/NullableDynamicDependencyHandler.kt b/errormessage/src/main/kotlin/motif/errormessage/NullableDynamicDependencyHandler.kt index 8f74fb51..12612c63 100644 --- a/errormessage/src/main/kotlin/motif/errormessage/NullableDynamicDependencyHandler.kt +++ b/errormessage/src/main/kotlin/motif/errormessage/NullableDynamicDependencyHandler.kt @@ -35,6 +35,8 @@ internal class NullableDynamicDependencyHandler(private val error: NullableDynam Suggestions: * Consider using Optional<...> instead. - """.trimIndent()) + """ + .trimIndent(), + ) } } diff --git a/errormessage/src/main/kotlin/motif/errormessage/NullableFactoryMethodHandler.kt b/errormessage/src/main/kotlin/motif/errormessage/NullableFactoryMethodHandler.kt index a70cfe84..da0cf4a5 100644 --- a/errormessage/src/main/kotlin/motif/errormessage/NullableFactoryMethodHandler.kt +++ b/errormessage/src/main/kotlin/motif/errormessage/NullableFactoryMethodHandler.kt @@ -31,6 +31,8 @@ internal class NullableFactoryMethodHandler(private val error: NullableFactoryMe Suggestions: * Consider using Optional<...> instead. - """.trimIndent()) + """ + .trimIndent(), + ) } } diff --git a/errormessage/src/main/kotlin/motif/errormessage/NullableParameterHandler.kt b/errormessage/src/main/kotlin/motif/errormessage/NullableParameterHandler.kt index 119c00f7..4a9aae52 100644 --- a/errormessage/src/main/kotlin/motif/errormessage/NullableParameterHandler.kt +++ b/errormessage/src/main/kotlin/motif/errormessage/NullableParameterHandler.kt @@ -34,6 +34,8 @@ internal class NullableParameterHandler(private val error: NullableParameter) : Suggestions: * Consider using Optional<...> instead. - """.trimIndent()) + """ + .trimIndent(), + ) } } diff --git a/errormessage/src/main/kotlin/motif/errormessage/NullableSpreadMethodHandler.kt b/errormessage/src/main/kotlin/motif/errormessage/NullableSpreadMethodHandler.kt index 57c8a58d..f037d2dd 100644 --- a/errormessage/src/main/kotlin/motif/errormessage/NullableSpreadMethodHandler.kt +++ b/errormessage/src/main/kotlin/motif/errormessage/NullableSpreadMethodHandler.kt @@ -34,6 +34,8 @@ internal class NullableSpreadMethodHandler(private val error: NullableSpreadMeth Suggestions: * Use Optional<...> instead. - """.trimIndent()) + """ + .trimIndent(), + ) } } diff --git a/errormessage/src/main/kotlin/motif/errormessage/ObjectsConstructorFoundHandler.kt b/errormessage/src/main/kotlin/motif/errormessage/ObjectsConstructorFoundHandler.kt index e6739632..ace6d98d 100644 --- a/errormessage/src/main/kotlin/motif/errormessage/ObjectsConstructorFoundHandler.kt +++ b/errormessage/src/main/kotlin/motif/errormessage/ObjectsConstructorFoundHandler.kt @@ -28,6 +28,8 @@ internal class ObjectsConstructorFoundHandler(private val error: ObjectsConstruc Objects class may not define constructors: ${error.objectClass.qualifiedName} - """.trimIndent()) + """ + .trimIndent(), + ) } } diff --git a/errormessage/src/main/kotlin/motif/errormessage/ObjectsFieldFoundHandler.kt b/errormessage/src/main/kotlin/motif/errormessage/ObjectsFieldFoundHandler.kt index 18a08668..9f71376e 100644 --- a/errormessage/src/main/kotlin/motif/errormessage/ObjectsFieldFoundHandler.kt +++ b/errormessage/src/main/kotlin/motif/errormessage/ObjectsFieldFoundHandler.kt @@ -27,6 +27,8 @@ internal class ObjectsFieldFoundHandler(private val error: ObjectsFieldFound) : Objects class may not have fields: ${error.objectClass.qualifiedName} - """.trimIndent()) + """ + .trimIndent(), + ) } } diff --git a/errormessage/src/main/kotlin/motif/errormessage/ScopeExtendsScopeMethodHandler.kt b/errormessage/src/main/kotlin/motif/errormessage/ScopeExtendsScopeMethodHandler.kt index 830820f0..50e08958 100644 --- a/errormessage/src/main/kotlin/motif/errormessage/ScopeExtendsScopeMethodHandler.kt +++ b/errormessage/src/main/kotlin/motif/errormessage/ScopeExtendsScopeMethodHandler.kt @@ -24,9 +24,11 @@ class ScopeExtendsScopeMethodHandler(private val error: ScopeExtendsScope) : Err override fun StringBuilder.handle() { appendLine( """ - Scopes can't extend other Scopes: + Scope can't extend other Scopes: ${error.scope.qualifiedName} - """.trimIndent()) + """ + .trimIndent(), + ) } } diff --git a/errormessage/src/main/kotlin/motif/errormessage/ScopeMustBeAnInterfaceHandler.kt b/errormessage/src/main/kotlin/motif/errormessage/ScopeMustBeAnInterfaceHandler.kt index 3cb11f72..eda45433 100644 --- a/errormessage/src/main/kotlin/motif/errormessage/ScopeMustBeAnInterfaceHandler.kt +++ b/errormessage/src/main/kotlin/motif/errormessage/ScopeMustBeAnInterfaceHandler.kt @@ -28,6 +28,8 @@ internal class ScopeMustBeAnInterfaceHandler(private val error: ScopeMustBeAnInt Scope must be an interface: ${error.scopeClass.qualifiedName} - """.trimIndent()) + """ + .trimIndent(), + ) } } diff --git a/errormessage/src/main/kotlin/motif/errormessage/UnexposedSourceHandler.kt b/errormessage/src/main/kotlin/motif/errormessage/UnexposedSourceHandler.kt index 2bfb8729..d0a5dd0f 100644 --- a/errormessage/src/main/kotlin/motif/errormessage/UnexposedSourceHandler.kt +++ b/errormessage/src/main/kotlin/motif/errormessage/UnexposedSourceHandler.kt @@ -34,6 +34,8 @@ internal class UnexposedSourceHandler(private val error: UnexposedSourceError) : """Suggestions: | * Annotate the source with @Expose. | * Resolve the descendant dependency elsewhere. - """.trimMargin()) + """ + .trimMargin(), + ) } } diff --git a/errormessage/src/main/kotlin/motif/errormessage/UnspreadableTypeHandler.kt b/errormessage/src/main/kotlin/motif/errormessage/UnspreadableTypeHandler.kt index b59ba6dc..86e8fa6c 100644 --- a/errormessage/src/main/kotlin/motif/errormessage/UnspreadableTypeHandler.kt +++ b/errormessage/src/main/kotlin/motif/errormessage/UnspreadableTypeHandler.kt @@ -31,6 +31,8 @@ internal class UnspreadableTypeHandler(private val error: UnspreadableType) : Er [Factory Method] ${error.objects.qualifiedName}.${error.method.name} - """.trimIndent()) + """ + .trimIndent(), + ) } } diff --git a/errormessage/src/main/kotlin/motif/errormessage/VoidDependenciesMethodHandler.kt b/errormessage/src/main/kotlin/motif/errormessage/VoidDependenciesMethodHandler.kt index 66468390..ebed9197 100644 --- a/errormessage/src/main/kotlin/motif/errormessage/VoidDependenciesMethodHandler.kt +++ b/errormessage/src/main/kotlin/motif/errormessage/VoidDependenciesMethodHandler.kt @@ -28,6 +28,8 @@ internal class VoidDependenciesMethodHandler(private val error: VoidDependencies Methods on dependencies interfaces must be non-void: void ${error.dependenciesClass.qualifiedName}.${error.method.name} - """.trimIndent()) + """ + .trimIndent(), + ) } } diff --git a/errormessage/src/main/kotlin/motif/errormessage/VoidFactoryMethodHandler.kt b/errormessage/src/main/kotlin/motif/errormessage/VoidFactoryMethodHandler.kt index 28b9b9e5..8d41cc1e 100644 --- a/errormessage/src/main/kotlin/motif/errormessage/VoidFactoryMethodHandler.kt +++ b/errormessage/src/main/kotlin/motif/errormessage/VoidFactoryMethodHandler.kt @@ -27,6 +27,8 @@ internal class VoidFactoryMethodHandler(private val error: VoidFactoryMethod) : Factory methods must be non-void: void ${error.objects.qualifiedName}.${error.method.name} - """.trimIndent()) + """ + .trimIndent(), + ) } } diff --git a/errormessage/src/main/kotlin/motif/errormessage/VoidScopeMethodHandler.kt b/errormessage/src/main/kotlin/motif/errormessage/VoidScopeMethodHandler.kt index 72511dc6..7ddc7672 100644 --- a/errormessage/src/main/kotlin/motif/errormessage/VoidScopeMethodHandler.kt +++ b/errormessage/src/main/kotlin/motif/errormessage/VoidScopeMethodHandler.kt @@ -27,6 +27,8 @@ internal class VoidScopeMethodHandler(private val error: VoidScopeMethod) : Erro Scope methods must be non-void: ${error.scope.qualifiedName}.${error.method.name} - """.trimIndent()) + """ + .trimIndent(), + ) } } diff --git a/gradle.properties b/gradle.properties index 5cfb5e00..567f2265 100644 --- a/gradle.properties +++ b/gradle.properties @@ -14,7 +14,7 @@ # limitations under the License. # GROUP=com.uber.motif -VERSION_NAME=0.3.9-SNAPSHOT +VERSION_NAME=0.4.0-alpha07 POM_DESCRIPTION=Simple DI API for Android / Java. POM_URL=https://github.com/uber/motif/ POM_SCM_URL=https://github.com/uber/motif/ diff --git a/gradle/dependencies.gradle b/gradle/dependencies.gradle index a17e3851..1092923c 100644 --- a/gradle/dependencies.gradle +++ b/gradle/dependencies.gradle @@ -15,46 +15,47 @@ */ def versions = [ - kotlin: '1.7.10', - ksp: '1.7.10-1.0.6', - ktlint: '0.41.0', - ktfmt: '0.23', - dagger: '2.24', + kotlin: '2.1.0', + ksp: '2.1.0-1.0.28', + ktlint: '1.5.0', + ktfmt: '0.54', + dagger: '2.47', butterknife: '10.1.0', glide: '4.9.0', - gjf: '1.7', - kotlinpoet: '1.10.2', + gjf: '1.8', + kotlinpoet: '1.12.0', room: '2.1.0', - roomCompilerProcessing: '2.5.0-alpha03', - dokka: '1.4.32', + roomCompilerProcessing: '2.7.0-alpha13', + dokka: '2.0.0', "gradleIntellijPlugin": [ - ide: '2020.1' + ide: '2023.2' ], ] ext.deps = [ "versions": versions, "build": [ - buildToolsVersion: '30.0.3', - compileSdkVersion: 31, + buildToolsVersion: '33.0.0', + compileSdkVersion: 33, ci: 'true' == System.getenv('CI'), - minSdkVersion: 14, + minSdkVersion: 21, targetSdkVersion: 30, gradlePlugins: [ - android: 'com.android.tools.build:gradle:4.2.0', + android: 'com.android.tools.build:gradle:7.4.2', + intellij: 'org.jetbrains.intellij:org.jetbrains.intellij.gradle.plugin:1.15.0', kotlin: "org.jetbrains.kotlin:kotlin-gradle-plugin:${versions.kotlin}", ksp: "com.google.devtools.ksp:com.google.devtools.ksp.gradle.plugin:${versions.ksp}", dokka: "org.jetbrains.dokka:dokka-gradle-plugin:${versions.dokka}", - mavenPublish: 'com.vanniktech:gradle-maven-publish-plugin:0.18.0', - spotless: "com.diffplug.spotless:spotless-plugin-gradle:5.11.0", - shadow: "com.github.jengelman.gradle.plugins:shadow:6.1.0", + mavenPublish: 'com.vanniktech:gradle-maven-publish-plugin:0.27.0', + spotless: "com.diffplug.spotless:spotless-plugin-gradle:7.0.2", + shadow: "com.github.johnrengelman:shadow:8.1.0", ] ], "kotlin": [ kapt: "org.jetbrains.kotlin:kotlin-annotation-processing-embeddable:${versions.kotlin}", reflection: "org.jetbrains.kotlin:kotlin-reflect:${versions.kotlin}", - stdlib: "org.jetbrains.kotlin:kotlin-stdlib:${versions.kotlin}" + stdlib: "org.jetbrains.kotlin:kotlin-stdlib:${versions.kotlin}", ], "rx": [ android: 'io.reactivex.rxjava2:rxandroid:2.1.1', @@ -79,10 +80,10 @@ ext.deps = [ ], dagger: "com.google.dagger:dagger:${versions.dagger}", daggerCompiler: "com.google.dagger:dagger-compiler:${versions.dagger}", - javapoet: 'com.squareup:javapoet:1.11.1', + javapoet: 'com.squareup:javapoet:1.13.0', kotlinpoet: "com.squareup:kotlinpoet:${versions.kotlinpoet}", kotlinpoetInteropMetadata: "com.squareup:kotlinpoet-metadata:${versions.kotlinpoet}", - kotlinxMetadata : 'org.jetbrains.kotlinx:kotlinx-metadata-jvm:0.5.0', + kotlinxMetadata : 'org.jetbrains.kotlinx:kotlinx-metadata-jvm:0.9.0', autoCommon: 'com.google.auto:auto-common:1.1.2', autoService: "com.google.auto.service:auto-service:1.0", commonsCodec: 'commons-codec:commons-codec:1.13', diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 7665b0fa..1f017e4e 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.9-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/intellij/ast/build.gradle b/intellij/ast/build.gradle index 1d840a3a..98255d4c 100644 --- a/intellij/ast/build.gradle +++ b/intellij/ast/build.gradle @@ -1,7 +1,7 @@ plugins { - id "org.jetbrains.intellij" version "1.0" + id "org.jetbrains.intellij" id 'org.jetbrains.kotlin.jvm' - id 'org.jetbrains.dokka' + id 'com.vanniktech.maven.publish' } intellij { @@ -11,6 +11,10 @@ intellij { updateSinceUntilBuild = false } +kotlin { + jvmToolchain(17) +} + dependencies { implementation project(':lib') implementation project(':ast') @@ -21,19 +25,6 @@ dependencies { testImplementation deps.test.junit testImplementation deps.test.assertj testImplementation deps.test.truth - testImplementation project(':intellij:testing') -} - -compileKotlin { - kotlinOptions { - jvmTarget = "1.8" - } -} - -compileTestKotlin { - kotlinOptions { - jvmTarget = "1.8" - } } tasks { @@ -41,5 +32,3 @@ tasks { enabled = false } } - -apply plugin: 'com.vanniktech.maven.publish' diff --git a/intellij/ast/src/main/kotlin/motif/ast/intellij/IntelliJAnnotation.kt b/intellij/ast/src/main/kotlin/motif/ast/intellij/IntelliJAnnotation.kt index ae6ab7b8..b6102ed1 100644 --- a/intellij/ast/src/main/kotlin/motif/ast/intellij/IntelliJAnnotation.kt +++ b/intellij/ast/src/main/kotlin/motif/ast/intellij/IntelliJAnnotation.kt @@ -31,6 +31,7 @@ import kotlin.reflect.KClass import motif.ast.IrAnnotation import motif.ast.IrMethod import motif.ast.IrType +import java.util.Collections class IntelliJAnnotation(private val project: Project, private val psiAnnotation: PsiAnnotation) : IrAnnotation { @@ -59,9 +60,11 @@ class IntelliJAnnotation(private val project: Project, private val psiAnnotation annotationClass.methods.map { IntelliJMethod(project, it, PsiSubstitutor.EMPTY) } } - override fun matchesClass(annotationClass: KClass): Boolean { - return psiAnnotation.qualifiedName == annotationClass.java.name - } + override fun matchesClass(annotationClass: KClass): Boolean = + psiAnnotation.qualifiedName == annotationClass.java.name + + override val annotationValueMap: Map + get() = Collections.emptyMap() override fun equals(other: Any?): Boolean { if (this === other) return true @@ -70,9 +73,7 @@ class IntelliJAnnotation(private val project: Project, private val psiAnnotation return key == other.key } - override fun hashCode(): Int { - return key.hashCode() - } + override fun hashCode(): Int = key.hashCode() override fun toString(): String { val value = stringValue?.let { "(\"$it\")" } ?: "" @@ -84,7 +85,7 @@ class IntelliJAnnotation(private val project: Project, private val psiAnnotation private fun getStringConstantValue( project: Project, annotation: PsiAnnotation, - attributeName: String + attributeName: String, ): String? { val value = annotation.findAttributeValue(attributeName) ?: return null @@ -101,8 +102,7 @@ class IntelliJAnnotation(private val project: Project, private val psiAnnotation val constant = JavaPsiFacade.getInstance(project) .constantEvaluationHelper - .computeConstantExpression(referenceTarget.initializer) - ?: return null + .computeConstantExpression(referenceTarget.initializer) ?: return null return constant as? String } diff --git a/intellij/ast/src/main/kotlin/motif/ast/intellij/IntelliJClass.kt b/intellij/ast/src/main/kotlin/motif/ast/intellij/IntelliJClass.kt index 86b628ea..7ce40e87 100644 --- a/intellij/ast/src/main/kotlin/motif/ast/intellij/IntelliJClass.kt +++ b/intellij/ast/src/main/kotlin/motif/ast/intellij/IntelliJClass.kt @@ -35,7 +35,7 @@ import motif.ast.IrType class IntelliJClass( private val project: Project, private val psiClassType: PsiClassType, - val psiClass: PsiClass + val psiClass: PsiClass, ) : IrUtil, IrClass { private val jvmPsiConversionHelper = @@ -48,9 +48,10 @@ class IntelliJClass( } override val typeArguments: List by lazy { - psiClassType.typeArguments().map { jvmPsiConversionHelper.convertType(it) }.map { - IntelliJType(project, it) - } + psiClassType + .typeArguments() + .map { jvmPsiConversionHelper.convertType(it) } + .map { IntelliJType(project, it) } } override val kind: IrClass.Kind by lazy { @@ -81,9 +82,9 @@ class IntelliJClass( } override val fields: List by lazy { - psiClass.allFields.filter { it.containingClass?.qualifiedName != "java.lang.Object" }.map { - IntelliJField(project, it) - } + psiClass.allFields + .filter { it.containingClass?.qualifiedName != "java.lang.Object" } + .map { IntelliJField(project, it) } } override val constructors: List by lazy { diff --git a/intellij/ast/src/main/kotlin/motif/ast/intellij/IntelliJMethod.kt b/intellij/ast/src/main/kotlin/motif/ast/intellij/IntelliJMethod.kt index f387a907..163efa13 100644 --- a/intellij/ast/src/main/kotlin/motif/ast/intellij/IntelliJMethod.kt +++ b/intellij/ast/src/main/kotlin/motif/ast/intellij/IntelliJMethod.kt @@ -27,7 +27,7 @@ import motif.ast.IrType class IntelliJMethod( private val project: Project, val psiMethod: PsiMethod, - val substitutor: PsiSubstitutor + val substitutor: PsiSubstitutor, ) : IrUtil, IrMethod { override val parameters: List by lazy { diff --git a/intellij/ast/src/main/kotlin/motif/ast/intellij/IntelliJMethodParameter.kt b/intellij/ast/src/main/kotlin/motif/ast/intellij/IntelliJMethodParameter.kt index 898d6fc2..5045b730 100644 --- a/intellij/ast/src/main/kotlin/motif/ast/intellij/IntelliJMethodParameter.kt +++ b/intellij/ast/src/main/kotlin/motif/ast/intellij/IntelliJMethodParameter.kt @@ -25,7 +25,7 @@ import motif.ast.IrType class IntelliJMethodParameter( private val project: Project, val psiParameter: PsiParameter, - val substitutor: PsiSubstitutor + val substitutor: PsiSubstitutor, ) : IrUtil, IrParameter { override val type: IrType by lazy { diff --git a/intellij/ast/src/main/kotlin/motif/ast/intellij/IntelliJType.kt b/intellij/ast/src/main/kotlin/motif/ast/intellij/IntelliJType.kt index 42304caf..1a738342 100644 --- a/intellij/ast/src/main/kotlin/motif/ast/intellij/IntelliJType.kt +++ b/intellij/ast/src/main/kotlin/motif/ast/intellij/IntelliJType.kt @@ -19,10 +19,10 @@ import com.intellij.openapi.project.Project import com.intellij.psi.GenericsUtil import com.intellij.psi.PsiClassType import com.intellij.psi.PsiType +import com.intellij.psi.PsiTypes import com.intellij.psi.util.TypeConversionUtil import kotlin.jvm.javaClass import kotlin.let -import kotlin.text.contains import motif.ast.IrClass import motif.ast.IrType @@ -32,18 +32,18 @@ class IntelliJType(private val project: Project, val psiType: PsiType) : IrType GenericsUtil.getVariableTypeByExpressionType(psiType).getCanonicalText(false) } - override val isVoid: Boolean by lazy { psiType == PsiType.VOID } + override val isVoid: Boolean by lazy { psiType == PsiTypes.voidType() } override val isPrimitive: Boolean by lazy { when (psiType) { - PsiType.BOOLEAN, - PsiType.BYTE, - PsiType.SHORT, - PsiType.INT, - PsiType.LONG, - PsiType.CHAR, - PsiType.FLOAT, - PsiType.DOUBLE -> true + PsiTypes.booleanType(), + PsiTypes.byteType(), + PsiTypes.shortType(), + PsiTypes.intType(), + PsiTypes.longType(), + PsiTypes.charType(), + PsiTypes.floatType(), + PsiTypes.doubleType(), -> true else -> false } } @@ -54,9 +54,8 @@ class IntelliJType(private val project: Project, val psiType: PsiType) : IrType return (psiType as? PsiClassType)?.let { IntelliJClass(project, it, psiClass) } } - override fun isAssignableTo(type: IrType): Boolean { - return TypeConversionUtil.isAssignable((type as IntelliJType).psiType, psiType, false) - } + override fun isAssignableTo(type: IrType): Boolean = + TypeConversionUtil.isAssignable((type as IntelliJType).psiType, psiType, false) override fun equals(other: Any?): Boolean { if (this === other) return true @@ -69,7 +68,5 @@ class IntelliJType(private val project: Project, val psiType: PsiType) : IrType return true } - override fun hashCode(): Int { - return psiType.hashCode() - } + override fun hashCode(): Int = psiType.hashCode() } diff --git a/intellij/ast/src/main/kotlin/motif/ast/intellij/IrUtil.kt b/intellij/ast/src/main/kotlin/motif/ast/intellij/IrUtil.kt index d9d8971d..2c8f9901 100644 --- a/intellij/ast/src/main/kotlin/motif/ast/intellij/IrUtil.kt +++ b/intellij/ast/src/main/kotlin/motif/ast/intellij/IrUtil.kt @@ -19,24 +19,19 @@ import com.intellij.openapi.project.Project import com.intellij.psi.PsiAnnotationOwner import com.intellij.psi.PsiModifier import com.intellij.psi.PsiModifierListOwner -import java.util.Locale import kotlin.collections.filter import kotlin.collections.map import kotlin.collections.toSet -import kotlin.text.toUpperCase import motif.ast.IrAnnotation import motif.ast.IrModifier interface IrUtil { - fun PsiModifierListOwner.irModifiers(): Set { - return PsiModifier.MODIFIERS - .filter { hasModifierProperty(it) } - .map { IrModifier.valueOf(it.toUpperCase(Locale.getDefault())) } - .toSet() - } + fun PsiModifierListOwner.irModifiers(): Set = + PsiModifier.MODIFIERS.filter { hasModifierProperty(it) } + .map { IrModifier.valueOf(it.uppercase()) } + .toSet() - fun PsiAnnotationOwner.irAnnotations(project: Project): List { - return annotations.map { IntelliJAnnotation(project, it) } - } + fun PsiAnnotationOwner.irAnnotations(project: Project): List = + annotations.map { IntelliJAnnotation(project, it) } } diff --git a/intellij/ast/src/test/kotlin/motif/ast/intellij/IntelliJAnnotationTest.kt b/intellij/ast/src/test/kotlin/motif/ast/intellij/IntelliJAnnotationTest.kt index 1ec2a3e3..7dc4c180 100644 --- a/intellij/ast/src/test/kotlin/motif/ast/intellij/IntelliJAnnotationTest.kt +++ b/intellij/ast/src/test/kotlin/motif/ast/intellij/IntelliJAnnotationTest.kt @@ -15,28 +15,24 @@ */ package motif.ast.intellij -import com.intellij.pom.java.LanguageLevel +import com.intellij.openapi.projectRoots.impl.JavaAwareProjectJdkTableImpl import com.intellij.psi.PsiElementFactory -import com.intellij.testFramework.LightProjectDescriptor -import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase -import motif.intellij.testing.InternalJdk +import com.intellij.testFramework.fixtures.DefaultLightProjectDescriptor +import com.intellij.testFramework.fixtures.LightJavaCodeInsightFixtureTestCase import org.assertj.core.api.Assertions.assertThat import org.intellij.lang.annotations.Language -class IntelliJAnnotationTest : LightCodeInsightFixtureTestCase() { +class IntelliJAnnotationTest : LightJavaCodeInsightFixtureTestCase() { lateinit var psiElementFactory: PsiElementFactory override fun setUp() { super.setUp() - psiElementFactory = PsiElementFactory.SERVICE.getInstance(project) } - override fun getProjectDescriptor(): LightProjectDescriptor { - return object : ProjectDescriptor(LanguageLevel.HIGHEST) { - override fun getSdk() = InternalJdk.instance - } + override fun getProjectDescriptor() = DefaultLightProjectDescriptor { + JavaAwareProjectJdkTableImpl.getInstanceEx().internalJdk } override fun getTestDataPath(): String { @@ -49,7 +45,8 @@ class IntelliJAnnotationTest : LightCodeInsightFixtureTestCase() { package test; @interface A {} - """.trimIndent()) + """ + .trimIndent()) val fooAnnotation = getClassAnnotation( @@ -57,7 +54,8 @@ class IntelliJAnnotationTest : LightCodeInsightFixtureTestCase() { package test; @A class Foo {} - """.trimIndent()) + """ + .trimIndent()) val barAnnotation = getClassAnnotation( @@ -65,7 +63,8 @@ class IntelliJAnnotationTest : LightCodeInsightFixtureTestCase() { package test; @A class Bar {} - """.trimIndent()) + """ + .trimIndent()) assertThat(fooAnnotation).isEqualTo(barAnnotation) } @@ -76,14 +75,16 @@ class IntelliJAnnotationTest : LightCodeInsightFixtureTestCase() { package test; @interface A {} - """.trimIndent()) + """ + .trimIndent()) createAnnotationClass( """ package test; @interface B {} - """.trimIndent()) + """ + .trimIndent()) val fooAnnotation = getClassAnnotation( @@ -91,7 +92,8 @@ class IntelliJAnnotationTest : LightCodeInsightFixtureTestCase() { package test; @A class Foo {} - """.trimIndent()) + """ + .trimIndent()) val barAnnotation = getClassAnnotation( @@ -99,7 +101,8 @@ class IntelliJAnnotationTest : LightCodeInsightFixtureTestCase() { package test; @B class Bar {} - """.trimIndent()) + """ + .trimIndent()) assertThat(fooAnnotation).isNotEqualTo(barAnnotation) } @@ -110,7 +113,8 @@ class IntelliJAnnotationTest : LightCodeInsightFixtureTestCase() { package test; @interface A {} - """.trimIndent()) + """ + .trimIndent()) val fooAnnotation = getClassAnnotation( @@ -118,7 +122,8 @@ class IntelliJAnnotationTest : LightCodeInsightFixtureTestCase() { package test; @test.A class Foo {} - """.trimIndent()) + """ + .trimIndent()) val barAnnotation = getClassAnnotation( @@ -126,7 +131,8 @@ class IntelliJAnnotationTest : LightCodeInsightFixtureTestCase() { package test; @A class Bar {} - """.trimIndent()) + """ + .trimIndent()) assertThat(fooAnnotation).isEqualTo(barAnnotation) } @@ -139,7 +145,8 @@ class IntelliJAnnotationTest : LightCodeInsightFixtureTestCase() { @javax.inject.Named("a") class Foo {} - """.trimIndent()) + """ + .trimIndent()) val barAnnotation = getClassAnnotation( @@ -148,7 +155,8 @@ class IntelliJAnnotationTest : LightCodeInsightFixtureTestCase() { @javax.inject.Named("a") class Bar {} - """.trimIndent()) + """ + .trimIndent()) assertThat(fooAnnotation).isEqualTo(barAnnotation) } @@ -161,7 +169,8 @@ class IntelliJAnnotationTest : LightCodeInsightFixtureTestCase() { @javax.inject.Named("a") class Foo {} - """.trimIndent()) + """ + .trimIndent()) val barAnnotation = getClassAnnotation( @@ -170,7 +179,8 @@ class IntelliJAnnotationTest : LightCodeInsightFixtureTestCase() { @javax.inject.Named("b") class Bar {} - """.trimIndent()) + """ + .trimIndent()) assertThat(fooAnnotation).isNotEqualTo(barAnnotation) } @@ -185,7 +195,8 @@ class IntelliJAnnotationTest : LightCodeInsightFixtureTestCase() { class Foo { static final String S = "a"; } - """.trimIndent()) + """ + .trimIndent()) val barAnnotation = getClassAnnotation( @@ -196,7 +207,8 @@ class IntelliJAnnotationTest : LightCodeInsightFixtureTestCase() { class Bar { static final String S = "a"; } - """.trimIndent()) + """ + .trimIndent()) assertThat(fooAnnotation).isEqualTo(barAnnotation) } @@ -211,7 +223,8 @@ class IntelliJAnnotationTest : LightCodeInsightFixtureTestCase() { class Foo { static final String S = "a"; } - """.trimIndent()) + """ + .trimIndent()) val barAnnotation = getClassAnnotation( @@ -222,7 +235,8 @@ class IntelliJAnnotationTest : LightCodeInsightFixtureTestCase() { class Bar { static final String S = "b"; } - """.trimIndent()) + """ + .trimIndent()) assertThat(fooAnnotation).isNotEqualTo(barAnnotation) } @@ -235,7 +249,8 @@ class IntelliJAnnotationTest : LightCodeInsightFixtureTestCase() { @javax.inject.Named("a") class Foo {} - """.trimIndent()) + """ + .trimIndent()) val barAnnotation = getClassAnnotation( @@ -246,7 +261,8 @@ class IntelliJAnnotationTest : LightCodeInsightFixtureTestCase() { class Bar { static final String S = "a"; } - """.trimIndent()) + """ + .trimIndent()) assertThat(fooAnnotation).isEqualTo(barAnnotation) } @@ -259,7 +275,8 @@ class IntelliJAnnotationTest : LightCodeInsightFixtureTestCase() { @javax.inject.Named("a") class Foo {} - """.trimIndent()) + """ + .trimIndent()) val barAnnotation = getClassAnnotation( @@ -270,7 +287,8 @@ class IntelliJAnnotationTest : LightCodeInsightFixtureTestCase() { class Bar { static final String S = "b"; } - """.trimIndent()) + """ + .trimIndent()) assertThat(fooAnnotation).isNotEqualTo(barAnnotation) } @@ -283,7 +301,8 @@ class IntelliJAnnotationTest : LightCodeInsightFixtureTestCase() { @javax.inject.Named("a") class Foo {} - """.trimIndent()) + """ + .trimIndent()) assertThat(fooAnnotation.className).isEqualTo("javax.inject.Named") } diff --git a/intellij/ast/src/test/kotlin/motif/ast/intellij/IntelliJClassTest.kt b/intellij/ast/src/test/kotlin/motif/ast/intellij/IntelliJClassTest.kt index 4ef652eb..e0020be0 100644 --- a/intellij/ast/src/test/kotlin/motif/ast/intellij/IntelliJClassTest.kt +++ b/intellij/ast/src/test/kotlin/motif/ast/intellij/IntelliJClassTest.kt @@ -15,34 +15,28 @@ */ package motif.ast.intellij -import com.intellij.pom.java.LanguageLevel +import com.intellij.openapi.projectRoots.impl.JavaAwareProjectJdkTableImpl import com.intellij.psi.PsiElementFactory -import com.intellij.testFramework.LightProjectDescriptor -import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase -import motif.intellij.testing.InternalJdk +import com.intellij.testFramework.fixtures.DefaultLightProjectDescriptor +import com.intellij.testFramework.fixtures.LightJavaCodeInsightFixtureTestCase import org.assertj.core.api.Assertions.assertThat import org.intellij.lang.annotations.Language import org.junit.Test -class IntelliJClassTest : LightCodeInsightFixtureTestCase() { +class IntelliJClassTest : LightJavaCodeInsightFixtureTestCase() { lateinit var psiElementFactory: PsiElementFactory override fun setUp() { super.setUp() - - psiElementFactory = PsiElementFactory.SERVICE.getInstance(project) + psiElementFactory = PsiElementFactory.getInstance(project) } - override fun getProjectDescriptor(): LightProjectDescriptor { - return object : ProjectDescriptor(LanguageLevel.HIGHEST) { - override fun getSdk() = InternalJdk.instance - } + override fun getProjectDescriptor() = DefaultLightProjectDescriptor { + JavaAwareProjectJdkTableImpl.getInstanceEx().internalJdk } - override fun getTestDataPath(): String { - return "testData" - } + override fun getTestDataPath(): String = "testData" fun testInheritedMethod() { val fooClass = @@ -55,7 +49,9 @@ class IntelliJClassTest : LightCodeInsightFixtureTestCase() { interface Bar { String a(); } - """.trimIndent()) + """ + .trimIndent(), + ) assertThat(fooClass.methods).hasSize(1) } @@ -71,7 +67,9 @@ class IntelliJClassTest : LightCodeInsightFixtureTestCase() { interface Bar { T a(T t); } - """.trimIndent()) + """ + .trimIndent(), + ) assertThat(fooClass.methods).hasSize(1) assertThat(fooClass.methods[0].returnType.qualifiedName).isEqualTo("java.lang.String") @@ -89,7 +87,9 @@ class IntelliJClassTest : LightCodeInsightFixtureTestCase() { class Bar { static String a() {} } - """.trimIndent()) + """ + .trimIndent(), + ) assertThat(fooClass.methods).hasSize(1) } @@ -105,7 +105,9 @@ class IntelliJClassTest : LightCodeInsightFixtureTestCase() { class Bar { private static String a() {} } - """.trimIndent()) + """ + .trimIndent(), + ) assertThat(fooClass.methods).isEmpty() } @@ -121,7 +123,9 @@ class IntelliJClassTest : LightCodeInsightFixtureTestCase() { class Bar { String a; } - """.trimIndent()) + """ + .trimIndent(), + ) assertThat(fooClass.fields).hasSize(1) } @@ -137,7 +141,9 @@ class IntelliJClassTest : LightCodeInsightFixtureTestCase() { class Bar { T t; } - """.trimIndent()) + """ + .trimIndent(), + ) assertThat(fooClass.fields).hasSize(1) @@ -154,7 +160,9 @@ class IntelliJClassTest : LightCodeInsightFixtureTestCase() { package motif.intellij; class Foo extends motif.intellij.Bar {} - """.trimIndent()) + """ + .trimIndent(), + ) val barClass = createIntelliJClass( @@ -162,7 +170,9 @@ class IntelliJClassTest : LightCodeInsightFixtureTestCase() { package motif.intellij; class Bar {} - """.trimIndent()) + """ + .trimIndent(), + ) assertThat(fooClass.type.isAssignableTo(barClass.type)).isTrue() } @@ -176,7 +186,9 @@ class IntelliJClassTest : LightCodeInsightFixtureTestCase() { class Foo extends Bar {} class Bar {} - """.trimIndent()) + """ + .trimIndent(), + ) val stringBarType = createIntelliJType("motif.intellij.Bar") @@ -194,7 +206,9 @@ class IntelliJClassTest : LightCodeInsightFixtureTestCase() { class Bar extends Baz {} class Baz {} - """.trimIndent()) + """ + .trimIndent(), + ) val stringBazType = createIntelliJType("motif.intellij.Baz") @@ -210,7 +224,9 @@ class IntelliJClassTest : LightCodeInsightFixtureTestCase() { class Foo extends Bar {} class Bar {} - """.trimIndent()) + """ + .trimIndent(), + ) val superClass = fooClass.supertypes.single().resolveClass() as IntelliJClass assertThat(superClass.typeArguments.map { it.qualifiedName }) @@ -229,7 +245,9 @@ class IntelliJClassTest : LightCodeInsightFixtureTestCase() { class Bar extends Baz {} class Baz {} - """.trimIndent()) + """ + .trimIndent(), + ) val barClass = fooClass.supertypes.single().resolveClass() as IntelliJClass val bazClass = barClass.supertypes.single().resolveClass() as IntelliJClass @@ -245,7 +263,9 @@ class IntelliJClassTest : LightCodeInsightFixtureTestCase() { class Foo extends Bar {} class Bar {} - """.trimIndent()) + """ + .trimIndent(), + ) val superClass = fooClass.supertypes.single().resolveClass() as IntelliJClass assertThat(superClass.typeArguments).isEmpty() @@ -258,7 +278,9 @@ class IntelliJClassTest : LightCodeInsightFixtureTestCase() { package test; class Foo {} - """.trimIndent()) + """ + .trimIndent(), + ) val objectClass = fooClass.supertypes.single().resolveClass()!! assertThat(objectClass.qualifiedName).isEqualTo("java.lang.Object") @@ -274,7 +296,9 @@ class IntelliJClassTest : LightCodeInsightFixtureTestCase() { class Foo implements Bar {} interface Bar {} - """.trimIndent()) + """ + .trimIndent(), + ) val superTypes = fooClass.supertypes.map { it.qualifiedName } assertThat(superTypes).containsExactly("java.lang.Object", "test.Bar") @@ -292,7 +316,9 @@ class IntelliJClassTest : LightCodeInsightFixtureTestCase() { interface Bar {} interface Baz {} - """.trimIndent()) + """ + .trimIndent(), + ) val superTypes = fooClass.supertypes.map { it.qualifiedName } assertThat(superTypes).containsExactly("java.lang.Object", "test.Bar", "test.Baz") diff --git a/intellij/ast/src/test/kotlin/motif/ast/intellij/IntelliJKotlinTest.kt b/intellij/ast/src/test/kotlin/motif/ast/intellij/IntelliJKotlinTest.kt index 896b71a7..b8568cad 100644 --- a/intellij/ast/src/test/kotlin/motif/ast/intellij/IntelliJKotlinTest.kt +++ b/intellij/ast/src/test/kotlin/motif/ast/intellij/IntelliJKotlinTest.kt @@ -15,35 +15,29 @@ */ package motif.ast.intellij -import com.intellij.pom.java.LanguageLevel +import com.intellij.openapi.projectRoots.impl.JavaAwareProjectJdkTableImpl import com.intellij.psi.PsiElementFactory -import com.intellij.testFramework.LightProjectDescriptor -import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase -import motif.intellij.testing.InternalJdk +import com.intellij.testFramework.fixtures.DefaultLightProjectDescriptor +import com.intellij.testFramework.fixtures.LightJavaCodeInsightFixtureTestCase import org.assertj.core.api.Assertions.assertThat import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.uast.UClass import org.jetbrains.uast.toUElementOfType -class IntelliJKotlinTest : LightCodeInsightFixtureTestCase() { +class IntelliJKotlinTest : LightJavaCodeInsightFixtureTestCase() { lateinit var psiElementFactory: PsiElementFactory override fun setUp() { super.setUp() - psiElementFactory = PsiElementFactory.SERVICE.getInstance(project) } - override fun getProjectDescriptor(): LightProjectDescriptor { - return object : ProjectDescriptor(LanguageLevel.HIGHEST) { - override fun getSdk() = InternalJdk.instance - } + override fun getProjectDescriptor() = DefaultLightProjectDescriptor { + JavaAwareProjectJdkTableImpl.getInstanceEx().internalJdk } - override fun getTestDataPath(): String { - return "testData" - } + override fun getTestDataPath(): String = "testData" fun testImplicitNullabilityAnnotationType() { val fooPsiFile = @@ -54,8 +48,9 @@ class IntelliJKotlinTest : LightCodeInsightFixtureTestCase() { internal fun a() {} } - """.trimIndent()) as - KtFile + """ + .trimIndent(), + ) as KtFile val fooPsiClass = fooPsiFile.declarations[0].toUElementOfType()!!.javaPsi val psiAnnotation = fooPsiClass.constructors[0].parameterList.parameters[0].annotations[0] diff --git a/intellij/build.gradle b/intellij/build.gradle index 89f998d5..b3f5c29c 100644 --- a/intellij/build.gradle +++ b/intellij/build.gradle @@ -1,7 +1,7 @@ plugins { - id "org.jetbrains.intellij" version "1.0" + id "org.jetbrains.intellij" id 'org.jetbrains.kotlin.jvm' - id 'org.jetbrains.dokka' + id 'com.vanniktech.maven.publish' } intellij { @@ -11,6 +11,10 @@ intellij { updateSinceUntilBuild = false } +kotlin { + jvmToolchain(17) +} + group 'com.uber.motif' version '0.0.5' // Plugin version @@ -22,7 +26,6 @@ dependencies { implementation deps.kotlin.reflection testImplementation deps.test.truth - testImplementation project(':intellij:testing') testImplementation project(':viewmodel') } @@ -39,5 +42,3 @@ tasks { enabled = false } } - -apply plugin: 'com.vanniktech.maven.publish' diff --git a/intellij/src/main/kotlin/motif/intellij/AttachMotifServiceActivity.kt b/intellij/src/main/kotlin/motif/intellij/AttachMotifServiceActivity.kt new file mode 100644 index 00000000..86f8d7ed --- /dev/null +++ b/intellij/src/main/kotlin/motif/intellij/AttachMotifServiceActivity.kt @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2023 Uber Technologies, Inc. + * + * 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 motif.intellij + +import com.intellij.openapi.components.service +import com.intellij.openapi.project.Project +import com.intellij.openapi.startup.ProjectActivity + +class AttachMotifServiceActivity : ProjectActivity { + override suspend fun execute(project: Project) { + project.service().attach() + } +} diff --git a/intellij/src/main/kotlin/motif/intellij/GraphFactory.kt b/intellij/src/main/kotlin/motif/intellij/GraphFactory.kt index 198bf6b7..ccb94672 100644 --- a/intellij/src/main/kotlin/motif/intellij/GraphFactory.kt +++ b/intellij/src/main/kotlin/motif/intellij/GraphFactory.kt @@ -65,21 +65,16 @@ class GraphFactory(private val project: Project) { .map { type -> IntelliJClass(project, type, type.resolve()!!) } } - private fun getScopeClasses(psiFile: PsiJavaFile): Iterable { - return psiFile.classes.toList() - } + private fun getScopeClasses(psiFile: PsiJavaFile): Iterable = psiFile.classes.toList() - private fun getScopeClasses(psiFile: KtFile): Iterable { - return psiFile.declarations.filterIsInstance().map { - it.toUElement(UClass::class.java)!!.javaPsi - } - } + private fun getScopeClasses(psiFile: KtFile): Iterable = + psiFile.declarations.filterIsInstance().map { + it.toUElement(UClass::class.java)!!.javaPsi + } - private fun getClasses(psiClass: PsiClass): List { - return listOf(psiClass) + psiClass.innerClasses.flatMap(this::getClasses) - } + private fun getClasses(psiClass: PsiClass): List = + listOf(psiClass) + psiClass.innerClasses.flatMap(this::getClasses) - private fun isScopeClass(psiClass: PsiClass): Boolean { - return psiClass.annotations.find { it.qualifiedName == Scope::class.qualifiedName } != null - } + private fun isScopeClass(psiClass: PsiClass): Boolean = + psiClass.annotations.find { it.qualifiedName == Scope::class.qualifiedName } != null } diff --git a/intellij/src/main/kotlin/motif/intellij/GraphManager.kt b/intellij/src/main/kotlin/motif/intellij/GraphManager.kt index 9900e82e..a618500e 100644 --- a/intellij/src/main/kotlin/motif/intellij/GraphManager.kt +++ b/intellij/src/main/kotlin/motif/intellij/GraphManager.kt @@ -77,7 +77,8 @@ class GraphManager(private val project: Project) : ProjectComponent { setGraphState(state) } } - }) + }, + ) } fun addListener(listener: Listener) { @@ -105,8 +106,7 @@ class GraphInvalidator(private val project: Project, private val graph: Resolved private val psiElementFactory = PsiElementFactory.SERVICE.getInstance(project) private val relevantTypes: Set by lazy { - graph - .scopes + graph.scopes .flatMap { scope -> (listOfNotNull(scope.objects?.clazz) + scope.clazz + @@ -118,22 +118,19 @@ class GraphInvalidator(private val project: Project, private val graph: Resolved .toSet() } - fun shouldInvalidate(changedElement: PsiElement): Boolean { - return (sequenceOf(changedElement) + changedElement.parentsWithSelf) - .mapNotNull { it as? PsiClass } - .map { psiElementFactory.createType(it) } - .any { IntelliJType(project, it) in relevantTypes } - } + fun shouldInvalidate(changedElement: PsiElement): Boolean = + (sequenceOf(changedElement) + changedElement.parentsWithSelf) + .mapNotNull { it as? PsiClass } + .map { psiElementFactory.createType(it) } + .any { IntelliJType(project, it) in relevantTypes } - private fun spreadClasses(scope: Scope): List { - return scope.factoryMethods.mapNotNull { it.spread }.map { spread -> spread.clazz } - } + private fun spreadClasses(scope: Scope): List = + scope.factoryMethods.mapNotNull { it.spread }.map { spread -> spread.clazz } - private fun constructorClasses(scope: Scope): List { - return scope.factoryMethods.filterIsInstance().mapNotNull { - it.returnType.type.type.resolveClass() - } - } + private fun constructorClasses(scope: Scope): List = + scope.factoryMethods.filterIsInstance().mapNotNull { + it.returnType.type.type.resolveClass() + } private fun typeAndSupertypes(psiClass: PsiClass): Set { if (psiClass.qualifiedName == "java.lang.Object") { diff --git a/intellij/src/main/kotlin/motif/intellij/MotifProjectComponent.kt b/intellij/src/main/kotlin/motif/intellij/MotifService.kt similarity index 86% rename from intellij/src/main/kotlin/motif/intellij/MotifProjectComponent.kt rename to intellij/src/main/kotlin/motif/intellij/MotifService.kt index df11f638..f62b9491 100644 --- a/intellij/src/main/kotlin/motif/intellij/MotifProjectComponent.kt +++ b/intellij/src/main/kotlin/motif/intellij/MotifService.kt @@ -19,10 +19,11 @@ import com.intellij.codeInsight.daemon.LineMarkerProviders import com.intellij.ide.plugins.PluginManager import com.intellij.lang.Language import com.intellij.lang.java.JavaLanguage +import com.intellij.openapi.Disposable import com.intellij.openapi.actionSystem.ActionManager import com.intellij.openapi.actionSystem.AnAction import com.intellij.openapi.application.ApplicationManager -import com.intellij.openapi.components.ProjectComponent +import com.intellij.openapi.components.Service import com.intellij.openapi.progress.ProgressIndicator import com.intellij.openapi.progress.ProgressManager import com.intellij.openapi.progress.Task @@ -37,14 +38,15 @@ import com.intellij.psi.PsiElement import com.intellij.ui.content.Content import com.intellij.ui.content.ContentFactory import motif.core.ResolvedGraph -import motif.intellij.analytics.AnalyticsProjectComponent +import motif.intellij.analytics.AnalyticsService import motif.intellij.analytics.MotifAnalyticsActions import motif.intellij.ui.MotifErrorPanel import motif.intellij.ui.MotifScopePanel import motif.intellij.ui.MotifUsagePanel import org.jetbrains.kotlin.idea.KotlinLanguage -class MotifProjectComponent(val project: Project) : ProjectComponent { +@Service(Service.Level.PROJECT) +class MotifService(val project: Project) : Disposable { companion object { const val TOOL_WINDOW_ID: String = "Motif" @@ -61,10 +63,6 @@ class MotifProjectComponent(val project: Project) : ProjectComponent { "Error computing Motif graph. If error persists after you rebuild your project and restart IDE, please make sure to report the issue." private val MOTIF_ACTION_IDS = listOf("motif_usage", "motif_graph", "motif_ancestor_graph") - - fun getInstance(project: Project): MotifProjectComponent { - return project.getComponent(MotifProjectComponent::class.java) - } } private val graphFactory: GraphFactory by lazy { GraphFactory(project) } @@ -79,19 +77,22 @@ class MotifProjectComponent(val project: Project) : ProjectComponent { private var isRefreshing: Boolean = false private var pendingAction: (() -> Unit)? = null - override fun projectOpened() { + fun attach() { DumbService.getInstance(project).runWhenSmart { ApplicationManager.getApplication().runReadAction { // Initialize plugin with empty graph to avoid IDE startup slowdown val emptyGraph: ResolvedGraph = ResolvedGraph.create(emptyList()) onGraphUpdated(emptyGraph) - AnalyticsProjectComponent.getInstance(project) + project + .getService(AnalyticsService::class.java) .logEvent(MotifAnalyticsActions.PROJECT_OPENED) } } } + override fun dispose() {} + fun refreshGraph() { if (isRefreshing) { return @@ -108,15 +109,18 @@ class MotifProjectComponent(val project: Project) : ProjectComponent { onGraphUpdated(updatedGraph) val eventName: String = - if (updatedGraph.errors.isNotEmpty()) - MotifAnalyticsActions.GRAPH_UPDATE_ERROR - else MotifAnalyticsActions.GRAPH_UPDATE_SUCCESS - AnalyticsProjectComponent.getInstance(project).logEvent(eventName) + if (updatedGraph.errors.isNotEmpty()) { + MotifAnalyticsActions.GRAPH_UPDATE_ERROR + } else { + MotifAnalyticsActions.GRAPH_UPDATE_SUCCESS + } + project.getService(AnalyticsService::class.java).logEvent(eventName) } catch (t: Throwable) { val emptyGraph: ResolvedGraph = ResolvedGraph.create(emptyList()) onGraphUpdated(emptyGraph) - AnalyticsProjectComponent.getInstance(project) + project + .getService(AnalyticsService::class.java) .logEvent(MotifAnalyticsActions.GRAPH_COMPUTATION_ERROR) PluginManager.getLogger().error(LABEL_GRAPH_COMPUTATION_ERROR, t) } finally { @@ -124,7 +128,8 @@ class MotifProjectComponent(val project: Project) : ProjectComponent { } } } - }) + }, + ) } fun refreshGraph(action: () -> Unit) { @@ -166,7 +171,7 @@ class MotifProjectComponent(val project: Project) : ProjectComponent { if (toolWindowManager.getToolWindow(TOOL_WINDOW_ID) == null) { val toolWindow: ToolWindow = toolWindowManager.registerToolWindow(TOOL_WINDOW_ID, true, ToolWindowAnchor.RIGHT) - toolWindow.setIcon(IconLoader.getIcon("/icons/icon.svg")) + toolWindow.setIcon(IconLoader.getIcon("/icons/icon.svg", this::class.java)) toolWindow.title = TOOL_WINDOW_TITLE scopePanel = MotifScopePanel(project, graph) @@ -211,16 +216,14 @@ class MotifProjectComponent(val project: Project) : ProjectComponent { } private fun createScopeContent(toolWindow: ToolWindow): Content { - val content = - ContentFactory.SERVICE.getInstance().createContent(scopePanel, TAB_NAME_SCOPES, true) + val content = ContentFactory.getInstance().createContent(scopePanel, TAB_NAME_SCOPES, true) content.isCloseable = false toolWindow.contentManager.addContent(content) return content } private fun createErrorContent(toolWindow: ToolWindow): Content { - val content = - ContentFactory.SERVICE.getInstance().createContent(errorPanel, TAB_NAME_ERRORS, true) + val content = ContentFactory.getInstance().createContent(errorPanel, TAB_NAME_ERRORS, true) content.isCloseable = false toolWindow.contentManager.addContent(content) return content @@ -228,7 +231,7 @@ class MotifProjectComponent(val project: Project) : ProjectComponent { private fun createUsageContent(toolWindow: ToolWindow): Content { val content: Content = - ContentFactory.SERVICE.getInstance().createContent(usagePanel, TAB_NAME_USAGE, true) + ContentFactory.getInstance().createContent(usagePanel, TAB_NAME_USAGE, true) content.description = TAB_NAME_USAGE content.isCloseable = true toolWindow.contentManager.addContent(content) @@ -237,16 +240,15 @@ class MotifProjectComponent(val project: Project) : ProjectComponent { private fun createAncestorContent(toolWindow: ToolWindow): Content { val content: Content = - ContentFactory.SERVICE.getInstance().createContent(ancestorPanel, TAB_NAME_ANCESTOR, true) + ContentFactory.getInstance().createContent(ancestorPanel, TAB_NAME_ANCESTOR, true) content.description = TAB_NAME_ANCESTOR content.isCloseable = true toolWindow.contentManager.addContent(content) return content } - private fun findContentByDescription(toolWindow: ToolWindow, description: String): Content? { - return toolWindow.contentManager.contents.firstOrNull { it.description == description } - } + private fun findContentByDescription(toolWindow: ToolWindow, description: String): Content? = + toolWindow.contentManager.contents.firstOrNull { it.description == description } interface Listener { diff --git a/intellij/src/main/kotlin/motif/intellij/PsiUtils.kt b/intellij/src/main/kotlin/motif/intellij/PsiUtils.kt index 1407551d..147741cc 100644 --- a/intellij/src/main/kotlin/motif/intellij/PsiUtils.kt +++ b/intellij/src/main/kotlin/motif/intellij/PsiUtils.kt @@ -23,18 +23,16 @@ import org.jetbrains.kotlin.asJava.toLightMethods import org.jetbrains.kotlin.psi.KtClass import org.jetbrains.kotlin.psi.KtFunction -internal fun PsiElement.toPsiClass(): PsiElement? { - return when (this) { - is PsiClass -> this - is KtClass -> toLightClass() - else -> this - } -} +internal fun PsiElement.toPsiClass(): PsiElement? = + when (this) { + is PsiClass -> this + is KtClass -> toLightClass() + else -> this + } -internal fun PsiElement.toPsiMethod(): PsiElement? { - return when (this) { - is PsiMethod -> this - is KtFunction -> toLightMethods().singleOrNull() - else -> this - } -} +internal fun PsiElement.toPsiMethod(): PsiElement? = + when (this) { + is PsiMethod -> this + is KtFunction -> toLightMethods().singleOrNull() + else -> this + } diff --git a/intellij/src/main/kotlin/motif/intellij/ScopeHierarchyUtils.kt b/intellij/src/main/kotlin/motif/intellij/ScopeHierarchyUtils.kt index 23300d0f..b16839a0 100644 --- a/intellij/src/main/kotlin/motif/intellij/ScopeHierarchyUtils.kt +++ b/intellij/src/main/kotlin/motif/intellij/ScopeHierarchyUtils.kt @@ -37,172 +37,153 @@ import motif.models.ScopeSource import motif.models.Sink import motif.models.Source -class ScopeHierarchyUtils { +object ScopeHierarchyUtils { - companion object { - - object ScopeComparator : Comparator { - override fun compare(o1: Scope, o2: Scope): Int { - return o1.simpleName.compareTo(o2.simpleName) - } - } + object ScopeComparator : Comparator { + override fun compare(o1: Scope, o2: Scope): Int = o1.simpleName.compareTo(o2.simpleName) + } - object ScopeEdgeParentComparator : Comparator { - override fun compare(o1: ScopeEdge, o2: ScopeEdge): Int { - return o1.parent.simpleName.compareTo(o2.parent.simpleName) - } - } + object ScopeEdgeParentComparator : Comparator { + override fun compare(o1: ScopeEdge, o2: ScopeEdge): Int = + o1.parent.simpleName.compareTo(o2.parent.simpleName) + } - object ScopeEdgeChildComparator : Comparator { - override fun compare(o1: ScopeEdge, o2: ScopeEdge): Int { - return o1.child.simpleName.compareTo(o2.child.simpleName) - } - } + object ScopeEdgeChildComparator : Comparator { + override fun compare(o1: ScopeEdge, o2: ScopeEdge): Int = + o1.child.simpleName.compareTo(o2.child.simpleName) + } - object SourceComparator : Comparator { - override fun compare(o1: Source, o2: Source): Int { - if (o1.isExposed != o2.isExposed) return o1.isExposed.compareTo(o2.isExposed) * -1 - return o1.type.simpleName.compareTo(o2.type.simpleName) - } + object SourceComparator : Comparator { + override fun compare(o1: Source, o2: Source): Int { + if (o1.isExposed != o2.isExposed) return o1.isExposed.compareTo(o2.isExposed) * -1 + return o1.type.simpleName.compareTo(o2.type.simpleName) } + } - object SinkComparator : Comparator { - override fun compare(o1: Sink, o2: Sink): Int { - return o1.type.simpleName.compareTo(o2.type.simpleName) - } - } + object SinkComparator : Comparator { + override fun compare(o1: Sink, o2: Sink): Int = o1.type.simpleName.compareTo(o2.type.simpleName) + } - object MethodComparator : Comparator { - override fun compare(o1: Dependencies.Method, o2: Dependencies.Method): Int { - return o1.method.name.compareTo(o2.method.name) - } - } + object MethodComparator : Comparator { + override fun compare(o1: Dependencies.Method, o2: Dependencies.Method): Int = + o1.method.name.compareTo(o2.method.name) + } - fun buildRootElement(project: Project): PsiClass { - return JavaPsiFacade.getInstance(project) + fun buildRootElement(project: Project): PsiClass = + JavaPsiFacade.getInstance(project) .findClass(Object::class.java.name, GlobalSearchScope.allScope(project))!! - } - fun isRootElement(element: PsiElement?): Boolean { - return element is PsiClass && element.qualifiedName == Object::class.java.name - } + fun isRootElement(element: PsiElement?): Boolean = + element is PsiClass && element.qualifiedName == Object::class.java.name - fun isInitializedGraph(graph: ResolvedGraph): Boolean { - return graph.roots.isNotEmpty() - } + fun isInitializedGraph(graph: ResolvedGraph): Boolean = graph.roots.isNotEmpty() - fun isMotifScopeClass(element: PsiClass?): Boolean { - return element?.hasAnnotation(motif.Scope::class.java.name) ?: false - } + fun isMotifScopeClass(element: PsiClass?): Boolean = + element?.hasAnnotation(motif.Scope::class.java.name) ?: false - fun isMotifChildScopeMethod(element: PsiElement?): Boolean { - if (element is PsiMethod) { - val classElement = PsiTreeUtil.getParentOfType(element, PsiClass::class.java) - if (isMotifScopeClass(classElement) && element.returnType is PsiClassReferenceType) { - val returnElementClass: PsiClass? = - (element.returnType as PsiClassReferenceType).resolve() - if (isMotifScopeClass(returnElementClass)) { - return true - } + fun isMotifChildScopeMethod(element: PsiElement?): Boolean { + if (element is PsiMethod) { + val classElement = PsiTreeUtil.getParentOfType(element, PsiClass::class.java) + if (isMotifScopeClass(classElement) && element.returnType is PsiClassReferenceType) { + val returnElementClass: PsiClass? = (element.returnType as PsiClassReferenceType).resolve() + if (isMotifScopeClass(returnElementClass)) { + return true } } - return false - } - - fun getParentScopes( - project: Project, - graph: ResolvedGraph, - element: PsiClass - ): Array? { - val scopeType: PsiType = PsiElementFactory.SERVICE.getInstance(project).createType(element) - val type: IrType = IntelliJType(project, scopeType) - val scope: Scope? = graph.getScope(type) - return if (scope != null) - Iterables.toArray(graph.getParentEdges(scope), ScopeEdge::class.java) - else null } + return false + } - /* - * Returns the list of sources for given scope to display in the UI - */ - fun getVisibleSources(graph: ResolvedGraph, scope: Scope): List { - return graph.getSources(scope).filter { it !is ChildParameterSource && it !is ScopeSource } + fun getParentScopes( + project: Project, + graph: ResolvedGraph, + element: PsiClass, + ): Array? { + val scopeType: PsiType = PsiElementFactory.SERVICE.getInstance(project).createType(element) + val type: IrType = IntelliJType(project, scopeType) + val scope: Scope? = graph.getScope(type) + return if (scope != null) { + Iterables.toArray(graph.getParentEdges(scope), ScopeEdge::class.java) as Array? + } else { + null } + } - /* - * Returns the number of usage for the given class. - */ - fun getUsageCount( - project: Project, - graph: ResolvedGraph, - clazz: PsiClass, - includeSources: Boolean = true, - includeSinks: Boolean = true - ): Int { - var count = 0 - val elementType: PsiType = PsiElementFactory.SERVICE.getInstance(project).createType(clazz) - val type: IrType = IntelliJType(project, elementType) - if (includeSources) { - graph.getSources(type).forEach { _ -> count++ } - } - if (includeSinks) { - graph.getSinks(type).forEach { _ -> count++ } - } - return count - } + /* + * Returns the list of sources for given scope to display in the UI + */ + fun getVisibleSources(graph: ResolvedGraph, scope: Scope): List = + graph.getSources(scope).filter { it !is ChildParameterSource && it !is ScopeSource } + + /* + * Returns the number of usage for the given class. + */ + fun getUsageCount( + project: Project, + graph: ResolvedGraph, + clazz: PsiClass, + includeSources: Boolean = true, + includeSinks: Boolean = true, + ): Int { + var count = 0 + val elementType: PsiType = PsiElementFactory.SERVICE.getInstance(project).createType(clazz) + val type: IrType = IntelliJType(project, elementType) + if (includeSources) { + graph.getSources(type).forEach { _ -> count++ } + } + if (includeSinks) { + graph.getSinks(type).forEach { _ -> count++ } + } + return count + } - fun getUsageString(count: Int): String { - return when (count) { + fun getUsageString(count: Int): String = + when (count) { 0 -> "No usage" 1 -> "1 usage" else -> "$count usages" } - } - fun getObjectString(count: Int): String { - return when (count) { + fun getObjectString(count: Int): String = + when (count) { 0 -> "No object" 1 -> "1 object" else -> "$count objects" } - } - fun formatQualifiedName(qualifiedName: String): String { - val index: Int = qualifiedName.lastIndexOf(".") - return if (index > 0) qualifiedName.substring(0, index) else qualifiedName - } - - fun formatMultilineText(text: String): String { - return "" + text.replace("\n", "
    ") + "" - } + fun formatQualifiedName(qualifiedName: String): String { + val index: Int = qualifiedName.lastIndexOf(".") + return if (index > 0) qualifiedName.substring(0, index) else qualifiedName + } - /* - * Returns all the paths, starting from root scopes, leading to the provided scope. - */ - fun getMotifScopePaths(scope: Scope, graph: ResolvedGraph): ArrayList> { - val list: ArrayList = ArrayList() - val all: ArrayList> = ArrayList() - all.add(list) - getMotifScopePathsRecursive(scope, graph, list, all) - return all - } + fun formatMultilineText(text: String): String = "" + text.replace("\n", "
    ") + "" + + /* + * Returns all the paths, starting from root scopes, leading to the provided scope. + */ + fun getMotifScopePaths(scope: Scope, graph: ResolvedGraph): ArrayList> { + val list: ArrayList = ArrayList() + val all: ArrayList> = ArrayList() + all.add(list) + getMotifScopePathsRecursive(scope, graph, list, all) + return all + } - private fun getMotifScopePathsRecursive( - scope: Scope, - graph: ResolvedGraph, - list: ArrayList, - all: ArrayList> - ) { - list.add(scope) - val parentEdgesIterator: Iterator = graph.getParentEdges(scope).iterator() - if (parentEdgesIterator.hasNext()) { - getMotifScopePathsRecursive(parentEdgesIterator.next().parent, graph, list, all) - } - for (edge: ScopeEdge in parentEdgesIterator) { - val newList: ArrayList = ArrayList() - all.add(newList) - getMotifScopePathsRecursive(edge.parent, graph, newList, all) - } + private fun getMotifScopePathsRecursive( + scope: Scope, + graph: ResolvedGraph, + list: ArrayList, + all: ArrayList>, + ) { + list.add(scope) + val parentEdgesIterator: Iterator = graph.getParentEdges(scope).iterator() + if (parentEdgesIterator.hasNext()) { + getMotifScopePathsRecursive(parentEdgesIterator.next().parent, graph, list, all) + } + for (edge: ScopeEdge in parentEdgesIterator) { + val newList: ArrayList = ArrayList() + all.add(newList) + getMotifScopePathsRecursive(edge.parent, graph, newList, all) } } } diff --git a/intellij/src/main/kotlin/motif/intellij/actions/MotifAncestorGraphAction.kt b/intellij/src/main/kotlin/motif/intellij/actions/MotifAncestorGraphAction.kt index 8c1e618b..8860c355 100644 --- a/intellij/src/main/kotlin/motif/intellij/actions/MotifAncestorGraphAction.kt +++ b/intellij/src/main/kotlin/motif/intellij/actions/MotifAncestorGraphAction.kt @@ -23,18 +23,18 @@ import com.intellij.openapi.wm.ToolWindowManager import com.intellij.psi.PsiClass import com.intellij.psi.PsiElement import motif.core.ResolvedGraph -import motif.intellij.MotifProjectComponent -import motif.intellij.MotifProjectComponent.Companion.TOOL_WINDOW_ID -import motif.intellij.ScopeHierarchyUtils.Companion.getParentScopes -import motif.intellij.ScopeHierarchyUtils.Companion.isInitializedGraph -import motif.intellij.ScopeHierarchyUtils.Companion.isMotifScopeClass -import motif.intellij.analytics.AnalyticsProjectComponent +import motif.intellij.MotifService +import motif.intellij.MotifService.Companion.TOOL_WINDOW_ID +import motif.intellij.ScopeHierarchyUtils.getParentScopes +import motif.intellij.ScopeHierarchyUtils.isInitializedGraph +import motif.intellij.ScopeHierarchyUtils.isMotifScopeClass +import motif.intellij.analytics.AnalyticsService import motif.intellij.analytics.MotifAnalyticsActions /* * {@AnAction} used to trigger displaying a particular scope ancestors hierarchy. */ -class MotifAncestorGraphAction : AnAction(), MotifProjectComponent.Listener { +class MotifAncestorGraphAction : AnAction(), MotifService.Listener { private var graph: ResolvedGraph? = null @@ -48,17 +48,18 @@ class MotifAncestorGraphAction : AnAction(), MotifProjectComponent.Listener { val graph = graph ?: return if (!isInitializedGraph(graph)) { - MotifProjectComponent.getInstance(project).refreshGraph { actionPerformed(event) } + project.getService(MotifService::class.java).refreshGraph { actionPerformed(event) } return } val toolWindow: ToolWindow = ToolWindowManager.getInstance(project).getToolWindow(TOOL_WINDOW_ID) ?: return toolWindow.activate { - MotifProjectComponent.getInstance(project).onSelectedAncestorScope(element) + project.getService(MotifService::class.java).onSelectedAncestorScope(element) } - AnalyticsProjectComponent.getInstance(project) + project + .getService(AnalyticsService::class.java) .logEvent(MotifAnalyticsActions.ANCESTOR_MENU_CLICK) } @@ -73,7 +74,5 @@ class MotifAncestorGraphAction : AnAction(), MotifProjectComponent.Listener { (getParentScopes(project, graph, element)?.isNotEmpty() == true)) } - private fun AnActionEvent.getPsiElement(): PsiElement? { - return getData(CommonDataKeys.PSI_ELEMENT) - } + private fun AnActionEvent.getPsiElement(): PsiElement? = getData(CommonDataKeys.PSI_ELEMENT) } diff --git a/intellij/src/main/kotlin/motif/intellij/actions/MotifGraphAction.kt b/intellij/src/main/kotlin/motif/intellij/actions/MotifGraphAction.kt index ae4e892b..9f989dff 100644 --- a/intellij/src/main/kotlin/motif/intellij/actions/MotifGraphAction.kt +++ b/intellij/src/main/kotlin/motif/intellij/actions/MotifGraphAction.kt @@ -20,16 +20,16 @@ import com.intellij.openapi.actionSystem.AnActionEvent import com.intellij.openapi.wm.ToolWindow import com.intellij.openapi.wm.ToolWindowManager import motif.core.ResolvedGraph -import motif.intellij.MotifProjectComponent -import motif.intellij.MotifProjectComponent.Companion.TOOL_WINDOW_ID -import motif.intellij.ScopeHierarchyUtils.Companion.isInitializedGraph -import motif.intellij.analytics.AnalyticsProjectComponent +import motif.intellij.MotifService +import motif.intellij.MotifService.Companion.TOOL_WINDOW_ID +import motif.intellij.ScopeHierarchyUtils.isInitializedGraph +import motif.intellij.analytics.AnalyticsService import motif.intellij.analytics.MotifAnalyticsActions /* * {@AnAction} used to trigger displaying entire scope hierarchy. */ -class MotifGraphAction : AnAction(), MotifProjectComponent.Listener { +class MotifGraphAction : AnAction(), MotifService.Listener { private var graph: ResolvedGraph? = null @@ -42,7 +42,7 @@ class MotifGraphAction : AnAction(), MotifProjectComponent.Listener { val graph = graph ?: return if (!isInitializedGraph(graph)) { - MotifProjectComponent.getInstance(project).refreshGraph { actionPerformed(event) } + project.getService(MotifService::class.java).refreshGraph { actionPerformed(event) } return } @@ -50,7 +50,9 @@ class MotifGraphAction : AnAction(), MotifProjectComponent.Listener { ToolWindowManager.getInstance(project).getToolWindow(TOOL_WINDOW_ID) ?: return toolWindow.activate {} - AnalyticsProjectComponent.getInstance(project).logEvent(MotifAnalyticsActions.GRAPH_MENU_CLICK) + project + .getService(AnalyticsService::class.java) + .logEvent(MotifAnalyticsActions.GRAPH_MENU_CLICK) } override fun update(e: AnActionEvent) { diff --git a/intellij/src/main/kotlin/motif/intellij/actions/MotifUsageAction.kt b/intellij/src/main/kotlin/motif/intellij/actions/MotifUsageAction.kt index 6a87669b..530da06f 100644 --- a/intellij/src/main/kotlin/motif/intellij/actions/MotifUsageAction.kt +++ b/intellij/src/main/kotlin/motif/intellij/actions/MotifUsageAction.kt @@ -23,17 +23,17 @@ import com.intellij.openapi.wm.ToolWindowManager import com.intellij.psi.PsiClass import com.intellij.psi.PsiElement import motif.core.ResolvedGraph -import motif.intellij.MotifProjectComponent -import motif.intellij.MotifProjectComponent.Companion.TOOL_WINDOW_ID +import motif.intellij.MotifService +import motif.intellij.MotifService.Companion.TOOL_WINDOW_ID import motif.intellij.ScopeHierarchyUtils -import motif.intellij.ScopeHierarchyUtils.Companion.getUsageCount -import motif.intellij.analytics.AnalyticsProjectComponent +import motif.intellij.ScopeHierarchyUtils.getUsageCount +import motif.intellij.analytics.AnalyticsService import motif.intellij.analytics.MotifAnalyticsActions /* * {@AnAction} used to trigger navigation to a particular scope in the scope hierarchy window. */ -class MotifUsageAction : AnAction(), MotifProjectComponent.Listener { +class MotifUsageAction : AnAction(), MotifService.Listener { private var graph: ResolvedGraph? = null @@ -47,15 +47,17 @@ class MotifUsageAction : AnAction(), MotifProjectComponent.Listener { val graph = graph ?: return if (!ScopeHierarchyUtils.isInitializedGraph(graph)) { - MotifProjectComponent.getInstance(project).refreshGraph { actionPerformed(event) } + project.getService(MotifService::class.java).refreshGraph { actionPerformed(event) } return } val toolWindow: ToolWindow = ToolWindowManager.getInstance(project).getToolWindow(TOOL_WINDOW_ID) ?: return - toolWindow.activate { MotifProjectComponent.getInstance(project).onSelectedClass(element) } + toolWindow.activate { project.getService(MotifService::class.java).onSelectedClass(element) } - AnalyticsProjectComponent.getInstance(project).logEvent(MotifAnalyticsActions.USAGE_MENU_CLICK) + project + .getService(AnalyticsService::class.java) + .logEvent(MotifAnalyticsActions.USAGE_MENU_CLICK) } override fun update(e: AnActionEvent) { @@ -68,7 +70,5 @@ class MotifUsageAction : AnAction(), MotifProjectComponent.Listener { getUsageCount(project, graph, element) > 0) } - private fun AnActionEvent.getPsiElement(): PsiElement? { - return getData(CommonDataKeys.PSI_ELEMENT) - } + private fun AnActionEvent.getPsiElement(): PsiElement? = getData(CommonDataKeys.PSI_ELEMENT) } diff --git a/intellij/src/main/kotlin/motif/intellij/analytics/AnalyticsProjectComponent.kt b/intellij/src/main/kotlin/motif/intellij/analytics/AnalyticsService.kt similarity index 81% rename from intellij/src/main/kotlin/motif/intellij/analytics/AnalyticsProjectComponent.kt rename to intellij/src/main/kotlin/motif/intellij/analytics/AnalyticsService.kt index 9034531a..9b0848d3 100644 --- a/intellij/src/main/kotlin/motif/intellij/analytics/AnalyticsProjectComponent.kt +++ b/intellij/src/main/kotlin/motif/intellij/analytics/AnalyticsService.kt @@ -15,23 +15,23 @@ */ package motif.intellij.analytics -import com.intellij.openapi.components.ProjectComponent +import com.intellij.openapi.Disposable +import com.intellij.openapi.components.Service import com.intellij.openapi.extensions.ExtensionPointName import com.intellij.openapi.project.Project import java.util.UUID -class AnalyticsProjectComponent(val project: Project) : ProjectComponent { +@Service(Service.Level.PROJECT) +class AnalyticsService(val project: Project) : Disposable { companion object { private val LOGGER_EXTENSION_POINT_NAME: ExtensionPointName = ExtensionPointName.create("com.uber.motif.motifAnalyticsLogger") private val SESSION_ID = UUID.randomUUID() - - fun getInstance(project: Project): AnalyticsProjectComponent { - return project.getComponent(AnalyticsProjectComponent::class.java) - } } + override fun dispose() {} + fun logEvent(action: String) { val metadata: Map = mapOf("SessionId" to SESSION_ID.toString(), "ActionName" to action) diff --git a/intellij/src/main/kotlin/motif/intellij/analytics/MotifAnalyticsActions.kt b/intellij/src/main/kotlin/motif/intellij/analytics/MotifAnalyticsActions.kt index 0a309f4e..3a22ca02 100644 --- a/intellij/src/main/kotlin/motif/intellij/analytics/MotifAnalyticsActions.kt +++ b/intellij/src/main/kotlin/motif/intellij/analytics/MotifAnalyticsActions.kt @@ -15,21 +15,19 @@ */ package motif.intellij.analytics -abstract class MotifAnalyticsActions { +object MotifAnalyticsActions { - companion object { - const val PROJECT_OPENED = "projectOpened" + const val PROJECT_OPENED = "projectOpened" - const val GRAPH_INIT = "graphInit" - const val GRAPH_UPDATE = "graphUpdate" - const val GRAPH_UPDATE_SUCCESS = "graphUpdateSuccess" - const val GRAPH_UPDATE_ERROR = "graphUpdateError" - const val GRAPH_COMPUTATION_ERROR = "graphComputationError" + const val GRAPH_INIT = "graphInit" + const val GRAPH_UPDATE = "graphUpdate" + const val GRAPH_UPDATE_SUCCESS = "graphUpdateSuccess" + const val GRAPH_UPDATE_ERROR = "graphUpdateError" + const val GRAPH_COMPUTATION_ERROR = "graphComputationError" - const val GRAPH_MENU_CLICK = "graphMenuClick" - const val ANCESTOR_MENU_CLICK = "ancestorMenuClick" - const val USAGE_MENU_CLICK = "usageMenuClick" - const val ANCESTOR_GUTTER_CLICK = "ancestorGutterClick" - const val NAVIGATION_GUTTER_CLICK = "navigationGutterClick" - } + const val GRAPH_MENU_CLICK = "graphMenuClick" + const val ANCESTOR_MENU_CLICK = "ancestorMenuClick" + const val USAGE_MENU_CLICK = "usageMenuClick" + const val ANCESTOR_GUTTER_CLICK = "ancestorGutterClick" + const val NAVIGATION_GUTTER_CLICK = "navigationGutterClick" } diff --git a/intellij/src/main/kotlin/motif/intellij/hierarchy/ErrorHierarchyBrowser.kt b/intellij/src/main/kotlin/motif/intellij/hierarchy/ErrorHierarchyBrowser.kt index 8722c30b..ac1ac69d 100644 --- a/intellij/src/main/kotlin/motif/intellij/hierarchy/ErrorHierarchyBrowser.kt +++ b/intellij/src/main/kotlin/motif/intellij/hierarchy/ErrorHierarchyBrowser.kt @@ -33,8 +33,8 @@ import javax.swing.JTree import javax.swing.tree.DefaultMutableTreeNode import motif.core.ResolvedGraph import motif.errormessage.ErrorMessage -import motif.intellij.MotifProjectComponent -import motif.intellij.ScopeHierarchyUtils.Companion.isRootElement +import motif.intellij.MotifService +import motif.intellij.ScopeHierarchyUtils.isRootElement import motif.intellij.hierarchy.ScopeHierarchyBrowser.Companion.LABEL_GO_NEXT_SCOPE import motif.intellij.hierarchy.ScopeHierarchyBrowser.Companion.LABEL_GO_PREVIOUS_SCOPE import motif.intellij.hierarchy.descriptor.ScopeHierarchyErrorDescriptor @@ -48,8 +48,8 @@ class ErrorHierarchyBrowser( project: Project, initialGraph: ResolvedGraph, private val rootElement: PsiElement, - private val selectionListener: Listener? -) : HierarchyBrowserBase(project, rootElement), MotifProjectComponent.Listener { + private val selectionListener: Listener?, +) : HierarchyBrowserBase(project, rootElement), MotifService.Listener { private var graph: ResolvedGraph = initialGraph @@ -59,19 +59,14 @@ class ErrorHierarchyBrowser( DataKey.create(ErrorHierarchyBrowser::class.java.name) } - override fun isApplicableElement(element: PsiElement): Boolean { - return element is PsiClass - } + override fun isApplicableElement(element: PsiElement): Boolean = element is PsiClass - override fun getActionPlace(): String { - return ActionPlaces.METHOD_HIERARCHY_VIEW_TOOLBAR - } + override fun getActionPlace(): String = ActionPlaces.METHOD_HIERARCHY_VIEW_TOOLBAR override fun prependActions(actionGroup: DefaultActionGroup) {} - override fun getComparator(): Comparator> { - return JavaHierarchyUtil.getComparator(myProject) - } + override fun getComparator(): Comparator> = + JavaHierarchyUtil.getComparator(myProject) override fun getElementFromDescriptor(descriptor: HierarchyNodeDescriptor): PsiElement? { if (isRootElement(descriptor.psiElement)) { @@ -80,19 +75,13 @@ class ErrorHierarchyBrowser( return descriptor.psiElement } - override fun getPrevOccurenceActionNameImpl(): String { - return LABEL_GO_PREVIOUS_SCOPE - } + override fun getPrevOccurenceActionNameImpl(): String = LABEL_GO_PREVIOUS_SCOPE - override fun getNextOccurenceActionNameImpl(): String { - return LABEL_GO_NEXT_SCOPE - } + override fun getNextOccurenceActionNameImpl(): String = LABEL_GO_NEXT_SCOPE - override fun createLegendPanel(): JPanel? { - return null - } + override fun createLegendPanel(): JPanel? = null - override fun createTrees(trees: MutableMap) { + override fun createTrees(trees: MutableMap) { trees[ERROR_HIERARCHY_TYPE] = createTree(true) } @@ -105,21 +94,25 @@ class ErrorHierarchyBrowser( val descriptor = node.userObject if (descriptor is ScopeHierarchyErrorDescriptor) { selectionListener?.onSelectedErrorChanged( - descriptor.element, descriptor.error, descriptor.errorMessage) + descriptor.element, + descriptor.error, + descriptor.errorMessage, + ) } } } } } - override fun getContentDisplayName(typeName: String, element: PsiElement): String? { - return MessageFormat.format( - typeName, ClassPresentationUtil.getNameForClass(element as PsiClass, false)) - } + override fun getContentDisplayName(typeName: String, element: PsiElement): String? = + MessageFormat.format( + typeName, + ClassPresentationUtil.getNameForClass(element as PsiClass, false), + ) override fun createHierarchyTreeStructure( typeName: String, - psiElement: PsiElement + psiElement: PsiElement, ): HierarchyTreeStructure? { if (psiElement == rootElement) { val descriptor: HierarchyNodeDescriptor = @@ -129,12 +122,8 @@ class ErrorHierarchyBrowser( return null } - override fun getBrowserDataKey(): String { - return DATA_KEY.name - } - override fun doRefresh(currentBuilderOnly: Boolean) { - MotifProjectComponent.getInstance(project).refreshGraph() + project.getService(MotifService::class.java).refreshGraph() } override fun onGraphUpdated(graph: ResolvedGraph) { diff --git a/intellij/src/main/kotlin/motif/intellij/hierarchy/HierarchyBrowserBase.kt b/intellij/src/main/kotlin/motif/intellij/hierarchy/HierarchyBrowserBase.kt index 7c2a2f75..a403aa1c 100644 --- a/intellij/src/main/kotlin/motif/intellij/hierarchy/HierarchyBrowserBase.kt +++ b/intellij/src/main/kotlin/motif/intellij/hierarchy/HierarchyBrowserBase.kt @@ -48,9 +48,8 @@ abstract class HierarchyBrowserBase(val project: Project, private val rootElemen TreeSpeedSearch(tree, { path -> path.lastPathComponent.toString() }, true) TreeUtil.installActions(tree) object : AutoScrollToSourceHandler() { - override fun isAutoScrollMode(): Boolean { - return HierarchyBrowserManager.getSettings(myProject).IS_AUTOSCROLL_TO_SOURCE - } + override fun isAutoScrollMode(): Boolean = + HierarchyBrowserManager.getSettings(myProject).IS_AUTOSCROLL_TO_SOURCE override fun setAutoScrollMode(state: Boolean) { HierarchyBrowserManager.getSettings(myProject).IS_AUTOSCROLL_TO_SOURCE = state diff --git a/intellij/src/main/kotlin/motif/intellij/hierarchy/ScopeHierarchyBrowser.kt b/intellij/src/main/kotlin/motif/intellij/hierarchy/ScopeHierarchyBrowser.kt index 5d4cfc98..58b1912e 100644 --- a/intellij/src/main/kotlin/motif/intellij/hierarchy/ScopeHierarchyBrowser.kt +++ b/intellij/src/main/kotlin/motif/intellij/hierarchy/ScopeHierarchyBrowser.kt @@ -42,10 +42,10 @@ import motif.ast.IrType import motif.ast.intellij.IntelliJClass import motif.ast.intellij.IntelliJType import motif.core.ResolvedGraph -import motif.intellij.MotifProjectComponent +import motif.intellij.MotifService import motif.intellij.ScopeHierarchyUtils -import motif.intellij.ScopeHierarchyUtils.Companion.isMotifScopeClass -import motif.intellij.analytics.AnalyticsProjectComponent +import motif.intellij.ScopeHierarchyUtils.isMotifScopeClass +import motif.intellij.analytics.AnalyticsService import motif.intellij.analytics.MotifAnalyticsActions import motif.intellij.hierarchy.descriptor.ScopeHierarchyRootDescriptor import motif.intellij.hierarchy.descriptor.ScopeHierarchyScopeAncestorDescriptor @@ -59,8 +59,8 @@ class ScopeHierarchyBrowser( project: Project, initialGraph: ResolvedGraph, private val rootElement: PsiElement, - private val selectionListener: Listener? -) : HierarchyBrowserBase(project, rootElement), MotifProjectComponent.Listener { + private val selectionListener: Listener?, +) : HierarchyBrowserBase(project, rootElement), MotifService.Listener { companion object { const val LABEL_GO_PREVIOUS_SCOPE: String = "Go to previous Scope." @@ -85,21 +85,14 @@ class ScopeHierarchyBrowser( super.doRefresh(true) } - fun isUpdating(): Boolean { - return status == Status.INITIALIZING || status == Status.REFRESHING - } + fun isUpdating(): Boolean = status == Status.INITIALIZING || status == Status.REFRESHING - override fun isApplicableElement(element: PsiElement): Boolean { - return element is PsiClass - } + override fun isApplicableElement(element: PsiElement): Boolean = element is PsiClass - override fun getActionPlace(): String { - return ActionPlaces.METHOD_HIERARCHY_VIEW_TOOLBAR - } + override fun getActionPlace(): String = ActionPlaces.METHOD_HIERARCHY_VIEW_TOOLBAR - override fun getComparator(): Comparator> { - return JavaHierarchyUtil.getComparator(myProject) - } + override fun getComparator(): Comparator> = + JavaHierarchyUtil.getComparator(myProject) override fun getElementFromDescriptor(descriptor: HierarchyNodeDescriptor): PsiElement? { if (ScopeHierarchyUtils.isRootElement(descriptor.psiElement)) { @@ -108,21 +101,15 @@ class ScopeHierarchyBrowser( return descriptor.psiElement } - override fun getPrevOccurenceActionNameImpl(): String { - return LABEL_GO_PREVIOUS_SCOPE - } + override fun getPrevOccurenceActionNameImpl(): String = LABEL_GO_PREVIOUS_SCOPE - override fun createLegendPanel(): JPanel? { - return null - } + override fun createLegendPanel(): JPanel? = null - override fun createTrees(trees: MutableMap) { + override fun createTrees(trees: MutableMap) { trees[TYPE_HIERARCHY_TYPE] = createTree(true) } - override fun getNextOccurenceActionNameImpl(): String { - return LABEL_GO_NEXT_SCOPE - } + override fun getNextOccurenceActionNameImpl(): String = LABEL_GO_NEXT_SCOPE override fun getContentDisplayName(typeName: String, element: PsiElement): String? { if (element !is PsiClass) { @@ -144,12 +131,15 @@ class ScopeHierarchyBrowser( override fun createHierarchyTreeStructure( typeName: String, - psiElement: PsiElement + psiElement: PsiElement, ): HierarchyTreeStructure? { if (psiElement == rootElement) { // Display entire graph hierarchy return ScopeHierarchyTreeStructure( - myProject, graph, ScopeHierarchyRootDescriptor(myProject, graph, psiElement, status)) + myProject, + graph, + ScopeHierarchyRootDescriptor(myProject, graph, psiElement, status), + ) } else if (psiElement is PsiClass && isMotifScopeClass(psiElement)) { // Display the scope ancestors hierarchy val scopeType: PsiType = PsiElementFactory.SERVICE.getInstance(project).createType(psiElement) @@ -164,10 +154,6 @@ class ScopeHierarchyBrowser( return null } - override fun getBrowserDataKey(): String { - return DATA_KEY.name - } - override fun configureTree(tree: Tree) { super.configureTree(tree) tree.addTreeSelectionListener { @@ -193,12 +179,15 @@ class ScopeHierarchyBrowser( else -> {} } - MotifProjectComponent.getInstance(project).refreshGraph() + project.getService(MotifService::class.java).refreshGraph() val action: String = - if (status == Status.INITIALIZING) MotifAnalyticsActions.GRAPH_INIT - else MotifAnalyticsActions.GRAPH_UPDATE - AnalyticsProjectComponent.getInstance(project).logEvent(action) + if (status == Status.INITIALIZING) { + MotifAnalyticsActions.GRAPH_INIT + } else { + MotifAnalyticsActions.GRAPH_UPDATE + } + project.getService(AnalyticsService::class.java).logEvent(action) } /* @@ -218,7 +207,8 @@ class ScopeHierarchyBrowser( com.intellij.ide.actions.RefreshAction( IdeBundle.message("action.refresh"), IdeBundle.message("action.refresh"), - AllIcons.Actions.Refresh) { + AllIcons.Actions.Refresh, + ) { override fun actionPerformed(e: AnActionEvent) { doRefresh(false) @@ -233,7 +223,8 @@ class ScopeHierarchyBrowser( AnAction( IdeBundle.message("action.help"), IdeBundle.message("action.help"), - AllIcons.General.TodoQuestion) { + AllIcons.General.TodoQuestion, + ) { override fun actionPerformed(e: AnActionEvent) { BrowserUtil.open("https://github.com/uber/motif/wiki/Motif-IntelliJ-IDE-Plugin-Help") @@ -248,11 +239,13 @@ class ScopeHierarchyBrowser( AnAction( "File an issue", "File an issue or feature request", - AllIcons.Toolwindows.ToolWindowDebugger) { + AllIcons.Toolwindows.ToolWindowDebugger, + ) { override fun actionPerformed(e: AnActionEvent) { BrowserUtil.open( - "https://github.com/uber/motif/issues/new?title=[Motif%20IDE%20Plugin]%20:%20%3Center%20issue%20title%20here%3E") + "https://github.com/uber/motif/issues/new?title=[Motif%20IDE%20Plugin]%20:%20%3Center%20issue%20title%20here%3E", + ) } override fun update(event: AnActionEvent) { diff --git a/intellij/src/main/kotlin/motif/intellij/hierarchy/ScopeHierarchyTreeStructure.kt b/intellij/src/main/kotlin/motif/intellij/hierarchy/ScopeHierarchyTreeStructure.kt index 0cf7cb9a..96a9b0a2 100644 --- a/intellij/src/main/kotlin/motif/intellij/hierarchy/ScopeHierarchyTreeStructure.kt +++ b/intellij/src/main/kotlin/motif/intellij/hierarchy/ScopeHierarchyTreeStructure.kt @@ -27,13 +27,13 @@ import motif.ast.intellij.IntelliJType import motif.core.ResolvedGraph import motif.errormessage.ErrorMessage import motif.intellij.ScopeHierarchyUtils -import motif.intellij.ScopeHierarchyUtils.Companion.MethodComparator -import motif.intellij.ScopeHierarchyUtils.Companion.ScopeComparator -import motif.intellij.ScopeHierarchyUtils.Companion.ScopeEdgeChildComparator -import motif.intellij.ScopeHierarchyUtils.Companion.ScopeEdgeParentComparator -import motif.intellij.ScopeHierarchyUtils.Companion.SinkComparator -import motif.intellij.ScopeHierarchyUtils.Companion.SourceComparator -import motif.intellij.ScopeHierarchyUtils.Companion.getVisibleSources +import motif.intellij.ScopeHierarchyUtils.MethodComparator +import motif.intellij.ScopeHierarchyUtils.ScopeComparator +import motif.intellij.ScopeHierarchyUtils.ScopeEdgeChildComparator +import motif.intellij.ScopeHierarchyUtils.ScopeEdgeParentComparator +import motif.intellij.ScopeHierarchyUtils.SinkComparator +import motif.intellij.ScopeHierarchyUtils.SourceComparator +import motif.intellij.ScopeHierarchyUtils.getVisibleSources import motif.intellij.hierarchy.descriptor.ScopeHierarchyDependenciesSectionDescriptor import motif.intellij.hierarchy.descriptor.ScopeHierarchyDependencyDescriptor import motif.intellij.hierarchy.descriptor.ScopeHierarchyErrorDescriptor @@ -57,7 +57,7 @@ import motif.models.Dependencies class ScopeHierarchyTreeStructure( val project: Project, val graph: ResolvedGraph, - descriptor: HierarchyNodeDescriptor + descriptor: HierarchyNodeDescriptor, ) : HierarchyTreeStructure(project, descriptor) { companion object { @@ -77,7 +77,13 @@ class ScopeHierarchyTreeStructure( graph.roots.sortedWith(ScopeComparator).forEach { scope -> descriptors.add( ScopeHierarchyScopeDescriptor( - myProject, graph, descriptor, (scope.clazz as IntelliJClass).psiClass, scope)) + myProject, + graph, + descriptor, + (scope.clazz as IntelliJClass).psiClass, + scope, + ), + ) } } is ScopeHierarchyScopeAncestorDescriptor -> { @@ -89,7 +95,9 @@ class ScopeHierarchyTreeStructure( graph, descriptor, (edge.parent.clazz as IntelliJClass).psiClass, - edge.parent)) + edge.parent, + ), + ) } } is ScopeHierarchyScopeDescriptor -> { @@ -100,7 +108,9 @@ class ScopeHierarchyTreeStructure( graph, descriptor, (edge.child.clazz as IntelliJClass).psiClass, - edge.child)) + edge.child, + ), + ) } } is ScopeHierarchySourcesSectionDescriptor -> { @@ -110,7 +120,13 @@ class ScopeHierarchyTreeStructure( if (descriptors.isEmpty()) { descriptors.add( ScopeHierarchySimpleDescriptor( - myProject, graph, descriptor, descriptor.element, LABEL_SCOPE_NO_PROVIDE)) + myProject, + graph, + descriptor, + descriptor.element, + LABEL_SCOPE_NO_PROVIDE, + ), + ) } } is ScopeHierarchySinksSectionDescriptor -> { @@ -120,16 +136,36 @@ class ScopeHierarchyTreeStructure( if (descriptors.isEmpty()) { descriptors.add( ScopeHierarchySimpleDescriptor( - myProject, graph, descriptor, descriptor.element, LABEL_SCOPE_NO_CONSUME)) + myProject, + graph, + descriptor, + descriptor.element, + LABEL_SCOPE_NO_CONSUME, + ), + ) } } is ScopeHierarchySourcesAndSinksSectionDescriptor -> { descriptors.add( ScopeHierarchySourcesSectionDescriptor( - myProject, graph, descriptor, descriptor.element, descriptor.scope, true)) + myProject, + graph, + descriptor, + descriptor.element, + descriptor.scope, + true, + ), + ) descriptors.add( ScopeHierarchySinksSectionDescriptor( - myProject, graph, descriptor, descriptor.element, descriptor.scope, true)) + myProject, + graph, + descriptor, + descriptor.element, + descriptor.scope, + true, + ), + ) } is ScopeHierarchyDependenciesSectionDescriptor -> { val dependencies: Dependencies? = descriptor.scope.dependencies @@ -141,7 +177,9 @@ class ScopeHierarchyTreeStructure( graph, descriptor, (method.method as IntelliJMethod).psiMethod, - method)) + method, + ), + ) } } if (descriptors.isEmpty()) { @@ -151,7 +189,9 @@ class ScopeHierarchyTreeStructure( graph, descriptor, descriptor.psiElement!!, - LABEL_SCOPE_NO_DEPENDENCIES)) + LABEL_SCOPE_NO_DEPENDENCIES, + ), + ) } } is ScopeHierarchySinkDetailsDescriptor -> { @@ -163,7 +203,8 @@ class ScopeHierarchyTreeStructure( is ScopeHierarchySinkDescriptor -> { graph.getProviders(descriptor.sink).sortedWith(SourceComparator).forEach { source -> descriptors.add( - ScopeHierarchySourceDetailsDescriptor(myProject, graph, descriptor, source)) + ScopeHierarchySourceDetailsDescriptor(myProject, graph, descriptor, source), + ) } } is ScopeHierarchySourceDescriptor -> { @@ -175,25 +216,46 @@ class ScopeHierarchyTreeStructure( graph.errors.forEach { error -> val errorMessage: ErrorMessage = ErrorMessage.get(error) descriptors.add( - ScopeHierarchyErrorDescriptor(myProject, graph, descriptor, error, errorMessage)) + ScopeHierarchyErrorDescriptor(myProject, graph, descriptor, error, errorMessage), + ) } } is ScopeHierarchyUsageSectionDescriptor -> { val countSources: Int = ScopeHierarchyUtils.getUsageCount( - project, graph, descriptor.clazz, includeSources = true, includeSinks = false) + project, + graph, + descriptor.clazz, + includeSources = true, + includeSinks = false, + ) val countSinks: Int = ScopeHierarchyUtils.getUsageCount( - project, graph, descriptor.clazz, includeSources = false, includeSinks = true) + project, + graph, + descriptor.clazz, + includeSources = false, + includeSinks = true, + ) if (countSources > 0) { descriptors.add( ScopeHierarchyUsageSourcesSectionDescriptor( - myProject, graph, descriptor, descriptor.clazz)) + myProject, + graph, + descriptor, + descriptor.clazz, + ), + ) } if (countSinks > 0) { descriptors.add( ScopeHierarchyUsageSinksSectionDescriptor( - myProject, graph, descriptor, descriptor.clazz)) + myProject, + graph, + descriptor, + descriptor.clazz, + ), + ) } } is ScopeHierarchyUsageSourcesSectionDescriptor -> { @@ -202,7 +264,8 @@ class ScopeHierarchyTreeStructure( val type: IrType = IntelliJType(project, elementType) graph.getSources(type).sortedWith(SourceComparator).forEach { source -> descriptors.add( - ScopeHierarchySourceDetailsDescriptor(myProject, graph, descriptor, source)) + ScopeHierarchySourceDetailsDescriptor(myProject, graph, descriptor, source), + ) } } is ScopeHierarchyUsageSinksSectionDescriptor -> { diff --git a/intellij/src/main/kotlin/motif/intellij/hierarchy/ScopePropertyHierarchyBrowser.kt b/intellij/src/main/kotlin/motif/intellij/hierarchy/ScopePropertyHierarchyBrowser.kt index 3f287506..e88412f6 100644 --- a/intellij/src/main/kotlin/motif/intellij/hierarchy/ScopePropertyHierarchyBrowser.kt +++ b/intellij/src/main/kotlin/motif/intellij/hierarchy/ScopePropertyHierarchyBrowser.kt @@ -41,8 +41,8 @@ import motif.ast.IrType import motif.ast.intellij.IntelliJClass import motif.ast.intellij.IntelliJType import motif.core.ResolvedGraph -import motif.intellij.MotifProjectComponent -import motif.intellij.ScopeHierarchyUtils.Companion.isMotifScopeClass +import motif.intellij.MotifService +import motif.intellij.ScopeHierarchyUtils.isMotifScopeClass import motif.intellij.hierarchy.ScopeHierarchyBrowser.Companion.LABEL_GO_NEXT_SCOPE import motif.intellij.hierarchy.ScopeHierarchyBrowser.Companion.LABEL_GO_PREVIOUS_SCOPE import motif.intellij.hierarchy.descriptor.ScopeHierarchyDependenciesSectionDescriptor @@ -60,8 +60,8 @@ class ScopePropertyHierarchyBrowser( project: Project, initialGraph: ResolvedGraph, private val rootElement: PsiElement, - private val hierarchyType: PropertyHierarchyType -) : HierarchyBrowserBase(project, rootElement), MotifProjectComponent.Listener { + private val hierarchyType: PropertyHierarchyType, +) : HierarchyBrowserBase(project, rootElement), MotifService.Listener { private var graph: ResolvedGraph = initialGraph @@ -69,14 +69,15 @@ class ScopePropertyHierarchyBrowser( CONSUME, PROVIDE, CONSUME_AND_PROVIDE, - DEPENDENCIES + DEPENDENCIES, } companion object { const val PROPERTY_HIERARCHY_TYPE: String = "Properties" private val DATA_KEY = DataKey.create( - ScopePropertyHierarchyBrowser::class.java.name) + ScopePropertyHierarchyBrowser::class.java.name, + ) private const val LABEL_NO_SCOPE: String = "No Scope is selected." } @@ -87,33 +88,23 @@ class ScopePropertyHierarchyBrowser( } } - override fun isApplicableElement(element: PsiElement): Boolean { - return element is PsiClass - } + override fun isApplicableElement(element: PsiElement): Boolean = element is PsiClass - override fun getActionPlace(): String { - return ActionPlaces.METHOD_HIERARCHY_VIEW_TOOLBAR - } + override fun getActionPlace(): String = ActionPlaces.METHOD_HIERARCHY_VIEW_TOOLBAR override fun prependActions(actionGroup: DefaultActionGroup) {} - override fun getComparator(): Comparator> { - return JavaHierarchyUtil.getComparator(myProject) - } + override fun getComparator(): Comparator> = + JavaHierarchyUtil.getComparator(myProject) - override fun getElementFromDescriptor(descriptor: HierarchyNodeDescriptor): PsiElement? { - return descriptor.psiElement - } + override fun getElementFromDescriptor(descriptor: HierarchyNodeDescriptor): PsiElement? = + descriptor.psiElement - override fun getPrevOccurenceActionNameImpl(): String { - return LABEL_GO_PREVIOUS_SCOPE - } + override fun getPrevOccurenceActionNameImpl(): String = LABEL_GO_PREVIOUS_SCOPE - override fun getNextOccurenceActionNameImpl(): String { - return LABEL_GO_NEXT_SCOPE - } + override fun getNextOccurenceActionNameImpl(): String = LABEL_GO_NEXT_SCOPE - override fun createTrees(trees: MutableMap) { + override fun createTrees(trees: MutableMap) { trees[PROPERTY_HIERARCHY_TYPE] = createTree(true) } @@ -122,9 +113,7 @@ class ScopePropertyHierarchyBrowser( actionGroup.add(actionManager.getAction(IdeActions.ACTION_EXPAND_ALL)) } - override fun createLegendPanel(): JPanel? { - return null - } + override fun createLegendPanel(): JPanel? = null override fun configureTree(tree: Tree) { super.configureTree(tree) @@ -139,14 +128,15 @@ class ScopePropertyHierarchyBrowser( } } - override fun getContentDisplayName(typeName: String, element: PsiElement): String? { - return MessageFormat.format( - typeName, ClassPresentationUtil.getNameForClass(element as PsiClass, false)) - } + override fun getContentDisplayName(typeName: String, element: PsiElement): String? = + MessageFormat.format( + typeName, + ClassPresentationUtil.getNameForClass(element as PsiClass, false), + ) override fun createHierarchyTreeStructure( typeName: String, - psiElement: PsiElement + psiElement: PsiElement, ): HierarchyTreeStructure? { if (psiElement is PsiClass && isMotifScopeClass(psiElement)) { val scopeType: PsiType = PsiElementFactory.SERVICE.getInstance(project).createType(psiElement) @@ -156,25 +146,45 @@ class ScopePropertyHierarchyBrowser( PropertyHierarchyType.CONSUME -> { val descriptor: HierarchyNodeDescriptor = ScopeHierarchySinksSectionDescriptor( - project, graph, null, (scope.clazz as IntelliJClass).psiClass, scope) + project, + graph, + null, + (scope.clazz as IntelliJClass).psiClass, + scope, + ) ScopeHierarchyTreeStructure(project, graph, descriptor) } PropertyHierarchyType.PROVIDE -> { val descriptor: HierarchyNodeDescriptor = ScopeHierarchySourcesSectionDescriptor( - project, graph, null, (scope.clazz as IntelliJClass).psiClass, scope) + project, + graph, + null, + (scope.clazz as IntelliJClass).psiClass, + scope, + ) ScopeHierarchyTreeStructure(project, graph, descriptor) } PropertyHierarchyType.CONSUME_AND_PROVIDE -> { val descriptor: HierarchyNodeDescriptor = ScopeHierarchySourcesAndSinksSectionDescriptor( - project, graph, null, (scope.clazz as IntelliJClass).psiClass, scope) + project, + graph, + null, + (scope.clazz as IntelliJClass).psiClass, + scope, + ) ScopeHierarchyTreeStructure(project, graph, descriptor) } PropertyHierarchyType.DEPENDENCIES -> { val descriptor: HierarchyNodeDescriptor = ScopeHierarchyDependenciesSectionDescriptor( - project, graph, null, (scope.clazz as IntelliJClass).psiClass, scope) + project, + graph, + null, + (scope.clazz as IntelliJClass).psiClass, + scope, + ) ScopeHierarchyTreeStructure(project, graph, descriptor) } } @@ -186,10 +196,6 @@ class ScopePropertyHierarchyBrowser( return null } - override fun getBrowserDataKey(): String { - return DATA_KEY.name - } - override fun onGraphUpdated(graph: ResolvedGraph) { this.graph = graph super.doRefresh(true) diff --git a/intellij/src/main/kotlin/motif/intellij/hierarchy/UsageHierarchyBrowser.kt b/intellij/src/main/kotlin/motif/intellij/hierarchy/UsageHierarchyBrowser.kt index 823b8993..cadba59d 100644 --- a/intellij/src/main/kotlin/motif/intellij/hierarchy/UsageHierarchyBrowser.kt +++ b/intellij/src/main/kotlin/motif/intellij/hierarchy/UsageHierarchyBrowser.kt @@ -30,7 +30,7 @@ import java.text.MessageFormat import javax.swing.JPanel import javax.swing.JTree import motif.core.ResolvedGraph -import motif.intellij.MotifProjectComponent +import motif.intellij.MotifService import motif.intellij.hierarchy.ScopeHierarchyBrowser.Companion.LABEL_GO_NEXT_SCOPE import motif.intellij.hierarchy.ScopeHierarchyBrowser.Companion.LABEL_GO_PREVIOUS_SCOPE import motif.intellij.hierarchy.descriptor.ScopeHierarchyUsageSectionDescriptor @@ -41,8 +41,8 @@ import motif.intellij.hierarchy.descriptor.ScopeHierarchyUsageSectionDescriptor class UsageHierarchyBrowser( project: Project, initialGraph: ResolvedGraph, - private val rootElement: PsiElement -) : HierarchyBrowserBase(project, rootElement), MotifProjectComponent.Listener { + private val rootElement: PsiElement, +) : HierarchyBrowserBase(project, rootElement), MotifService.Listener { private var graph: ResolvedGraph = initialGraph @@ -57,48 +57,37 @@ class UsageHierarchyBrowser( super.doRefresh(true) } - override fun isApplicableElement(element: PsiElement): Boolean { - return element is PsiClass - } + override fun isApplicableElement(element: PsiElement): Boolean = element is PsiClass - override fun getActionPlace(): String { - return ActionPlaces.METHOD_HIERARCHY_VIEW_TOOLBAR - } + override fun getActionPlace(): String = ActionPlaces.METHOD_HIERARCHY_VIEW_TOOLBAR override fun prependActions(actionGroup: DefaultActionGroup) {} - override fun getComparator(): Comparator> { - return JavaHierarchyUtil.getComparator(myProject) - } + override fun getComparator(): Comparator> = + JavaHierarchyUtil.getComparator(myProject) - override fun getElementFromDescriptor(descriptor: HierarchyNodeDescriptor): PsiElement? { - return descriptor.psiElement - } + override fun getElementFromDescriptor(descriptor: HierarchyNodeDescriptor): PsiElement? = + descriptor.psiElement - override fun getPrevOccurenceActionNameImpl(): String { - return LABEL_GO_PREVIOUS_SCOPE - } + override fun getPrevOccurenceActionNameImpl(): String = LABEL_GO_PREVIOUS_SCOPE - override fun getNextOccurenceActionNameImpl(): String { - return LABEL_GO_NEXT_SCOPE - } + override fun getNextOccurenceActionNameImpl(): String = LABEL_GO_NEXT_SCOPE - override fun createLegendPanel(): JPanel? { - return null - } + override fun createLegendPanel(): JPanel? = null - override fun createTrees(trees: MutableMap) { + override fun createTrees(trees: MutableMap) { trees[USAGE_HIERARCHY_TYPE] = createTree(true) } - override fun getContentDisplayName(typeName: String, element: PsiElement): String? { - return MessageFormat.format( - typeName, ClassPresentationUtil.getNameForClass(element as PsiClass, false)) - } + override fun getContentDisplayName(typeName: String, element: PsiElement): String? = + MessageFormat.format( + typeName, + ClassPresentationUtil.getNameForClass(element as PsiClass, false), + ) override fun createHierarchyTreeStructure( typeName: String, - psiElement: PsiElement + psiElement: PsiElement, ): HierarchyTreeStructure? { if (psiElement is PsiClass) { val descriptor: HierarchyNodeDescriptor = @@ -108,10 +97,6 @@ class UsageHierarchyBrowser( return null } - override fun getBrowserDataKey(): String { - return DATA_KEY.name - } - override fun onGraphUpdated(graph: ResolvedGraph) { this.graph = graph super.doRefresh(true) diff --git a/intellij/src/main/kotlin/motif/intellij/hierarchy/descriptor/ScopeHierarchyDependenciesSectionDescriptor.kt b/intellij/src/main/kotlin/motif/intellij/hierarchy/descriptor/ScopeHierarchyDependenciesSectionDescriptor.kt index 1e447ea4..bfc460af 100644 --- a/intellij/src/main/kotlin/motif/intellij/hierarchy/descriptor/ScopeHierarchyDependenciesSectionDescriptor.kt +++ b/intellij/src/main/kotlin/motif/intellij/hierarchy/descriptor/ScopeHierarchyDependenciesSectionDescriptor.kt @@ -27,14 +27,12 @@ open class ScopeHierarchyDependenciesSectionDescriptor( graph: ResolvedGraph, parentDescriptor: HierarchyNodeDescriptor?, element: PsiElement, - val scope: Scope + val scope: Scope, ) : ScopeHierarchyNodeDescriptor(project, graph, parentDescriptor, element, false) { override fun updateText(text: CompositeAppearance) { text.ending.addText(scope.simpleName) } - override fun toString(): String { - return scope.simpleName - } + override fun toString(): String = scope.simpleName } diff --git a/intellij/src/main/kotlin/motif/intellij/hierarchy/descriptor/ScopeHierarchyDependencyDescriptor.kt b/intellij/src/main/kotlin/motif/intellij/hierarchy/descriptor/ScopeHierarchyDependencyDescriptor.kt index 11e722cf..5d2c1533 100644 --- a/intellij/src/main/kotlin/motif/intellij/hierarchy/descriptor/ScopeHierarchyDependencyDescriptor.kt +++ b/intellij/src/main/kotlin/motif/intellij/hierarchy/descriptor/ScopeHierarchyDependencyDescriptor.kt @@ -20,7 +20,7 @@ import com.intellij.openapi.project.Project import com.intellij.openapi.roots.ui.util.CompositeAppearance import com.intellij.psi.PsiElement import motif.core.ResolvedGraph -import motif.intellij.ScopeHierarchyUtils.Companion.formatQualifiedName +import motif.intellij.ScopeHierarchyUtils.formatQualifiedName import motif.models.Dependencies open class ScopeHierarchyDependencyDescriptor( @@ -28,17 +28,16 @@ open class ScopeHierarchyDependencyDescriptor( graph: ResolvedGraph, parentDescriptor: HierarchyNodeDescriptor, element: PsiElement, - private val method: Dependencies.Method + private val method: Dependencies.Method, ) : ScopeHierarchyNodeDescriptor(project, graph, parentDescriptor, element, false) { override fun updateText(text: CompositeAppearance) { text.ending.addText(method.returnType.simpleName) text.ending.addText( " (" + formatQualifiedName(method.returnType.qualifiedName) + ")", - getPackageNameAttributes()) + getPackageNameAttributes(), + ) } - override fun toString(): String { - return method.returnType.simpleName - } + override fun toString(): String = method.returnType.simpleName } diff --git a/intellij/src/main/kotlin/motif/intellij/hierarchy/descriptor/ScopeHierarchyErrorDescriptor.kt b/intellij/src/main/kotlin/motif/intellij/hierarchy/descriptor/ScopeHierarchyErrorDescriptor.kt index 9565dd83..f6963ac6 100644 --- a/intellij/src/main/kotlin/motif/intellij/hierarchy/descriptor/ScopeHierarchyErrorDescriptor.kt +++ b/intellij/src/main/kotlin/motif/intellij/hierarchy/descriptor/ScopeHierarchyErrorDescriptor.kt @@ -57,104 +57,102 @@ open class ScopeHierarchyErrorDescriptor( graph: ResolvedGraph, parentDescriptor: HierarchyNodeDescriptor?, val error: MotifError, - val errorMessage: ErrorMessage + val errorMessage: ErrorMessage, ) : ScopeHierarchyNodeDescriptor( - project, graph, parentDescriptor, getElementFromError(error), false) { + project, + graph, + parentDescriptor, + getElementFromError(error), + false, + ) { companion object { - fun getElementFromError(error: MotifError): PsiElement { - return when (error) { - is ScopeMustBeAnInterface -> { - (error.scopeClass as IntelliJClass).psiClass + fun getElementFromError(error: MotifError): PsiElement = + when (error) { + is ScopeMustBeAnInterface -> { + (error.scopeClass as IntelliJClass).psiClass + } + is VoidScopeMethod -> { + (error.method as IntelliJMethod).psiMethod + } + is AccessMethodParameters -> { + (error.scope.clazz as IntelliJClass).psiClass + } + is ObjectsFieldFound -> { + (error.scope.clazz as IntelliJClass).psiClass + } + is ObjectsConstructorFound -> { + (error.scope.clazz as IntelliJClass).psiClass + } + is VoidFactoryMethod -> { + (error.method as IntelliJMethod).psiMethod + } + is NullableFactoryMethod -> { + (error.method as IntelliJMethod).psiMethod + } + is NullableParameter -> { + (error.parameter as IntelliJMethodParameter).psiParameter + } + is NullableDynamicDependency -> { + (error.parameter as IntelliJMethodParameter).psiParameter + } + is InvalidFactoryMethod -> { + (error.method as IntelliJMethod).psiMethod + } + is UnspreadableType -> { + (error.method as IntelliJMethod).psiMethod + } + is NoSuitableConstructor -> { + (error.method as IntelliJMethod).psiMethod + } + is InjectAnnotationRequired -> { + (error.method as IntelliJMethod).psiMethod + } + is NotAssignableBindsMethod -> { + (error.method as IntelliJMethod).psiMethod + } + is VoidDependenciesMethod -> { + (error.method as IntelliJMethod).psiMethod + } + is DependencyMethodWithParameters -> { + (error.method as IntelliJMethod).psiMethod + } + is NullableSpreadMethod -> { + (error.spreadMethod as IntelliJMethod).psiMethod + } + is InvalidQualifier -> { + (error.annotated.annotations[0].members[0] as IntelliJMethod).psiMethod + } + is DuplicatedChildParameterSource -> { + (error.childScopeMethod.method as IntelliJMethod).psiMethod + } + is ScopeCycleError -> { + (error.path[0].clazz as IntelliJClass).psiClass + } + is UnsatisfiedDependencyError -> { + (error.top.clazz as IntelliJClass).psiClass + } + is DependencyCycleError -> { + (error.path[0].scope.clazz as IntelliJClass).psiClass + } + is UnexposedSourceError -> { + (error.source.scope.clazz as IntelliJClass).psiClass + } + is AlreadySatisfiedError -> { + (error.scope.clazz as IntelliJClass).psiClass + } + else -> throw UnsupportedOperationException() } - is VoidScopeMethod -> { - (error.method as IntelliJMethod).psiMethod - } - is AccessMethodParameters -> { - (error.scope.clazz as IntelliJClass).psiClass - } - is ObjectsFieldFound -> { - (error.scope.clazz as IntelliJClass).psiClass - } - is ObjectsConstructorFound -> { - (error.scope.clazz as IntelliJClass).psiClass - } - is VoidFactoryMethod -> { - (error.method as IntelliJMethod).psiMethod - } - is NullableFactoryMethod -> { - (error.method as IntelliJMethod).psiMethod - } - is NullableParameter -> { - (error.parameter as IntelliJMethodParameter).psiParameter - } - is NullableDynamicDependency -> { - (error.parameter as IntelliJMethodParameter).psiParameter - } - is InvalidFactoryMethod -> { - (error.method as IntelliJMethod).psiMethod - } - is UnspreadableType -> { - (error.method as IntelliJMethod).psiMethod - } - is NoSuitableConstructor -> { - (error.method as IntelliJMethod).psiMethod - } - is InjectAnnotationRequired -> { - (error.method as IntelliJMethod).psiMethod - } - is NotAssignableBindsMethod -> { - (error.method as IntelliJMethod).psiMethod - } - is VoidDependenciesMethod -> { - (error.method as IntelliJMethod).psiMethod - } - is DependencyMethodWithParameters -> { - (error.method as IntelliJMethod).psiMethod - } - is NullableSpreadMethod -> { - (error.spreadMethod as IntelliJMethod).psiMethod - } - is InvalidQualifier -> { - (error.annotated.annotations[0].members[0] as IntelliJMethod).psiMethod - } - is DuplicatedChildParameterSource -> { - (error.childScopeMethod.method as IntelliJMethod).psiMethod - } - is ScopeCycleError -> { - (error.path[0].clazz as IntelliJClass).psiClass - } - is UnsatisfiedDependencyError -> { - (error.top.clazz as IntelliJClass).psiClass - } - is DependencyCycleError -> { - (error.path[0].scope.clazz as IntelliJClass).psiClass - } - is UnexposedSourceError -> { - (error.source.scope.clazz as IntelliJClass).psiClass - } - is AlreadySatisfiedError -> { - (error.scope.clazz as IntelliJClass).psiClass - } - else -> throw UnsupportedOperationException() - } - } } override fun updateText(text: CompositeAppearance) { text.ending.addText(errorMessage.name) } - override fun getIcon(element: PsiElement): Icon? { - return AllIcons.RunConfigurations.TestFailed - } + override fun getIcon(element: PsiElement): Icon? = AllIcons.RunConfigurations.TestFailed - override fun getLegend(): String? { - return errorMessage.text - } + override fun getLegend(): String? = errorMessage.text - override fun toString(): String { - return errorMessage.name - } + override fun toString(): String = errorMessage.name } diff --git a/intellij/src/main/kotlin/motif/intellij/hierarchy/descriptor/ScopeHierarchyNodeDescriptor.kt b/intellij/src/main/kotlin/motif/intellij/hierarchy/descriptor/ScopeHierarchyNodeDescriptor.kt index 14b173f9..ca0747df 100644 --- a/intellij/src/main/kotlin/motif/intellij/hierarchy/descriptor/ScopeHierarchyNodeDescriptor.kt +++ b/intellij/src/main/kotlin/motif/intellij/hierarchy/descriptor/ScopeHierarchyNodeDescriptor.kt @@ -35,14 +35,12 @@ open class ScopeHierarchyNodeDescriptor( val graph: ResolvedGraph, val parentDescriptor: HierarchyNodeDescriptor?, val element: PsiElement, - isBase: Boolean + isBase: Boolean, ) : HierarchyNodeDescriptor(project, parentDescriptor, element, isBase) { open fun updateText(text: CompositeAppearance) {} - open fun getLegend(): String? { - return null - } + open fun getLegend(): String? = null override fun update(): Boolean { val changes = super.update() @@ -60,7 +58,10 @@ open class ScopeHierarchyNodeDescriptor( fun getDefaultTextAttributes(isError: Boolean = false): TextAttributes { val font: Int = if (myIsBase) Font.BOLD else Font.PLAIN - return if (isError) TextAttributes(myColor, null, Color.red, EffectType.WAVE_UNDERSCORE, font) - else TextAttributes(myColor, null, null, null, font) + return if (isError) { + TextAttributes(myColor, null, Color.red, EffectType.WAVE_UNDERSCORE, font) + } else { + TextAttributes(myColor, null, null, null, font) + } } } diff --git a/intellij/src/main/kotlin/motif/intellij/hierarchy/descriptor/ScopeHierarchyRootScopeNodeDescriptor.kt b/intellij/src/main/kotlin/motif/intellij/hierarchy/descriptor/ScopeHierarchyRootDescriptor.kt similarity index 97% rename from intellij/src/main/kotlin/motif/intellij/hierarchy/descriptor/ScopeHierarchyRootScopeNodeDescriptor.kt rename to intellij/src/main/kotlin/motif/intellij/hierarchy/descriptor/ScopeHierarchyRootDescriptor.kt index 2e09dfde..095e54be 100644 --- a/intellij/src/main/kotlin/motif/intellij/hierarchy/descriptor/ScopeHierarchyRootScopeNodeDescriptor.kt +++ b/intellij/src/main/kotlin/motif/intellij/hierarchy/descriptor/ScopeHierarchyRootDescriptor.kt @@ -26,7 +26,7 @@ class ScopeHierarchyRootDescriptor( project: Project, graph: ResolvedGraph, element: PsiElement, - private val status: ScopeHierarchyBrowser.Status + private val status: ScopeHierarchyBrowser.Status, ) : ScopeHierarchyNodeDescriptor(project, graph, null, element, true) { companion object { diff --git a/intellij/src/main/kotlin/motif/intellij/hierarchy/descriptor/ScopeHierarchyRootErrorDescriptor.kt b/intellij/src/main/kotlin/motif/intellij/hierarchy/descriptor/ScopeHierarchyRootErrorDescriptor.kt index e5205da4..b215bf85 100644 --- a/intellij/src/main/kotlin/motif/intellij/hierarchy/descriptor/ScopeHierarchyRootErrorDescriptor.kt +++ b/intellij/src/main/kotlin/motif/intellij/hierarchy/descriptor/ScopeHierarchyRootErrorDescriptor.kt @@ -27,7 +27,7 @@ open class ScopeHierarchyRootErrorDescriptor( project: Project, graph: ResolvedGraph, parentDescriptor: HierarchyNodeDescriptor?, - element: PsiElement + element: PsiElement, ) : ScopeHierarchyNodeDescriptor(project, graph, parentDescriptor, element, false) { override fun updateText(text: CompositeAppearance) { @@ -38,7 +38,6 @@ open class ScopeHierarchyRootErrorDescriptor( } } - override fun getIcon(element: PsiElement): Icon? { - return if (graph.errors.isNotEmpty()) null else AllIcons.RunConfigurations.TestPassed - } + override fun getIcon(element: PsiElement): Icon? = + if (graph.errors.isNotEmpty()) null else AllIcons.RunConfigurations.TestPassed } diff --git a/intellij/src/main/kotlin/motif/intellij/hierarchy/descriptor/ScopeHierarchyScopeAncestorDescriptor.kt b/intellij/src/main/kotlin/motif/intellij/hierarchy/descriptor/ScopeHierarchyScopeAncestorDescriptor.kt index 75d76ec5..a34f475c 100644 --- a/intellij/src/main/kotlin/motif/intellij/hierarchy/descriptor/ScopeHierarchyScopeAncestorDescriptor.kt +++ b/intellij/src/main/kotlin/motif/intellij/hierarchy/descriptor/ScopeHierarchyScopeAncestorDescriptor.kt @@ -30,5 +30,5 @@ class ScopeHierarchyScopeAncestorDescriptor( parentDescriptor: HierarchyNodeDescriptor?, clazz: PsiClass, scope: Scope, - isBase: Boolean = false + isBase: Boolean = false, ) : ScopeHierarchyScopeDescriptor(project, graph, parentDescriptor, clazz, scope, isBase) diff --git a/intellij/src/main/kotlin/motif/intellij/hierarchy/descriptor/ScopeHierarchyScopeDescriptor.kt b/intellij/src/main/kotlin/motif/intellij/hierarchy/descriptor/ScopeHierarchyScopeDescriptor.kt index 8b0e8feb..eb96fa49 100644 --- a/intellij/src/main/kotlin/motif/intellij/hierarchy/descriptor/ScopeHierarchyScopeDescriptor.kt +++ b/intellij/src/main/kotlin/motif/intellij/hierarchy/descriptor/ScopeHierarchyScopeDescriptor.kt @@ -24,7 +24,7 @@ import com.intellij.psi.PsiClass import com.intellij.psi.PsiElement import javax.swing.Icon import motif.core.ResolvedGraph -import motif.intellij.ScopeHierarchyUtils.Companion.formatQualifiedName +import motif.intellij.ScopeHierarchyUtils.formatQualifiedName import motif.models.ErrorScope import motif.models.Scope @@ -37,7 +37,7 @@ open class ScopeHierarchyScopeDescriptor( parentDescriptor: HierarchyNodeDescriptor?, private val clazz: PsiClass, val scope: Scope, - isBase: Boolean = false + isBase: Boolean = false, ) : ScopeHierarchyNodeDescriptor(project, graph, parentDescriptor, clazz, isBase) { override fun updateText(text: CompositeAppearance) { @@ -49,11 +49,7 @@ open class ScopeHierarchyScopeDescriptor( } } - override fun getIcon(element: PsiElement): Icon? { - return AllIcons.Nodes.Interface - } + override fun getIcon(element: PsiElement): Icon? = AllIcons.Nodes.Interface - override fun toString(): String { - return clazz.name ?: "" - } + override fun toString(): String = clazz.name ?: "" } diff --git a/intellij/src/main/kotlin/motif/intellij/hierarchy/descriptor/ScopeHierarchySimpleDescriptor.kt b/intellij/src/main/kotlin/motif/intellij/hierarchy/descriptor/ScopeHierarchySimpleDescriptor.kt index a6796089..4459570e 100644 --- a/intellij/src/main/kotlin/motif/intellij/hierarchy/descriptor/ScopeHierarchySimpleDescriptor.kt +++ b/intellij/src/main/kotlin/motif/intellij/hierarchy/descriptor/ScopeHierarchySimpleDescriptor.kt @@ -32,7 +32,7 @@ class ScopeHierarchySimpleDescriptor( graph: ResolvedGraph, parentDescriptor: HierarchyNodeDescriptor?, element: PsiElement, - private val label: String + private val label: String, ) : ScopeHierarchyNodeDescriptor(project, graph, parentDescriptor, element, false) { override fun updateText(text: CompositeAppearance) { @@ -40,11 +40,7 @@ class ScopeHierarchySimpleDescriptor( text.ending.addText(label, textAttr) } - override fun getIcon(element: PsiElement): Icon? { - return null - } + override fun getIcon(element: PsiElement): Icon? = null - override fun toString(): String { - return label - } + override fun toString(): String = label } diff --git a/intellij/src/main/kotlin/motif/intellij/hierarchy/descriptor/ScopeHierarchySinkDescriptor.kt b/intellij/src/main/kotlin/motif/intellij/hierarchy/descriptor/ScopeHierarchySinkDescriptor.kt index 0b08311c..098b3a4a 100644 --- a/intellij/src/main/kotlin/motif/intellij/hierarchy/descriptor/ScopeHierarchySinkDescriptor.kt +++ b/intellij/src/main/kotlin/motif/intellij/hierarchy/descriptor/ScopeHierarchySinkDescriptor.kt @@ -35,33 +35,36 @@ open class ScopeHierarchySinkDescriptor( project: Project, graph: ResolvedGraph, parentDescriptor: HierarchyNodeDescriptor?, - val sink: Sink + val sink: Sink, ) : ScopeHierarchyNodeDescriptor( - project, graph, parentDescriptor, getElementFromSink(sink), false) { + project, + graph, + parentDescriptor, + getElementFromSink(sink), + false, + ) { companion object { - fun getElementFromSink(sink: Sink): PsiElement { - return when (sink) { - is FactoryMethodSink -> { - (sink.parameter.factoryMethod.method as IntelliJMethod).psiMethod - } - is AccessMethodSink -> { - (sink.accessMethod.method as IntelliJMethod).psiMethod + fun getElementFromSink(sink: Sink): PsiElement = + when (sink) { + is FactoryMethodSink -> { + (sink.parameter.factoryMethod.method as IntelliJMethod).psiMethod + } + is AccessMethodSink -> { + (sink.accessMethod.method as IntelliJMethod).psiMethod + } } - } - } - fun getConsumingTypeFromSink(sink: Sink): Type { - return when (sink) { - is FactoryMethodSink -> { - sink.parameter.factoryMethod.returnType.type - } - is AccessMethodSink -> { - sink.type + fun getConsumingTypeFromSink(sink: Sink): Type = + when (sink) { + is FactoryMethodSink -> { + sink.parameter.factoryMethod.returnType.type + } + is AccessMethodSink -> { + sink.type + } } - } - } } override fun updateText(text: CompositeAppearance) { @@ -70,10 +73,12 @@ open class ScopeHierarchySinkDescriptor( text.ending.addText(" → ${consumingType.simpleName}", getPackageNameAttributes()) } - override fun getIcon(element: PsiElement): Icon? { - return if (element is PsiClass && element.isInterface) AllIcons.Nodes.Interface - else AllIcons.Nodes.Class - } + override fun getIcon(element: PsiElement): Icon? = + if (element is PsiClass && element.isInterface) { + AllIcons.Nodes.Interface + } else { + AllIcons.Nodes.Class + } override fun getLegend(): String? { val sb: StringBuilder = StringBuilder() @@ -89,7 +94,8 @@ open class ScopeHierarchySinkDescriptor( "" + " has a dependency on type " + sink.type.simpleName + - ".") + ".", + ) } graph.getProviders(sink).forEach { source -> when (source) { @@ -100,7 +106,8 @@ open class ScopeHierarchySinkDescriptor( "" + ", via Motif Factory method " + source.factoryMethod.name + - "().") + "().", + ) } else -> { // TODO : Handle all types @@ -116,7 +123,5 @@ open class ScopeHierarchySinkDescriptor( return null } - override fun toString(): String { - return sink.type.simpleName - } + override fun toString(): String = sink.type.simpleName } diff --git a/intellij/src/main/kotlin/motif/intellij/hierarchy/descriptor/ScopeHierarchySinkDetailsDescriptor.kt b/intellij/src/main/kotlin/motif/intellij/hierarchy/descriptor/ScopeHierarchySinkDetailsDescriptor.kt index a057303a..16d87b93 100644 --- a/intellij/src/main/kotlin/motif/intellij/hierarchy/descriptor/ScopeHierarchySinkDetailsDescriptor.kt +++ b/intellij/src/main/kotlin/motif/intellij/hierarchy/descriptor/ScopeHierarchySinkDetailsDescriptor.kt @@ -21,14 +21,14 @@ import com.intellij.openapi.roots.ui.util.CompositeAppearance import com.intellij.psi.PsiElement import javax.swing.Icon import motif.core.ResolvedGraph -import motif.intellij.ScopeHierarchyUtils.Companion.formatQualifiedName +import motif.intellij.ScopeHierarchyUtils.formatQualifiedName import motif.models.Sink open class ScopeHierarchySinkDetailsDescriptor( project: Project, graph: ResolvedGraph, parentDescriptor: HierarchyNodeDescriptor?, - sink: Sink + sink: Sink, ) : ScopeHierarchySinkDescriptor(project, graph, parentDescriptor, sink) { override fun updateText(text: CompositeAppearance) { @@ -38,10 +38,9 @@ open class ScopeHierarchySinkDetailsDescriptor( " (" + formatQualifiedName(sink.scope.qualifiedName) + ")", - getPackageNameAttributes()) + getPackageNameAttributes(), + ) } - override fun getIcon(element: PsiElement): Icon? { - return null - } + override fun getIcon(element: PsiElement): Icon? = null } diff --git a/intellij/src/main/kotlin/motif/intellij/hierarchy/descriptor/ScopeHierarchySinksSectionDescriptor.kt b/intellij/src/main/kotlin/motif/intellij/hierarchy/descriptor/ScopeHierarchySinksSectionDescriptor.kt index 445ba448..77b723b0 100644 --- a/intellij/src/main/kotlin/motif/intellij/hierarchy/descriptor/ScopeHierarchySinksSectionDescriptor.kt +++ b/intellij/src/main/kotlin/motif/intellij/hierarchy/descriptor/ScopeHierarchySinksSectionDescriptor.kt @@ -30,7 +30,7 @@ open class ScopeHierarchySinksSectionDescriptor( parentDescriptor: HierarchyNodeDescriptor?, element: PsiElement, val scope: Scope, - private val useLabel: Boolean = false + private val useLabel: Boolean = false, ) : ScopeHierarchyNodeDescriptor(project, graph, parentDescriptor, element, false) { override fun updateText(text: CompositeAppearance) { @@ -38,10 +38,10 @@ open class ScopeHierarchySinksSectionDescriptor( val count: Int = graph.getSinks(scope).count() text.ending.addText(label) text.ending.addText( - " " + ScopeHierarchyUtils.getObjectString(count), getPackageNameAttributes()) + " " + ScopeHierarchyUtils.getObjectString(count), + getPackageNameAttributes(), + ) } - override fun getIcon(element: PsiElement): Icon? { - return null - } + override fun getIcon(element: PsiElement): Icon? = null } diff --git a/intellij/src/main/kotlin/motif/intellij/hierarchy/descriptor/ScopeHierarchySourceDescriptor.kt b/intellij/src/main/kotlin/motif/intellij/hierarchy/descriptor/ScopeHierarchySourceDescriptor.kt index a51f9694..797a2472 100644 --- a/intellij/src/main/kotlin/motif/intellij/hierarchy/descriptor/ScopeHierarchySourceDescriptor.kt +++ b/intellij/src/main/kotlin/motif/intellij/hierarchy/descriptor/ScopeHierarchySourceDescriptor.kt @@ -27,7 +27,7 @@ import javax.swing.Icon import motif.ast.intellij.IntelliJClass import motif.ast.intellij.IntelliJMethod import motif.core.ResolvedGraph -import motif.intellij.ScopeHierarchyUtils.Companion.formatQualifiedName +import motif.intellij.ScopeHierarchyUtils.formatQualifiedName import motif.models.ChildParameterSource import motif.models.FactoryMethodSource import motif.models.ScopeSource @@ -38,28 +38,32 @@ open class ScopeHierarchySourceDescriptor( project: Project, graph: ResolvedGraph, parentDescriptor: HierarchyNodeDescriptor?, - val source: Source + val source: Source, ) : ScopeHierarchyNodeDescriptor( - project, graph, parentDescriptor, getElementFromSource(source), false) { + project, + graph, + parentDescriptor, + getElementFromSource(source), + false, + ) { companion object { - fun getElementFromSource(source: Source): PsiElement { - return when (source) { - is FactoryMethodSource -> { - (source.factoryMethod.method as IntelliJMethod).psiMethod + fun getElementFromSource(source: Source): PsiElement = + when (source) { + is FactoryMethodSource -> { + (source.factoryMethod.method as IntelliJMethod).psiMethod + } + is ScopeSource -> { + (source.scope.clazz as IntelliJClass).psiClass + } + is SpreadSource -> { + (source.spreadMethod.method as IntelliJMethod).psiMethod + } + is ChildParameterSource -> { + (source.parameter.method.method as IntelliJMethod).psiMethod + } } - is ScopeSource -> { - (source.scope.clazz as IntelliJClass).psiClass - } - is SpreadSource -> { - (source.spreadMethod.method as IntelliJMethod).psiMethod - } - is ChildParameterSource -> { - (source.parameter.method.method as IntelliJMethod).psiMethod - } - } - } } override fun updateText(text: CompositeAppearance) { @@ -68,7 +72,9 @@ open class ScopeHierarchySourceDescriptor( } text.ending.addText(source.type.simpleName) text.ending.addText( - " (" + formatQualifiedName(source.type.qualifiedName) + ")", getPackageNameAttributes()) + " (" + formatQualifiedName(source.type.qualifiedName) + ")", + getPackageNameAttributes(), + ) } override fun getLegend(): String? { @@ -76,12 +82,12 @@ open class ScopeHierarchySourceDescriptor( return super.getLegend() } - override fun getIcon(element: PsiElement): Icon? { - return if (element is PsiClass && element.isInterface) AllIcons.Nodes.Interface - else AllIcons.Nodes.Class - } + override fun getIcon(element: PsiElement): Icon? = + if (element is PsiClass && element.isInterface) { + AllIcons.Nodes.Interface + } else { + AllIcons.Nodes.Class + } - override fun toString(): String { - return source.type.simpleName - } + override fun toString(): String = source.type.simpleName } diff --git a/intellij/src/main/kotlin/motif/intellij/hierarchy/descriptor/ScopeHierarchySourceDetailsDescriptor.kt b/intellij/src/main/kotlin/motif/intellij/hierarchy/descriptor/ScopeHierarchySourceDetailsDescriptor.kt index eace0ee1..9feae8a3 100644 --- a/intellij/src/main/kotlin/motif/intellij/hierarchy/descriptor/ScopeHierarchySourceDetailsDescriptor.kt +++ b/intellij/src/main/kotlin/motif/intellij/hierarchy/descriptor/ScopeHierarchySourceDetailsDescriptor.kt @@ -21,14 +21,14 @@ import com.intellij.openapi.roots.ui.util.CompositeAppearance import com.intellij.psi.PsiElement import javax.swing.Icon import motif.core.ResolvedGraph -import motif.intellij.ScopeHierarchyUtils.Companion.formatQualifiedName +import motif.intellij.ScopeHierarchyUtils.formatQualifiedName import motif.models.Source open class ScopeHierarchySourceDetailsDescriptor( project: Project, graph: ResolvedGraph, parentDescriptor: HierarchyNodeDescriptor?, - source: Source + source: Source, ) : ScopeHierarchySourceDescriptor(project, graph, parentDescriptor, source) { override fun updateText(text: CompositeAppearance) { @@ -38,10 +38,9 @@ open class ScopeHierarchySourceDetailsDescriptor( " (" + formatQualifiedName(source.scope.qualifiedName) + ")", - getPackageNameAttributes()) + getPackageNameAttributes(), + ) } - override fun getIcon(element: PsiElement): Icon? { - return null - } + override fun getIcon(element: PsiElement): Icon? = null } diff --git a/intellij/src/main/kotlin/motif/intellij/hierarchy/descriptor/ScopeHierarchySourcesAndSinksSectionDescriptor.kt b/intellij/src/main/kotlin/motif/intellij/hierarchy/descriptor/ScopeHierarchySourcesAndSinksSectionDescriptor.kt index 74bb0367..d60153a5 100644 --- a/intellij/src/main/kotlin/motif/intellij/hierarchy/descriptor/ScopeHierarchySourcesAndSinksSectionDescriptor.kt +++ b/intellij/src/main/kotlin/motif/intellij/hierarchy/descriptor/ScopeHierarchySourcesAndSinksSectionDescriptor.kt @@ -22,7 +22,7 @@ import com.intellij.openapi.roots.ui.util.CompositeAppearance import com.intellij.psi.PsiElement import java.awt.Font.BOLD import motif.core.ResolvedGraph -import motif.intellij.ScopeHierarchyUtils.Companion.formatQualifiedName +import motif.intellij.ScopeHierarchyUtils.formatQualifiedName import motif.models.Scope open class ScopeHierarchySourcesAndSinksSectionDescriptor( @@ -30,16 +30,16 @@ open class ScopeHierarchySourcesAndSinksSectionDescriptor( graph: ResolvedGraph, parentDescriptor: HierarchyNodeDescriptor?, element: PsiElement, - val scope: Scope + val scope: Scope, ) : ScopeHierarchyNodeDescriptor(project, graph, parentDescriptor, element, false) { override fun updateText(text: CompositeAppearance) { text.ending.addText(scope.simpleName, TextAttributes(myColor, null, null, null, BOLD)) text.ending.addText( - " (" + formatQualifiedName(scope.qualifiedName) + ")", getPackageNameAttributes()) + " (" + formatQualifiedName(scope.qualifiedName) + ")", + getPackageNameAttributes(), + ) } - override fun toString(): String { - return scope.simpleName - } + override fun toString(): String = scope.simpleName } diff --git a/intellij/src/main/kotlin/motif/intellij/hierarchy/descriptor/ScopeHierarchySourcesSectionDescriptor.kt b/intellij/src/main/kotlin/motif/intellij/hierarchy/descriptor/ScopeHierarchySourcesSectionDescriptor.kt index c1b37233..0f639b48 100644 --- a/intellij/src/main/kotlin/motif/intellij/hierarchy/descriptor/ScopeHierarchySourcesSectionDescriptor.kt +++ b/intellij/src/main/kotlin/motif/intellij/hierarchy/descriptor/ScopeHierarchySourcesSectionDescriptor.kt @@ -22,7 +22,7 @@ import com.intellij.psi.PsiElement import javax.swing.Icon import motif.core.ResolvedGraph import motif.intellij.ScopeHierarchyUtils -import motif.intellij.ScopeHierarchyUtils.Companion.getVisibleSources +import motif.intellij.ScopeHierarchyUtils.getVisibleSources import motif.models.Scope open class ScopeHierarchySourcesSectionDescriptor( @@ -31,7 +31,7 @@ open class ScopeHierarchySourcesSectionDescriptor( parentDescriptor: HierarchyNodeDescriptor?, element: PsiElement, val scope: Scope, - private val useLabel: Boolean = false + private val useLabel: Boolean = false, ) : ScopeHierarchyNodeDescriptor(project, graph, parentDescriptor, element, false) { override fun updateText(text: CompositeAppearance) { @@ -39,10 +39,10 @@ open class ScopeHierarchySourcesSectionDescriptor( val count: Int = getVisibleSources(graph, scope).count() text.ending.addText(label) text.ending.addText( - " " + ScopeHierarchyUtils.getObjectString(count), getPackageNameAttributes()) + " " + ScopeHierarchyUtils.getObjectString(count), + getPackageNameAttributes(), + ) } - override fun getIcon(element: PsiElement): Icon? { - return null - } + override fun getIcon(element: PsiElement): Icon? = null } diff --git a/intellij/src/main/kotlin/motif/intellij/hierarchy/descriptor/ScopeHierarchyUsageSectionDescriptor.kt b/intellij/src/main/kotlin/motif/intellij/hierarchy/descriptor/ScopeHierarchyUsageSectionDescriptor.kt index 3144ac54..40f310b4 100644 --- a/intellij/src/main/kotlin/motif/intellij/hierarchy/descriptor/ScopeHierarchyUsageSectionDescriptor.kt +++ b/intellij/src/main/kotlin/motif/intellij/hierarchy/descriptor/ScopeHierarchyUsageSectionDescriptor.kt @@ -20,14 +20,14 @@ import com.intellij.openapi.project.Project import com.intellij.openapi.roots.ui.util.CompositeAppearance import com.intellij.psi.PsiClass import motif.core.ResolvedGraph -import motif.intellij.ScopeHierarchyUtils.Companion.getUsageCount -import motif.intellij.ScopeHierarchyUtils.Companion.getUsageString +import motif.intellij.ScopeHierarchyUtils.getUsageCount +import motif.intellij.ScopeHierarchyUtils.getUsageString open class ScopeHierarchyUsageSectionDescriptor( private val nonNullproject: Project, graph: ResolvedGraph, parentDescriptor: HierarchyNodeDescriptor?, - val clazz: PsiClass + val clazz: PsiClass, ) : ScopeHierarchyNodeDescriptor(nonNullproject, graph, parentDescriptor, clazz, false) { override fun updateText(text: CompositeAppearance) { diff --git a/intellij/src/main/kotlin/motif/intellij/hierarchy/descriptor/ScopeHierarchyUsageSinksSectionDescriptor.kt b/intellij/src/main/kotlin/motif/intellij/hierarchy/descriptor/ScopeHierarchyUsageSinksSectionDescriptor.kt index a1c47e20..8d5f74d8 100644 --- a/intellij/src/main/kotlin/motif/intellij/hierarchy/descriptor/ScopeHierarchyUsageSinksSectionDescriptor.kt +++ b/intellij/src/main/kotlin/motif/intellij/hierarchy/descriptor/ScopeHierarchyUsageSinksSectionDescriptor.kt @@ -22,14 +22,14 @@ import com.intellij.psi.PsiClass import com.intellij.psi.PsiElement import javax.swing.Icon import motif.core.ResolvedGraph -import motif.intellij.ScopeHierarchyUtils.Companion.getUsageCount -import motif.intellij.ScopeHierarchyUtils.Companion.getUsageString +import motif.intellij.ScopeHierarchyUtils.getUsageCount +import motif.intellij.ScopeHierarchyUtils.getUsageString open class ScopeHierarchyUsageSinksSectionDescriptor( private val nonNullProject: Project, graph: ResolvedGraph, parentDescriptor: HierarchyNodeDescriptor?, - val clazz: PsiClass + val clazz: PsiClass, ) : ScopeHierarchyNodeDescriptor(nonNullProject, graph, parentDescriptor, clazz, false) { override fun updateText(text: CompositeAppearance) { @@ -39,7 +39,5 @@ open class ScopeHierarchyUsageSinksSectionDescriptor( text.ending.addText(" " + getUsageString(count), getPackageNameAttributes()) } - override fun getIcon(element: PsiElement): Icon? { - return null - } + override fun getIcon(element: PsiElement): Icon? = null } diff --git a/intellij/src/main/kotlin/motif/intellij/hierarchy/descriptor/ScopeHierarchyUsageSourcesSectionDescriptor.kt b/intellij/src/main/kotlin/motif/intellij/hierarchy/descriptor/ScopeHierarchyUsageSourcesSectionDescriptor.kt index 80b6d4f0..3c131060 100644 --- a/intellij/src/main/kotlin/motif/intellij/hierarchy/descriptor/ScopeHierarchyUsageSourcesSectionDescriptor.kt +++ b/intellij/src/main/kotlin/motif/intellij/hierarchy/descriptor/ScopeHierarchyUsageSourcesSectionDescriptor.kt @@ -22,14 +22,14 @@ import com.intellij.psi.PsiClass import com.intellij.psi.PsiElement import javax.swing.Icon import motif.core.ResolvedGraph -import motif.intellij.ScopeHierarchyUtils.Companion.getUsageCount -import motif.intellij.ScopeHierarchyUtils.Companion.getUsageString +import motif.intellij.ScopeHierarchyUtils.getUsageCount +import motif.intellij.ScopeHierarchyUtils.getUsageString open class ScopeHierarchyUsageSourcesSectionDescriptor( private val nonNullProject: Project, graph: ResolvedGraph, parentDescriptor: HierarchyNodeDescriptor?, - val clazz: PsiClass + val clazz: PsiClass, ) : ScopeHierarchyNodeDescriptor(nonNullProject, graph, parentDescriptor, clazz, false) { override fun updateText(text: CompositeAppearance) { @@ -39,7 +39,5 @@ open class ScopeHierarchyUsageSourcesSectionDescriptor( text.ending.addText(" " + getUsageString(count), getPackageNameAttributes()) } - override fun getIcon(element: PsiElement): Icon? { - return null - } + override fun getIcon(element: PsiElement): Icon? = null } diff --git a/intellij/src/main/kotlin/motif/intellij/provider/ScopeHierarchyLineMarkerProvider.kt b/intellij/src/main/kotlin/motif/intellij/provider/ScopeHierarchyLineMarkerProvider.kt index 0480ee0f..2deef999 100644 --- a/intellij/src/main/kotlin/motif/intellij/provider/ScopeHierarchyLineMarkerProvider.kt +++ b/intellij/src/main/kotlin/motif/intellij/provider/ScopeHierarchyLineMarkerProvider.kt @@ -32,17 +32,17 @@ import com.intellij.psi.impl.source.PsiClassReferenceType import com.intellij.util.ConstantFunction import java.awt.event.MouseEvent import motif.core.ResolvedGraph -import motif.intellij.MotifProjectComponent -import motif.intellij.MotifProjectComponent.Companion.TOOL_WINDOW_ID -import motif.intellij.ScopeHierarchyUtils.Companion.getParentScopes -import motif.intellij.ScopeHierarchyUtils.Companion.isMotifScopeClass -import motif.intellij.analytics.AnalyticsProjectComponent +import motif.intellij.MotifService +import motif.intellij.MotifService.Companion.TOOL_WINDOW_ID +import motif.intellij.ScopeHierarchyUtils.getParentScopes +import motif.intellij.ScopeHierarchyUtils.isMotifScopeClass +import motif.intellij.analytics.AnalyticsService import motif.intellij.analytics.MotifAnalyticsActions /* * {@LineMarkerProvider} used to display icon in gutter to navigate to motif scope ancestors hierarchy. */ -class ScopeHierarchyLineMarkerProvider : LineMarkerProvider, MotifProjectComponent.Listener { +class ScopeHierarchyLineMarkerProvider : LineMarkerProvider, MotifService.Listener { companion object { const val LABEL_ANCESTORS_SCOPE: String = "View Scope Ancestors." @@ -73,7 +73,8 @@ class ScopeHierarchyLineMarkerProvider : LineMarkerProvider, MotifProjectCompone UPDATE_ALL, ConstantFunction(LABEL_ANCESTORS_SCOPE), ScopeHierarchyHandler(element.project), - LEFT) + LEFT, + ) } private class ScopeHierarchyHandler(val project: Project) : @@ -83,18 +84,19 @@ class ScopeHierarchyLineMarkerProvider : LineMarkerProvider, MotifProjectCompone ToolWindowManager.getInstance(project).getToolWindow(TOOL_WINDOW_ID) ?: return if (element is PsiClass) { toolWindow.activate { - MotifProjectComponent.getInstance(project).onSelectedAncestorScope(element) + project.getService(MotifService::class.java).onSelectedAncestorScope(element) } } else if (element is PsiMethod) { if (element.returnType is PsiClassReferenceType) { val returnElementClass: PsiClass = (element.returnType as PsiClassReferenceType).resolve() ?: return toolWindow.activate { - MotifProjectComponent.getInstance(project).onSelectedAncestorScope(returnElementClass) + project.getService(MotifService::class.java).onSelectedAncestorScope(returnElementClass) } } } - AnalyticsProjectComponent.getInstance(project) + project + .getService(AnalyticsService::class.java) .logEvent(MotifAnalyticsActions.ANCESTOR_GUTTER_CLICK) } } diff --git a/intellij/src/main/kotlin/motif/intellij/provider/ScopeNavigationLineMarkerProvider.kt b/intellij/src/main/kotlin/motif/intellij/provider/ScopeNavigationLineMarkerProvider.kt index cf3afa47..ffc4b324 100644 --- a/intellij/src/main/kotlin/motif/intellij/provider/ScopeNavigationLineMarkerProvider.kt +++ b/intellij/src/main/kotlin/motif/intellij/provider/ScopeNavigationLineMarkerProvider.kt @@ -40,11 +40,11 @@ import java.awt.event.MouseEvent import motif.ast.intellij.IntelliJClass import motif.core.ResolvedGraph import motif.core.ScopeEdge -import motif.intellij.MotifProjectComponent -import motif.intellij.ScopeHierarchyUtils.Companion.getParentScopes -import motif.intellij.ScopeHierarchyUtils.Companion.isMotifChildScopeMethod -import motif.intellij.ScopeHierarchyUtils.Companion.isMotifScopeClass -import motif.intellij.analytics.AnalyticsProjectComponent +import motif.intellij.MotifService +import motif.intellij.ScopeHierarchyUtils.getParentScopes +import motif.intellij.ScopeHierarchyUtils.isMotifChildScopeMethod +import motif.intellij.ScopeHierarchyUtils.isMotifScopeClass +import motif.intellij.analytics.AnalyticsService import motif.intellij.analytics.MotifAnalyticsActions import motif.intellij.toPsiClass import motif.intellij.toPsiMethod @@ -52,7 +52,7 @@ import motif.intellij.toPsiMethod /* * {@LineMarkerProvider} used to display navigation icons in gutter to navigate to parent/children of Motif scopes. */ -class ScopeNavigationLineMarkerProvider : LineMarkerProvider, MotifProjectComponent.Listener { +class ScopeNavigationLineMarkerProvider : LineMarkerProvider, MotifService.Listener { companion object { const val LABEL_NAVIGATE_PARENT_SCOPE: String = "Navigate to parent Scope." @@ -84,7 +84,8 @@ class ScopeNavigationLineMarkerProvider : LineMarkerProvider, MotifProjectCompon UPDATE_ALL, ConstantFunction(LABEL_NAVIGATE_PARENT_SCOPE), NavigationScopeHandler(element.project, graph), - LEFT) + LEFT, + ) } } else { val methodElement = element.toPsiMethod() @@ -96,7 +97,8 @@ class ScopeNavigationLineMarkerProvider : LineMarkerProvider, MotifProjectCompon UPDATE_ALL, ConstantFunction(LABEL_NAVIGATE_CHILD_SCOPE), NavigationScopeHandler(element.project, graph), - LEFT) + LEFT, + ) } } return null @@ -123,19 +125,21 @@ class ScopeNavigationLineMarkerProvider : LineMarkerProvider, MotifProjectCompon .createListPopup( object : BaseListPopupStep( - "Select Parent Scope", scopeEdges.toMutableList()) { - override fun getTextFor(value: ScopeEdge): String { - return value.parent.clazz.simpleName - } + "Select Parent Scope", + scopeEdges.toMutableList(), + ) { + override fun getTextFor(value: ScopeEdge): String = + value.parent.clazz.simpleName override fun onChosen( selectedValue: ScopeEdge?, - finalChoice: Boolean + finalChoice: Boolean, ): PopupStep<*>? { selectedValue?.let { navigateToParent(it) } return super.onChosen(selectedValue, finalChoice) } - }) + }, + ) listPopup.show(RelativePoint(mouseEvent)) } } @@ -157,7 +161,8 @@ class ScopeNavigationLineMarkerProvider : LineMarkerProvider, MotifProjectCompon } } } - AnalyticsProjectComponent.getInstance(project) + project + .getService(AnalyticsService::class.java) .logEvent(MotifAnalyticsActions.NAVIGATION_GUTTER_CLICK) } diff --git a/intellij/src/main/kotlin/motif/intellij/ui/MotifErrorPanel.kt b/intellij/src/main/kotlin/motif/intellij/ui/MotifErrorPanel.kt index b1d57be9..c775789d 100644 --- a/intellij/src/main/kotlin/motif/intellij/ui/MotifErrorPanel.kt +++ b/intellij/src/main/kotlin/motif/intellij/ui/MotifErrorPanel.kt @@ -23,13 +23,13 @@ import javax.swing.JSplitPane import javax.swing.JTextArea import motif.core.ResolvedGraph import motif.errormessage.ErrorMessage -import motif.intellij.MotifProjectComponent +import motif.intellij.MotifService import motif.intellij.ScopeHierarchyUtils import motif.intellij.hierarchy.ErrorHierarchyBrowser import motif.models.MotifError class MotifErrorPanel(project: Project, graph: ResolvedGraph) : - JPanel(), MotifProjectComponent.Listener, ErrorHierarchyBrowser.Listener { + JPanel(), MotifService.Listener, ErrorHierarchyBrowser.Listener { private val splitPane: JSplitPane private val errorBrowser: ErrorHierarchyBrowser @@ -62,7 +62,7 @@ class MotifErrorPanel(project: Project, graph: ResolvedGraph) : override fun onSelectedErrorChanged( element: PsiElement, error: MotifError, - errorMessage: ErrorMessage + errorMessage: ErrorMessage, ) { errorDetails.text = errorMessage.text } diff --git a/intellij/src/main/kotlin/motif/intellij/ui/MotifScopePanel.kt b/intellij/src/main/kotlin/motif/intellij/ui/MotifScopePanel.kt index 572a3b19..575d2d83 100644 --- a/intellij/src/main/kotlin/motif/intellij/ui/MotifScopePanel.kt +++ b/intellij/src/main/kotlin/motif/intellij/ui/MotifScopePanel.kt @@ -24,7 +24,7 @@ import javax.swing.JPanel import javax.swing.JSplitPane import javax.swing.JSplitPane.RIGHT import motif.core.ResolvedGraph -import motif.intellij.MotifProjectComponent +import motif.intellij.MotifService import motif.intellij.ScopeHierarchyUtils import motif.intellij.hierarchy.ScopeHierarchyBrowser import motif.intellij.hierarchy.ScopePropertyHierarchyBrowser @@ -32,7 +32,7 @@ import motif.intellij.hierarchy.ScopePropertyHierarchyBrowser.PropertyHierarchyT import motif.models.Scope class MotifScopePanel(val project: Project, initialGraph: ResolvedGraph) : - JPanel(), ScopeHierarchyBrowser.Listener, MotifProjectComponent.Listener { + JPanel(), ScopeHierarchyBrowser.Listener, MotifService.Listener { private var graph: ResolvedGraph = initialGraph @@ -51,7 +51,11 @@ class MotifScopePanel(val project: Project, initialGraph: ResolvedGraph) : consumeAndProvideBrowser = buildPropertyHierarchyBrowser( - project, graph, rootElement, PropertyHierarchyType.CONSUME_AND_PROVIDE) + project, + graph, + rootElement, + PropertyHierarchyType.CONSUME_AND_PROVIDE, + ) tabs = JBTabbedPane() splitPane = JSplitPane(JSplitPane.VERTICAL_SPLIT, scopeBrowser, consumeAndProvideBrowser) @@ -79,7 +83,11 @@ class MotifScopePanel(val project: Project, initialGraph: ResolvedGraph) : val previousDividerLocation = splitPane.dividerLocation consumeAndProvideBrowser = buildPropertyHierarchyBrowser( - project, graph, element, PropertyHierarchyType.CONSUME_AND_PROVIDE) + project, + graph, + element, + PropertyHierarchyType.CONSUME_AND_PROVIDE, + ) splitPane.add(consumeAndProvideBrowser, RIGHT) splitPane.dividerLocation = previousDividerLocation } @@ -88,7 +96,7 @@ class MotifScopePanel(val project: Project, initialGraph: ResolvedGraph) : project: Project, graph: ResolvedGraph, rootElement: PsiElement, - type: PropertyHierarchyType + type: PropertyHierarchyType, ): ScopePropertyHierarchyBrowser { val propertyBrowser = ScopePropertyHierarchyBrowser(project, graph, rootElement, type) propertyBrowser.changeView(ScopePropertyHierarchyBrowser.PROPERTY_HIERARCHY_TYPE) diff --git a/intellij/src/main/kotlin/motif/intellij/ui/MotifUsagePanel.kt b/intellij/src/main/kotlin/motif/intellij/ui/MotifUsagePanel.kt index b4f85c08..5c312f51 100644 --- a/intellij/src/main/kotlin/motif/intellij/ui/MotifUsagePanel.kt +++ b/intellij/src/main/kotlin/motif/intellij/ui/MotifUsagePanel.kt @@ -21,12 +21,11 @@ import com.intellij.psi.PsiElement import java.awt.BorderLayout import javax.swing.JPanel import motif.core.ResolvedGraph -import motif.intellij.MotifProjectComponent +import motif.intellij.MotifService import motif.intellij.ScopeHierarchyUtils import motif.intellij.hierarchy.UsageHierarchyBrowser -class MotifUsagePanel(project: Project, graph: ResolvedGraph) : - JPanel(), MotifProjectComponent.Listener { +class MotifUsagePanel(project: Project, graph: ResolvedGraph) : JPanel(), MotifService.Listener { private val usageBrowser: UsageHierarchyBrowser diff --git a/intellij/src/main/resources/META-INF/plugin.xml b/intellij/src/main/resources/META-INF/plugin.xml index 27b5c055..f1b5da87 100644 --- a/intellij/src/main/resources/META-INF/plugin.xml +++ b/intellij/src/main/resources/META-INF/plugin.xml @@ -1,6 +1,6 @@ com.uber.motif - Motif Plugin + Uber Motif 0.0.5 Uber @@ -15,10 +15,10 @@ ]]> - + + Release 0.0.5: Add logging extension point + IntelliJ 2023.2 compatibility.
    Release 0.0.4: Update plugin email address.
    Release 0.0.3: Minor fixes.
    Release 0.0.2: Minor fixes.
    @@ -30,16 +30,9 @@ org.jetbrains.kotlin com.intellij.modules.lang - - - motif.intellij.MotifProjectComponent - - - motif.intellij.analytics.AnalyticsProjectComponent - - + + + ) { - val path = clazz.java.protectionDomain.codeSource.location.path - val file = File(path) - val libName = file.name - PsiTestUtil.addLibrary(myFixture.projectDisposable, module, libName, file.parent, libName) + val fileUri = + clazz.java + .getResource(clazz.simpleName + ".class") + ?.toString() + ?.let { Regex("file:.*[.]jar").find(it)?.value } + ?.let { URI.create(it) } + if (fileUri != null) { + val file = File(fileUri) + val libName = file.name + PsiTestUtil.addLibrary(myFixture.projectDisposable, module, libName, file.parent, libName) + } } @Test fun test() { val testFiles = testDir.walk() val externalFiles = EXTERNAL_ROOT.resolve(testDir.name).walk() - (testFiles + externalFiles).filter { !it.isDirectory }.forEach { sourceFile -> - when { - sourceFile.name.endsWith(".java") -> myFixture.addClass(sourceFile.readText()) - sourceFile.name.endsWith(".kt") -> - myFixture.addFileToProject(sourceFile.name, sourceFile.readText()) - } - } + (testFiles + externalFiles) + .filter { !it.isDirectory } + .forEach { sourceFile -> + when { + sourceFile.name.endsWith(".java") -> myFixture.addClass(sourceFile.readText()) + sourceFile.name.endsWith(".kt") -> + myFixture.addFileToProject(sourceFile.name, sourceFile.readText()) + } + } val graph = GraphFactory(project).compute() val errorFile = testDir.resolve("ERROR.txt") @@ -157,9 +151,9 @@ class TestHarness : LightCodeInsightFixtureTestCase() { @JvmStatic fun data(clazz: Class<*>): List> { val testDirs = TEST_CASE_ROOT.listFiles() ?: throw IllegalStateException() - return testDirs.filter { !it.resolve("SKIP_INTELLIJ").exists() }.map { testDir -> - arrayOf(testDir) - } + return testDirs + .filter { !it.resolve("SKIP_INTELLIJ").exists() } + .map { testDir -> arrayOf(testDir) } } @org.junit.runners.Parameterized.Parameters diff --git a/intellij/testing/build.gradle b/intellij/testing/build.gradle deleted file mode 100644 index a1df613e..00000000 --- a/intellij/testing/build.gradle +++ /dev/null @@ -1,21 +0,0 @@ -plugins { - id "org.jetbrains.intellij" - id 'org.jetbrains.kotlin.jvm' -} - -intellij { - version = deps.versions.gradleIntellijPlugin.ide - plugins = [ 'java', 'Kotlin' ] - pluginName = 'Motif Plugin' - updateSinceUntilBuild = false -} - -dependencies { - implementation deps.test.truth -} - -tasks { - buildSearchableOptions { - enabled = false - } -} diff --git a/intellij/testing/src/main/kotlin/motif/intellij/testing/IntelliJRule.kt b/intellij/testing/src/main/kotlin/motif/intellij/testing/IntelliJRule.kt deleted file mode 100644 index 1d882feb..00000000 --- a/intellij/testing/src/main/kotlin/motif/intellij/testing/IntelliJRule.kt +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (c) 2018-2019 Uber Technologies, Inc. - * - * 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 motif.intellij.testing - -import com.intellij.openapi.application.ApplicationManager -import com.intellij.testFramework.TestApplicationManager -import java.util.concurrent.CountDownLatch -import org.junit.rules.TestRule -import org.junit.runner.Description -import org.junit.runners.model.Statement - -class IntelliJRule : TestRule { - - override fun apply(base: Statement, description: Description): Statement { - return object : Statement() { - - override fun evaluate() { - TestApplicationManager.getInstance() - var e: Throwable? = null - val latch = CountDownLatch(1) - ApplicationManager.getApplication().invokeLater { - ApplicationManager.getApplication().runReadAction { - try { - base.evaluate() - } catch (throwable: Throwable) { - e = throwable - } finally { - latch.countDown() - } - } - } - latch.await() - e?.let { throw it } - } - } - } -} diff --git a/lib/build.gradle b/lib/build.gradle index d8569938..a918dabf 100644 --- a/lib/build.gradle +++ b/lib/build.gradle @@ -1,11 +1,16 @@ -apply plugin: 'java-library' +plugins { + id 'java-library' + id 'com.vanniktech.maven.publish' +} + +sourceCompatibility = 1.8 +targetCompatibility = 1.8 -sourceCompatibility = 1.7 -targetCompatibility = 1.7 +java { + toolchain.languageVersion.set(JavaLanguageVersion.of(8)) +} dependencies { // Dagger is part of the API since we generate code which depends on Dagger's API in the consuming gradle module. api deps.dagger } - -apply plugin: 'com.vanniktech.maven.publish' diff --git a/lib/src/main/java/motif/Scope.java b/lib/src/main/java/motif/Scope.java index 1a4eab4f..fc2c2313 100644 --- a/lib/src/main/java/motif/Scope.java +++ b/lib/src/main/java/motif/Scope.java @@ -15,4 +15,10 @@ */ package motif; -public @interface Scope {} +public @interface Scope { + /** + * + * @return on false, the field will be initialized with [None.NONE]. Otherwise, null & [Initialized.INITIALIZED] will be used to skip the field initialization. + */ + boolean useNullFieldInitialization() default false; +} diff --git a/models/build.gradle b/models/build.gradle index 7ec1c7b1..e1d865c0 100644 --- a/models/build.gradle +++ b/models/build.gradle @@ -1,13 +1,8 @@ plugins { id 'org.jetbrains.kotlin.jvm' - id 'org.jetbrains.dokka' + id 'com.vanniktech.maven.publish' } -apply plugin: 'kotlin-kapt' - -sourceCompatibility = 1.8 - - dependencies { api project(':ast') @@ -19,5 +14,3 @@ dependencies { testImplementation deps.test.truth testImplementation deps.test.compileTesting } - -apply plugin: 'com.vanniktech.maven.publish' diff --git a/models/src/main/kotlin/motif/models/Dependencies.kt b/models/src/main/kotlin/motif/models/Dependencies.kt index 88c118fa..d20d6627 100644 --- a/models/src/main/kotlin/motif/models/Dependencies.kt +++ b/models/src/main/kotlin/motif/models/Dependencies.kt @@ -62,11 +62,13 @@ class Dependencies(val clazz: IrClass, val scope: Scope) { return clazz } - clazz.supertypes.mapNotNull { it.resolveClass() }.forEach { superinterface -> - findCreatableSuperinterface(superinterface)?.let { - return it - } - } + clazz.supertypes + .mapNotNull { it.resolveClass() } + .forEach { superinterface -> + findCreatableSuperinterface(superinterface)?.let { + return it + } + } return null } diff --git a/models/src/main/kotlin/motif/models/FactoryMethod.kt b/models/src/main/kotlin/motif/models/FactoryMethod.kt index 567a0cea..4ef69ca4 100644 --- a/models/src/main/kotlin/motif/models/FactoryMethod.kt +++ b/models/src/main/kotlin/motif/models/FactoryMethod.kt @@ -49,18 +49,17 @@ sealed class FactoryMethod(val method: IrMethod, val objects: Objects) { val name = method.name val qualifiedName: String by lazy { "${objects.qualifiedName}.${method.name}" } - protected fun getParameters(owner: IrClass, method: IrMethod): List { - return method.parameters.map { parameter -> - Parameter(owner, method, parameter, this, Type.fromParameter(parameter)) - } - } + protected fun getParameters(owner: IrClass, method: IrMethod): List = + method.parameters.map { parameter -> + Parameter(owner, method, parameter, this, Type.fromParameter(parameter)) + } class Parameter( val owner: IrClass, val method: IrMethod, val parameter: IrParameter, val factoryMethod: FactoryMethod, - val type: Type + val type: Type, ) { val qualifiedName: String by lazy { type.qualifiedName } @@ -75,8 +74,9 @@ sealed class FactoryMethod(val method: IrMethod, val objects: Objects) { fun fromObjectsMethod(objects: Objects, method: IrMethod): FactoryMethod { if (method.isVoid()) throw VoidFactoryMethod(objects, method) - if (method.isNullable() || method.returnType.toString().endsWith("?")) - throw NullableFactoryMethod(objects, method) + if (method.isNullable() || method.returnType.toString().endsWith("?")) { + throw NullableFactoryMethod(objects, method) + } ensureNonNullParameters(objects.scope, objects.clazz, method) @@ -98,9 +98,8 @@ class BasicFactoryMethod private constructor(objects: Objects, method: IrMethod) companion object { - fun create(objects: Objects, method: IrMethod): BasicFactoryMethod { - return BasicFactoryMethod(objects, method) - } + fun create(objects: Objects, method: IrMethod): BasicFactoryMethod = + BasicFactoryMethod(objects, method) } } @@ -140,9 +139,8 @@ class ConstructorFactoryMethod private constructor(objects: Objects, method: IrM companion object { - fun create(objects: Objects, method: IrMethod): ConstructorFactoryMethod { - return ConstructorFactoryMethod(objects, method) - } + fun create(objects: Objects, method: IrMethod): ConstructorFactoryMethod = + ConstructorFactoryMethod(objects, method) } } @@ -174,8 +172,9 @@ private fun ensureNonNullParameter( scope: Scope, owner: IrClass, method: IrMethod, - parameter: IrParameter + parameter: IrParameter, ) { - if (parameter.isNullable() || parameter.type.toString().endsWith("?")) - throw NullableParameter(scope, owner, method, parameter) + if (parameter.isNullable() || parameter.type.toString().endsWith("?")) { + throw NullableParameter(scope, owner, method, parameter) + } } diff --git a/models/src/main/kotlin/motif/models/Objects.kt b/models/src/main/kotlin/motif/models/Objects.kt index e64a9970..1d30f892 100644 --- a/models/src/main/kotlin/motif/models/Objects.kt +++ b/models/src/main/kotlin/motif/models/Objects.kt @@ -32,8 +32,9 @@ class Objects private constructor(val clazz: IrClass, val scope: Scope) { val objectsClass = scope.clazz.annotatedInnerClass(motif.Objects::class) ?: return null if (objectsClass.fields.any { !it.isStatic() }) throw ObjectsFieldFound(scope, objectsClass) - if (objectsClass.hasNonDefaultConstructor()) - throw ObjectsConstructorFound(scope, objectsClass) + if (objectsClass.hasNonDefaultConstructor()) { + throw ObjectsConstructorFound(scope, objectsClass) + } return Objects(objectsClass, scope) } diff --git a/models/src/main/kotlin/motif/models/ParsingError.kt b/models/src/main/kotlin/motif/models/ParsingError.kt index 2ebf2190..130e0737 100644 --- a/models/src/main/kotlin/motif/models/ParsingError.kt +++ b/models/src/main/kotlin/motif/models/ParsingError.kt @@ -42,13 +42,13 @@ class NullableParameter( val scope: Scope, val owner: IrClass, val method: IrMethod, - val parameter: IrParameter + val parameter: IrParameter, ) : ParsingError() class NullableDynamicDependency( val scope: Scope, val method: IrMethod, - val parameter: IrParameter + val parameter: IrParameter, ) : ParsingError() class InvalidFactoryMethod(val objects: Objects, val method: IrMethod) : ParsingError() @@ -66,26 +66,26 @@ class NotAssignableBindsMethod( val objects: Objects, val method: IrMethod, val returnType: IrType, - val parameterType: IrType + val parameterType: IrType, ) : ParsingError() class VoidDependenciesMethod( val scope: Scope, val dependenciesClass: IrClass, - val method: IrMethod + val method: IrMethod, ) : ParsingError() class DependencyMethodWithParameters( val scope: Scope, val dependenciesClass: IrClass, - val method: IrMethod + val method: IrMethod, ) : ParsingError() class NullableSpreadMethod( val objects: Objects, val factoryMethod: IrMethod, val spreadClass: IrClass, - val spreadMethod: IrMethod + val spreadMethod: IrMethod, ) : ParsingError() class InvalidQualifier(val annotated: IrAnnotated, val annotation: IrAnnotation) : ParsingError() @@ -93,12 +93,12 @@ class InvalidQualifier(val annotated: IrAnnotated, val annotation: IrAnnotation) class DuplicatedChildParameterSource( val scope: Scope, val childScopeMethod: ChildMethod, - val duplicatedParameters: List + val duplicatedParameters: List, ) : ParsingError() class DuplicatedDependenciesMethod( val scope: Scope, - val duplicatedMethods: List + val duplicatedMethods: List, ) : ParsingError() class ScopeExtendsScope(val scope: Scope) : ParsingError() diff --git a/models/src/main/kotlin/motif/models/Scope.kt b/models/src/main/kotlin/motif/models/Scope.kt index c2eaaa88..e5254954 100644 --- a/models/src/main/kotlin/motif/models/Scope.kt +++ b/models/src/main/kotlin/motif/models/Scope.kt @@ -19,8 +19,7 @@ import motif.ast.IrClass import motif.ast.IrType /** [Wiki](https://github.com/uber/motif/wiki#scope) */ -sealed class Scope(val clazz: IrClass) { - +sealed class Scope(val useNullFieldInitialization : Boolean, val clazz: IrClass) { val source by lazy { ScopeSource(this) } val simpleName: String by lazy { clazz.simpleName } val qualifiedName: String by lazy { clazz.qualifiedName } @@ -33,14 +32,12 @@ sealed class Scope(val clazz: IrClass) { companion object { - fun fromClasses(scopeClasses: List): List { - return ScopeFactory(scopeClasses).create() - } + fun fromClasses(scopeClasses: List): List = ScopeFactory(scopeClasses).create() } } class ErrorScope internal constructor(clazz: IrClass, val parsingError: ParsingError) : - Scope(clazz) { + Scope(useNullFieldInitialization = false, clazz) { override val objects: Objects? = null override val accessMethods: List = emptyList() override val childMethods: List = emptyList() @@ -48,7 +45,7 @@ class ErrorScope internal constructor(clazz: IrClass, val parsingError: ParsingE override val dependencies: Dependencies? = null } -class ValidScope internal constructor(clazz: IrClass) : Scope(clazz) { +class ValidScope internal constructor(clazz: IrClass, useNullFieldInitialization: Boolean = false) : Scope(useNullFieldInitialization, clazz) { init { if (clazz.kind != IrClass.Kind.INTERFACE) throw ScopeMustBeAnInterface(clazz) diff --git a/models/src/main/kotlin/motif/models/ScopeMethod.kt b/models/src/main/kotlin/motif/models/ScopeMethod.kt index b237d332..5199416b 100644 --- a/models/src/main/kotlin/motif/models/ScopeMethod.kt +++ b/models/src/main/kotlin/motif/models/ScopeMethod.kt @@ -40,9 +40,11 @@ sealed class ScopeMethod { } if (returnClass != null && returnClass.hasAnnotation(motif.Scope::class)) { - method.parameters.find { it.isNullable() }?.let { nullableParameter -> - throw NullableDynamicDependency(scope, method, nullableParameter) - } + method.parameters + .find { it.isNullable() } + ?.let { nullableParameter -> + throw NullableDynamicDependency(scope, method, nullableParameter) + } val childMethod = ChildMethod(method, scope, returnClass) val duplicatedParameterTypes = childMethod.parameters - childMethod.parameters.distinctBy { it.type } diff --git a/models/src/main/kotlin/motif/models/Spread.kt b/models/src/main/kotlin/motif/models/Spread.kt index ee148221..b2ce7d1c 100644 --- a/models/src/main/kotlin/motif/models/Spread.kt +++ b/models/src/main/kotlin/motif/models/Spread.kt @@ -25,8 +25,7 @@ class Spread(val clazz: IrClass, val factoryMethod: FactoryMethod) { val qualifiedName: String by lazy { clazz.qualifiedName } val methods: List = - clazz - .methods + clazz.methods .filter { method -> isSpreadMethod(method) } .onEach { method -> if (method.isNullable()) { @@ -44,8 +43,7 @@ class Spread(val clazz: IrClass, val factoryMethod: FactoryMethod) { companion object { - private fun isSpreadMethod(method: IrMethod): Boolean { - return !method.isVoid() && method.isPublic() && !method.hasParameters() - } + private fun isSpreadMethod(method: IrMethod): Boolean = + !method.isVoid() && method.isPublic() && !method.hasParameters() } } diff --git a/models/src/main/kotlin/motif/models/Type.kt b/models/src/main/kotlin/motif/models/Type.kt index 5c39d12b..c81d26d9 100644 --- a/models/src/main/kotlin/motif/models/Type.kt +++ b/models/src/main/kotlin/motif/models/Type.kt @@ -39,9 +39,7 @@ data class Type(val type: IrType, val qualifier: IrAnnotation?) : Comparable val annotationClass: IrClass = annotation.type?.resolveClass() ?: return@find false annotationClass.hasAnnotation(Qualifier::class) - } - ?: return null + } ?: return null val members = qualifier.members if (members.size > 1) { diff --git a/models/src/test/kotlin/motif/models/motif/BaseTest.kt b/models/src/test/kotlin/motif/models/motif/BaseTest.kt index be7789d1..3315ab5f 100644 --- a/models/src/test/kotlin/motif/models/motif/BaseTest.kt +++ b/models/src/test/kotlin/motif/models/motif/BaseTest.kt @@ -71,12 +71,8 @@ abstract class BaseTest { return true } - override fun getSupportedSourceVersion(): SourceVersion { - return SourceVersion.latestSupported() - } + override fun getSupportedSourceVersion(): SourceVersion = SourceVersion.latestSupported() - override fun getSupportedAnnotationTypes(): Set { - return setOf(motif.Scope::class.java.name) - } + override fun getSupportedAnnotationTypes(): Set = setOf(motif.Scope::class.java.name) } } diff --git a/models/src/test/kotlin/motif/models/motif/ModelsSmokeTest.kt b/models/src/test/kotlin/motif/models/motif/ModelsSmokeTest.kt index 31f3358c..0f750f84 100644 --- a/models/src/test/kotlin/motif/models/motif/ModelsSmokeTest.kt +++ b/models/src/test/kotlin/motif/models/motif/ModelsSmokeTest.kt @@ -22,15 +22,16 @@ import org.junit.Test * Smoke test for :models. * * There are a couple reasons why the tests in this module should remain minimal: - * * 1. Running the integration tests in the :tests module should give us full confidence that our - * implementation is + * implementation is + * * ``` * correct. We should not rely on ModelsSmokeTest for validating correctness. Full coverage in this module * would likely result in missing coverage in :tests. * ``` * 2. Full coverage in this module would couple us to a specific implementation. We want it to be - * possible to + * possible to + * * ``` * safely rewrite the compiler with minimal changes to our test suite. * ``` @@ -46,7 +47,9 @@ class ModelsSmokeTest : BaseTest() { @motif.Scope interface FooScope {} - """.trimIndent()) + """ + .trimIndent(), + ) val scopes = getScopes() @@ -71,7 +74,9 @@ class ModelsSmokeTest : BaseTest() { @motif.Objects class Objects {} } - """.trimIndent()) + """ + .trimIndent(), + ) val scope = getScopes()[0] @@ -95,7 +100,9 @@ class ModelsSmokeTest : BaseTest() { } } } - """.trimIndent()) + """ + .trimIndent(), + ) val factoryMethods = getScopes()[0].factoryMethods @@ -119,7 +126,9 @@ class ModelsSmokeTest : BaseTest() { class Foo { Foo(int i) {} } - """.trimIndent()) + """ + .trimIndent(), + ) addClass( "test.FooScope", """ @@ -133,7 +142,9 @@ class ModelsSmokeTest : BaseTest() { abstract Foo foo(); } } - """.trimIndent()) + """ + .trimIndent(), + ) val factoryMethod = getScopes()[0].factoryMethods[0] @@ -150,7 +161,9 @@ class ModelsSmokeTest : BaseTest() { package test; class Foo {} - """.trimIndent()) + """ + .trimIndent(), + ) addClass( "test.Bar", @@ -158,7 +171,9 @@ class ModelsSmokeTest : BaseTest() { package test; class Bar extends Foo {} - """.trimIndent()) + """ + .trimIndent(), + ) addClass( "test.FooScope", """ @@ -172,7 +187,9 @@ class ModelsSmokeTest : BaseTest() { abstract Foo bar(Bar bar); } } - """.trimIndent()) + """ + .trimIndent(), + ) val factoryMethod = getScopes()[0].factoryMethods[0] @@ -195,7 +212,9 @@ class ModelsSmokeTest : BaseTest() { return ""; } } - """.trimIndent()) + """ + .trimIndent(), + ) addClass( "test.FooScope", """ @@ -211,7 +230,9 @@ class ModelsSmokeTest : BaseTest() { abstract Foo foo(); } } - """.trimIndent()) + """ + .trimIndent(), + ) val factoryMethod = getScopes()[0].factoryMethods[0] @@ -235,7 +256,9 @@ class ModelsSmokeTest : BaseTest() { interface FooScope { String string(); } - """.trimIndent()) + """ + .trimIndent(), + ) val scope = getScopes()[0] @@ -258,7 +281,9 @@ class ModelsSmokeTest : BaseTest() { interface FooScope { BarScope bar(String string); } - """.trimIndent()) + """ + .trimIndent(), + ) addClass( "test.BarScope", @@ -267,7 +292,9 @@ class ModelsSmokeTest : BaseTest() { @motif.Scope interface BarScope {} - """.trimIndent()) + """ + .trimIndent(), + ) // getScopes is alphabetically sorted so FooScope is last val fooScope = getScopes()[1] @@ -296,7 +323,9 @@ class ModelsSmokeTest : BaseTest() { String string(); } } - """.trimIndent()) + """ + .trimIndent(), + ) val scope = getScopes()[0] diff --git a/samples/dagger-comparison/build.gradle b/samples/dagger-comparison/build.gradle index 619cc488..f23c0be2 100644 --- a/samples/dagger-comparison/build.gradle +++ b/samples/dagger-comparison/build.gradle @@ -21,11 +21,6 @@ android { abortOnError false quiet true } - - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 - } } dependencies { diff --git a/samples/sample-kotlin-ksp/build.gradle b/samples/sample-kotlin-ksp/build.gradle index 8740fd20..db4e8854 100644 --- a/samples/sample-kotlin-ksp/build.gradle +++ b/samples/sample-kotlin-ksp/build.gradle @@ -1,8 +1,12 @@ plugins { id 'com.google.devtools.ksp' id 'com.android.application' + id 'org.jetbrains.kotlin.android' +} + +kotlin { + jvmToolchain(11) } -apply plugin: 'kotlin-android' android { compileSdkVersion deps.build.compileSdkVersion @@ -25,11 +29,6 @@ android { } } - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 - } - variantFilter { variant -> if (variant.buildType.name == 'release') { variant.setIgnore(true) @@ -43,6 +42,3 @@ dependencies { implementation project(':lib') implementation deps.kotlin.stdlib } -repositories { - mavenCentral() -} diff --git a/samples/sample-kotlin-ksp/src/main/java/motif/sample/Greeter.kt b/samples/sample-kotlin-ksp/src/main/java/motif/sample/Greeter.kt index 28b5673f..82906eb9 100644 --- a/samples/sample-kotlin-ksp/src/main/java/motif/sample/Greeter.kt +++ b/samples/sample-kotlin-ksp/src/main/java/motif/sample/Greeter.kt @@ -17,7 +17,5 @@ package motif.sample class Greeter(private val name: String) { - fun greet(): String { - return "Hello $name!" - } + fun greet(): String = "Hello $name!" } diff --git a/samples/sample-kotlin/build.gradle b/samples/sample-kotlin/build.gradle index d3a8bca6..b41a8aa6 100644 --- a/samples/sample-kotlin/build.gradle +++ b/samples/sample-kotlin/build.gradle @@ -1,8 +1,12 @@ plugins { id 'com.android.application' + id 'kotlin-android' + id 'kotlin-kapt' +} + +kotlin { + jvmToolchain(11) } -apply plugin: 'kotlin-android' -apply plugin: 'kotlin-kapt' android { compileSdkVersion deps.build.compileSdkVersion @@ -24,11 +28,6 @@ android { quiet true } - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 - } - variantFilter { variant -> if (variant.buildType.name == 'release') { variant.setIgnore(true) diff --git a/samples/sample-kotlin/src/main/java/motif/sample/Greeter.kt b/samples/sample-kotlin/src/main/java/motif/sample/Greeter.kt index de8f41dd..d9d0cd09 100644 --- a/samples/sample-kotlin/src/main/java/motif/sample/Greeter.kt +++ b/samples/sample-kotlin/src/main/java/motif/sample/Greeter.kt @@ -17,7 +17,5 @@ package motif.sample class Greeter(private val name: String) { - fun greet(): String { - return "Hello $name!" - } + fun greet(): String = "Hello $name!" } diff --git a/samples/sample-kotlin/src/main/java/motif/sample/MainScope.kt b/samples/sample-kotlin/src/main/java/motif/sample/MainScope.kt index e4977ed6..4e6b2ac2 100644 --- a/samples/sample-kotlin/src/main/java/motif/sample/MainScope.kt +++ b/samples/sample-kotlin/src/main/java/motif/sample/MainScope.kt @@ -19,7 +19,7 @@ import javax.inject.Named import motif.Creatable import motif.Scope -@Scope +@Scope(useNullFieldInitialization = true) interface MainScope : Creatable { fun greeter(): Greeter diff --git a/samples/sample-lib-ksp/build.gradle b/samples/sample-lib-ksp/build.gradle new file mode 100644 index 00000000..c790276b --- /dev/null +++ b/samples/sample-lib-ksp/build.gradle @@ -0,0 +1,44 @@ +plugins { + id 'com.google.devtools.ksp' + id 'com.android.library' + id 'org.jetbrains.kotlin.android' +} + +kotlin { + jvmToolchain(11) +} + +android { + namespace 'com.example.sample_lib_ksp' + compileSdkVersion deps.build.compileSdkVersion + + defaultConfig { + minSdkVersion deps.build.minSdkVersion + targetSdkVersion deps.build.targetSdkVersion + } + + // No need for lint. This is just a tutorial. + lintOptions { + abortOnError false + quiet true + } + + sourceSets { + libraryVariants.all { variant -> + main.java.srcDirs += ["build/generated/ksp/${variant.name}/kotlin"] + } + } + + androidComponents { + beforeVariants(selector().withBuildType("debug")) { variantBuilder -> + variantBuilder.enabled = false + } + } +} + +dependencies { + ksp project(':compiler-ksp') + + implementation project(':lib') + implementation deps.kotlin.stdlib +} diff --git a/samples/sample-lib-ksp/src/main/AndroidManifest.xml b/samples/sample-lib-ksp/src/main/AndroidManifest.xml new file mode 100644 index 00000000..1d26c87a --- /dev/null +++ b/samples/sample-lib-ksp/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/compiler/ksp/src/main/kotlin/com/google/devtools/ksp/KspErrorThrower.kt b/samples/sample-lib-ksp/src/main/java/com/example/sample_lib_ksp/Greeter.java similarity index 76% rename from compiler/ksp/src/main/kotlin/com/google/devtools/ksp/KspErrorThrower.kt rename to samples/sample-lib-ksp/src/main/java/com/example/sample_lib_ksp/Greeter.java index f48e735a..f6272834 100644 --- a/compiler/ksp/src/main/kotlin/com/google/devtools/ksp/KspErrorThrower.kt +++ b/samples/sample-lib-ksp/src/main/java/com/example/sample_lib_ksp/Greeter.java @@ -13,10 +13,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.google.devtools.ksp +package com.example.sample_lib_ksp; -internal object KspErrorThrower { - internal fun rethrowKspError(msg: String) { - throw IllegalStateException(msg) +class Greeter { + private final String name; + + public Greeter(String name) { + this.name = name; + } + + String greet() { + return "Hello " + name + "!"; } } diff --git a/samples/sample-lib-ksp/src/main/java/com/example/sample_lib_ksp/JavaScope.java b/samples/sample-lib-ksp/src/main/java/com/example/sample_lib_ksp/JavaScope.java new file mode 100644 index 00000000..4031e0ff --- /dev/null +++ b/samples/sample-lib-ksp/src/main/java/com/example/sample_lib_ksp/JavaScope.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2022 Uber Technologies, Inc. + * + * 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.example.sample_lib_ksp; + +import javax.inject.Named; +import motif.Scope; + +@Scope +public interface JavaScope { + String greeter(); + + @motif.Objects + class Objects { + + @Named("name") + String name() { + return "World"; + } + + Greeter greeter(@Named("name") String name) { + return new Greeter(name); + } + } + + interface Dependencies {} +} diff --git a/samples/sample-lib/build.gradle b/samples/sample-lib/build.gradle index 35e2d1ad..926ca2cc 100644 --- a/samples/sample-lib/build.gradle +++ b/samples/sample-lib/build.gradle @@ -21,11 +21,6 @@ android { abortOnError false quiet true } - - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 - } } dependencies { diff --git a/samples/sample-lib/src/main/java/motif/sample/lib/bottom_header/BottomHeaderScope.java b/samples/sample-lib/src/main/java/motif/sample/lib/bottom_header/BottomHeaderScope.java index 03b5aae4..b3b2d869 100644 --- a/samples/sample-lib/src/main/java/motif/sample/lib/bottom_header/BottomHeaderScope.java +++ b/samples/sample-lib/src/main/java/motif/sample/lib/bottom_header/BottomHeaderScope.java @@ -18,7 +18,7 @@ import motif.Scope; import motif.sample.lib.controller.ControllerObjects; -@Scope +@Scope(useNullFieldInitialization = true) public interface BottomHeaderScope { BottomHeaderView view(); diff --git a/samples/sample/build.gradle b/samples/sample/build.gradle index 2c9c197e..8ef8443d 100644 --- a/samples/sample/build.gradle +++ b/samples/sample/build.gradle @@ -11,6 +11,11 @@ android { targetSdkVersion deps.build.targetSdkVersion } + compileOptions { + sourceCompatibility JavaVersion.VERSION_11 + targetCompatibility JavaVersion.VERSION_11 + } + defaultConfig { minSdkVersion deps.build.minSdkVersion targetSdkVersion deps.build.targetSdkVersion @@ -22,11 +27,6 @@ android { quiet true } - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 - } - variantFilter { variant -> if (variant.buildType.name == 'release') { variant.setIgnore(true) @@ -38,7 +38,7 @@ dependencies { annotationProcessor deps.butterknifeCompiler annotationProcessor deps.glideCompiler annotationProcessor project(':compiler') - implementation project(':samples:sample-lib') + implementation project(':samples-sample-lib') implementation project(':lib') implementation deps.glide implementation deps.stetho @@ -47,5 +47,4 @@ dependencies { testImplementation deps.test.junit testImplementation deps.test.truth - } diff --git a/intellij/testing/src/main/kotlin/motif/intellij/testing/InternalJdk.kt b/samples/sample/src/main/java/demo/Bar.java similarity index 70% rename from intellij/testing/src/main/kotlin/motif/intellij/testing/InternalJdk.kt rename to samples/sample/src/main/java/demo/Bar.java index 0f1c4598..8eacd62d 100644 --- a/intellij/testing/src/main/kotlin/motif/intellij/testing/InternalJdk.kt +++ b/samples/sample/src/main/java/demo/Bar.java @@ -13,12 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package motif.intellij.testing +package demo; -import com.intellij.openapi.projectRoots.Sdk -import com.intellij.openapi.projectRoots.impl.JavaAwareProjectJdkTableImpl -object InternalJdk { +import javax.inject.Inject; - val instance: Sdk by lazy { JavaAwareProjectJdkTableImpl.getInstanceEx().internalJdk } +public class Bar { + + @Inject + public Bar() { + } } diff --git a/samples/sample/src/main/java/demo/RootScope.java b/samples/sample/src/main/java/demo/RootScope.java new file mode 100644 index 00000000..128bcd46 --- /dev/null +++ b/samples/sample/src/main/java/demo/RootScope.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2018-2019 Uber Technologies, Inc. + * + * 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 demo; + + +@motif.Scope +public interface RootScope { + + String foo(); + + @motif.Objects + abstract class Objects { + + abstract Bar bar(); + + String foo(int param) { + return String.valueOf(param); + } + } +} diff --git a/samples/sample/src/main/java/demo/RootScope2.java b/samples/sample/src/main/java/demo/RootScope2.java new file mode 100644 index 00000000..68fce7ad --- /dev/null +++ b/samples/sample/src/main/java/demo/RootScope2.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2018-2019 Uber Technologies, Inc. + * + * 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 demo; + + +@motif.Scope(useNullFieldInitialization = true) +public interface RootScope2 { + + String foo(); + + @motif.Objects + abstract class Objects { + + abstract Bar bar(); + + String foo(int param) { + return String.valueOf(param); + } + } +} diff --git a/samples/sample/src/main/java/motif/sample/app/bottom_sheet/BottomSheetScope.java b/samples/sample/src/main/java/motif/sample/app/bottom_sheet/BottomSheetScope.java index 3f3465d7..986c0699 100644 --- a/samples/sample/src/main/java/motif/sample/app/bottom_sheet/BottomSheetScope.java +++ b/samples/sample/src/main/java/motif/sample/app/bottom_sheet/BottomSheetScope.java @@ -22,7 +22,7 @@ import motif.sample.lib.bottom_header.BottomHeaderScope; import motif.sample.lib.controller.ControllerObjects; -@Scope +@Scope(useNullFieldInitialization = true) public interface BottomSheetScope { BottomSheetView view(); diff --git a/samples/sample/src/main/java/motif/sample/app/photo_grid/PhotoGridScope.java b/samples/sample/src/main/java/motif/sample/app/photo_grid/PhotoGridScope.java index 404d8a93..88acbdbc 100644 --- a/samples/sample/src/main/java/motif/sample/app/photo_grid/PhotoGridScope.java +++ b/samples/sample/src/main/java/motif/sample/app/photo_grid/PhotoGridScope.java @@ -21,7 +21,7 @@ import motif.sample.lib.controller.ControllerObjects; import motif.sample.lib.db.Photo; -@Scope +@Scope(useNullFieldInitialization = true) public interface PhotoGridScope { PhotoGridView view(); diff --git a/samples/sample/src/main/java/motif/sample/app/photo_list/PhotoListScope.java b/samples/sample/src/main/java/motif/sample/app/photo_list/PhotoListScope.java index 7b8936ad..ce22f0e7 100644 --- a/samples/sample/src/main/java/motif/sample/app/photo_list/PhotoListScope.java +++ b/samples/sample/src/main/java/motif/sample/app/photo_list/PhotoListScope.java @@ -21,7 +21,7 @@ import motif.sample.lib.controller.ControllerObjects; import motif.sample.lib.db.Photo; -@Scope +@Scope(useNullFieldInitialization = true) public interface PhotoListScope { PhotoListView view(); diff --git a/samples/sample/src/main/java/motif/sample/app/root/RootScope.java b/samples/sample/src/main/java/motif/sample/app/root/RootScope.java index 02394488..d943ea69 100644 --- a/samples/sample/src/main/java/motif/sample/app/root/RootScope.java +++ b/samples/sample/src/main/java/motif/sample/app/root/RootScope.java @@ -27,7 +27,7 @@ import motif.sample.lib.db.Database; import motif.sample.lib.multiselect.MultiSelector; -@Scope +@Scope(useNullFieldInitialization = true) public interface RootScope { RootView view(); diff --git a/samples/sample/src/test/java/motif/sample/app/root/RootScopeTest.java b/samples/sample/src/test/java/motif/sample/app/root/RootScopeTest.java index 30555c79..ce8659fe 100644 --- a/samples/sample/src/test/java/motif/sample/app/root/RootScopeTest.java +++ b/samples/sample/src/test/java/motif/sample/app/root/RootScopeTest.java @@ -15,10 +15,18 @@ */ package motif.sample.app.root; +import com.google.common.truth.Truth; +import demo.RootScope2Impl; +import demo.RootScope2Impl.Dependencies; import org.junit.Test; public class RootScopeTest { @Test - public void testIt() {} + public void testIt() { + Dependencies dependencies = () -> 123; + RootScope2Impl rootScope2 = new RootScope2Impl(dependencies); + String foo = rootScope2.foo(); + Truth.assertThat(foo).isEqualTo("123"); + } } diff --git a/samples/sample/src/test/java/motif/sample/app/root/manual_1/RootScope.java b/samples/sample/src/test/java/motif/sample/app/root/manual_1/RootScope.java new file mode 100644 index 00000000..169db4b4 --- /dev/null +++ b/samples/sample/src/test/java/motif/sample/app/root/manual_1/RootScope.java @@ -0,0 +1,18 @@ +package motif.sample.app.root.manual_1; + +import demo.Bar; + +public interface RootScope { + + String foo(); + + @motif.Objects + abstract class Objects { + + abstract Bar bar(); + + String foo(int param) { + return String.valueOf(param); + } + } +} diff --git a/samples/sample/src/test/java/motif/sample/app/root/manual_1/RootScopeImpl.java b/samples/sample/src/test/java/motif/sample/app/root/manual_1/RootScopeImpl.java new file mode 100644 index 00000000..9780d18e --- /dev/null +++ b/samples/sample/src/test/java/motif/sample/app/root/manual_1/RootScopeImpl.java @@ -0,0 +1,73 @@ +package motif.sample.app.root.manual_1; + +import demo.Bar; +import motif.internal.None; + +public class RootScopeImpl implements RootScope { + + private final RootScope.Objects objects = new Objects(); + + private final Dependencies dependencies; + + private volatile Object bar = None.NONE; + + private volatile Object string = None.NONE; + + public RootScopeImpl(Dependencies dependencies) { + this.dependencies = dependencies; + } + + @Override + public String foo() { + return string(); + } + + RootScope rootScope() { + return this; + } + + Bar bar() { + if (bar == None.NONE) { + synchronized (this) { + if (bar == None.NONE) { + bar = new Bar(); + } + } + } + return (Bar) bar; + } + + String string() { + if (string == None.NONE) { + synchronized (this) { + if (string == None.NONE) { + string = objects.foo(integer()); + } + } + } + return (String) string; + } + + int integer() { + return dependencies.integer(); + } + + public interface Dependencies { + + /** + *
      + * Requested from: + *
    • {@link demo.RootScope.Objects#foo(int)}
    • + *
    + */ + int integer(); + } + + private static class Objects extends RootScope.Objects { + + @Override + Bar bar() { + throw new UnsupportedOperationException(); + } + } +} diff --git a/samples/sample/src/test/java/motif/sample/app/root/manual_1/RootScopeManualTest.java b/samples/sample/src/test/java/motif/sample/app/root/manual_1/RootScopeManualTest.java new file mode 100644 index 00000000..1b1b4a66 --- /dev/null +++ b/samples/sample/src/test/java/motif/sample/app/root/manual_1/RootScopeManualTest.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2018-2019 Uber Technologies, Inc. + * + * 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 motif.sample.app.root.manual_1; + +import com.google.common.truth.Truth; +import demo.Bar; +import motif.sample.app.root.manual_1.RootScopeImpl.Dependencies; +import org.junit.Test; + +public class RootScopeManualTest { + + @Test + public void testIt() { + Dependencies dependencies = () -> 123; + RootScopeImpl rootScope2 = new RootScopeImpl(dependencies); + Bar bar1 = rootScope2.bar(); + Bar bar2 = rootScope2.bar(); + Truth.assertThat(bar2).isEqualTo(bar1); +// Truth.assertThat(rootScope2.foo()).isEqualTo("123"); + } +} diff --git a/samples/sample/src/test/java/motif/sample/app/root/manual_2/RootScope.java b/samples/sample/src/test/java/motif/sample/app/root/manual_2/RootScope.java new file mode 100644 index 00000000..a2310c34 --- /dev/null +++ b/samples/sample/src/test/java/motif/sample/app/root/manual_2/RootScope.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2018-2019 Uber Technologies, Inc. + * + * 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 motif.sample.app.root.manual_2; + + +import demo.Bar; + +public interface RootScope { + + String foo(); + + @motif.Objects + abstract class Objects { + + abstract Bar bar(); + + String foo(int param) { + return String.valueOf(param); + } + } +} diff --git a/samples/sample/src/test/java/motif/sample/app/root/manual_2/RootScopeImpl.java b/samples/sample/src/test/java/motif/sample/app/root/manual_2/RootScopeImpl.java new file mode 100644 index 00000000..0dee22f2 --- /dev/null +++ b/samples/sample/src/test/java/motif/sample/app/root/manual_2/RootScopeImpl.java @@ -0,0 +1,86 @@ +package motif.sample.app.root.manual_2; + +import demo.Bar; +import motif.internal.Initialized; + +public class RootScopeImpl implements RootScope { + + private final RootScope.Objects objects = new Objects(); + + private final Dependencies dependencies; + + private volatile Object bar; + + private volatile Object string; + + public RootScopeImpl(Dependencies dependencies) { + this.dependencies = dependencies; + } + + @Override + public String foo() { + return string(); + } + + RootScope rootScope2() { + return this; + } + + Bar bar() { + Object _bar = bar; + if (_bar == null) { + synchronized (this) { + if (bar == null) { + _bar = new Bar(); + bar = _bar; + } + } + } + if (_bar == Initialized.INITIALIZED) { + return null; + } + return (Bar) _bar; + } + + String string() { + Object _string = string; + if (_string == null) { + synchronized (this) { + if (string == null) { + _string = objects.foo(integer()); + if (_string == null) { + _string = Initialized.INITIALIZED; + } + string = _string; + } + } + } + if (_string == Initialized.INITIALIZED) { + return null; + } + return (String) _string; + } + + int integer() { + return dependencies.integer(); + } + + public interface Dependencies { + + /** + *
      + * Requested from: + *
    • {@link demo.RootScope2.Objects#foo(int)}
    • + *
    + */ + int integer(); + } + + private static class Objects extends RootScope.Objects { + + @Override + Bar bar() { + throw new UnsupportedOperationException(); + } + } +} diff --git a/samples/sample/src/test/java/motif/sample/app/root/manual_2/RootScopeManualTest.java b/samples/sample/src/test/java/motif/sample/app/root/manual_2/RootScopeManualTest.java new file mode 100644 index 00000000..30c67fce --- /dev/null +++ b/samples/sample/src/test/java/motif/sample/app/root/manual_2/RootScopeManualTest.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2018-2019 Uber Technologies, Inc. + * + * 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 motif.sample.app.root.manual_2; + +import com.google.common.truth.Truth; +import demo.Bar; +import motif.sample.app.root.manual_2.RootScopeImpl.Dependencies; +import org.junit.Test; + +public class RootScopeManualTest { + + @Test + public void testIt() { + Dependencies dependencies = () -> 123; + RootScopeImpl rootScope2 = new RootScopeImpl(dependencies); + Bar bar1 = rootScope2.bar(); + Bar bar2 = rootScope2.bar(); + Truth.assertThat(bar2).isEqualTo(bar1); +// Truth.assertThat(rootScope2.foo()).isEqualTo("123"); + } +} diff --git a/samples/sample/src/test/resources/java/1_raw.txt b/samples/sample/src/test/resources/java/1_raw.txt new file mode 100644 index 00000000..4f0212fa --- /dev/null +++ b/samples/sample/src/test/resources/java/1_raw.txt @@ -0,0 +1,77 @@ +package demo; + +import java.lang.Object; +import java.lang.Override; +import java.lang.String; +import java.lang.UnsupportedOperationException; +import motif.ScopeImpl; +import motif.internal.None; + +@ScopeImpl( + children = {}, + scope = RootScope.class, + dependencies = RootScopeImpl.Dependencies.class +) +public class RootScopeImpl implements RootScope { + private final RootScope.Objects objects = new Objects(); + + private final Dependencies dependencies; + + private volatile Object bar = None.NONE; + + private volatile Object string = None.NONE; + + public RootScopeImpl(Dependencies dependencies) { + this.dependencies = dependencies; + } + + @Override + public String foo() { + return string(); + } + + RootScope rootScope() { + return this; + } + + Bar bar() { + if (bar == None.NONE) { + synchronized (this) { + if (bar == None.NONE) { + bar = new Bar();} + } + } + return (Bar) bar; + } + + String string() { + if (string == None.NONE) { + synchronized (this) { + if (string == None.NONE) { + string = objects.foo(integer());} + } + } + return (String) string; + } + + int integer() { + return dependencies.integer(); + } + + public interface Dependencies { + /** + *
      + * Requested from: + *
    • {@link demo.RootScope.Objects#foo(int)}
    • + *
    + */ + int integer(); + } + + private static class Objects extends RootScope.Objects { + @Override + Bar bar() { + throw new UnsupportedOperationException(); + } + } +} diff --git a/samples/sample/src/test/resources/java/2_raw.txt b/samples/sample/src/test/resources/java/2_raw.txt new file mode 100644 index 00000000..1e1cca9b --- /dev/null +++ b/samples/sample/src/test/resources/java/2_raw.txt @@ -0,0 +1,95 @@ +package demo; + +import java.lang.Object; +import java.lang.Override; +import java.lang.String; +import java.lang.UnsupportedOperationException; +import motif.ScopeImpl; +import motif.internal.Initialized; + +@ScopeImpl( + children = {}, + scope = RootScope.class, + dependencies = RootScopeImpl.Dependencies.class +) +public class RootScopeImpl implements RootScope { + private final RootScope.Objects objects = new Objects(); + + private final Dependencies dependencies; + + private volatile Object bar; + + private volatile Object string; + + public RootScopeImpl(Dependencies dependencies) { + this.dependencies = dependencies; + } + + @Override + public String foo() { + return string(); + } + + RootScope rootScope() { + return this; + } + + Bar bar() { + Object _bar = bar; + if (_bar == null) { + synchronized (this) { + if (bar == null) { + _bar = new Bar(); + if (_bar == null) { + _bar = Initialized.INITIALIZED; + } + bar = _bar; + } + } + } + if (_bar == Initialized.INITIALIZED) { + return null; + } + return (Bar) _bar; + } + + String string() { + Object _string = string; + if (_string == null) { + synchronized (this) { + if (string == null) { + _string = objects.foo(integer()); + if (_string == null) { + _string = Initialized.INITIALIZED; + } + string = _string; + } + } + } + if (_string == Initialized.INITIALIZED) { + return null; + } + return (String) _string; + } + + int integer() { + return dependencies.integer(); + } + + public interface Dependencies { + /** + *
      + * Requested from: + *
    • {@link demo.RootScope.Objects#foo(int)}
    • + *
    + */ + int integer(); + } + + private static class Objects extends RootScope.Objects { + @Override + Bar bar() { + throw new UnsupportedOperationException(); + } + } +} diff --git a/settings.gradle b/settings.gradle index 609e3b29..64db481b 100644 --- a/settings.gradle +++ b/settings.gradle @@ -29,24 +29,24 @@ pluginManagement { } rootProject.name = 'motif' -include 'lib' +include 'ast' include 'compiler' include 'compiler-ast' include 'compiler-ksp' -include 'samples:sample' -include 'samples:sample-lib' -include 'samples:sample-kotlin' -include 'samples:sample-kotlin-ksp' -include 'samples:dagger-comparison' -include 'intellij' -include 'intellij-ast' -include 'intellij:testing' include 'core' include 'errormessage' +include 'intellij' +include 'intellij-ast' +include 'lib' include 'models' -include 'ast' +include 'samples-dagger-comparison' +include 'samples-sample' +include 'samples-sample-kotlin' +include 'samples-sample-kotlin-ksp' +include 'samples-sample-lib' +include 'samples-sample-lib-ksp' include 'tests' -include 'tests:compiler' +include 'tests-compiler' include 'viewmodel' include 'xprocessing' include 'xprocessing-testing' @@ -54,3 +54,10 @@ include 'xprocessing-testing' project(':compiler-ast').projectDir = file('compiler/ast') project(':compiler-ksp').projectDir = file('compiler/ksp') project(':intellij-ast').projectDir = file('intellij/ast') +project(':samples-dagger-comparison').projectDir = file('samples/dagger-comparison') +project(':samples-sample').projectDir = file('samples/sample') +project(':samples-sample-kotlin').projectDir = file('samples/sample-kotlin') +project(':samples-sample-kotlin-ksp').projectDir = file('samples/sample-kotlin-ksp') +project(':samples-sample-lib').projectDir = file('samples/sample-lib') +project(':samples-sample-lib-ksp').projectDir = file('samples/sample-lib-ksp') +project(':tests-compiler').projectDir = file('tests/compiler') diff --git a/tests/build.gradle b/tests/build.gradle index a50a9011..b3c6cea4 100644 --- a/tests/build.gradle +++ b/tests/build.gradle @@ -1,14 +1,10 @@ plugins { - id 'net.ltgt.apt-idea' version '0.21' id 'com.google.devtools.ksp' + id 'java-library' + id 'kotlin' + id 'kotlin-kapt' } -apply plugin: 'java-library' -apply plugin: 'kotlin' -apply plugin: 'kotlin-kapt' - -sourceCompatibility = 1.8 - kotlin { sourceSets { main.kotlin.srcDirs += 'build/generated/ksp/main/kotlin' @@ -17,7 +13,7 @@ kotlin { } dependencies { - kapt project(':tests:compiler') + kapt project(':tests-compiler') kapt deps.daggerCompiler implementation project(':core') implementation project(':lib') diff --git a/tests/compiler/build.gradle b/tests/compiler/build.gradle index 7e1404fb..4767775e 100644 --- a/tests/compiler/build.gradle +++ b/tests/compiler/build.gradle @@ -2,8 +2,6 @@ plugins { id 'org.jetbrains.kotlin.jvm' } -sourceCompatibility = 1.8 - dependencies { implementation deps.autoCommon implementation deps.kotlin.stdlib diff --git a/tests/compiler/src/main/java/motif/stubcompiler/StubProcessor.kt b/tests/compiler/src/main/java/motif/stubcompiler/StubProcessor.kt index e7acbf11..fed0c43f 100644 --- a/tests/compiler/src/main/java/motif/stubcompiler/StubProcessor.kt +++ b/tests/compiler/src/main/java/motif/stubcompiler/StubProcessor.kt @@ -43,21 +43,20 @@ class StubProcessor : AbstractProcessor() { private val env: ProcessingEnvironment by lazy { processingEnv } - override fun getSupportedSourceVersion(): SourceVersion { - return SourceVersion.latestSupported() - } + override fun getSupportedSourceVersion(): SourceVersion = SourceVersion.latestSupported() - override fun getSupportedAnnotationTypes(): Set { - return Collections.singleton(Scope::class.java.name) - } + override fun getSupportedAnnotationTypes(): Set = + Collections.singleton(Scope::class.java.name) override fun process(annotations: Set, roundEnv: RoundEnvironment): Boolean { - roundEnv.getElementsAnnotatedWith(Scope::class.java).map { it as TypeElement }.forEach { - scopeElement -> - val packageName: String = MoreElements.getPackage(scopeElement).qualifiedName.toString() - val spec: TypeSpec = spec(scopeElement.asType() as DeclaredType) ?: return@forEach - JavaFile.builder(packageName, spec).build().writeTo(processingEnv.filer) - } + roundEnv + .getElementsAnnotatedWith(Scope::class.java) + .map { it as TypeElement } + .forEach { scopeElement -> + val packageName: String = MoreElements.getPackage(scopeElement).qualifiedName.toString() + val spec: TypeSpec = spec(scopeElement.asType() as DeclaredType) ?: return@forEach + JavaFile.builder(packageName, spec).build().writeTo(processingEnv.filer) + } return true } @@ -76,7 +75,8 @@ class StubProcessor : AbstractProcessor() { builder.addMethod( MethodSpec.constructorBuilder() .addParameter(TypeName.get(dependenciesType), "dependencies") - .build()) + .build(), + ) } if (scopeType.asElement().kind == ElementKind.INTERFACE) { @@ -103,8 +103,9 @@ class StubProcessor : AbstractProcessor() { private fun dependenciesType(scopeType: DeclaredType): DeclaredType? { val scopeElement = (scopeType.asElement() as? TypeElement) ?: return null val superInterface = scopeElement.interfaces.firstOrNull() as? DeclaredType ?: return null - if (Creatable::class.java.simpleName !in superInterface.asElement().simpleName.toString()) - return null + if (Creatable::class.java.simpleName !in superInterface.asElement().simpleName.toString()) { + return null + } return superInterface.typeArguments.singleOrNull() as? DeclaredType ?: return null } @@ -114,7 +115,6 @@ class StubProcessor : AbstractProcessor() { return ClassName.get(scopeClassName.packageName(), "${prefix}Impl") } - private fun ClassName.qualifiedName(): String { - return "${packageName()}.${simpleNames().joinToString(".")}" - } + private fun ClassName.qualifiedName(): String = + "${packageName()}.${simpleNames().joinToString(".")}" } diff --git a/tests/src/main/java/testcases/E057_scope_extends_scope/ERROR.txt b/tests/src/main/java/testcases/E057_scope_extends_scope/ERROR.txt index dda5980a..00045c19 100644 --- a/tests/src/main/java/testcases/E057_scope_extends_scope/ERROR.txt +++ b/tests/src/main/java/testcases/E057_scope_extends_scope/ERROR.txt @@ -17,7 +17,7 @@ [SCOPE EXTENDS SCOPE] - Scopes can't extend other Scopes: + Scope can't extend other Scopes: testcases.E057_scope_extends_scope.FooScope diff --git a/tests/src/main/java/testcases/E058_scope_extends_scope_intermediate_class/ERROR.txt b/tests/src/main/java/testcases/E058_scope_extends_scope_intermediate_class/ERROR.txt index b53093fc..a0e75e1c 100644 --- a/tests/src/main/java/testcases/E058_scope_extends_scope_intermediate_class/ERROR.txt +++ b/tests/src/main/java/testcases/E058_scope_extends_scope_intermediate_class/ERROR.txt @@ -17,7 +17,7 @@ [SCOPE EXTENDS SCOPE] - Scopes can't extend other Scopes: + Scope can't extend other Scopes: testcases.E058_scope_extends_scope_intermediate_class.FooScope diff --git a/tests/src/main/java/testcases/KT006_reference_self/Child.kt b/tests/src/main/java/testcases/KT006_reference_self/Child.kt new file mode 100644 index 00000000..0ccfeed6 --- /dev/null +++ b/tests/src/main/java/testcases/KT006_reference_self/Child.kt @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2023 Uber Technologies, Inc. + * + * 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 testcases.KT006_reference_self + +@motif.Scope +interface Child { + + @motif.Objects + abstract class Objects : ObjectComponent + +} \ No newline at end of file diff --git a/tests/src/main/java/testcases/KT006_reference_self/Foo.kt b/tests/src/main/java/testcases/KT006_reference_self/Foo.kt new file mode 100644 index 00000000..b68ef420 --- /dev/null +++ b/tests/src/main/java/testcases/KT006_reference_self/Foo.kt @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2023 Uber Technologies, Inc. + * + * 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 testcases.KT006_reference_self + +interface Foo +class Bar : Foo +interface ObjectComponent> { + fun generic(someFoo: T): Foo +} \ No newline at end of file diff --git a/tests/src/main/java/testcases/KT006_reference_self/GRAPH.txt b/tests/src/main/java/testcases/KT006_reference_self/GRAPH.txt new file mode 100644 index 00000000..33bc45a4 --- /dev/null +++ b/tests/src/main/java/testcases/KT006_reference_self/GRAPH.txt @@ -0,0 +1,57 @@ +######################################################################## +# # +# This file is auto-generated by running the Motif compiler tests and # +# serves a as validation of graph correctness. IntelliJ plugin tests # +# also rely on this file to ensure that the plugin graph understanding # +# is equivalent to the compiler's. # +# # +# - Do not edit manually. # +# - Commit changes to source control. # +# - Since this file is autogenerated, code review changes carefully to # +# ensure correctness. # +# # +######################################################################## + + ------- +| Scope | + ------- + + ==== Required ==== + + ==== Provides ==== + + ---- Bar | Objects.bar ---- + [ Required ] + [ Consumed By ] + * Child | Objects.generic(someFoo) + + ---- Scope | implicit ---- + [ Required ] + [ Consumed By ] + + ------- + | Child | + ------- + + ==== Required ==== + + ---- Bar ---- + [ Provided By ] + * Scope | Objects.bar + [ Consumed By ] + * Child | Objects.generic(someFoo) + + ==== Provides ==== + + ---- Child | implicit ---- + [ Required ] + [ Consumed By ] + + ---- Foo | Objects.generic ---- + [ Required ] + Bar + [ Provided By ] + * Scope | Objects.bar + [ Consumed By ] + + diff --git a/tests/src/main/java/testcases/KT006_reference_self/Scope.kt b/tests/src/main/java/testcases/KT006_reference_self/Scope.kt new file mode 100644 index 00000000..6138d23c --- /dev/null +++ b/tests/src/main/java/testcases/KT006_reference_self/Scope.kt @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2023 Uber Technologies, Inc. + * + * 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 testcases.KT006_reference_self + +import motif.Expose + +@motif.Scope +interface Scope { + + fun child(): Child + + @motif.Objects + abstract class Objects { + + @Expose + fun bar(): Bar = Bar() + } +} \ No newline at end of file diff --git a/tests/src/main/java/testcases/KT006_reference_self/Test.java b/tests/src/main/java/testcases/KT006_reference_self/Test.java new file mode 100644 index 00000000..21ed2131 --- /dev/null +++ b/tests/src/main/java/testcases/KT006_reference_self/Test.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2023 Uber Technologies, Inc. + * + * 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 testcases.KT006_reference_self; + +import static com.google.common.truth.Truth.assertThat; + +public class Test { + + public static void run() { + Scope scope = new ScopeImpl(); + assertThat(scope.child()).isNotNull(); + } +} diff --git a/tests/src/main/java/testcases/KT007_scope_factory_dependencies/GRAPH.txt b/tests/src/main/java/testcases/KT007_scope_factory_dependencies/GRAPH.txt new file mode 100644 index 00000000..550c4318 --- /dev/null +++ b/tests/src/main/java/testcases/KT007_scope_factory_dependencies/GRAPH.txt @@ -0,0 +1,27 @@ +######################################################################## +# # +# This file is auto-generated by running the Motif compiler tests and # +# serves a as validation of graph correctness. IntelliJ plugin tests # +# also rely on this file to ensure that the plugin graph understanding # +# is equivalent to the compiler's. # +# # +# - Do not edit manually. # +# - Commit changes to source control. # +# - Since this file is autogenerated, code review changes carefully to # +# ensure correctness. # +# # +######################################################################## + + ------- +| Scope | + ------- + + ==== Required ==== + + ==== Provides ==== + + ---- Scope | implicit ---- + [ Required ] + [ Consumed By ] + + diff --git a/tests/src/main/java/testcases/KT007_scope_factory_dependencies/Scope.kt b/tests/src/main/java/testcases/KT007_scope_factory_dependencies/Scope.kt new file mode 100644 index 00000000..f0304f89 --- /dev/null +++ b/tests/src/main/java/testcases/KT007_scope_factory_dependencies/Scope.kt @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2023 Uber Technologies, Inc. + * + * 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 testcases.KT007_scope_factory_dependencies + +import motif.Creatable + +@motif.Scope +interface Scope : Creatable { + // note the absence of string() accessor (e.g. sink) in this scope + interface Dependencies { + fun string(): String + } +} \ No newline at end of file diff --git a/tests/src/main/java/testcases/KT007_scope_factory_dependencies/Test.java b/tests/src/main/java/testcases/KT007_scope_factory_dependencies/Test.java new file mode 100644 index 00000000..ba3c02bf --- /dev/null +++ b/tests/src/main/java/testcases/KT007_scope_factory_dependencies/Test.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2023 Uber Technologies, Inc. + * + * 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 testcases.KT007_scope_factory_dependencies; + +import motif.ScopeFactory; +import org.jetbrains.annotations.NotNull; + +public class Test { + + public static void run() { + ScopeFactory.create( + Scope.class, new Scope.Dependencies() { + @NotNull + @Override + public String string() { + return "s"; + } + } + ); + } +} diff --git a/tests/src/main/java/testcases/KT007_scope_factory_dependencies/config.pro b/tests/src/main/java/testcases/KT007_scope_factory_dependencies/config.pro new file mode 100644 index 00000000..8d56db15 --- /dev/null +++ b/tests/src/main/java/testcases/KT007_scope_factory_dependencies/config.pro @@ -0,0 +1,10 @@ +-libraryjars /jmods/java.base.jmod(!**.jar;!module-info.class) +-dontshrink +-keep class **Test { + public static void run(); +} + +-keepnames @motif.Scope interface * +-keepnames @motif.ScopeImpl class * { + (...); +} \ No newline at end of file diff --git a/tests/src/main/java/testcases/KT008_use_null_field_init_kotlin/GRAPH.txt b/tests/src/main/java/testcases/KT008_use_null_field_init_kotlin/GRAPH.txt new file mode 100644 index 00000000..abf4850c --- /dev/null +++ b/tests/src/main/java/testcases/KT008_use_null_field_init_kotlin/GRAPH.txt @@ -0,0 +1,42 @@ +######################################################################## +# # +# This file is auto-generated by running the Motif compiler tests and # +# serves a as validation of graph correctness. IntelliJ plugin tests # +# also rely on this file to ensure that the plugin graph understanding # +# is equivalent to the compiler's. # +# # +# - Do not edit manually. # +# - Commit changes to source control. # +# - Since this file is autogenerated, code review changes carefully to # +# ensure correctness. # +# # +######################################################################## + + ------- +| Scope | + ------- + + ==== Required ==== + + ==== Provides ==== + + ---- int | Objects.fooInt ---- + [ Required ] + [ Consumed By ] + * Scope | Scope.fooInt() + + ---- Object | Objects.fooObject ---- + [ Required ] + [ Consumed By ] + * Scope | Scope.fooObject() + + ---- String | Objects.fooString ---- + [ Required ] + [ Consumed By ] + * Scope | Scope.fooString() + + ---- Scope | implicit ---- + [ Required ] + [ Consumed By ] + + diff --git a/tests/src/main/java/testcases/KT008_use_null_field_init_kotlin/Scope.kt b/tests/src/main/java/testcases/KT008_use_null_field_init_kotlin/Scope.kt new file mode 100644 index 00000000..15e32c55 --- /dev/null +++ b/tests/src/main/java/testcases/KT008_use_null_field_init_kotlin/Scope.kt @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2018-2019 Uber Technologies, Inc. + * + * 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 testcases.KT008_use_null_field_init_kotlin + +import motif.Creatable + +@motif.Scope(useNullFieldInitialization = true) +interface Scope : Creatable { + fun fooObject(): Any + + fun fooInt(): Int + + fun fooString(): String + + @motif.Objects + abstract class Objects { + fun fooObject(): Any { + return Any() + } + + fun fooInt(): Int { + return 3 + } + + fun fooString(): String { + return "fooString" + } + } + + interface Dependencies +} \ No newline at end of file diff --git a/tests/src/main/java/testcases/KT008_use_null_field_init_kotlin/Test.java b/tests/src/main/java/testcases/KT008_use_null_field_init_kotlin/Test.java new file mode 100644 index 00000000..1cb8bf80 --- /dev/null +++ b/tests/src/main/java/testcases/KT008_use_null_field_init_kotlin/Test.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2018-2019 Uber Technologies, Inc. + * + * 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 testcases.KT008_use_null_field_init_kotlin; + +import static com.google.common.truth.Truth.assertThat; + +public class Test { + + public static void run() { + Scope scope = new ScopeImpl(); + assertThat(scope.fooString()).isEqualTo("fooString"); + assertThat(scope.fooInt()).isEqualTo(3); + assertThat(scope.fooObject()).isNotNull(); + assertThat(scope.fooObject()).isEqualTo(scope.fooObject()); + } +} diff --git a/tests/src/main/java/testcases/T077_use_null_field_init_java/GRAPH.txt b/tests/src/main/java/testcases/T077_use_null_field_init_java/GRAPH.txt new file mode 100644 index 00000000..abf4850c --- /dev/null +++ b/tests/src/main/java/testcases/T077_use_null_field_init_java/GRAPH.txt @@ -0,0 +1,42 @@ +######################################################################## +# # +# This file is auto-generated by running the Motif compiler tests and # +# serves a as validation of graph correctness. IntelliJ plugin tests # +# also rely on this file to ensure that the plugin graph understanding # +# is equivalent to the compiler's. # +# # +# - Do not edit manually. # +# - Commit changes to source control. # +# - Since this file is autogenerated, code review changes carefully to # +# ensure correctness. # +# # +######################################################################## + + ------- +| Scope | + ------- + + ==== Required ==== + + ==== Provides ==== + + ---- int | Objects.fooInt ---- + [ Required ] + [ Consumed By ] + * Scope | Scope.fooInt() + + ---- Object | Objects.fooObject ---- + [ Required ] + [ Consumed By ] + * Scope | Scope.fooObject() + + ---- String | Objects.fooString ---- + [ Required ] + [ Consumed By ] + * Scope | Scope.fooString() + + ---- Scope | implicit ---- + [ Required ] + [ Consumed By ] + + diff --git a/tests/src/main/java/testcases/T077_use_null_field_init_java/Scope.java b/tests/src/main/java/testcases/T077_use_null_field_init_java/Scope.java new file mode 100644 index 00000000..535ddd56 --- /dev/null +++ b/tests/src/main/java/testcases/T077_use_null_field_init_java/Scope.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2018-2019 Uber Technologies, Inc. + * + * 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 testcases.T077_use_null_field_init_java; + +import motif.Creatable; + +@motif.Scope(useNullFieldInitialization = true) +public interface Scope extends Creatable { + + Object fooObject(); + + int fooInt(); + + String fooString(); + + @motif.Objects + class Objects { + + Object fooObject() { + return new Object(); + } + + int fooInt() { + return 3; + } + + String fooString() { + return "fooString"; + } + } + + interface Dependencies {} +} \ No newline at end of file diff --git a/tests/src/main/java/testcases/T077_use_null_field_init_java/Test.java b/tests/src/main/java/testcases/T077_use_null_field_init_java/Test.java new file mode 100644 index 00000000..1601cd1b --- /dev/null +++ b/tests/src/main/java/testcases/T077_use_null_field_init_java/Test.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2018-2019 Uber Technologies, Inc. + * + * 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 testcases.T077_use_null_field_init_java; + +import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.fail; +public class Test { + + public static void run() { + Scope scope = new ScopeImpl(); + assertThat(scope.fooString()).isEqualTo("fooString"); + assertThat(scope.fooInt()).isEqualTo(3); + assertThat(scope.fooObject()).isNotNull(); + assertThat(scope.fooObject()).isEqualTo(scope.fooObject()); + } +} diff --git a/viewmodel/build.gradle b/viewmodel/build.gradle index 386cacda..1738742e 100644 --- a/viewmodel/build.gradle +++ b/viewmodel/build.gradle @@ -1,10 +1,8 @@ plugins { id 'org.jetbrains.kotlin.jvm' - id 'org.jetbrains.dokka' + id 'com.vanniktech.maven.publish' } -sourceCompatibility = 1.8 - dependencies { api project(':core') api project(':models') @@ -12,5 +10,3 @@ dependencies { implementation deps.kotlin.stdlib } - -apply plugin: 'com.vanniktech.maven.publish' diff --git a/viewmodel/src/main/kotlin/motif/viewmodel/GraphViewModel.kt b/viewmodel/src/main/kotlin/motif/viewmodel/GraphViewModel.kt index 521bc3ea..13a436ed 100644 --- a/viewmodel/src/main/kotlin/motif/viewmodel/GraphViewModel.kt +++ b/viewmodel/src/main/kotlin/motif/viewmodel/GraphViewModel.kt @@ -25,9 +25,7 @@ class GraphViewModel(val rootScopes: List) { companion object { - fun create(graph: ResolvedGraph): GraphViewModel { - return GraphViewModelFactory(graph).create() - } + fun create(graph: ResolvedGraph): GraphViewModel = GraphViewModelFactory(graph).create() } } @@ -40,16 +38,15 @@ private class GraphViewModelFactory(private val graph: ResolvedGraph) { return GraphViewModel(rootScopes) } - private fun getScopeViewModel(scope: Scope): ScopeViewModel { - return scopeViewModels[scope] - ?: createScopeViewModel(scope).apply { scopeViewModels[scope] = this } - } + private fun getScopeViewModel(scope: Scope): ScopeViewModel = + scopeViewModels[scope] ?: createScopeViewModel(scope).apply { scopeViewModels[scope] = this } private fun createScopeViewModel(scope: Scope): ScopeViewModel { val children = - graph.getChildEdges(scope).map { edge -> getScopeViewModel(edge.child) }.sortedBy { - it.scope.qualifiedName - } + graph + .getChildEdges(scope) + .map { edge -> getScopeViewModel(edge.child) } + .sortedBy { it.scope.qualifiedName } val providedDependencies = graph .getSources(scope) @@ -83,7 +80,8 @@ private class GraphViewModelFactory(private val graph: ResolvedGraph) { sources = graph.getProviders(sink) if (prevSources != null && sources != prevSources) { throw IllegalStateException( - "Inconsistent sources for sinks of the same type: $scope, $type") + "Inconsistent sources for sinks of the same type: $scope, $type", + ) } } diff --git a/viewmodel/src/main/kotlin/motif/viewmodel/ProvidedDependency.kt b/viewmodel/src/main/kotlin/motif/viewmodel/ProvidedDependency.kt index 1697fdf1..c1cab715 100644 --- a/viewmodel/src/main/kotlin/motif/viewmodel/ProvidedDependency.kt +++ b/viewmodel/src/main/kotlin/motif/viewmodel/ProvidedDependency.kt @@ -21,5 +21,5 @@ import motif.models.Source class ProvidedDependency( val source: Source, val consumedBy: List, - val requiredDependencies: List + val requiredDependencies: List, ) diff --git a/viewmodel/src/main/kotlin/motif/viewmodel/ScopeViewModel.kt b/viewmodel/src/main/kotlin/motif/viewmodel/ScopeViewModel.kt index de479274..fd2c7e80 100644 --- a/viewmodel/src/main/kotlin/motif/viewmodel/ScopeViewModel.kt +++ b/viewmodel/src/main/kotlin/motif/viewmodel/ScopeViewModel.kt @@ -21,5 +21,5 @@ class ScopeViewModel( val scope: Scope, val children: List, val providedDependencies: List, - val requiredDependencies: List + val requiredDependencies: List, ) diff --git a/viewmodel/src/main/kotlin/motif/viewmodel/TestRenderer.kt b/viewmodel/src/main/kotlin/motif/viewmodel/TestRenderer.kt index da05ed22..66040354 100644 --- a/viewmodel/src/main/kotlin/motif/viewmodel/TestRenderer.kt +++ b/viewmodel/src/main/kotlin/motif/viewmodel/TestRenderer.kt @@ -54,7 +54,7 @@ object TestRenderer { private fun StringBuilder.renderRequired( indent: Int, requiredDependencies: List, - topLevel: Boolean = true + topLevel: Boolean = true, ) { var header = "Required" header = if (topLevel) "==== $header ====" else "[ $header ]" @@ -67,7 +67,7 @@ object TestRenderer { private fun StringBuilder.renderProvided( indent: Int, - providedDependencies: List + providedDependencies: List, ) { appendLine(indent, "==== Provides ====") appendLine() @@ -79,7 +79,7 @@ object TestRenderer { private fun StringBuilder.renderRequired( indent: Int, requiredDependency: RequiredDependency, - topLevel: Boolean + topLevel: Boolean, ) { var header = requiredDependency.type.simpleName.toJvmSimpleName() header = if (topLevel) "---- $header ----" else header @@ -140,11 +140,10 @@ object TestRenderer { } /** HACK: Map kotlin types to Java for graph validation (issue when KSP processes Java sources) */ -private fun String.toJvmSimpleName(): String { - return when (this) { - "Int" -> "int" - "Boolean" -> "boolean" - "Byte" -> "byte" - else -> this - } -} +private fun String.toJvmSimpleName(): String = + when (this) { + "Int" -> "int" + "Boolean" -> "boolean" + "Byte" -> "byte" + else -> this + } diff --git a/xprocessing-testing/build.gradle b/xprocessing-testing/build.gradle index 2985bfd5..75c06fb6 100644 --- a/xprocessing-testing/build.gradle +++ b/xprocessing-testing/build.gradle @@ -1,6 +1,7 @@ plugins { id 'java' id 'com.github.johnrengelman.shadow' + id 'com.vanniktech.maven.publish' } java { @@ -21,7 +22,7 @@ shadowJar { dependencies { include dependency(deps.test.roomCompilerProcessingTesting) } - classifier = '' + archiveClassifier = '' configurations = [shadedConfig] mergeServiceFiles() relocate 'androidx.room.compiler.processing', 'motif.compiler.processing' @@ -43,5 +44,3 @@ artifacts { runtimeOnly shadowJar archives shadowJar } - -apply plugin: "com.vanniktech.maven.publish" diff --git a/xprocessing/build.gradle b/xprocessing/build.gradle index 2e4e8383..41eb88c7 100644 --- a/xprocessing/build.gradle +++ b/xprocessing/build.gradle @@ -1,6 +1,7 @@ plugins { id 'java' id 'com.github.johnrengelman.shadow' + id 'com.vanniktech.maven.publish' } java { @@ -21,7 +22,7 @@ shadowJar { dependencies { include dependency(deps.roomCompilerProcessing) } - classifier = '' + archiveClassifier = '' configurations = [shadedConfig] mergeServiceFiles() relocate 'androidx.room.compiler.processing', 'motif.compiler.processing' @@ -43,5 +44,3 @@ artifacts { runtimeOnly shadowJar archives shadowJar } - -apply plugin: "com.vanniktech.maven.publish"