Distinguish between property + local and the rest
diff --git a/compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/invalidates.fir.txt b/compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/invalidates.fir.txt index 9586bb7..07ae446 100644 --- a/compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/invalidates.fir.txt +++ b/compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/invalidates.fir.txt
@@ -79,3 +79,22 @@ lval z: R|B| = R|<local>/y| R|<local>/x|.R|/B.n| = Int(2) } + public final data class C : R|kotlin/Any| { + public constructor(b: R|B|): R|C| { + super<R|kotlin/Any|>() + } + + public final var b: R|B| = R|<local>/b| + public get(): R|B| + public set(value: R|B|): R|kotlin/Unit| + + public final operator fun component1(): R|B| + + public final fun copy(b: R|B| = this@R|/C|.R|/C.b|): R|C| + + } + public final fun test6(): R|kotlin/Unit| { + lval x: R|C| = R|/C.C|(R|/B.B|(Int(1))) + lvar y: R|B| = R|<local>/x|.R|/C.b| + R|<local>/y|.R|/B.n| = Int(2) + }
diff --git a/compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/invalidates.kt b/compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/invalidates.kt index 2e3d267..5762ca0 100644 --- a/compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/invalidates.kt +++ b/compiler/fir/analysis-tests/testData/resolveWithStdlib/contracts/invalidates.kt
@@ -22,13 +22,13 @@ val t = x.uses { it.n } val g = <!INVALIDATED_REFERENCE("INVALIDATED")!>x<!>.n val h = <!INVALIDATED_REFERENCE("INVALIDATED")!>x<!> - <!INVALIDATED_REFERENCE("INVALIDATED"), TWO_REFERENCES("[x, h]")!>x<!>.foo() + <!INVALIDATED_REFERENCE("INVALIDATED"), MULTIPLE_REFERENCES("[x, h]")!>x<!>.foo() } fun test2() { val x = A(1) val y = x - val t = <!TWO_REFERENCES("[x, y]")!>x<!>.uses { it.n } + val t = <!MULTIPLE_REFERENCES("[x, y]")!>x<!>.uses { it.n } <!INVALIDATED_REFERENCE("INVALIDATED")!>y<!>.foo() } @@ -37,7 +37,7 @@ fun test3() { val x = B(1) val y = x - <!TWO_REFERENCES("[x, y]")!>y<!>.n = 2 + <!MULTIPLE_REFERENCES("[x, y]")!>y<!>.n = 2 } fun test4() { @@ -49,10 +49,18 @@ fun test5() { val x = B(1) val y = x - val z = <!TWO_REFERENCES("[x, y]")!>y<!> + val z = <!MULTIPLE_REFERENCES("[x, y]")!>y<!> <!MULTIPLE_REFERENCES("[x, y, z]")!>x<!>.n = 2 } +data class C(var b: B) + +fun test6() { + val x = C(B(1)) + var y = x.b + <!TWO_REFERENCES("[/C.b(x), y]")!>y<!>.n = 2 +} + /* GENERATED_FIR_TAGS: classDeclaration, classReference, contracts, funWithExtensionReceiver, functionDeclaration, functionalType, inline, lambdaLiteral, localProperty, nullableType, primaryConstructor, propertyDeclaration, thisExpression, typeParameter */
diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirMultipleReferencesChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirMultipleReferencesChecker.kt index ec77036..2058c11 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirMultipleReferencesChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirMultipleReferencesChecker.kt
@@ -16,7 +16,10 @@ import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccessExpression import org.jetbrains.kotlin.fir.isDisabled import org.jetbrains.kotlin.fir.types.hasResolvedType +import org.jetbrains.kotlin.fir.types.isNullableString import org.jetbrains.kotlin.fir.types.isPrimitiveOrNullablePrimitive +import org.jetbrains.kotlin.fir.types.isString +import org.jetbrains.kotlin.fir.types.isUnsignedTypeOrNullableUnsignedType import org.jetbrains.kotlin.fir.types.resolvedType import org.jetbrains.kotlin.fir.visitors.FirDefaultVisitorVoid @@ -39,12 +42,16 @@ override fun visitQualifiedAccessExpression(qualifiedAccessExpression: FirQualifiedAccessExpression) { qualifiedAccessExpression.acceptChildren(this) - if (qualifiedAccessExpression.hasResolvedType && qualifiedAccessExpression.resolvedType.isPrimitiveOrNullablePrimitive) return + if (qualifiedAccessExpression.hasResolvedType) { + val type = qualifiedAccessExpression.resolvedType + if (type.isPrimitiveOrNullablePrimitive || type.isUnsignedTypeOrNullableUnsignedType || type.isString || type.isNullableString) return + } + val references = qualifiedAccessExpression.domainReferences?.map { it.first } ?: return val problem = when { references in alreadyReported -> null references.size < 2 -> null - references.size == 2 -> FirErrors.TWO_REFERENCES + references.oneSimpleAndOneProperty() -> FirErrors.TWO_REFERENCES else -> FirErrors.MULTIPLE_REFERENCES } if (problem != null) { @@ -53,4 +60,7 @@ } } } + + fun List<String>.oneSimpleAndOneProperty(): Boolean = + size == 2 && any { it.contains("(") } && any { !it.contains("(") } }