Kapt: Do not write references to anonymous classes to stubs & metadata (#KT-25374)

This commit is contained in:
Yan Zhulanow
2018-07-13 22:34:58 +03:00
parent 43f5971863
commit ef210d7122
14 changed files with 277 additions and 188 deletions
@@ -0,0 +1,13 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.resolve
import org.jetbrains.kotlin.descriptors.DeclarationDescriptorWithVisibility
import org.jetbrains.kotlin.types.KotlinType
interface DeclarationSignatureAnonymousTypeTransformer {
fun transformAnonymousType(descriptor: DeclarationDescriptorWithVisibility, type: KotlinType): KotlinType?
}
@@ -88,6 +88,7 @@ public class DescriptorResolver {
private final TypeApproximator typeApproximator;
private final DeclarationReturnTypeSanitizer declarationReturnTypeSanitizer;
private final DataFlowValueFactory dataFlowValueFactory;
private final Iterable<DeclarationSignatureAnonymousTypeTransformer> anonymousTypeTransformers;
public DescriptorResolver(
@NotNull AnnotationResolver annotationResolver,
@@ -106,7 +107,8 @@ public class DescriptorResolver {
@NotNull Project project,
@NotNull TypeApproximator approximator,
@NotNull DeclarationReturnTypeSanitizer declarationReturnTypeSanitizer,
@NotNull DataFlowValueFactory dataFlowValueFactory
@NotNull DataFlowValueFactory dataFlowValueFactory,
@NotNull Iterable<DeclarationSignatureAnonymousTypeTransformer> anonymousTypeTransformers
) {
this.annotationResolver = annotationResolver;
this.builtIns = builtIns;
@@ -125,6 +127,7 @@ public class DescriptorResolver {
typeApproximator = approximator;
this.declarationReturnTypeSanitizer = declarationReturnTypeSanitizer;
this.dataFlowValueFactory = dataFlowValueFactory;
this.anonymousTypeTransformers = anonymousTypeTransformers;
}
public List<KotlinType> resolveSupertypes(
@@ -977,8 +980,16 @@ public class DescriptorResolver {
@NotNull DeclarationDescriptorWithVisibility descriptor,
@NotNull KtDeclaration declaration,
@NotNull KotlinType type,
@NotNull BindingTrace trace
@NotNull BindingTrace trace,
@NotNull Iterable<DeclarationSignatureAnonymousTypeTransformer> anonymousTypeTransformers
) {
for (DeclarationSignatureAnonymousTypeTransformer transformer : anonymousTypeTransformers) {
KotlinType transformedType = transformer.transformAnonymousType(descriptor, type);
if (transformedType != null) {
return transformedType;
}
}
ClassifierDescriptor classifier = type.getConstructor().getDeclarationDescriptor();
if (classifier == null || !DescriptorUtils.isAnonymousObject(classifier) || DescriptorUtils.isLocal(descriptor)) {
return type;
@@ -996,7 +1007,6 @@ public class DescriptorResolver {
return type;
}
@Nullable
private PropertySetterDescriptor resolvePropertySetterDescriptor(
@NotNull LexicalScope scopeWithTypeParameters,
@@ -1159,7 +1169,7 @@ public class DescriptorResolver {
return wrappedTypeFactory.createRecursionIntolerantDeferredType(trace, () -> {
PreliminaryDeclarationVisitor.Companion.createForDeclaration(function, trace, languageVersionSettings);
KotlinType type = expressionTypingServices.getBodyExpressionType(trace, scope, dataFlowInfo, function, functionDescriptor);
KotlinType publicType = transformAnonymousTypeIfNeeded(functionDescriptor, function, type, trace);
KotlinType publicType = transformAnonymousTypeIfNeeded(functionDescriptor, function, type, trace, anonymousTypeTransformers);
UnwrappedType approximatedType = typeApproximator.approximateDeclarationType(publicType, false, languageVersionSettings);
KotlinType sanitizedType = declarationReturnTypeSanitizer.sanitizeReturnType(approximatedType, wrappedTypeFactory, trace, languageVersionSettings);
functionsTypingVisitor.checkTypesForReturnStatements(function, trace, sanitizedType);
@@ -31,7 +31,8 @@ class VariableTypeAndInitializerResolver(
private val wrappedTypeFactory: WrappedTypeFactory,
private val typeApproximator: TypeApproximator,
private val declarationReturnTypeSanitizer: DeclarationReturnTypeSanitizer,
private val languageVersionSettings: LanguageVersionSettings
private val languageVersionSettings: LanguageVersionSettings,
private val anonymousTypeTransformers: Iterable<DeclarationSignatureAnonymousTypeTransformer>
) {
companion object {
@JvmField
@@ -82,7 +83,7 @@ class VariableTypeAndInitializerResolver(
)
val initializerType =
resolveInitializerType(scopeForInitializer, variable.initializer!!, dataFlowInfo, trace, local)
transformAnonymousTypeIfNeeded(variableDescriptor, variable, initializerType, trace)
transformAnonymousTypeIfNeeded(variableDescriptor, variable, initializerType, trace, anonymousTypeTransformers)
}
else -> resolveInitializerType(scopeForInitializer, variable.initializer!!, dataFlowInfo, trace, local)
@@ -1,6 +1,7 @@
package org.jetbrains.kotlin.gradle
import org.jetbrains.kotlin.gradle.util.isLegacyAndroidGradleVersion
import org.jetbrains.kotlin.gradle.util.getFileByName
import org.jetbrains.kotlin.gradle.util.modify
import org.jetbrains.kotlin.test.KotlinTestUtils
import org.junit.Test
@@ -126,29 +127,32 @@ open class Kapt3AndroidIT : Kapt3BaseIT() {
}
}
@Test
fun testICWithAnonymousClasses() {
val project = Project("icAnonymousTypes", directoryPrefix = "kapt2")
setupDataBinding(project, null)
project.build("assembleDebug") {
assertSuccessful()
assertKaptSuccessful()
}
val aKt = project.projectDir.getFileByName("a.kt").also { assert(it.exists()) }
aKt.modify {
assert(it.contains("CrashMe2(1000)"))
it.replace("CrashMe2(1000)", "CrashMe2(2000)")
}
project.build("assembleDebug") {
assertSuccessful()
assertKaptSuccessful()
}
}
@Test
open fun testDatabinding() {
val project = Project("android-databinding", directoryPrefix = "kapt2")
if (!isLegacyAndroidGradleVersion(androidGradlePluginVersion)) {
project.setupWorkingDir()
// With new AGP, there's no need in the Databinding kapt dependency:
project.gradleBuildScript("app").modify {
it.lines().filterNot {
it.contains("kapt \"com.android.databinding:compiler")
}.joinToString("\n")
}
// Workaround for KT-24915
project.gradleBuildScript("app").appendText(
"\n" + """
afterEvaluate {
kaptDebugKotlin.dependsOn dataBindingExportFeaturePackageIdsDebug
}
""".trimIndent()
)
}
setupDataBinding(project, "app")
project.build("assembleDebug") {
assertSuccessful()
@@ -165,4 +169,26 @@ open class Kapt3AndroidIT : Kapt3BaseIT() {
assertNotContains("The following options were not recognized by any processor")
}
}
private fun setupDataBinding(project: Project, projectName: String?) {
if (!isLegacyAndroidGradleVersion(androidGradlePluginVersion)) {
project.setupWorkingDir()
// With new AGP, there's no need in the Databinding kapt dependency:
project.gradleBuildScript(projectName).modify {
it.lines().filterNot {
it.contains("kapt \"com.android.databinding:compiler")
}.joinToString("\n")
}
// Workaround for KT-24915
project.gradleBuildScript(projectName).appendText(
"\n" + """
afterEvaluate {
kaptDebugKotlin.dependsOn dataBindingExportFeaturePackageIdsDebug
}
""".trimIndent()
)
}
}
}
@@ -0,0 +1,46 @@
buildscript {
repositories {
mavenLocal()
jcenter()
maven { url 'https://maven.google.com' }
}
dependencies {
classpath "com.android.tools.build:gradle:$android_tools_version"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-kapt'
android {
compileSdkVersion 27
buildToolsVersion "27.0.3"
defaultConfig {
applicationId "org.kotlinlang.test"
minSdkVersion 16
targetSdkVersion 27
versionCode 1
versionName "1.0"
}
dataBinding {
enabled true
}
}
repositories {
mavenLocal()
jcenter()
maven { url 'https://maven.google.com' }
}
dependencies {
compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
kapt "com.android.databinding:compiler:$android_tools_version"
}
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.databinding">
<application android:label="test">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
</application>
</manifest>
@@ -0,0 +1,7 @@
package test
class CrashMe {
private val crashMe = object : CrashMe2(1000) {
// empty
}
}
@@ -0,0 +1,8 @@
package test
abstract class CrashMe2(value: Long) {
val crashMe2 = object : Any() {
// empty
}
}
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<LinearLayout
android:layout_width="match_parent" android:layout_height="match_parent"
android:orientation="vertical"/>
</layout>
@@ -33,8 +33,11 @@ import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.config.CompilerConfigurationKey
import org.jetbrains.kotlin.config.JVMConfigurationKeys
import org.jetbrains.kotlin.container.ComponentProvider
import org.jetbrains.kotlin.container.StorageComponentContainer
import org.jetbrains.kotlin.container.useInstance
import org.jetbrains.kotlin.context.ProjectContext
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.extensions.StorageComponentContainerContributor
import org.jetbrains.kotlin.kapt3.Kapt3CommandLineProcessor.Companion.ANNOTATION_PROCESSORS_OPTION
import org.jetbrains.kotlin.kapt3.Kapt3CommandLineProcessor.Companion.ANNOTATION_PROCESSOR_CLASSPATH_OPTION
import org.jetbrains.kotlin.kapt3.Kapt3CommandLineProcessor.Companion.APT_MODE_OPTION
@@ -59,7 +62,9 @@ import org.jetbrains.kotlin.kapt3.base.log
import org.jetbrains.kotlin.kapt3.util.MessageCollectorBackedKaptLogger
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.resolve.BindingTrace
import org.jetbrains.kotlin.resolve.TargetPlatform
import org.jetbrains.kotlin.resolve.jvm.extensions.AnalysisHandlerExtension
import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatform
import org.jetbrains.kotlin.utils.decodePluginOptions
import java.io.ByteArrayInputStream
import java.io.File
@@ -314,6 +319,19 @@ class Kapt3ComponentRegistrar : ComponentRegistrar {
)
AnalysisHandlerExtension.registerExtension(project, kapt3AnalysisCompletedHandlerExtension)
StorageComponentContainerContributor.registerExtension(project, KaptComponentContributor())
}
class KaptComponentContributor : StorageComponentContainerContributor {
override fun registerModuleComponents(
container: StorageComponentContainer,
platform: TargetPlatform,
moduleDescriptor: ModuleDescriptor
) {
if (platform != JvmPlatform) return
container.useInstance(KaptAnonymousTypeTransformer())
}
}
/* This extension simply disables both code analysis and code generation.
@@ -340,6 +358,7 @@ class Kapt3ComponentRegistrar : ComponentRegistrar {
return AnalysisResult.Companion.success(bindingTrace.bindingContext, module, shouldGenerateCode = false)
}
}
}
enum class AptMode {
@@ -0,0 +1,73 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.kapt3
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptorWithVisibility
import org.jetbrains.kotlin.resolve.DeclarationSignatureAnonymousTypeTransformer
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.DescriptorUtils.isAnonymousObject
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassNotAny
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.TypeProjectionImpl
import org.jetbrains.kotlin.types.replace
import org.jetbrains.kotlin.types.typeUtil.builtIns
import org.jetbrains.kotlin.types.typeUtil.isAnyOrNullableAny
class KaptAnonymousTypeTransformer : DeclarationSignatureAnonymousTypeTransformer {
override fun transformAnonymousType(descriptor: DeclarationDescriptorWithVisibility, type: KotlinType): KotlinType? {
if (DescriptorUtils.isLocal(descriptor))
return type
return convertPossiblyAnonymousType(type)
}
private fun convertPossiblyAnonymousType(type: KotlinType): KotlinType {
val declaration = type.constructor.declarationDescriptor as? ClassDescriptor ?: return type
if (KotlinBuiltIns.isArray(type)) {
val elementTypeProjection = type.arguments.singleOrNull()
if (elementTypeProjection != null && !elementTypeProjection.isStarProjection) {
return type.builtIns.getArrayType(
elementTypeProjection.projectionKind,
convertPossiblyAnonymousType(elementTypeProjection.type)
)
}
}
val actualType = when {
isAnonymousObject(declaration) -> findMostSuitableParentForAnonymousType(declaration)
else -> type
}
if (actualType.arguments.isEmpty()) return actualType
val arguments = actualType.arguments.map { typeArg ->
if (typeArg.isStarProjection)
return@map typeArg
TypeProjectionImpl(typeArg.projectionKind, convertPossiblyAnonymousType(typeArg.type))
}
return actualType.replace(newArguments = arguments)
}
private fun findMostSuitableParentForAnonymousType(descriptor: ClassDescriptor): KotlinType {
descriptor.getSuperClassNotAny()?.let { return it.defaultType }
val sortedSuperTypes = descriptor.typeConstructor.supertypes
.sortedBy { it.constructor.declarationDescriptor?.name?.asString() ?: "" }
for (candidate in sortedSuperTypes) {
if (!candidate.isAnyOrNullableAny())
return candidate
}
return descriptor.builtIns.anyType
}
}
@@ -1,137 +0,0 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* 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 org.jetbrains.kotlin.kapt3.stubs
import com.sun.tools.javac.code.BoundKind
import com.sun.tools.javac.tree.JCTree
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.kapt3.base.mapJList
import org.jetbrains.kotlin.load.kotlin.TypeMappingMode
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassNotAny
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.TypeProjectionImpl
import org.jetbrains.kotlin.types.typeUtil.isAnyOrNullableAny
import org.jetbrains.kotlin.resolve.DescriptorUtils.isAnonymousObject
import org.jetbrains.kotlin.types.Variance
import org.jetbrains.kotlin.types.replace
import org.jetbrains.kotlin.types.typeUtil.builtIns
class AnonymousTypeHandler(private val converter: ClassFileToSourceStubConverter) {
fun <T : JCTree.JCExpression?> getNonAnonymousType(descriptor: DeclarationDescriptor?, f: () -> T): T {
val classType = when (descriptor) {
is ClassDescriptor -> descriptor.defaultType
is CallableDescriptor -> descriptor.returnType
else -> null
} ?: return f()
return getNonAnonymousType(classType, f)
}
fun <T : JCTree.JCExpression?> getNonAnonymousType(type: KotlinType, f: () -> T): T {
if (!checkIfAnonymousRecursively(type)) return f()
@Suppress("UNCHECKED_CAST")
return convertKotlinType(convertPossiblyAnonymousType(type)) as T
}
private fun checkIfAnonymousRecursively(type: KotlinType): Boolean {
val declaration = type.constructor.declarationDescriptor as? ClassDescriptor ?: return false
if (isAnonymousObject(declaration)) return true
return type.arguments.any {
if (it.isStarProjection) return@any false
checkIfAnonymousRecursively(it.type)
}
}
private fun convertPossiblyAnonymousType(type: KotlinType): KotlinType {
val declaration = type.constructor.declarationDescriptor as? ClassDescriptor ?: return type
if (KotlinBuiltIns.isArray(type)) {
val elementTypeProjection = type.arguments.singleOrNull()
if (elementTypeProjection != null && !elementTypeProjection.isStarProjection) {
return type.builtIns.getArrayType(
elementTypeProjection.projectionKind,
convertPossiblyAnonymousType(elementTypeProjection.type)
)
}
}
val actualType = when {
isAnonymousObject(declaration) -> findMostSuitableParentForAnonymousType(declaration)
else -> type
}
if (actualType.arguments.isEmpty()) return actualType
val arguments = actualType.arguments.map { typeArg ->
if (typeArg.isStarProjection) return@map typeArg
TypeProjectionImpl(typeArg.projectionKind, convertPossiblyAnonymousType(typeArg.type))
}
return actualType.replace(arguments)
}
private fun findMostSuitableParentForAnonymousType(descriptor: ClassDescriptor): KotlinType {
descriptor.getSuperClassNotAny()?.let { return it.defaultType }
val sortedSuperTypes = descriptor.typeConstructor.supertypes
.sortedBy { it.constructor.declarationDescriptor?.name?.asString() ?: "" }
for (candidate in sortedSuperTypes) {
if (!candidate.isAnyOrNullableAny()) return candidate
}
return descriptor.builtIns.anyType
}
private fun convertKotlinType(type: KotlinType): JCTree.JCExpression {
val treeMaker = converter.treeMaker
if (KotlinBuiltIns.isArray(type)) {
val elementTypeProjection = type.arguments.single()
val elementType = if (elementTypeProjection.isStarProjection || elementTypeProjection.projectionKind == Variance.IN_VARIANCE) {
type.builtIns.anyType
} else {
elementTypeProjection.type
}
return treeMaker.TypeArray(convertKotlinType(elementType))
}
val typeMapper = converter.kaptContext.generationState.typeMapper
// Primitive types can't be anonymous, so if we get here, we want to map a class type or its generic argument
val selfType = treeMaker.Type(typeMapper.mapType(type, null, TypeMappingMode.GENERIC_ARGUMENT))
if (type.arguments.isEmpty()) return selfType
return treeMaker.TypeApply(selfType, mapJList(type.arguments) { projection ->
if (projection.isStarProjection) return@mapJList treeMaker.Wildcard(treeMaker.TypeBoundKind(BoundKind.UNBOUND), null)
val renderedArg = convertKotlinType(projection.type)
when (projection.projectionKind) {
Variance.INVARIANT -> renderedArg
Variance.OUT_VARIANCE -> treeMaker.Wildcard(treeMaker.TypeBoundKind(BoundKind.EXTENDS), renderedArg)
Variance.IN_VARIANCE -> treeMaker.Wildcard(treeMaker.TypeBoundKind(BoundKind.SUPER), renderedArg)
}
})
}
}
@@ -107,8 +107,6 @@ class ClassFileToSourceStubConverter(
private val signatureParser = SignatureParser(treeMaker)
private val anonymousTypeHandler = AnonymousTypeHandler(this)
private val kdocCommentKeeper = KDocCommentKeeper(kaptContext)
private var done = false
@@ -479,16 +477,16 @@ class ClassFileToSourceStubConverter(
val typeExpression = if (isEnum(field.access))
treeMaker.SimpleName(treeMaker.getQualifiedName(type).substringAfterLast('.'))
else
anonymousTypeHandler.getNonAnonymousType(descriptor) {
getNonErrorType((descriptor as? CallableDescriptor)?.returnType, RETURN_TYPE,
ktTypeProvider = {
val fieldOrigin = (kaptContext.origins[field]?.element as? KtCallableDeclaration)
?.takeIf { it !is KtFunction }
getNonErrorType(
(descriptor as? CallableDescriptor)?.returnType, RETURN_TYPE,
ktTypeProvider = {
val fieldOrigin = (kaptContext.origins[field]?.element as? KtCallableDeclaration)
?.takeIf { it !is KtFunction }
fieldOrigin?.typeReference
},
ifNonError = { signatureParser.parseFieldSignature(field.signature, treeMaker.Type(type)) })
}
fieldOrigin?.typeReference
},
ifNonError = { signatureParser.parseFieldSignature(field.signature, treeMaker.Type(type)) }
)
val value = field.value
@@ -646,19 +644,19 @@ class ClassFileToSourceStubConverter(
}
})
val returnType = anonymousTypeHandler.getNonAnonymousType(descriptor) {
getNonErrorType(descriptor.returnType, RETURN_TYPE,
ktTypeProvider = {
val element = kaptContext.origins[method]?.element
when (element) {
is KtFunction -> element.typeReference
is KtProperty -> if (descriptor is PropertyGetterDescriptor) element.typeReference else null
is KtParameter -> if (descriptor is PropertyGetterDescriptor) element.typeReference else null
else -> null
}
},
ifNonError = { genericSignature.returnType })
}
val returnType = getNonErrorType(
descriptor.returnType, RETURN_TYPE,
ktTypeProvider = {
val element = kaptContext.origins[method]?.element
when (element) {
is KtFunction -> element.typeReference
is KtProperty -> if (descriptor is PropertyGetterDescriptor) element.typeReference else null
is KtParameter -> if (descriptor is PropertyGetterDescriptor) element.typeReference else null
else -> null
}
},
ifNonError = { genericSignature.returnType }
)
return Pair(genericSignature, returnType)
}
@@ -30,7 +30,9 @@ import org.jetbrains.kotlin.cli.common.messages.PrintingMessageCollector
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import org.jetbrains.kotlin.cli.jvm.config.JvmClasspathRoot
import org.jetbrains.kotlin.codegen.*
import org.jetbrains.kotlin.extensions.StorageComponentContainerContributor
import org.jetbrains.kotlin.kapt3.*
import org.jetbrains.kotlin.kapt3.Kapt3ComponentRegistrar.KaptComponentContributor
import org.jetbrains.kotlin.kapt3.base.KaptContext
import org.jetbrains.kotlin.kapt3.base.KaptPaths
import org.jetbrains.kotlin.kapt3.base.doAnnotationProcessing
@@ -105,7 +107,9 @@ abstract class AbstractKotlinKapt3Test : CodegenTestCase() {
addAnnotationProcessingRuntimeLibrary(myEnvironment)
// Use light analysis mode in tests
AnalysisHandlerExtension.registerExtension(myEnvironment.project, PartialAnalysisHandlerExtension())
val project = myEnvironment.project
AnalysisHandlerExtension.registerExtension(project, PartialAnalysisHandlerExtension())
StorageComponentContainerContributor.registerExtension(project, KaptComponentContributor())
loadMultiFiles(files)