Repository: evant/kotlin-inject
Pull request #92 (2021-01-15 00:11:46)

Update ksp


Patch:
diff --git a/.static/detekt-config.yml b/.static/detekt-config.yml
index 17fcca6..081333d 100644
--- a/.static/detekt-config.yml
+++ b/.static/detekt-config.yml
@@ -22,7 +22,6 @@ formatting:
     active: false
   AnnotationSpacing:
     active: true
-    autoCorrect: true
 
 naming:
   ConstructorParameterNaming:
diff --git a/README.md b/README.md
index d750126..da46cbc 100644
--- a/README.md
+++ b/README.md
@@ -29,8 +29,8 @@ val repo = appComponent.repo
 
 ```groovy
 plugins {
-    id 'org.jetbrains.kotlin.jvm' version '1.4.0'
-    id 'org.jetbrains.kotlin.kapt' version '1.4.0'
+    id 'org.jetbrains.kotlin.jvm' version '1.4.20'
+    id 'org.jetbrains.kotlin.kapt' version '1.4.20'
 }
 
 dependencies {
@@ -47,15 +47,6 @@ kapt.
 `settings.gradle`
 ```groovy
 pluginManagement {
-    resolutionStrategy {
-        eachPlugin {
-            switch (requested.id.id) {
-                case "symbol-processing":
-                    useModule("com.google.devtools.ksp:symbol-processing:${requested.version}")
-            }
-        }
-    }
-
     repositories {
         gradlePluginPortal()
         google()
@@ -66,8 +57,8 @@ pluginManagement {
 `build.gradle`
 ```groovy
 plugins {
-    id 'org.jetbrains.kotlin.jvm' version '1.4.10'
-    id 'symbol-processing' version '1.4.10-dev-experimental-20200924'
+    id 'org.jetbrains.kotlin.jvm' version '1.4.20'
+    id 'com.google.devtools.ksp' version '1.4.20-dev-experimental-20210111'
 }
 
 repositories {
diff --git a/build.gradle b/build.gradle
index f34a6d9..3529f68 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,7 +1,7 @@
 plugins {
     id 'org.jetbrains.kotlin.jvm' version "$kotlin_version" apply false
-    id 'symbol-processing' version "$ksp_version" apply false
-    id "io.gitlab.arturbosch.detekt" version "1.14.2"
+    id 'com.google.devtools.ksp' version "$ksp_version" apply false
+    id "io.gitlab.arturbosch.detekt" version "1.15.0"
 }
 
 repositories {
@@ -14,11 +14,11 @@ subprojects {
         mavenCentral()
     }
     afterEvaluate { project ->
-        project.tasks.findByName("check")?.dependsOn rootProject.tasks["detektAll"]
+        project.tasks.findByName("check")?.dependsOn rootProject.tasks["detekt"]
     }
 }
 
-task detektAll(type: io.gitlab.arturbosch.detekt.Detekt) {
+tasks.withType(io.gitlab.arturbosch.detekt.Detekt).configureEach {
     parallel = true
     failFast = false
     buildUponDefaultConfig = true
@@ -26,6 +26,8 @@ task detektAll(type: io.gitlab.arturbosch.detekt.Detekt) {
     config.from = project.file(".static/detekt-config.yml")
     include("**/*.kt")
     exclude("**/build/**")
+    autoCorrect = findProperty('fix') == 'true'
+    jvmTarget = "1.8"
     reports {
         html {
             enabled = true
diff --git a/gradle.properties b/gradle.properties
index b262f59..d0a24a1 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,3 +1,4 @@
 kotlin_version=1.4.20
-ksp_version=1.4.20-dev-experimental-20201204
-org.gradle.parallel=true
\ No newline at end of file
+ksp_version=1.4.20-dev-experimental-20210111
+org.gradle.parallel=true
+ksp.incremental=true
\ No newline at end of file
diff --git a/kotlin-inject-compiler-core/src/main/kotlin/me/tatarka/inject/compiler/Ast.kt b/kotlin-inject-compiler-core/src/main/kotlin/me/tatarka/inject/compiler/Ast.kt
index 7720bb7..8809113 100644
--- a/kotlin-inject-compiler-core/src/main/kotlin/me/tatarka/inject/compiler/Ast.kt
+++ b/kotlin-inject-compiler-core/src/main/kotlin/me/tatarka/inject/compiler/Ast.kt
@@ -1,6 +1,7 @@
 package me.tatarka.inject.compiler
 
 import com.squareup.kotlinpoet.ClassName
+import com.squareup.kotlinpoet.FileSpec
 import com.squareup.kotlinpoet.MemberName
 import com.squareup.kotlinpoet.ParameterSpec
 import com.squareup.kotlinpoet.TypeName
@@ -20,8 +21,6 @@ interface AstProvider {
         messenger.error(message, element)
 
     fun AstElement.toTrace(): String
-
-    fun TypeSpec.Builder.addOriginatingElement(astClass: AstClass): TypeSpec.Builder
 }
 
 interface Messenger {
@@ -216,4 +215,19 @@ interface AstHasModifiers {
     val visibility: AstVisibility
 
     val isAbstract: Boolean
+}
+
+interface OutputProvider<Output> {
+
+    fun astTypeSpec(typeSpecBuilder: TypeSpec.Builder, originatingElement: AstClass): AstTypeSpec
+
+    fun astFileSpec(fileSpecBuilder: FileSpec.Builder, astTypeSpec: AstTypeSpec): AstFileSpec<Output>
+}
+
+interface AstTypeSpec {
+    val typeSpec: TypeSpec
+}
+
+interface AstFileSpec<Output> {
+    fun writeTo(output: Output)
 }
\ No newline at end of file
diff --git a/kotlin-inject-compiler-core/src/main/kotlin/me/tatarka/inject/compiler/InjectGenerator.kt b/kotlin-inject-compiler-core/src/main/kotlin/me/tatarka/inject/compiler/InjectGenerator.kt
index 00e3b73..7a9547f 100644
--- a/kotlin-inject-compiler-core/src/main/kotlin/me/tatarka/inject/compiler/InjectGenerator.kt
+++ b/kotlin-inject-compiler-core/src/main/kotlin/me/tatarka/inject/compiler/InjectGenerator.kt
@@ -15,17 +15,18 @@ import me.tatarka.inject.annotations.Scope
 private val SCOPED_COMPONENT = ClassName("me.tatarka.inject.internal", "ScopedComponent")
 private val LAZY_MAP = ClassName("me.tatarka.inject.internal", "LazyMap")
 
-class InjectGenerator(
-    provider: AstProvider,
+class InjectGenerator<Output, Provider>(
+    private val provider: Provider,
     private val options: Options,
-) : AstProvider by provider {
+) : AstProvider by provider
+        where Provider : AstProvider, Provider : OutputProvider<Output> {
 
     private val createGenerator = CreateGenerator(provider, options)
 
     var scopeType: AstType? = null
         private set
 
-    fun generate(astClass: AstClass): FileSpec {
+    fun generate(astClass: AstClass): AstFileSpec<Output> {
         if (!astClass.isAbstract) {
             throw FailedToGenerateException("@Component class: $astClass must be abstract", astClass)
         } else if (astClass.visibility == AstVisibility.PRIVATE) {
@@ -35,121 +36,122 @@ class InjectGenerator(
         val constructor = astClass.primaryConstructor
 
         val injectComponent = generateInjectComponent(astClass, constructor)
-        val createFunction = createGenerator.create(astClass, constructor, injectComponent)
+        val createFunction = createGenerator.create(astClass, constructor, injectComponent.typeSpec)
 
-        return FileSpec.builder(astClass.packageName, "Inject${astClass.name}")
-            .addType(injectComponent)
-            .apply {
-                createFunction.forEach { addFunction(it) }
-            }
-            .build()
+        return provider.astFileSpec(
+            FileSpec.builder(astClass.packageName, "Inject${astClass.name}")
+                .apply {
+                    createFunction.forEach { addFunction(it) }
+                }, injectComponent
+        )
     }
 
     @Suppress("ComplexMethod", "LongMethod", "NestedBlockDepth")
     private fun generateInjectComponent(
         astClass: AstClass,
         constructor: AstConstructor?
-    ): TypeSpec {
+    ): AstTypeSpec {
         val context = collectTypes(astClass)
         val scope = context.collector.scopeClass
         scopeType = scope?.scopeType(options)
 
-        return TypeSpec.classBuilder("Inject${astClass.name}")
-            .addOriginatingElement(astClass)
-            .apply {
-                if (astClass.isInterface) {
-                    addSuperinterface(astClass.asClassName())
-                } else {
-                    superclass(astClass.asClassName())
-                }
-                if (scope != null) {
-                    addSuperinterface(SCOPED_COMPONENT)
-                }
-                addModifiers(astClass.visibility.toModifier())
-                if (constructor != null) {
-                    val funSpec = FunSpec.constructorBuilder()
-                    val params = constructor.parameters
-                    for (param in params) {
-                        if (param.isComponent()) {
-                            if (!param.isVal) {
-                                error("@Component parameter: ${param.name} must be val", param)
-                            } else if (param.isPrivate) {
-                                error("@Component parameter: ${param.name} must not be private", param)
+        return provider.astTypeSpec(
+            TypeSpec.classBuilder("Inject${astClass.name}")
+                .apply {
+                    if (astClass.isInterface) {
+                        addSuperinterface(astClass.asClassName())
+                    } else {
+                        superclass(astClass.asClassName())
+                    }
+                    if (scope != null) {
+                        addSuperinterface(SCOPED_COMPONENT)
+                    }
+                    addModifiers(astClass.visibility.toModifier())
+                    if (constructor != null) {
+                        val funSpec = FunSpec.constructorBuilder()
+                        val params = constructor.parameters
+                        for (param in params) {
+                            if (param.isComponent()) {
+                                if (!param.isVal) {
+                                    error("@Component parameter: ${param.name} must be val", param)
+                                } else if (param.isPrivate) {
+                                    error("@Component parameter: ${param.name} must not be private", param)
+                                }
                             }
                         }
-                    }
-                    val paramSpecs = params.map { it.asParameterSpec() }
-                    val nonDefaultParamSpecs =
-                        constructor.parameters.filter { !it.hasDefault }.map { it.asParameterSpec() }
-                    if (paramSpecs.size == nonDefaultParamSpecs.size) {
-                        for (p in paramSpecs) {
-                            funSpec.addParameter(p)
-                            addSuperclassConstructorParameter("%N", p)
+                        val paramSpecs = params.map { it.asParameterSpec() }
+                        val nonDefaultParamSpecs =
+                            constructor.parameters.filter { !it.hasDefault }.map { it.asParameterSpec() }
+                        if (paramSpecs.size == nonDefaultParamSpecs.size) {
+                            for (p in paramSpecs) {
+                                funSpec.addParameter(p)
+                                addSuperclassConstructorParameter("%N", p)
+                            }
+                            primaryConstructor(funSpec.build())
+                        } else {
+                            addFunction(
+                                FunSpec.constructorBuilder()
+                                    .addParameters(paramSpecs)
+                                    .callSuperConstructor(paramSpecs.map { CodeBlock.of("%N", it) })
+                                    .build()
+                            )
+                            addFunction(
+                                FunSpec.constructorBuilder()
+                                    .addParameters(nonDefaultParamSpecs)
+                                    .callSuperConstructor(nonDefaultParamSpecs.map { CodeBlock.of("%1N = %1N", it) })
+                                    .build()
+                            )
                         }
-                        primaryConstructor(funSpec.build())
-                    } else {
-                        addFunction(
-                            FunSpec.constructorBuilder()
-                                .addParameters(paramSpecs)
-                                .callSuperConstructor(paramSpecs.map { CodeBlock.of("%N", it) })
-                                .build()
-                        )
-                        addFunction(
-                            FunSpec.constructorBuilder()
-                                .addParameters(nonDefaultParamSpecs)
-                                .callSuperConstructor(nonDefaultParamSpecs.map { CodeBlock.of("%1N = %1N", it) })
-                                .build()
-                        )
                     }
-                }
 
-                try {
-                    if (scope != null) {
-                        addProperty(
-                            PropertySpec.builder("_scoped", LAZY_MAP)
-                                .addModifiers(KModifier.OVERRIDE)
-                                .initializer("%T()", LAZY_MAP)
-                                .build()
-                        )
-                    }
+                    try {
+                        if (scope != null) {
+                            addProperty(
+                                PropertySpec.builder("_scoped", LAZY_MAP)
+                                    .addModifiers(KModifier.OVERRIDE)
+                                    .initializer("%T()", LAZY_MAP)
+                                    .build()
+                            )
+                        }
 
-                    for (method in context.collector.providerMethods) {
-                        val returnType = method.returnTypeFor(astClass)
+                        for (method in context.collector.providerMethods) {
+                            val returnType = method.returnTypeFor(astClass)
 
-                        context.withoutProvider(returnType).use(method) { context ->
-                            val codeBlock = CodeBlock.builder()
-                            codeBlock.add("return ")
+                            context.withoutProvider(returnType).use(method) { context ->
+                                val codeBlock = CodeBlock.builder()
+                                codeBlock.add("return ")
 
-                            codeBlock.add(
-                                provide(
-                                    key = TypeKey(returnType, method.qualifier(options)),
-                                    context = context
+                                codeBlock.add(
+                                    provide(
+                                        key = TypeKey(returnType, method.qualifier(options)),
+                                        context = context
+                                    )
                                 )
-                            )
 
-                            when (method) {
-                                is AstProperty -> addProperty(
-                                    PropertySpec.builder(
-                                        method.name,
-                                        returnType.asTypeName(),
-                                        KModifier.OVERRIDE
-                                    ).getter(FunSpec.getterBuilder().addCode(codeBlock.build()).build()).build()
-                                )
-                                is AstFunction -> addFunction(
-                                    FunSpec.builder(method.name).returns(returnType.asTypeName())
-                                        .addModifiers(KModifier.OVERRIDE)
-                                        .apply { if (method.isSuspend) addModifiers(KModifier.SUSPEND) }
-                                        .addCode(codeBlock.build()).build()
-                                )
+                                when (method) {
+                                    is AstProperty -> addProperty(
+                                        PropertySpec.builder(
+                                            method.name,
+                                            returnType.asTypeName(),
+                                            KModifier.OVERRIDE
+                                        ).getter(FunSpec.getterBuilder().addCode(codeBlock.build()).build()).build()
+                                    )
+                                    is AstFunction -> addFunction(
+                                        FunSpec.builder(method.name).returns(returnType.asTypeName())
+                                            .addModifiers(KModifier.OVERRIDE)
+                                            .apply { if (method.isSuspend) addModifiers(KModifier.SUSPEND) }
+                                            .addCode(codeBlock.build()).build()
+                                    )
+                                }
                             }
                         }
+                    } catch (e: FailedToGenerateException) {
+                        error(e.message.orEmpty(), e.element)
+                        // Create a stub component to prevent extra compile errors,
+                        // the original one will still be reported.
                     }
-                } catch (e: FailedToGenerateException) {
-                    error(e.message.orEmpty(), e.element)
-                    // Create a stub component to prevent extra compile errors, the original one will still be reported.
-                }
-            }
-            .build()
+                }, astClass
+        )
     }
 
     private fun collectTypes(
@@ -501,8 +503,8 @@ class InjectGenerator(
         }
 
         fun trace(message: String): String = "$message\n" +
-            cycleDetector.elements.reversed()
-                .joinToString(separator = "\n") { with(provider) { it.toTrace() } }
+                cycleDetector.elements.reversed()
+                    .joinToString(separator = "\n") { with(provider) { it.toTrace() } }
     }
 
     sealed class Result(val name: String?) {
diff --git a/kotlin-inject-compiler-kapt/src/main/kotlin/me/tatarka/inject/compiler/kapt/BaseInjectCompiler.kt b/kotlin-inject-compiler-kapt/src/main/kotlin/me/tatarka/inject/compiler/kapt/BaseInjectCompiler.kt
index 5643f23..adb4283 100644
--- a/kotlin-inject-compiler-kapt/src/main/kotlin/me/tatarka/inject/compiler/kapt/BaseInjectCompiler.kt
+++ b/kotlin-inject-compiler-kapt/src/main/kotlin/me/tatarka/inject/compiler/kapt/BaseInjectCompiler.kt
@@ -10,7 +10,8 @@ private const val OPTION_GENERATE_COMPANION_EXTENSIONS = "me.tatarka.inject.gene
 
 abstract class BaseInjectCompiler :
     AbstractProcessor(),
-    ModelAstProvider {
+    ModelAstProvider,
+    ModelOutputProvider {
 
     protected lateinit var options: Options
     protected lateinit var filer: Filer
diff --git a/kotlin-inject-compiler-kapt/src/main/kotlin/me/tatarka/inject/compiler/kapt/InjectCompiler.kt b/kotlin-inject-compiler-kapt/src/main/kotlin/me/tatarka/inject/compiler/kapt/InjectCompiler.kt
index 47db278..b375489 100644
--- a/kotlin-inject-compiler-kapt/src/main/kotlin/me/tatarka/inject/compiler/kapt/InjectCompiler.kt
+++ b/kotlin-inject-compiler-kapt/src/main/kotlin/me/tatarka/inject/compiler/kapt/InjectCompiler.kt
@@ -1,7 +1,6 @@
 package me.tatarka.inject.compiler.kapt
 
 import me.tatarka.inject.annotations.Component
-import me.tatarka.inject.compiler.CreateGenerator
 import me.tatarka.inject.compiler.FailedToGenerateException
 import me.tatarka.inject.compiler.InjectGenerator
 import me.tatarka.inject.compiler.Profiler
diff --git a/kotlin-inject-compiler-kapt/src/main/kotlin/me/tatarka/inject/compiler/kapt/ModelAst.kt b/kotlin-inject-compiler-kapt/src/main/kotlin/me/tatarka/inject/compiler/kapt/ModelAst.kt
index 14333a1..319676f 100644
--- a/kotlin-inject-compiler-kapt/src/main/kotlin/me/tatarka/inject/compiler/kapt/ModelAst.kt
+++ b/kotlin-inject-compiler-kapt/src/main/kotlin/me/tatarka/inject/compiler/kapt/ModelAst.kt
@@ -1,16 +1,45 @@
 package me.tatarka.inject.compiler.kapt
 
 import com.squareup.kotlinpoet.ClassName
+import com.squareup.kotlinpoet.FileSpec
 import com.squareup.kotlinpoet.MemberName
 import com.squareup.kotlinpoet.ParameterSpec
 import com.squareup.kotlinpoet.TypeName
 import com.squareup.kotlinpoet.TypeSpec
 import com.squareup.kotlinpoet.asClassName
-import kotlinx.metadata.*
+import kotlinx.metadata.Flag
+import kotlinx.metadata.Flags
+import kotlinx.metadata.KmAnnotation
+import kotlinx.metadata.KmClass
+import kotlinx.metadata.KmClassifier
+import kotlinx.metadata.KmConstructor
+import kotlinx.metadata.KmFunction
+import kotlinx.metadata.KmProperty
+import kotlinx.metadata.KmType
+import kotlinx.metadata.KmTypeProjection
+import kotlinx.metadata.KmValueParameter
+import kotlinx.metadata.KmVariance
 import kotlinx.metadata.jvm.annotations
 import kotlinx.metadata.jvm.getterSignature
 import kotlinx.metadata.jvm.signature
-import me.tatarka.inject.compiler.*
+import me.tatarka.inject.compiler.AstAnnotated
+import me.tatarka.inject.compiler.AstAnnotation
+import me.tatarka.inject.compiler.AstBasicElement
+import me.tatarka.inject.compiler.AstClass
+import me.tatarka.inject.compiler.AstConstructor
+import me.tatarka.inject.compiler.AstElement
+import me.tatarka.inject.compiler.AstFileSpec
+import me.tatarka.inject.compiler.AstFunction
+import me.tatarka.inject.compiler.AstMethod
+import me.tatarka.inject.compiler.AstParam
+import me.tatarka.inject.compiler.AstProperty
+import me.tatarka.inject.compiler.AstProvider
+import me.tatarka.inject.compiler.AstType
+import me.tatarka.inject.compiler.AstTypeSpec
+import me.tatarka.inject.compiler.AstVisibility
+import me.tatarka.inject.compiler.Messenger
+import me.tatarka.inject.compiler.OutputProvider
+import javax.annotation.processing.Filer
 import javax.annotation.processing.Messager
 import javax.annotation.processing.ProcessingEnvironment
 import javax.lang.model.element.AnnotationMirror
@@ -124,10 +153,17 @@ interface ModelAstProvider : AstProvider {
             else -> toString()
         }
     }
+}
+
+interface ModelOutputProvider : OutputProvider<Filer> {
+
+    override fun astTypeSpec(typeSpecBuilder: TypeSpec.Builder, originatingElement: AstClass): AstTypeSpec {
+        require(originatingElement is ModelAstClass)
+        return ModelAstTypeSpec(typeSpecBuilder, originatingElement)
+    }
 
-    override fun TypeSpec.Builder.addOriginatingElement(astClass: AstClass): TypeSpec.Builder = apply {
-        require(astClass is ModelAstClass)
-        addOriginatingElement(astClass.element)
+    override fun astFileSpec(fileSpecBuilder: FileSpec.Builder, astTypeSpec: AstTypeSpec): AstFileSpec<Filer> {
+        return ModelAstFileSpec(fileSpecBuilder, astTypeSpec)
     }
 }
 
@@ -734,4 +770,20 @@ val AstClass.element: TypeElement
     get() {
         require(this is ModelAstClass)
         return element
-    }
\ No newline at end of file
+    }
+
+private class ModelAstTypeSpec(typeSpecBuilder: TypeSpec.Builder, astClass: ModelAstClass) : AstTypeSpec {
+    override val typeSpec: TypeSpec = typeSpecBuilder
+        .addOriginatingElement(astClass.element)
+        .build()
+}
+
+private class ModelAstFileSpec(fileSpecBuilder: FileSpec.Builder, typeSpec: AstTypeSpec) : AstFileSpec<Filer> {
+    private val fileSpec: FileSpec = fileSpecBuilder
+        .addType(typeSpec.typeSpec)
+        .build()
+
+    override fun writeTo(output: Filer) {
+        fileSpec.writeTo(output)
+    }
+}
diff --git a/kotlin-inject-compiler-ksp/src/main/kotlin/me/tatarka/inject/compiler/ksp/InjectProcessor.kt b/kotlin-inject-compiler-ksp/src/main/kotlin/me/tatarka/inject/compiler/ksp/InjectProcessor.kt
index c06fa78..39eb026 100644
--- a/kotlin-inject-compiler-ksp/src/main/kotlin/me/tatarka/inject/compiler/ksp/InjectProcessor.kt
+++ b/kotlin-inject-compiler-ksp/src/main/kotlin/me/tatarka/inject/compiler/ksp/InjectProcessor.kt
@@ -5,9 +5,7 @@ import com.google.devtools.ksp.processing.KSPLogger
 import com.google.devtools.ksp.processing.Resolver
 import com.google.devtools.ksp.processing.SymbolProcessor
 import com.google.devtools.ksp.symbol.KSClassDeclaration
-import com.squareup.kotlinpoet.FileSpec
 import me.tatarka.inject.annotations.Component
-import me.tatarka.inject.compiler.CreateGenerator
 import me.tatarka.inject.compiler.FailedToGenerateException
 import me.tatarka.inject.compiler.InjectGenerator
 import me.tatarka.inject.compiler.Options
@@ -59,10 +57,4 @@ class InjectProcessor(private val profiler: Profiler? = null) : SymbolProcessor,
     override fun finish() {
         messenger.finalize()
     }
-
-    private fun FileSpec.writeTo(codeGenerator: CodeGenerator) {
-        codeGenerator.createNewFile(packageName, name).bufferedWriter().use {
-            writeTo(it)
-        }
-    }
 }
\ No newline at end of file
diff --git a/kotlin-inject-compiler-ksp/src/main/kotlin/me/tatarka/inject/compiler/ksp/KSAst.kt b/kotlin-inject-compiler-ksp/src/main/kotlin/me/tatarka/inject/compiler/ksp/KSAst.kt
index 465e59f..6a4c689 100644
--- a/kotlin-inject-compiler-ksp/src/main/kotlin/me/tatarka/inject/compiler/ksp/KSAst.kt
+++ b/kotlin-inject-compiler-ksp/src/main/kotlin/me/tatarka/inject/compiler/ksp/KSAst.kt
@@ -1,20 +1,59 @@
 package me.tatarka.inject.compiler.ksp
 
-import com.google.devtools.ksp.*
+import com.google.devtools.ksp.findActualType
+import com.google.devtools.ksp.getConstructors
+import com.google.devtools.ksp.getDeclaredFunctions
+import com.google.devtools.ksp.getDeclaredProperties
+import com.google.devtools.ksp.getVisibility
+import com.google.devtools.ksp.isAbstract
+import com.google.devtools.ksp.isPrivate
+import com.google.devtools.ksp.processing.CodeGenerator
+import com.google.devtools.ksp.processing.Dependencies
 import com.google.devtools.ksp.processing.KSPLogger
 import com.google.devtools.ksp.processing.Resolver
-import com.google.devtools.ksp.symbol.*
+import com.google.devtools.ksp.symbol.AnnotationUseSiteTarget
+import com.google.devtools.ksp.symbol.ClassKind
+import com.google.devtools.ksp.symbol.FileLocation
+import com.google.devtools.ksp.symbol.KSAnnotated
+import com.google.devtools.ksp.symbol.KSAnnotation
+import com.google.devtools.ksp.symbol.KSClassDeclaration
+import com.google.devtools.ksp.symbol.KSDeclaration
+import com.google.devtools.ksp.symbol.KSFunctionDeclaration
+import com.google.devtools.ksp.symbol.KSPropertyDeclaration
+import com.google.devtools.ksp.symbol.KSType
+import com.google.devtools.ksp.symbol.KSTypeAlias
+import com.google.devtools.ksp.symbol.KSValueParameter
+import com.google.devtools.ksp.symbol.Modifier
+import com.google.devtools.ksp.symbol.Variance
+import com.google.devtools.ksp.symbol.Visibility
 import com.squareup.kotlinpoet.ClassName
+import com.squareup.kotlinpoet.FileSpec
 import com.squareup.kotlinpoet.MemberName
 import com.squareup.kotlinpoet.ParameterSpec
 import com.squareup.kotlinpoet.TypeName
 import com.squareup.kotlinpoet.TypeSpec
-import me.tatarka.inject.compiler.*
+import me.tatarka.inject.compiler.AstAnnotated
+import me.tatarka.inject.compiler.AstAnnotation
+import me.tatarka.inject.compiler.AstBasicElement
+import me.tatarka.inject.compiler.AstClass
+import me.tatarka.inject.compiler.AstConstructor
+import me.tatarka.inject.compiler.AstElement
+import me.tatarka.inject.compiler.AstFileSpec
+import me.tatarka.inject.compiler.AstFunction
+import me.tatarka.inject.compiler.AstMethod
+import me.tatarka.inject.compiler.AstParam
+import me.tatarka.inject.compiler.AstProperty
+import me.tatarka.inject.compiler.AstProvider
+import me.tatarka.inject.compiler.AstType
+import me.tatarka.inject.compiler.AstTypeSpec
+import me.tatarka.inject.compiler.AstVisibility
+import me.tatarka.inject.compiler.Messenger
+import me.tatarka.inject.compiler.OutputProvider
 import org.jetbrains.kotlin.analyzer.AnalysisResult
-import java.nio.file.attribute.AttributeView
+
 import kotlin.reflect.KClass
 
-interface KSAstProvider : AstProvider {
+interface KSAstProvider : AstProvider, OutputProvider<CodeGenerator> {
 
     val resolver: Resolver
 
@@ -64,7 +103,15 @@ interface KSAstProvider : AstProvider {
         }
     }
 
-    override fun TypeSpec.Builder.addOriginatingElement(astClass: AstClass): TypeSpec.Builder = this
+    override fun astTypeSpec(typeSpecBuilder: TypeSpec.Builder, originatingElement: AstClass): AstTypeSpec {
+        require(originatingElement is KSAstClass)
+        return KSAstTypeSpec(typeSpecBuilder, originatingElement)
+    }
+
+    override fun astFileSpec(fileSpecBuilder: FileSpec.Builder, astTypeSpec: AstTypeSpec): AstFileSpec<CodeGenerator> {
+        require(astTypeSpec is KSAstTypeSpec)
+        return KSAstFileSpec(fileSpecBuilder, astTypeSpec)
+    }
 }
 
 object KSAstMessenger : Messenger {
@@ -493,4 +540,23 @@ private fun Visibility.astVisibility(): AstVisibility =
         Visibility.PROTECTED -> AstVisibility.PROTECTED
         Visibility.INTERNAL -> AstVisibility.INTERNAL
         else -> throw UnsupportedOperationException("unsupported visibility: $this")
-    }
\ No newline at end of file
+    }
+
+private class KSAstTypeSpec(typeSpecBuilder: TypeSpec.Builder, val astClass: KSAstClass) : AstTypeSpec {
+    override val typeSpec: TypeSpec = typeSpecBuilder.build()
+}
+
+private class KSAstFileSpec(fileSpecBuilder: FileSpec.Builder, val typeSpec: KSAstTypeSpec) :
+    AstFileSpec<CodeGenerator> {
+    private val fileSpec: FileSpec = fileSpecBuilder
+        .addType(typeSpec.typeSpec)
+        .build()
+
+    override fun writeTo(output: CodeGenerator) {
+        output.createNewFile(
+            Dependencies(true, typeSpec.astClass.declaration.containingFile!!),
+            fileSpec.packageName,
+            fileSpec.name
+        ).bufferedWriter().use { fileSpec.writeTo(it) }
+    }
+}
\ No newline at end of file
diff --git a/settings.gradle b/settings.gradle
index 3ad3c2d..3ba513a 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -1,16 +1,7 @@
 pluginManagement {
     repositories {
-        google()
-        mavenCentral()
         gradlePluginPortal()
-    }
-    resolutionStrategy {
-        eachPlugin {
-            switch (requested.id.id) {
-                case "symbol-processing":
-                    useModule("com.google.devtools.ksp:symbol-processing:${requested.version}")
-            }
-        }
+        google()
     }
 }
 


Test patch:
diff --git a/compiler-tests/build.gradle b/compiler-tests/build.gradle
index b68316b..a726ad9 100644
--- a/compiler-tests/build.gradle
+++ b/compiler-tests/build.gradle
@@ -9,7 +9,7 @@ dependencies {
     implementation project(':kotlin-inject-compiler-kapt')
     implementation project(':kotlin-inject-compiler-ksp')
 
-    implementation 'com.github.tschuchortdev:kotlin-compile-testing-ksp:1.3.1'
+    implementation 'com.github.tschuchortdev:kotlin-compile-testing-ksp:1.3.4'
     implementation "org.jetbrains.kotlin:kotlin-compiler-embeddable:$kotlin_version"
     implementation "org.jetbrains.kotlin:kotlin-annotation-processing-embeddable:$kotlin_version"
     implementation "com.google.devtools.ksp:symbol-processing:$ksp_version"
diff --git a/integration-tests/common/src/test/kotlin/me/tatarka/inject/test/VisibilityTest.kt b/integration-tests/common/src/test/kotlin/me/tatarka/inject/test/VisibilityTest.kt
index 5fb8a05..b5d3bac 100644
--- a/integration-tests/common/src/test/kotlin/me/tatarka/inject/test/VisibilityTest.kt
+++ b/integration-tests/common/src/test/kotlin/me/tatarka/inject/test/VisibilityTest.kt
@@ -14,5 +14,4 @@ class VisibilityTest {
     fun internal_component_generates_internal_create() {
         assertThat(InternalVisibilityTestComponent::class.create()::class.visibility).isEqualTo(KVisibility.INTERNAL)
     }
-
 }
\ No newline at end of file
diff --git a/integration-tests/ksp-companion/build.gradle b/integration-tests/ksp-companion/build.gradle
index c8c7a2a..16dcce0 100644
--- a/integration-tests/ksp-companion/build.gradle
+++ b/integration-tests/ksp-companion/build.gradle
@@ -1,6 +1,6 @@
 plugins {
     id 'org.jetbrains.kotlin.jvm'
-    id 'symbol-processing'
+    id 'com.google.devtools.ksp'
 }
 
 dependencies {
diff --git a/integration-tests/ksp/build.gradle b/integration-tests/ksp/build.gradle
index 7b5131b..7e19fdd 100644
--- a/integration-tests/ksp/build.gradle
+++ b/integration-tests/ksp/build.gradle
@@ -1,6 +1,6 @@
 plugins {
     id 'org.jetbrains.kotlin.jvm'
-    id 'symbol-processing'
+    id 'com.google.devtools.ksp'
 }
 
 dependencies {