diff --git a/compiler/fir/analysis-tests/testData/resolve/headerMode/classDeclaration.fir.txt b/compiler/fir/analysis-tests/testData/resolve/headerMode/classDeclaration.fir.txt index a13d70a2e206f..139efa8e23e1b 100644 --- a/compiler/fir/analysis-tests/testData/resolve/headerMode/classDeclaration.fir.txt +++ b/compiler/fir/analysis-tests/testData/resolve/headerMode/classDeclaration.fir.txt @@ -21,6 +21,14 @@ FILE: classDeclaration.kt public final fun funD(): R|kotlin/Int| + public final inline fun funE(): R|kotlin/String| { + local final fun funF(): R|kotlin/String| { + ^funF String(funF body) + } + + ^funE R|/funF|() + } + } public abstract interface B : R|kotlin/Any| { public open fun funA(): R|kotlin/String| diff --git a/compiler/fir/analysis-tests/testData/resolve/headerMode/classDeclaration.kt b/compiler/fir/analysis-tests/testData/resolve/headerMode/classDeclaration.kt index 8bf2a50c4177c..32d1560181af8 100644 --- a/compiler/fir/analysis-tests/testData/resolve/headerMode/classDeclaration.kt +++ b/compiler/fir/analysis-tests/testData/resolve/headerMode/classDeclaration.kt @@ -25,6 +25,11 @@ class A { } fun funD() = 1 + 2 + + inline fun funE(): String { + fun funF() = "funF body" + return funF() + } } interface B { diff --git a/compiler/fir/analysis-tests/testData/resolve/headerMode/constructorDeclaration.fir.txt b/compiler/fir/analysis-tests/testData/resolve/headerMode/constructorDeclaration.fir.txt new file mode 100644 index 0000000000000..2af9dd0e03743 --- /dev/null +++ b/compiler/fir/analysis-tests/testData/resolve/headerMode/constructorDeclaration.fir.txt @@ -0,0 +1,41 @@ +FILE: constructorDeclaration.kt + public final class A : R|kotlin/Any| { + public constructor(a: R|kotlin/String|, b: R|kotlin/Int|): R|A| { + super() + } + + public final val a: R|kotlin/String| = R|/a| + public get(): R|kotlin/String| + + public final val b: R|kotlin/Int| = R|/b| + public get(): R|kotlin/Int| + + public constructor(e: R|kotlin/String|): R|A| { + this(R|/e|, Int(0)) + } + + public constructor(): R|A| { + this(String()) + } + + } + public final class B : R|kotlin/Any| { + private constructor(a: R|kotlin/String|, b: R|kotlin/Int|): R|B| { + super() + } + + public final val a: R|kotlin/String| = R|/a| + public get(): R|kotlin/String| + + public final val b: R|kotlin/Int| = R|/b| + public get(): R|kotlin/Int| + + public constructor(e: R|kotlin/String|): R|B| { + this(R|/e|, Int(0)) + } + + public constructor(): R|B| { + this(String()) + } + + } diff --git a/compiler/fir/analysis-tests/testData/resolve/headerMode/constructorDeclaration.kt b/compiler/fir/analysis-tests/testData/resolve/headerMode/constructorDeclaration.kt new file mode 100644 index 0000000000000..af45301414b84 --- /dev/null +++ b/compiler/fir/analysis-tests/testData/resolve/headerMode/constructorDeclaration.kt @@ -0,0 +1,22 @@ +// RUN_PIPELINE_TILL: BACKEND +// FIR_DUMP +class A(val a: String, val b: Int) { + constructor(e: String): this(e, 0) { + val message = "Secondary constructor body" + } + constructor(): this("") { + val message = "Secondary constructor body with delegated constructor call" + } +} + +class B private constructor(val a: String, val b: Int) { + constructor(e: String): this(e, 0) { + val message = "Secondary constructor body" + } + constructor(): this("") { + val message = "Secondary constructor body with delegated constructor call" + } +} + +/* GENERATED_FIR_TAGS: classDeclaration, functionDeclaration, primaryConstructor, propertyDeclaration, +secondaryConstructor */ diff --git a/compiler/fir/analysis-tests/testData/resolve/headerMode/dataClassDeclaration.fir.txt b/compiler/fir/analysis-tests/testData/resolve/headerMode/dataClassDeclaration.fir.txt new file mode 100644 index 0000000000000..5610c90334714 --- /dev/null +++ b/compiler/fir/analysis-tests/testData/resolve/headerMode/dataClassDeclaration.fir.txt @@ -0,0 +1,19 @@ +FILE: dataClassDeclaration.kt + public final data class User : R|kotlin/Any| { + public constructor(name: R|kotlin/String|, age: R|kotlin/Int|): R|User| { + super() + } + + public final val name: R|kotlin/String| = R|/name| + public get(): R|kotlin/String| + + public final val age: R|kotlin/Int| = R|/age| + public get(): R|kotlin/Int| + + public final operator fun component1(): R|kotlin/String| + + public final operator fun component2(): R|kotlin/Int| + + public final fun copy(name: R|kotlin/String| = this@R|/User|.R|/User.name|, age: R|kotlin/Int| = this@R|/User|.R|/User.age|): R|User| + + } diff --git a/compiler/fir/analysis-tests/testData/resolve/headerMode/dataClassDeclaration.kt b/compiler/fir/analysis-tests/testData/resolve/headerMode/dataClassDeclaration.kt new file mode 100644 index 0000000000000..569f76922e90a --- /dev/null +++ b/compiler/fir/analysis-tests/testData/resolve/headerMode/dataClassDeclaration.kt @@ -0,0 +1,5 @@ +// RUN_PIPELINE_TILL: BACKEND +// FIR_DUMP +data class User(val name: String, val age: Int) + +/* GENERATED_FIR_TAGS: classDeclaration, data, primaryConstructor, propertyDeclaration */ diff --git a/compiler/fir/analysis-tests/testData/resolve/headerMode/enumClassDeclaration.fir.txt b/compiler/fir/analysis-tests/testData/resolve/headerMode/enumClassDeclaration.fir.txt new file mode 100644 index 0000000000000..5cf6febb0b612 --- /dev/null +++ b/compiler/fir/analysis-tests/testData/resolve/headerMode/enumClassDeclaration.fir.txt @@ -0,0 +1,52 @@ +FILE: enumClassDeclaration.kt + public final enum class A : R|kotlin/Enum| { + private constructor(): R|A| { + super|>() + } + + public final static enum entry EAST: R|A| + public final static enum entry WEST: R|A| + public final static fun values(): R|kotlin/Array| + + public final static fun valueOf(value: R|kotlin/String|): R|A| + + public final static val entries: R|kotlin/enums/EnumEntries| + public get(): R|kotlin/enums/EnumEntries| + + } + public final enum class B : R|kotlin/Enum| { + private constructor(): R|B| { + super|>() + } + + public final static enum entry NORTH: R|B| = object : R|B| { + private constructor(): R|| { + super() + } + + public open override fun getString(): R|kotlin/String| { + ^getString String(north) + } + + } + + public final static enum entry SOUTH: R|B| = object : R|B| { + private constructor(): R|| { + super() + } + + public open override fun getString(): R|kotlin/String| { + } + + } + + public abstract fun getString(): R|kotlin/String| + + public final static fun values(): R|kotlin/Array| + + public final static fun valueOf(value: R|kotlin/String|): R|B| + + public final static val entries: R|kotlin/enums/EnumEntries| + public get(): R|kotlin/enums/EnumEntries| + + } diff --git a/compiler/fir/analysis-tests/testData/resolve/headerMode/enumClassDeclaration.kt b/compiler/fir/analysis-tests/testData/resolve/headerMode/enumClassDeclaration.kt new file mode 100644 index 0000000000000..3dfbb88e069ea --- /dev/null +++ b/compiler/fir/analysis-tests/testData/resolve/headerMode/enumClassDeclaration.kt @@ -0,0 +1,21 @@ +// RUN_PIPELINE_TILL: BACKEND +// FIR_DUMP +enum class A { + EAST, + WEST +} + +enum class B { + NORTH { + override fun getString() = "north" + }, + SOUTH { + override fun getString(): String { + return "south" + } + }; + + abstract fun getString(): String +} + +/* GENERATED_FIR_TAGS: enumDeclaration, enumEntry */ diff --git a/compiler/fir/analysis-tests/testData/resolve/headerMode/functionDeclaration.fir.txt b/compiler/fir/analysis-tests/testData/resolve/headerMode/functionDeclaration.fir.txt index 437bfa60dc812..87a70f6f4f335 100644 --- a/compiler/fir/analysis-tests/testData/resolve/headerMode/functionDeclaration.fir.txt +++ b/compiler/fir/analysis-tests/testData/resolve/headerMode/functionDeclaration.fir.txt @@ -11,3 +11,27 @@ FILE: functionDeclaration.kt private final fun funC(): R|kotlin/String| public final fun funD(): R|kotlin/Int| + public final inline fun funE(): R|kotlin/String| { + local final fun funF(): R|kotlin/String| { + ^funF String(funF body) + } + + ^funE R|/funF|() + } + public final inline fun funG(): R|kotlin/String| { + local final class classA : R|kotlin/Any| { + public constructor(): R|/classA| { + super() + } + + public final fun funH(): R|kotlin/String| { + ^funH String(funH body) + } + + } + + lval a: R|/classA| = R|/classA.classA|() + ^funG R|/a|.R|/funH|() + } + public final fun funI(): R|kotlin/Int| + public final fun funJ(): R|kotlin/String| diff --git a/compiler/fir/analysis-tests/testData/resolve/headerMode/functionDeclaration.kt b/compiler/fir/analysis-tests/testData/resolve/headerMode/functionDeclaration.kt index b079cdb85db35..682707fac2a28 100644 --- a/compiler/fir/analysis-tests/testData/resolve/headerMode/functionDeclaration.kt +++ b/compiler/fir/analysis-tests/testData/resolve/headerMode/functionDeclaration.kt @@ -3,14 +3,17 @@ import kotlin.contracts.ExperimentalContracts import kotlin.contracts.contract +// Public function fun funA(): String { return "funA body" } +// Inline function inline fun funB(): String { return "funB body" } +// Function with contract @OptIn(ExperimentalContracts::class) fun isNotNull(value: Any?): Boolean { contract { @@ -19,11 +22,38 @@ fun isNotNull(value: Any?): Boolean { return value != null } +// Private function private fun funC(): String { return "funC body" } +// Implicit return type fun funD() = 1 + 2 +// Function inside a function +inline fun funE(): String { + fun funF(): String { + return "funF body" + } + return funF() +} + +// Class inside a function +inline fun funG(): String { + class classA { + fun funH() = "funH body" + } + val a = classA() + return a.funH() +} + +// Implicit type reference from another function. +fun funI() = funD() + +fun funJ(): String { + inline fun funK() = "funK body" + return funK() +} + /* GENERATED_FIR_TAGS: classReference, contractConditionalEffect, contracts, functionDeclaration, inline, nullableType, stringLiteral */ diff --git a/compiler/fir/analysis-tests/testData/resolve/headerMode/objectDeclaration.fir.txt b/compiler/fir/analysis-tests/testData/resolve/headerMode/objectDeclaration.fir.txt index 3da4fa38b5a1f..d5bb11b458b18 100644 --- a/compiler/fir/analysis-tests/testData/resolve/headerMode/objectDeclaration.fir.txt +++ b/compiler/fir/analysis-tests/testData/resolve/headerMode/objectDeclaration.fir.txt @@ -21,4 +21,12 @@ FILE: objectDeclaration.kt public final fun funD(): R|kotlin/Int| + public final inline fun funE(): R|kotlin/String| { + local final fun funF(): R|kotlin/String| { + ^funF String(funF body) + } + + ^funE R|/funF|() + } + } diff --git a/compiler/fir/analysis-tests/testData/resolve/headerMode/objectDeclaration.kt b/compiler/fir/analysis-tests/testData/resolve/headerMode/objectDeclaration.kt index 93a679fc9f311..69a04868ddb4a 100644 --- a/compiler/fir/analysis-tests/testData/resolve/headerMode/objectDeclaration.kt +++ b/compiler/fir/analysis-tests/testData/resolve/headerMode/objectDeclaration.kt @@ -25,6 +25,11 @@ object A { } fun funD() = 1 + 2 + + inline fun funE(): String { + fun funF() = "funF body" + return funF() + } } /* GENERATED_FIR_TAGS: classReference, contractConditionalEffect, contracts, functionDeclaration, inline, nullableType, diff --git a/compiler/fir/analysis-tests/testData/resolve/headerMode/propertyDeclaration.fir.txt b/compiler/fir/analysis-tests/testData/resolve/headerMode/propertyDeclaration.fir.txt new file mode 100644 index 0000000000000..f4bf8a6276eb2 --- /dev/null +++ b/compiler/fir/analysis-tests/testData/resolve/headerMode/propertyDeclaration.fir.txt @@ -0,0 +1,8 @@ +FILE: propertyDeclaration.kt + public final val a: R|kotlin/String| = String(A) + public get(): R|kotlin/String| + public final val b: R|kotlin/String| = String(B) + public get(): R|kotlin/String| + public final fun getC(): R|kotlin/String| + public final val c: + public get(): diff --git a/compiler/fir/analysis-tests/testData/resolve/headerMode/propertyDeclaration.kt b/compiler/fir/analysis-tests/testData/resolve/headerMode/propertyDeclaration.kt new file mode 100644 index 0000000000000..389733f9482f6 --- /dev/null +++ b/compiler/fir/analysis-tests/testData/resolve/headerMode/propertyDeclaration.kt @@ -0,0 +1,11 @@ +// RUN_PIPELINE_TILL: BACKEND +// FIR_DUMP +// Public property with explicit type +//val a: String = "A" +// Public property with implicit type +//val b = "B" +// Property with overriden getter and implicit type. +fun getC() = "C" +val c get() = getC() + +/* GENERATED_FIR_TAGS: propertyDeclaration, stringLiteral */ diff --git a/compiler/fir/raw-fir/light-tree2fir/src/org/jetbrains/kotlin/fir/lightTree/converter/LightTreeRawFirDeclarationBuilder.kt b/compiler/fir/raw-fir/light-tree2fir/src/org/jetbrains/kotlin/fir/lightTree/converter/LightTreeRawFirDeclarationBuilder.kt index 5e8d542232816..4d2c5c54aafed 100644 --- a/compiler/fir/raw-fir/light-tree2fir/src/org/jetbrains/kotlin/fir/lightTree/converter/LightTreeRawFirDeclarationBuilder.kt +++ b/compiler/fir/raw-fir/light-tree2fir/src/org/jetbrains/kotlin/fir/lightTree/converter/LightTreeRawFirDeclarationBuilder.kt @@ -56,6 +56,7 @@ import org.jetbrains.kotlin.psi.stubs.elements.KtStubElementTypes import org.jetbrains.kotlin.util.getChildren import org.jetbrains.kotlin.utils.addToStdlib.runIf import org.jetbrains.kotlin.utils.addToStdlib.shouldNotBeCalled +import org.jetbrains.kotlin.config.AnalysisFlags class LightTreeRawFirDeclarationBuilder( session: FirSession, @@ -65,6 +66,7 @@ class LightTreeRawFirDeclarationBuilder( ) : AbstractLightTreeRawFirBuilder(session, tree, context) { private val expressionConverter = LightTreeRawFirExpressionBuilder(session, tree, this, context) + private val headerMode = session.languageVersionSettings.getFlag(AnalysisFlags.headerMode) /** * [org.jetbrains.kotlin.parsing.KotlinParsing.parseFile] @@ -133,21 +135,33 @@ class LightTreeRawFirDeclarationBuilder( /** * @see org.jetbrains.kotlin.parsing.KotlinParsing.parseBlockExpression */ - fun convertBlockExpression(block: LighterASTNode): FirBlock { - return convertBlockExpressionWithoutBuilding(block).build() + fun convertBlockExpression( + block: LighterASTNode, + convertOnlyFirstStatement: Boolean = false, + ): FirBlock { + return convertBlockExpressionWithoutBuilding(block, convertOnlyFirstStatement = convertOnlyFirstStatement).build() } - fun convertBlockExpressionWithoutBuilding(block: LighterASTNode, kind: KtFakeSourceElementKind? = null): FirBlockBuilder { + /** + * @param convertOnlyFirstStatement Convert only the first statement of the block, which can be a contract, for header generation. + */ + fun convertBlockExpressionWithoutBuilding( + block: LighterASTNode, + kind: KtFakeSourceElementKind? = null, + convertOnlyFirstStatement: Boolean = false + ): FirBlockBuilder { val firStatements = block.forEachChildrenReturnList { node, container -> - when (node.tokenType) { - CLASS, OBJECT_DECLARATION -> container += convertClass(node) as FirStatement - FUN -> container += convertFunctionDeclaration(node) - KtNodeTypes.PROPERTY -> container += convertPropertyDeclaration(node) as FirStatement - DESTRUCTURING_DECLARATION -> container += - convertDestructingDeclaration(node).toFirDestructingDeclaration(this, baseModuleData) - TYPEALIAS -> container += convertTypeAlias(node) as FirStatement - CLASS_INITIALIZER -> shouldNotBeCalled("CLASS_INITIALIZER expected to be processed during class body conversion") - else -> if (node.isExpression()) container += expressionConverter.getAsFirStatement(node) + if (!convertOnlyFirstStatement || container.isEmpty()) { + when (node.tokenType) { + CLASS, OBJECT_DECLARATION -> container += convertClass(node) as FirStatement + FUN -> container += convertFunctionDeclaration(node) + KtNodeTypes.PROPERTY -> container += convertPropertyDeclaration(node) as FirStatement + DESTRUCTURING_DECLARATION -> container += + convertDestructingDeclaration(node).toFirDestructingDeclaration(this, baseModuleData) + TYPEALIAS -> container += convertTypeAlias(node) as FirStatement + CLASS_INITIALIZER -> shouldNotBeCalled("CLASS_INITIALIZER expected to be processed during class body conversion") + else -> if (node.isExpression()) container += expressionConverter.getAsFirStatement(node) + } } } return FirBlockBuilder().apply { @@ -2063,7 +2077,8 @@ class LightTreeRawFirDeclarationBuilder( } val allowLegacyContractDescription = outerContractDescription == null - val bodyWithContractDescription = withForcedLocalContext { + val forceKeepingTheBodyInHeaderMode = context.forceKeepingTheBodyInHeaderMode || functionBuilder.status.isInline + val bodyWithContractDescription = withForcedLocalContext(forceKeepingTheBodyInHeaderMode) { convertFunctionBody(block, expression, allowLegacyContractDescription) } this.body = bodyWithContractDescription.first @@ -2103,9 +2118,10 @@ class LightTreeRawFirDeclarationBuilder( expression: LighterASTNode?, allowLegacyContractDescription: Boolean ): Pair { + val generateHeader = headerMode && !context.forceKeepingTheBodyInHeaderMode return when { blockNode != null -> { - val block = convertBlock(blockNode) + val block = convertBlock(blockNode, convertOnlyFirstStatement = generateHeader) val contractDescription = runIf(allowLegacyContractDescription) { val blockSource = block.source val diagnostic = when { @@ -2115,7 +2131,11 @@ class LightTreeRawFirDeclarationBuilder( } processLegacyContractDescription(block, diagnostic) } - block to contractDescription + if (generateHeader) { + return buildEmptyExpressionBlock() to contractDescription + } else { + block to contractDescription + } } expression != null -> FirSingleExpressionBlock( expressionConverter.getAsFirExpression(expression, "Function has no body (but should)").toReturn() @@ -2137,7 +2157,10 @@ class LightTreeRawFirDeclarationBuilder( /** * @see org.jetbrains.kotlin.parsing.KotlinParsing.parseBlock */ - fun convertBlock(block: LighterASTNode?): FirBlock { + fun convertBlock( + block: LighterASTNode?, + convertOnlyFirstStatement: Boolean = false, + ): FirBlock { if (block == null) return buildEmptyExpressionBlock() if (block.tokenType != BLOCK) { return FirSingleExpressionBlock( @@ -2145,7 +2168,7 @@ class LightTreeRawFirDeclarationBuilder( ) } - return convertBlockExpression(block) + return convertBlockExpression(block, convertOnlyFirstStatement) } /** diff --git a/compiler/fir/raw-fir/raw-fir.common/src/org/jetbrains/kotlin/fir/builder/AbstractRawFirBuilder.kt b/compiler/fir/raw-fir/raw-fir.common/src/org/jetbrains/kotlin/fir/builder/AbstractRawFirBuilder.kt index d2f5f3acff73c..7cb26f0f4a0a7 100644 --- a/compiler/fir/raw-fir/raw-fir.common/src/org/jetbrains/kotlin/fir/builder/AbstractRawFirBuilder.kt +++ b/compiler/fir/raw-fir/raw-fir.common/src/org/jetbrains/kotlin/fir/builder/AbstractRawFirBuilder.kt @@ -126,7 +126,9 @@ abstract class AbstractRawFirBuilder(val baseSession: FirSession, val c } } - inline fun withForcedLocalContext(block: () -> R): R { + inline fun withForcedLocalContext(forceKeepingTheBodyInHeaderMode: Boolean = false, block: () -> R): R { + val oldForceKeepingTheBodyInHeaderMode = context.forceKeepingTheBodyInHeaderMode + context.forceKeepingTheBodyInHeaderMode = oldForceKeepingTheBodyInHeaderMode || forceKeepingTheBodyInHeaderMode val oldForcedLocalContext = context.inLocalContext context.inLocalContext = true val oldClassNameBeforeLocalContext = context.classNameBeforeLocalContext @@ -141,6 +143,7 @@ abstract class AbstractRawFirBuilder(val baseSession: FirSession, val c context.classNameBeforeLocalContext = oldClassNameBeforeLocalContext context.inLocalContext = oldForcedLocalContext context.className = oldClassName + context.forceKeepingTheBodyInHeaderMode = oldForceKeepingTheBodyInHeaderMode } } diff --git a/compiler/fir/raw-fir/raw-fir.common/src/org/jetbrains/kotlin/fir/builder/Context.kt b/compiler/fir/raw-fir/raw-fir.common/src/org/jetbrains/kotlin/fir/builder/Context.kt index 71370b38ed8a0..407a383a182a3 100644 --- a/compiler/fir/raw-fir/raw-fir.common/src/org/jetbrains/kotlin/fir/builder/Context.kt +++ b/compiler/fir/raw-fir/raw-fir.common/src/org/jetbrains/kotlin/fir/builder/Context.kt @@ -59,6 +59,8 @@ class Context { val dispatchReceiverTypesStack: MutableList = mutableListOf() var containerIsExpect: Boolean = false + var forceKeepingTheBodyInHeaderMode: Boolean = false + var containingScriptSymbol: FirScriptSymbol? = null var containingReplSymbol: FirReplSnippetSymbol? = null diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirDeclarationsResolveTransformer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirDeclarationsResolveTransformer.kt index 9f467bf9437fb..ae43ac2edb14a 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirDeclarationsResolveTransformer.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirDeclarationsResolveTransformer.kt @@ -1039,7 +1039,7 @@ open class FirDeclarationsResolveTransformer( } result.transformReturnTypeRef(transformer, ResolutionMode.UpdateImplicitTypeRef(returnTypeRef)) } - if (session.languageVersionSettings.getFlag(AnalysisFlags.headerMode) && !function.isInline) { + if (session.languageVersionSettings.getFlag(AnalysisFlags.headerMode) && !function.isInline && !function.isLocal) { // Header mode: once the return type for non-inline function is known, the body can be removed. result.replaceBody(null) } diff --git a/compiler/tests-common-new/testFixtures/org/jetbrains/kotlin/test/configuration/BaseCodegenConfiguration.kt b/compiler/tests-common-new/testFixtures/org/jetbrains/kotlin/test/configuration/BaseCodegenConfiguration.kt index a6ef42e697963..a770bfacdff53 100644 --- a/compiler/tests-common-new/testFixtures/org/jetbrains/kotlin/test/configuration/BaseCodegenConfiguration.kt +++ b/compiler/tests-common-new/testFixtures/org/jetbrains/kotlin/test/configuration/BaseCodegenConfiguration.kt @@ -32,6 +32,7 @@ import org.jetbrains.kotlin.test.frontend.fir.FirOutputArtifact import org.jetbrains.kotlin.test.model.* import org.jetbrains.kotlin.test.services.AdditionalSourceProvider import org.jetbrains.kotlin.test.services.configuration.* +import org.jetbrains.kotlin.test.services.fir.FirSpecificParserSuppressor import org.jetbrains.kotlin.test.services.sourceProviders.AdditionalDiagnosticsSourceFilesProvider import org.jetbrains.kotlin.test.services.sourceProviders.CoroutineHelpersSourceFilesProvider import org.jetbrains.kotlin.utils.bind @@ -92,6 +93,8 @@ fun TestConfigurationBuilder.commonServicesConfigurationForCodegenAndDebugTest(t ::AdditionalDiagnosticsSourceFilesProvider, ::CoroutineHelpersSourceFilesProvider, ) + + useMetaTestConfigurators(::FirSpecificParserSuppressor) } /** diff --git a/compiler/tests-common-new/testFixtures/org/jetbrains/kotlin/test/configuration/BaseDiagnosticConfiguration.kt b/compiler/tests-common-new/testFixtures/org/jetbrains/kotlin/test/configuration/BaseDiagnosticConfiguration.kt index 878b3c76c80b7..906d9bb2a1744 100644 --- a/compiler/tests-common-new/testFixtures/org/jetbrains/kotlin/test/configuration/BaseDiagnosticConfiguration.kt +++ b/compiler/tests-common-new/testFixtures/org/jetbrains/kotlin/test/configuration/BaseDiagnosticConfiguration.kt @@ -21,6 +21,7 @@ import org.jetbrains.kotlin.test.builders.irHandlersStep import org.jetbrains.kotlin.test.cli.CliDirectives.CHECK_COMPILER_OUTPUT import org.jetbrains.kotlin.test.directives.ConfigurationDirectives.WITH_STDLIB import org.jetbrains.kotlin.test.directives.DiagnosticsDirectives.DIAGNOSTICS +import org.jetbrains.kotlin.test.directives.FirDiagnosticsDirectives.DISABLE_WITH_PARSER import org.jetbrains.kotlin.test.directives.FirDiagnosticsDirectives.DUMP_VFIR import org.jetbrains.kotlin.test.directives.FirDiagnosticsDirectives.TEST_ALONGSIDE_K1_TESTDATA import org.jetbrains.kotlin.test.directives.FirDiagnosticsDirectives.USE_LATEST_LANGUAGE_VERSION @@ -298,6 +299,7 @@ fun TestConfigurationBuilder.configureCommonDiagnosticTestPaths( forTestsMatching("compiler/fir/analysis-tests/testData/resolve/headerMode/*") { defaultDirectives { +HEADER_MODE + DISABLE_WITH_PARSER with FirParser.Psi } } } diff --git a/compiler/tests-common-new/testFixtures/org/jetbrains/kotlin/test/directives/FirDiagnosticsDirectives.kt b/compiler/tests-common-new/testFixtures/org/jetbrains/kotlin/test/directives/FirDiagnosticsDirectives.kt index d37c3db79871e..05a65fdc09d23 100644 --- a/compiler/tests-common-new/testFixtures/org/jetbrains/kotlin/test/directives/FirDiagnosticsDirectives.kt +++ b/compiler/tests-common-new/testFixtures/org/jetbrains/kotlin/test/directives/FirDiagnosticsDirectives.kt @@ -157,6 +157,10 @@ object FirDiagnosticsDirectives : SimpleDirectivesContainer() { val DISABLE_GENERATED_FIR_TAGS by directive( description = "Disables generating and checking for GENERATED_FIR_TAGS" ) + + val DISABLE_WITH_PARSER by enumDirective( + description = "Disables the test if it's analyzed with specified parser" + ) } object DumpCfgOption { diff --git a/compiler/tests-common-new/testFixtures/org/jetbrains/kotlin/test/services/fir/FirSpecificParserSuppressor.kt b/compiler/tests-common-new/testFixtures/org/jetbrains/kotlin/test/services/fir/FirSpecificParserSuppressor.kt new file mode 100644 index 0000000000000..a776f7ebec635 --- /dev/null +++ b/compiler/tests-common-new/testFixtures/org/jetbrains/kotlin/test/services/fir/FirSpecificParserSuppressor.kt @@ -0,0 +1,27 @@ +/* + * Copyright 2010-2025 JetBrains s.r.o. and Kotlin Programming Language contributors. + * 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.test.services.fir + +import org.jetbrains.kotlin.test.directives.FirDiagnosticsDirectives +import org.jetbrains.kotlin.test.directives.FirDiagnosticsDirectives.DISABLE_WITH_PARSER +import org.jetbrains.kotlin.test.directives.FirDiagnosticsDirectives.FIR_PARSER +import org.jetbrains.kotlin.test.directives.model.DirectivesContainer +import org.jetbrains.kotlin.test.directives.model.singleOrZeroValue +import org.jetbrains.kotlin.test.services.MetaTestConfigurator +import org.jetbrains.kotlin.test.services.TestServices +import org.jetbrains.kotlin.test.services.moduleStructure + +class FirSpecificParserSuppressor(testServices: TestServices) : MetaTestConfigurator(testServices) { + override val directiveContainers: List + get() = listOf(FirDiagnosticsDirectives) + + override fun shouldSkipTest(): Boolean { + val directives = testServices.moduleStructure.allDirectives + val suppressedParser = directives.singleOrZeroValue(DISABLE_WITH_PARSER) ?: return false + val currentParser = directives.singleOrZeroValue(FIR_PARSER) ?: return false + return currentParser == suppressedParser + } +} diff --git a/compiler/tests-compiler-utils/testFixtures/org/jetbrains/kotlin/fir/session/FirSessionFactoryHelper.kt b/compiler/tests-compiler-utils/testFixtures/org/jetbrains/kotlin/fir/session/FirSessionFactoryHelper.kt index 88c5067141bbc..24f6536ba2b36 100644 --- a/compiler/tests-compiler-utils/testFixtures/org/jetbrains/kotlin/fir/session/FirSessionFactoryHelper.kt +++ b/compiler/tests-compiler-utils/testFixtures/org/jetbrains/kotlin/fir/session/FirSessionFactoryHelper.kt @@ -108,7 +108,7 @@ object FirSessionFactoryHelper { override fun isPreRelease(): Boolean = stub() - override fun getFlag(flag: AnalysisFlag): T = stub() + override fun getFlag(flag: AnalysisFlag): T = flag.defaultValue override val apiVersion: ApiVersion get() = stub()