From 951aa8185e72b67300d9cf86a5062890d6bc0abd Mon Sep 17 00:00:00 2001 From: Mikhail Glukhikh Date: Sun, 2 Aug 2020 13:37:13 +0300 Subject: [PATCH] [FIR] Avoid duplicate diagnostics on expression.typeRef --- .../collectors/AbstractDiagnosticCollector.kt | 90 +++++++++++++------ .../diagnostics/tests/EnumEntryAsType.fir.kt | 6 +- .../tests/cast/bare/ToErrorType.fir.kt | 2 +- .../typeParametersInAnnonymousObject.fir.kt | 4 +- ...eParametersInAnnonymousObject_after.fir.kt | 4 +- .../capturedParameters/uncheckedCast.fir.kt | 2 +- .../SupertypeOfErrorType.fir.kt | 2 +- ...tedTypeAliasQualifiedByDerivedClass.fir.kt | 2 +- .../typealias/innerTypeAliasAsType.fir.kt | 12 +-- 9 files changed, 82 insertions(+), 42 deletions(-) diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/collectors/AbstractDiagnosticCollector.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/collectors/AbstractDiagnosticCollector.kt index 237bb060833..b1f68c7f26b 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/collectors/AbstractDiagnosticCollector.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/collectors/AbstractDiagnosticCollector.kt @@ -17,11 +17,9 @@ import org.jetbrains.kotlin.fir.resolve.ScopeSession import org.jetbrains.kotlin.fir.resolve.SessionHolder import org.jetbrains.kotlin.fir.resolve.collectImplicitReceivers import org.jetbrains.kotlin.fir.resolve.defaultType -import org.jetbrains.kotlin.fir.types.ConeKotlinType -import org.jetbrains.kotlin.fir.types.FirTypeRef +import org.jetbrains.kotlin.fir.types.* import org.jetbrains.kotlin.fir.types.builder.buildResolvedTypeRef -import org.jetbrains.kotlin.fir.types.coneTypeSafe -import org.jetbrains.kotlin.fir.visitors.FirVisitorVoid +import org.jetbrains.kotlin.fir.visitors.FirDefaultVisitor import org.jetbrains.kotlin.name.Name abstract class AbstractDiagnosticCollector( @@ -33,7 +31,7 @@ abstract class AbstractDiagnosticCollector( throw IllegalStateException("Components are not initialized") } initializeCollector() - firFile.accept(visitor) + firFile.accept(visitor, null) return getCollectedDiagnostics() } @@ -48,6 +46,8 @@ abstract class AbstractDiagnosticCollector( @Suppress("LeakingThis") private var context = PersistentCheckerContext(this) + private var lastExpression: FirExpression? = null + fun initializeComponents(vararg components: AbstractDiagnosticCollectorComponent) { if (componentsInitialized) { throw IllegalStateException() @@ -56,29 +56,29 @@ abstract class AbstractDiagnosticCollector( componentsInitialized = true } - private inner class Visitor : FirVisitorVoid() { + private inner class Visitor : FirDefaultVisitor() { private fun T.runComponents() { components.forEach { this.accept(it, context) } } - override fun visitElement(element: FirElement) { + override fun visitElement(element: FirElement, data: Nothing?) { element.runComponents() - element.acceptChildren(this) + element.acceptChildren(this, null) } private fun visitJump(loopJump: FirLoopJump) { loopJump.runComponents() - loopJump.acceptChildren(this) - loopJump.target.labeledElement.takeIf { it is FirErrorLoop }?.accept(this) + loopJump.acceptChildren(this, null) + loopJump.target.labeledElement.takeIf { it is FirErrorLoop }?.accept(this, null) } - override fun visitBreakExpression(breakExpression: FirBreakExpression) { + override fun visitBreakExpression(breakExpression: FirBreakExpression, data: Nothing?) { visitJump(breakExpression) } - override fun visitContinueExpression(continueExpression: FirContinueExpression) { + override fun visitContinueExpression(continueExpression: FirContinueExpression, data: Nothing?) { visitJump(continueExpression) } @@ -90,23 +90,23 @@ abstract class AbstractDiagnosticCollector( } - override fun visitRegularClass(regularClass: FirRegularClass) { + override fun visitRegularClass(regularClass: FirRegularClass, data: Nothing?) { visitClassAndChildren(regularClass, regularClass.defaultType()) } - override fun visitAnonymousObject(anonymousObject: FirAnonymousObject) { + override fun visitAnonymousObject(anonymousObject: FirAnonymousObject, data: Nothing?) { visitClassAndChildren(anonymousObject, anonymousObject.defaultType()) } - override fun visitSimpleFunction(simpleFunction: FirSimpleFunction) { + override fun visitSimpleFunction(simpleFunction: FirSimpleFunction, data: Nothing?) { visitWithDeclarationAndReceiver(simpleFunction, simpleFunction.name, simpleFunction.receiverTypeRef) } - override fun visitConstructor(constructor: FirConstructor) { + override fun visitConstructor(constructor: FirConstructor, data: Nothing?) { visitWithDeclaration(constructor) } - override fun visitAnonymousFunction(anonymousFunction: FirAnonymousFunction) { + override fun visitAnonymousFunction(anonymousFunction: FirAnonymousFunction, data: Nothing?) { val labelName = anonymousFunction.label?.name?.let { Name.identifier(it) } visitWithDeclarationAndReceiver( anonymousFunction, @@ -115,35 +115,65 @@ abstract class AbstractDiagnosticCollector( ) } - override fun visitProperty(property: FirProperty) { + override fun visitProperty(property: FirProperty, data: Nothing?) { visitWithDeclaration(property) } - override fun visitPropertyAccessor(propertyAccessor: FirPropertyAccessor) { + override fun visitPropertyAccessor(propertyAccessor: FirPropertyAccessor, data: Nothing?) { val property = context.containingDeclarations.last() as FirProperty visitWithDeclarationAndReceiver(propertyAccessor, property.name, property.receiverTypeRef) } - override fun visitValueParameter(valueParameter: FirValueParameter) { + override fun visitValueParameter(valueParameter: FirValueParameter, data: Nothing?) { visitWithDeclaration(valueParameter) } - override fun visitEnumEntry(enumEntry: FirEnumEntry) { + override fun visitEnumEntry(enumEntry: FirEnumEntry, data: Nothing?) { visitWithDeclaration(enumEntry) } - override fun visitFile(file: FirFile) { + override fun visitFile(file: FirFile, data: Nothing?) { visitWithDeclaration(file) } - override fun visitAnonymousInitializer(anonymousInitializer: FirAnonymousInitializer) { + override fun visitAnonymousInitializer(anonymousInitializer: FirAnonymousInitializer, data: Nothing?) { visitWithDeclaration(anonymousInitializer) } + override fun visitExpression(expression: FirExpression, data: Nothing?) { + expression.runComponents() + withExpression(expression) { + expression.acceptChildren(this, null) + } + } + + override fun visitBlock(block: FirBlock, data: Nothing?) { + visitExpression(block, data) + } + + override fun visitTypeRef(typeRef: FirTypeRef, data: Nothing?) { + if (typeRef === lastExpression?.typeRef) { + return + } + super.visitTypeRef(typeRef, null) + } + + override fun visitTypeOperatorCall(typeOperatorCall: FirTypeOperatorCall, data: Nothing?) { + typeOperatorCall.runComponents() + withExpression(typeOperatorCall) { + typeOperatorCall.acceptChildren(this, null) + } + if (typeOperatorCall.operation == FirOperation.AS) { + // NB: in this case conversionTypeRef === typeRef, so we *should* visit it explicitly + typeOperatorCall.conversionTypeRef.runComponents() + typeOperatorCall.conversionTypeRef.acceptChildren(this, null) + } + } + private fun visitWithDeclaration(declaration: FirDeclaration) { declaration.runComponents() withDeclaration(declaration) { - declaration.acceptChildren(this) + declaration.acceptChildren(this, null) } } @@ -155,7 +185,7 @@ abstract class AbstractDiagnosticCollector( declaration, receiverTypeRef?.coneTypeSafe() ) { - declaration.acceptChildren(this) + declaration.acceptChildren(this, null) } } } @@ -171,6 +201,16 @@ abstract class AbstractDiagnosticCollector( } } + private inline fun withExpression(expression: FirExpression, block: () -> R): R { + val previousExpression = lastExpression + lastExpression = expression + try { + return block() + } finally { + lastExpression = previousExpression + } + } + private inline fun withLabelAndReceiverType( labelName: Name?, owner: FirDeclaration, diff --git a/compiler/testData/diagnostics/tests/EnumEntryAsType.fir.kt b/compiler/testData/diagnostics/tests/EnumEntryAsType.fir.kt index 6cec2bd2f5f..22e54ee7a22 100644 --- a/compiler/testData/diagnostics/tests/EnumEntryAsType.fir.kt +++ b/compiler/testData/diagnostics/tests/EnumEntryAsType.fir.kt @@ -12,15 +12,15 @@ class MyColor(val x: Color.RED, y: Color.RED = Color.RED set(arg: Color.RED) { z = arg } - fun foo(arg: Color.RED): Color.RED = arg + fun foo(arg: Color.RED): Color.RED = arg fun bar(): Color.RED { class Local : Color.RED - fun local(arg: Color.RED): Color.RED = arg + fun local(arg: Color.RED): Color.RED = arg val temp: Color.RED = Color.RED temp as? Color.RED if (temp is Color.RED) { - return temp as Color.RED + return temp as Color.RED } val obj = object : Color.RED {} if (obj is Color.RED) { diff --git a/compiler/testData/diagnostics/tests/cast/bare/ToErrorType.fir.kt b/compiler/testData/diagnostics/tests/cast/bare/ToErrorType.fir.kt index f7957c26bd5..65f47e1f487 100644 --- a/compiler/testData/diagnostics/tests/cast/bare/ToErrorType.fir.kt +++ b/compiler/testData/diagnostics/tests/cast/bare/ToErrorType.fir.kt @@ -1,6 +1,6 @@ class P fun foo(p: P): Any { - val v = p as G + val v = p as G return v } \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/classObjects/typeParametersInAnnonymousObject.fir.kt b/compiler/testData/diagnostics/tests/classObjects/typeParametersInAnnonymousObject.fir.kt index c2dd8466f4b..867811148ad 100644 --- a/compiler/testData/diagnostics/tests/classObjects/typeParametersInAnnonymousObject.fir.kt +++ b/compiler/testData/diagnostics/tests/classObjects/typeParametersInAnnonymousObject.fir.kt @@ -15,12 +15,12 @@ fun case_3() { } val x = object> { - fun test() = 10 as T // OK + fun test() = 10 as T // OK } fun case_4() { val x = object { - fun test() = 10 as T + fun test() = 10 as T } val y = x.test() // type y is T diff --git a/compiler/testData/diagnostics/tests/classObjects/typeParametersInAnnonymousObject_after.fir.kt b/compiler/testData/diagnostics/tests/classObjects/typeParametersInAnnonymousObject_after.fir.kt index 01ac5be3a6a..98e5b7c9b47 100644 --- a/compiler/testData/diagnostics/tests/classObjects/typeParametersInAnnonymousObject_after.fir.kt +++ b/compiler/testData/diagnostics/tests/classObjects/typeParametersInAnnonymousObject_after.fir.kt @@ -15,12 +15,12 @@ fun case_3() { } val x = object> { - fun test() = 10 as T // OK + fun test() = 10 as T // OK } fun case_4() { val x = object { - fun test() = 10 as T + fun test() = 10 as T } val y = x.test() // type y is T diff --git a/compiler/testData/diagnostics/tests/generics/capturedParameters/uncheckedCast.fir.kt b/compiler/testData/diagnostics/tests/generics/capturedParameters/uncheckedCast.fir.kt index 28b3eda8b61..96b9143f201 100644 --- a/compiler/testData/diagnostics/tests/generics/capturedParameters/uncheckedCast.fir.kt +++ b/compiler/testData/diagnostics/tests/generics/capturedParameters/uncheckedCast.fir.kt @@ -22,7 +22,7 @@ fun foo(x: Any, y: Any) : Any { return y } - y as Outer<*>.Inner + y as Outer<*>.Inner return C() } diff --git a/compiler/testData/diagnostics/tests/incompleteCode/SupertypeOfErrorType.fir.kt b/compiler/testData/diagnostics/tests/incompleteCode/SupertypeOfErrorType.fir.kt index e97bd30e331..81c11001f22 100644 --- a/compiler/testData/diagnostics/tests/incompleteCode/SupertypeOfErrorType.fir.kt +++ b/compiler/testData/diagnostics/tests/incompleteCode/SupertypeOfErrorType.fir.kt @@ -15,7 +15,7 @@ fun foo() { } } -fun bar(i: Int, a: U) { +fun bar(i: Int, a: U) { val r = if (true) i else a val b: Any = r } diff --git a/compiler/testData/diagnostics/tests/typealias/inhreritedTypeAliasQualifiedByDerivedClass.fir.kt b/compiler/testData/diagnostics/tests/typealias/inhreritedTypeAliasQualifiedByDerivedClass.fir.kt index e5189925823..426a57db05d 100644 --- a/compiler/testData/diagnostics/tests/typealias/inhreritedTypeAliasQualifiedByDerivedClass.fir.kt +++ b/compiler/testData/diagnostics/tests/typealias/inhreritedTypeAliasQualifiedByDerivedClass.fir.kt @@ -6,7 +6,7 @@ open class Base { class Derived : Base() -fun test(x: Derived.Nested) = x +fun test(x: Derived.Nested) = x fun Base.testWithImplicitReceiver(x: Nested) { val y: Nested = x diff --git a/compiler/testData/diagnostics/tests/typealias/innerTypeAliasAsType.fir.kt b/compiler/testData/diagnostics/tests/typealias/innerTypeAliasAsType.fir.kt index 04837f435e2..cfc76a6ec91 100644 --- a/compiler/testData/diagnostics/tests/typealias/innerTypeAliasAsType.fir.kt +++ b/compiler/testData/diagnostics/tests/typealias/innerTypeAliasAsType.fir.kt @@ -11,12 +11,12 @@ class Outer { typealias InnerAlias = Inner typealias GenericInnerAlias = GenericInner - fun test1(x: NestedAlias) = x - fun test2(x: GenericNestedAlias) = x - fun test3(x: GenericNestedAlias) = x - fun test4(x: InnerAlias) = x - fun test5(x: GenericInnerAlias) = x - fun test6(x: GenericInnerAlias) = x + fun test1(x: NestedAlias) = x + fun test2(x: GenericNestedAlias) = x + fun test3(x: GenericNestedAlias) = x + fun test4(x: InnerAlias) = x + fun test5(x: GenericInnerAlias) = x + fun test6(x: GenericInnerAlias) = x } fun test1(x: Outer.NestedAlias) = x fun test2(x: Outer.NestedAlias) = x