diff --git a/compiler/fir/raw-fir/psi2fir/tests/org/jetbrains/kotlin/fir/builder/AbstractRawFirBuilderTestCase.kt b/compiler/fir/raw-fir/psi2fir/tests/org/jetbrains/kotlin/fir/builder/AbstractRawFirBuilderTestCase.kt index 46107e6d154..92ecf6fc57c 100644 --- a/compiler/fir/raw-fir/psi2fir/tests/org/jetbrains/kotlin/fir/builder/AbstractRawFirBuilderTestCase.kt +++ b/compiler/fir/raw-fir/psi2fir/tests/org/jetbrains/kotlin/fir/builder/AbstractRawFirBuilderTestCase.kt @@ -18,10 +18,7 @@ import org.jetbrains.kotlin.fir.declarations.FirFile import org.jetbrains.kotlin.fir.declarations.FirTypeParameter import org.jetbrains.kotlin.fir.declarations.FirValueParameter import org.jetbrains.kotlin.fir.declarations.impl.FirResolvedDeclarationStatusImpl -import org.jetbrains.kotlin.fir.expressions.FirAnnotation -import org.jetbrains.kotlin.fir.expressions.FirAnnotationArgumentMapping -import org.jetbrains.kotlin.fir.expressions.FirEmptyArgumentList -import org.jetbrains.kotlin.fir.expressions.FirExpression +import org.jetbrains.kotlin.fir.expressions.* import org.jetbrains.kotlin.fir.expressions.impl.FirNoReceiverExpression import org.jetbrains.kotlin.fir.expressions.impl.FirStubStatement import org.jetbrains.kotlin.fir.references.impl.FirStubReference @@ -117,17 +114,19 @@ abstract class AbstractRawFirBuilderTestCase : KtParsingTestCase( return firImplClassPropertiesWithNoAcceptAndTransform[className] == propertyName } - private fun FirFile.visitChildren(): Set = - ConsistencyVisitor().let { - this@visitChildren.accept(it) - it.result - } + private fun FirFile.visitChildren(): Set { + val result = HashSet() + val processor = ConsistencyProcessor(result) + accept(ConsistencyVisitor(processor)) + return result + } - private fun FirFile.transformChildren(): Set = - ConsistencyTransformer().let { - this@transformChildren.transform(it, Unit) - it.result - } + private fun FirFile.transformChildren(): Set { + val result = HashSet() + val processor = ConsistencyProcessor(result) + transform(ConsistencyTransformer(processor), Unit) + return result + } protected fun FirFile.checkChildren() { val children = traverseChildren() @@ -151,34 +150,39 @@ abstract class AbstractRawFirBuilderTestCase : KtParsingTestCase( } } - private class ConsistencyVisitor : FirVisitorVoid() { - var result = hashSetOf() - + private class ConsistencyVisitor(private val processor: ConsistencyProcessor) : FirVisitorVoid() { override fun visitElement(element: FirElement) { - // NB: types are reused sometimes (e.g. in accessors) - if (!result.add(element)) { - throwTwiceVisitingError(element) - } else { - element.acceptChildren(this) - } + processor.process(element) { it.acceptChildren(this@ConsistencyVisitor) } } } - private class ConsistencyTransformer : FirTransformer() { - var result = hashSetOf() - + private class ConsistencyTransformer(private val processor: ConsistencyProcessor) : FirTransformer() { override fun transformElement(element: E, data: Unit): E { - if (!result.add(element)) { - throwTwiceVisitingError(element) - } else { - element.transformChildren(this, Unit) - } + processor.process(element) { it.transformChildren(this@ConsistencyTransformer, Unit) } return element } } + + private class ConsistencyProcessor(private val result: MutableSet) { + private var parent: FirElement? = null + + fun process(element: FirElement, processChildren: (FirElement) -> Unit) { + if (!result.add(element)) { + throwTwiceVisitingError(element, parent) + } else { + val oldParent = parent + try { + parent = element + processChildren(element) + } finally { + parent = oldParent + } + } + } + } } -private fun throwTwiceVisitingError(element: FirElement) { +private fun throwTwiceVisitingError(element: FirElement, @Suppress("UNUSED_PARAMETER") parent: FirElement?) { if (element is FirTypeRef || element is FirNoReceiverExpression || element is FirTypeParameter || element is FirTypeProjection || element is FirValueParameter || element is FirAnnotation || element is FirFunctionTypeParameter || element is FirEmptyContractDescription ||