Skip to content

Commit

Permalink
[FIR] Fix the user project failure
Browse files Browse the repository at this point in the history
The problem is caused by
`FirExpression.tryToSetSourceForImplicitReceiver()`:
at some point a smartcast expression is
instantiated with an `originalExpression`
with no source, but later it's replaced
with a one that does have a one,
which leads to inconsistency.

Also the receiver expression may have `null` source
instead of a fake source, which can also make
`FirJvmModuleAccessibilityTypeChecker` fail.

The change in `FirElementsRecorder` is
backed by:
`K2HighlightingMetaInfoTestGenerated.testImplicitAndExplicit_functions`
(otherwise `descr='Implicit receiver smart cast to Foo'`
is replaced with `descr='Smart cast to Foo'`),
as well as:
- `testImplicitAndExplicit_operators`
- `testImplicitAndExplicit_properties`
- `testImplicit_stable`
and `K2IdeK2CodeKotlinSteppingTestGenerated$Custom.testSmartStepIntoInterfaceImpl`.

The new `ImplicitThisReceiverExpression`
was introduced, because for some reason
assigning non-`-1` offsets to the
`IrGetValue` expressions (in
`Fir2IrVisitor.generateThisReceiverAccessForCallable`) in
`FirLightTreeBlackBoxInlineCodegenWithIrInlinerTestGenerated.testAnonymousObjectInCallChildren`
leads to incorrect SMAP generation:
when in `AbstractClassBuilder.visitSMAP`,
we can see that some `RangeMapping` are
incorrect in a sence that this code doesn't
expect them to be like this for some reason.
This is important because it also affects
the following pair of tests in intelij:

- `K1IdeK2CodeKotlinSteppingTestGenerated.StepInto#testKotlinSamFunction`
- `K1IdeK2CodeKotlinSteppingTestGenerated.SmartStepInto#testKotlinSamFunction`

These tests under the hood rely on a single
test file, and with such line numbers it's
impossible to satisfy them both at once:
they produce 2 contradicting changes. Namely,
the first one leads to:

```
kotlinSamFunction.kt:15
kotlinSamFunction.kt:6
resuming kotlinSamFunction.kt:15
Disconnected from the target VM
```

...while the second one - to:

```
kotlinSamFunction.kt:15
kotlinSamFunction.kt:15
Disconnected from the target VM
```

^KT-67469 Fixed
  • Loading branch information
lunakoly authored and qodana-bot committed Apr 19, 2024
1 parent 4409bb7 commit 8049cf9
Show file tree
Hide file tree
Showing 8 changed files with 34 additions and 160 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ internal open class FirElementsRecorder : FirVisitor<Unit, MutableMap<KtElement,
// For secondary constructors without explicit delegated constructor call, the PSI tree always create an empty
// KtConstructorDelegationCall. In this case, the source in FIR has this fake source kind.
it.kind == KtFakeSourceElementKind.ImplicitConstructor ||
it.kind == KtFakeSourceElementKind.SmartCastExpression ||
it.isSourceForSmartCasts(element) ||
it.kind == KtFakeSourceElementKind.DanglingModifierList ||
it.isSourceForArrayAugmentedAssign(element) ||
it.isSourceForCompoundAccess(element)
Expand Down Expand Up @@ -161,6 +161,16 @@ internal open class FirElementsRecorder : FirVisitor<Unit, MutableMap<KtElement,
// We need to filter it out to map this source element to set/plusAssign call, so we check `is FirFunctionCall`
(kind is KtFakeSourceElementKind.DesugaredAugmentedAssign) && fir is FirFunctionCall

// `FirSmartCastExpression` forward the source from the original expression,
// and implicit receivers have fake sources pointing to a wider part of the expression.
// Thus, `FirElementsRecorder` may try assigning an unnecessarily wide source
// to smart cast expressions, which will affect the
// `org.jetbrains.kotlin.idea.highlighting.highlighters.ExpressionsSmartcastHighlighter#highlightExpression`
// function in intellij.git
private fun KtSourceElement.isSourceForSmartCasts(fir: FirElement) =
(kind is KtFakeSourceElementKind.SmartCastExpression) && fir is FirSmartCastExpression && !fir.originalExpression.isImplicitThisReceiver

private val FirExpression.isImplicitThisReceiver get() = this is FirThisReceiverExpression && this.isImplicit

private fun FirElement.isReadInCompoundCall(): Boolean {
if (this is FirPropertyAccessExpression) return true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,11 @@ internal inline fun <T : IrElement> KtSourceElement?.convertWithOffsets(f: (star
val startOffset: Int
val endOffset: Int

if (isCompiledElement(psi) || this?.kind == KtFakeSourceElementKind.DataClassGeneratedMembers) {
if (
isCompiledElement(psi) ||
this?.kind == KtFakeSourceElementKind.DataClassGeneratedMembers ||
this?.kind == KtFakeSourceElementKind.ImplicitThisReceiverExpression
) {
startOffset = UNDEFINED_OFFSET
endOffset = UNDEFINED_OFFSET
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -180,13 +180,16 @@ private fun receiverExpression(
boundSymbol = symbol
this.contextReceiverNumber = contextReceiverNumber
}
val newSource = symbol.source?.fakeElement(KtFakeSourceElementKind.ImplicitThisReceiverExpression)
return when (inaccessibleReceiver) {
false -> buildThisReceiverExpression {
source = newSource
this.calleeReference = calleeReference
this.coneTypeOrNull = type
isImplicit = true
}
true -> buildInaccessibleReceiverExpression {
source = newSource
this.calleeReference = calleeReference
this.coneTypeOrNull = type
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ internal class FirSmartCastExpressionImpl(
override var smartcastTypeWithoutNullableNothing: FirTypeRef?,
override val smartcastStability: SmartcastStability,
) : FirSmartCastExpression() {
override val source: KtSourceElement? = originalExpression.source?.fakeElement(KtFakeSourceElementKind.SmartCastExpression)
override val source: KtSourceElement?
get() = originalExpression.source?.fakeElement(KtFakeSourceElementKind.SmartCastExpression)
override val isStable: Boolean
get() = smartcastStability == SmartcastStability.STABLE_VALUE

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -440,6 +440,7 @@ object ImplementationConfigurator : AbstractFirTreeImplementationConfigurator()
}
default("source") {
value = "originalExpression.source?.fakeElement(KtFakeSourceElementKind.SmartCastExpression)"
withGetter = true
}
additionalImports(fakeElementImport, fakeSourceElementKindImport)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ sealed class KtFakeSourceElementKind(final override val shouldSkipErrorTypeRepor
// destruction parameters, function literals, explicitly boolean expressions
object ImplicitTypeRef : KtFakeSourceElementKind(shouldSkipErrorTypeReporting = true)

// for errors on smartcast types then the type is brought in by an implicit this receiver expression
object ImplicitThisReceiverExpression : KtFakeSourceElementKind(shouldSkipErrorTypeReporting = false)

// for type arguments that were inferred as opposed to specified
// explicitly via `<>`
object ImplicitTypeArgument : KtFakeSourceElementKind()
Expand Down
Loading

0 comments on commit 8049cf9

Please sign in to comment.