From 69e6d960c6504bc8b5df77cc030dc498e1d20ebc Mon Sep 17 00:00:00 2001 From: Kirill Bochkarev Date: Wed, 17 Dec 2025 17:57:32 +0100 Subject: [PATCH] IJPL-15941 Workspace Model: codegen 7.11 GitOrigin-RevId: 69c54c2a6aa712f848bd0e7ace674cc47ba91e92 --- .../workspace/codegen-impl/gradle.properties | 2 +- .../codegen/impl/engine/CodeGeneratorImpl.kt | 50 +++++++-------- .../codegen/impl/writer/LinesBuilder.kt | 2 +- .../codegen/impl/writer/apiCode.kt | 11 ++-- .../writer/classes/implWsEntityClassCode.kt | 62 ++++++++++++++++--- .../codegen/impl/writer/moduleChecker.kt | 2 +- .../codegen/impl/writer/wsEntityGenCode.kt | 5 +- 7 files changed, 91 insertions(+), 43 deletions(-) diff --git a/platform/workspace/codegen-impl/gradle.properties b/platform/workspace/codegen-impl/gradle.properties index 733c19f5058a..e7b06b5f1860 100644 --- a/platform/workspace/codegen-impl/gradle.properties +++ b/platform/workspace/codegen-impl/gradle.properties @@ -1,3 +1,3 @@ -codegenImplMinorVersion = 10 +codegenImplMinorVersion = 11 codegenImplMajorVersion = 7 codegenApiVersion = 3 \ No newline at end of file diff --git a/platform/workspace/codegen-impl/src/main/kotlin/com/intellij/workspaceModel/codegen/impl/engine/CodeGeneratorImpl.kt b/platform/workspace/codegen-impl/src/main/kotlin/com/intellij/workspaceModel/codegen/impl/engine/CodeGeneratorImpl.kt index b603e44d29cd..dae0b3bdad27 100644 --- a/platform/workspace/codegen-impl/src/main/kotlin/com/intellij/workspaceModel/codegen/impl/engine/CodeGeneratorImpl.kt +++ b/platform/workspace/codegen-impl/src/main/kotlin/com/intellij/workspaceModel/codegen/impl/engine/CodeGeneratorImpl.kt @@ -21,23 +21,25 @@ class CodeGeneratorImpl : CodeGenerator { return failedGenerationResult(reporter) } - val objClassToTopLevelCode = module.types.associateWith { - val topLevelCode = it.generateTopLevelCode(reporter) - topLevelCode - } - - if (reporter.hasErrors()) { - return failedGenerationResult(reporter) - } - - - val generatedCode = objClassToTopLevelCode.map { (objClass, topLevelCode) -> - ObjClassGeneratedCode( - target = objClass, - builderInterface = objClass.generateCompatabilityBuilder(), - companionObject = objClass.generateCompatibilityCompanion(), - topLevelCode = topLevelCode, - implementationClass = objClass.implWsCode() + val generatedCode: MutableList = arrayListOf() + for (type in module.types) { + checkSuperTypes(type, reporter) + checkSymbolicId(type, reporter) + if (reporter.hasErrors()) return failedGenerationResult(reporter) + val topLevelCode = type.generateTopLevelCode(reporter) + if (reporter.hasErrors()) return failedGenerationResult(reporter) + val compatibilityBuilder = type.generateCompatabilityBuilder() + val compatibilityCompanion = type.generateCompatibilityCompanion() + val implementationClass = type.implWsCode(reporter) + if (reporter.hasErrors()) return failedGenerationResult(reporter) + generatedCode.add( + ObjClassGeneratedCode( + target = type, + builderInterface = compatibilityBuilder, + companionObject = compatibilityCompanion, + topLevelCode = topLevelCode, + implementationClass = implementationClass + ) ) } @@ -62,17 +64,15 @@ class CodeGeneratorImpl : CodeGenerator { val generatedCode = arrayListOf() - addMetadataStorageCode( - generatedCode, metadataStorageImplModule, - implWsMetadataStorageCode(metadataStorageImplModule, notEmptyModules.flatMap { it.types }, notEmptyModules.flatMap { it.abstractTypes }) - ) + addMetadataStorageCode(generatedCode, + metadataStorageImplModule, + implWsMetadataStorageCode(metadataStorageImplModule, + notEmptyModules.flatMap { it.types }, + notEmptyModules.flatMap { it.abstractTypes })) val metadataStorageImplFqn = fqn(metadataStorageImplModule.implPackage, MetadataStorage.IMPL_NAME) metadataStorageBridgeModules.forEach { - addMetadataStorageCode( - generatedCode, it, - it.implWsMetadataStorageBridgeCode(metadataStorageImplFqn) - ) + addMetadataStorageCode(generatedCode, it, it.implWsMetadataStorageBridgeCode(metadataStorageImplFqn)) } return GenerationResult(generatedCode, emptyList()) diff --git a/platform/workspace/codegen-impl/src/main/kotlin/com/intellij/workspaceModel/codegen/impl/writer/LinesBuilder.kt b/platform/workspace/codegen-impl/src/main/kotlin/com/intellij/workspaceModel/codegen/impl/writer/LinesBuilder.kt index 7e5a56fcde1e..1d6e8a3c98ab 100644 --- a/platform/workspace/codegen-impl/src/main/kotlin/com/intellij/workspaceModel/codegen/impl/writer/LinesBuilder.kt +++ b/platform/workspace/codegen-impl/src/main/kotlin/com/intellij/workspaceModel/codegen/impl/writer/LinesBuilder.kt @@ -22,7 +22,7 @@ class LinesBuilder(val result: StringBuilder) { } } - fun section(head: String, s: LinesBuilder.() -> Unit) { + inline fun section(head: String, s: LinesBuilder.() -> Unit) { lineNoNl(head) val sub = LinesBuilder(result) sub.result.append("{\n") diff --git a/platform/workspace/codegen-impl/src/main/kotlin/com/intellij/workspaceModel/codegen/impl/writer/apiCode.kt b/platform/workspace/codegen-impl/src/main/kotlin/com/intellij/workspaceModel/codegen/impl/writer/apiCode.kt index d3d67834ec1d..dcbe0fe8f82e 100644 --- a/platform/workspace/codegen-impl/src/main/kotlin/com/intellij/workspaceModel/codegen/impl/writer/apiCode.kt +++ b/platform/workspace/codegen-impl/src/main/kotlin/com/intellij/workspaceModel/codegen/impl/writer/apiCode.kt @@ -10,8 +10,6 @@ import com.intellij.workspaceModel.codegen.impl.writer.extensions.* import com.intellij.workspaceModel.codegen.impl.writer.fields.* fun ObjClass<*>.generateMutableCode(reporter: ProblemReporter): String = lines { - checkSuperTypes(this@generateMutableCode, reporter) - checkSymbolicId(this@generateMutableCode, reporter) if (additionalAnnotations.isNotEmpty()) { line(additionalAnnotations) } @@ -23,9 +21,10 @@ fun ObjClass<*>.generateMutableCode(reporter: ProblemReporter): String = lines { val header = "${generatedCodeVisibilityModifier}interface $defaultJavaBuilderName$typeDeclaration: ${WorkspaceEntity.Builder}<$typeParameter>$superBuilders" section(header) { - list(allFields.noSymbolicId()) { - checkProperty(this, reporter) - getWsBuilderApi(this@generateMutableCode) + for (field in allFields.noSymbolicId()) { + checkProperty(field, reporter) + if (reporter.hasErrors()) return@generateMutableCode "" + line(field.getWsBuilderApi(this@generateMutableCode)) } } } @@ -43,7 +42,7 @@ fun checkSuperTypes(objClass: ObjClass<*>, reporter: ProblemReporter) { } } -private fun checkSymbolicId(objClass: ObjClass<*>, reporter: ProblemReporter) { +fun checkSymbolicId(objClass: ObjClass<*>, reporter: ProblemReporter) { if (!objClass.isEntityWithSymbolicId) return if (objClass.openness == ObjClass.Openness.abstract) return if (objClass.fields.none { it.name == "symbolicId" }) { diff --git a/platform/workspace/codegen-impl/src/main/kotlin/com/intellij/workspaceModel/codegen/impl/writer/classes/implWsEntityClassCode.kt b/platform/workspace/codegen-impl/src/main/kotlin/com/intellij/workspaceModel/codegen/impl/writer/classes/implWsEntityClassCode.kt index cb00c7f0dcfe..ba9dfadeac06 100644 --- a/platform/workspace/codegen-impl/src/main/kotlin/com/intellij/workspaceModel/codegen/impl/writer/classes/implWsEntityClassCode.kt +++ b/platform/workspace/codegen-impl/src/main/kotlin/com/intellij/workspaceModel/codegen/impl/writer/classes/implWsEntityClassCode.kt @@ -2,15 +2,18 @@ package com.intellij.workspaceModel.codegen.impl.writer.classes import com.intellij.workspaceModel.codegen.deft.meta.ObjClass -import com.intellij.workspaceModel.codegen.impl.writer.* -import com.intellij.workspaceModel.codegen.impl.writer.fields.implWsEntityFieldCode -import com.intellij.workspaceModel.codegen.impl.writer.fields.refsConnectionId -import com.intellij.workspaceModel.codegen.impl.writer.fields.refsConnectionIdCode +import com.intellij.workspaceModel.codegen.engine.GenerationProblem +import com.intellij.workspaceModel.codegen.engine.ProblemLocation import com.intellij.workspaceModel.codegen.impl.CodeGeneratorVersionCalculator +import com.intellij.workspaceModel.codegen.impl.engine.ProblemReporter +import com.intellij.workspaceModel.codegen.impl.writer.* import com.intellij.workspaceModel.codegen.impl.writer.extensions.* -import com.intellij.workspaceModel.codegen.impl.writer.fields.javaType +import com.intellij.workspaceModel.codegen.impl.writer.fields.* + +fun ObjClass<*>.implWsEntityCode(reporter: ProblemReporter): String { + checkReferences(this@implWsEntityCode, reporter) + if (reporter.hasErrors()) return "" -fun ObjClass<*>.implWsEntityCode(): String { val inheritanceModifier = when { openness.extendable && !openness.instantiatable -> "abstract " openness.extendable && openness.instantiatable -> "open " @@ -60,6 +63,51 @@ private val ObjClass<*>.implWsEntityAnnotations: String private fun getLinksOfConnectionIds(type: ObjClass<*>): String { return lines { - line(type.allRefsFields.joinToString(separator = ",", prefix = "private val connections = listOf<$ConnectionId>(", postfix = ")") { it.refsConnectionId }) + line(type.allRefsFields.joinToString(separator = ",", + prefix = "private val connections = listOf<$ConnectionId>(", + postfix = ")") { it.refsConnectionId }) + } +} + +private fun checkReferences(objClass: ObjClass<*>, reporter: ProblemReporter) { + for (refField in objClass.allRefsFields) { + val ref = refField.valueType.getRefType() + val declaredReferenceFromChild = + ref.target.refsFields.filter { it.valueType.getRefType().target == objClass && it != refField } + setOf(ref.target.module, + objClass.module).flatMap { it.extensions } + .filter { it.valueType.getRefType().target == objClass && it.receiver == ref.target && it != refField } + if (declaredReferenceFromChild.isEmpty()) { + reporter.reportProblem( + GenerationProblem("Reference should be declared at both entities. It exist at ${objClass.name}#${refField.name}, but is absent from ${ref.target.name}", + GenerationProblem.Level.ERROR, + ProblemLocation.Property(refField)) + ) + return@checkReferences + } + if (declaredReferenceFromChild.size > 1) { + reporter.reportProblem( + GenerationProblem(""" + |More then one reference to ${objClass.name} declared at ${declaredReferenceFromChild[0].receiver.name}#${declaredReferenceFromChild[0].name}, + |${declaredReferenceFromChild[1].receiver.name}#${declaredReferenceFromChild[1].name} + |""".trimMargin(), + GenerationProblem.Level.ERROR, + ProblemLocation.Property(declaredReferenceFromChild[0])) + ) + return@checkReferences + } + val referencedField = declaredReferenceFromChild[0] + if (ref.child == referencedField.valueType.getRefType().child) { + val (childStr, fix) = if (ref.child) { + "child" to "Probably @Parent annotation is missing from one of the properties." + } + else { + "parent" to "Probably both properties are annotated with @Parent, while only one should be." + } + reporter.reportProblem( + GenerationProblem("Both fields ${objClass.name}#${refField.name} and ${ref.target.name}#${referencedField.name} are marked as $childStr. $fix", + GenerationProblem.Level.ERROR, + ProblemLocation.Property(refField)) + ) + } } } diff --git a/platform/workspace/codegen-impl/src/main/kotlin/com/intellij/workspaceModel/codegen/impl/writer/moduleChecker.kt b/platform/workspace/codegen-impl/src/main/kotlin/com/intellij/workspaceModel/codegen/impl/writer/moduleChecker.kt index 24ece03a44e1..44bc4d04ea98 100644 --- a/platform/workspace/codegen-impl/src/main/kotlin/com/intellij/workspaceModel/codegen/impl/writer/moduleChecker.kt +++ b/platform/workspace/codegen-impl/src/main/kotlin/com/intellij/workspaceModel/codegen/impl/writer/moduleChecker.kt @@ -10,7 +10,7 @@ import com.intellij.workspaceModel.codegen.impl.writer.extensions.isRefType internal fun checkExtensionFields(module: CompiledObjModule, reporter: ProblemReporter) { module.extensions.forEach { extProperty -> - if (!extProperty.valueType.isRefType() && !extProperty.isComputable) { + if (!extProperty.valueType.isRefType()) { reporter.reportProblem(GenerationProblem("Extension property is supposed to be a reference to another entity only.", GenerationProblem.Level.ERROR, ProblemLocation.Property(extProperty))) } diff --git a/platform/workspace/codegen-impl/src/main/kotlin/com/intellij/workspaceModel/codegen/impl/writer/wsEntityGenCode.kt b/platform/workspace/codegen-impl/src/main/kotlin/com/intellij/workspaceModel/codegen/impl/writer/wsEntityGenCode.kt index 9582edc41a8a..fe9aaf464958 100644 --- a/platform/workspace/codegen-impl/src/main/kotlin/com/intellij/workspaceModel/codegen/impl/writer/wsEntityGenCode.kt +++ b/platform/workspace/codegen-impl/src/main/kotlin/com/intellij/workspaceModel/codegen/impl/writer/wsEntityGenCode.kt @@ -4,6 +4,7 @@ package com.intellij.workspaceModel.codegen.impl.writer import com.intellij.workspaceModel.codegen.impl.writer.classes.implWsDataClassCode import com.intellij.workspaceModel.codegen.impl.writer.classes.implWsEntityCode import com.intellij.workspaceModel.codegen.deft.meta.ObjClass +import com.intellij.workspaceModel.codegen.impl.engine.ProblemReporter /** * TODO: @@ -29,7 +30,7 @@ import com.intellij.workspaceModel.codegen.deft.meta.ObjClass * Перенести тесты идеи к нам или наоборрот <-- */ -fun ObjClass<*>.implWsCode(): String? { +fun ObjClass<*>.implWsCode(reporter: ProblemReporter): String? { if (!openness.instantiatable) return null - return "${implWsEntityCode()}\n${implWsDataClassCode()}" + return "${implWsEntityCode(reporter)}\n${implWsDataClassCode()}" } \ No newline at end of file