diff --git a/compiler/fir/analysis-tests/testData/resolveWithStdlib/diagnostics/forLoopChecker.fir.txt b/compiler/fir/analysis-tests/testData/resolveWithStdlib/diagnostics/forLoopChecker.fir.txt new file mode 100644 index 00000000000..7f2c5817073 --- /dev/null +++ b/compiler/fir/analysis-tests/testData/resolveWithStdlib/diagnostics/forLoopChecker.fir.txt @@ -0,0 +1,206 @@ +FILE: forLoopChecker.kt + public final class NotRange1 : R|kotlin/Any| { + public constructor(): R|NotRange1| { + super() + } + + } + public abstract class NotRange2 : R|kotlin/Any| { + public constructor(): R|NotRange2| { + super() + } + + public abstract operator fun iterator(): R|kotlin/Unit| + + } + public abstract class ImproperIterator1 : R|kotlin/Any| { + public constructor(): R|ImproperIterator1| { + super() + } + + public abstract operator fun hasNext(): R|kotlin/Boolean| + + } + public abstract class NotRange3 : R|kotlin/Any| { + public constructor(): R|NotRange3| { + super() + } + + public abstract operator fun iterator(): R|ImproperIterator1| + + } + public abstract class ImproperIterator2 : R|kotlin/Any| { + public constructor(): R|ImproperIterator2| { + super() + } + + public abstract operator fun next(): R|kotlin/Boolean| + + } + public abstract class NotRange4 : R|kotlin/Any| { + public constructor(): R|NotRange4| { + super() + } + + public abstract operator fun iterator(): R|ImproperIterator2| + + } + public abstract class ImproperIterator3 : R|kotlin/Any| { + public constructor(): R|ImproperIterator3| { + super() + } + + public abstract operator fun hasNext(): R|kotlin/Int| + + public abstract operator fun next(): R|kotlin/Int| + + } + public abstract class NotRange5 : R|kotlin/Any| { + public constructor(): R|NotRange5| { + super() + } + + public abstract operator fun iterator(): R|ImproperIterator3| + + } + public abstract class AmbiguousHasNextIterator : R|kotlin/Any| { + public constructor(): R|AmbiguousHasNextIterator| { + super() + } + + public abstract operator fun hasNext(): R|kotlin/Boolean| + + public final val hasNext: R|kotlin/Boolean| + public get(): R|kotlin/Boolean| { + ^ Boolean(false) + } + + public abstract operator fun next(): R|kotlin/Int| + + } + public abstract class NotRange6 : R|kotlin/Any| { + public constructor(): R|NotRange6| { + super() + } + + public abstract operator fun iterator(): R|AmbiguousHasNextIterator| + + } + public abstract class ImproperIterator4 : R|kotlin/Any| { + public constructor(): R|ImproperIterator4| { + super() + } + + public final val hasNext: R|kotlin/Int| + public get(): R|kotlin/Int| { + ^ Int(1) + } + + public abstract operator fun next(): R|kotlin/Int| + + } + public abstract class NotRange7 : R|kotlin/Any| { + public constructor(): R|NotRange7| { + super() + } + + public abstract operator fun iterator(): R|ImproperIterator3| + + } + public abstract class GoodIterator : R|kotlin/Any| { + public constructor(): R|GoodIterator| { + super() + } + + public abstract operator fun hasNext(): R|kotlin/Boolean| + + public abstract operator fun next(): R|kotlin/Int| + + } + public abstract class Range0 : R|kotlin/Any| { + public constructor(): R|Range0| { + super() + } + + public abstract operator fun iterator(): R|GoodIterator| + + } + public abstract class Range1 : R|kotlin/Any| { + public constructor(): R|Range1| { + super() + } + + public abstract operator fun iterator(): R|kotlin/collections/Iterator| + + } + public abstract class ImproperIterator5 : R|kotlin/Any| { + public constructor(): R|ImproperIterator5| { + super() + } + + public abstract val R|kotlin/String|.hasNext: R|kotlin/Boolean| + public get(): R|kotlin/Boolean| + + public abstract operator fun next(): R|kotlin/Int| + + } + public abstract class NotRange8 : R|kotlin/Any| { + public constructor(): R|NotRange8| { + super() + } + + public abstract operator fun iterator(): R|ImproperIterator5| + + } + public final fun test(notRange1: R|NotRange1|, notRange2: R|NotRange2|, notRange3: R|NotRange3|, notRange4: R|NotRange4|, notRange5: R|NotRange5|, notRange6: R|NotRange6|, notRange7: R|NotRange7|, notRange8: R|NotRange8|, range0: R|Range0|, range1: R|Range1|): R|kotlin/Unit| { + lval : R|ERROR CLASS: Unresolved name: iterator| = R|/notRange1|.#() + while(R|/|.#()) { + lval i: R|ERROR CLASS: Unresolved name: next| = R|/|.#() + } + + lval : R|kotlin/Unit| = R|/notRange2|.R|/NotRange2.iterator|() + while(R|/|.#()) { + lval i: R|ERROR CLASS: Unresolved name: next| = R|/|.#() + } + + lval : R|ImproperIterator1| = R|/notRange3|.R|/NotRange3.iterator|() + while(R|/|.R|/ImproperIterator1.hasNext|()) { + lval i: R|ERROR CLASS: Unresolved name: next| = R|/|.#() + } + + lval : R|ImproperIterator2| = R|/notRange4|.R|/NotRange4.iterator|() + while(R|/|.#()) { + lval i: R|kotlin/Boolean| = R|/|.R|/ImproperIterator2.next|() + } + + lval : R|ImproperIterator3| = R|/notRange5|.R|/NotRange5.iterator|() + while(R|/|.R|/ImproperIterator3.hasNext|()) { + lval i: R|kotlin/Int| = R|/|.R|/ImproperIterator3.next|() + } + + lval : R|AmbiguousHasNextIterator| = R|/notRange6|.R|/NotRange6.iterator|() + while(R|/|.R|/AmbiguousHasNextIterator.hasNext|()) { + lval i: R|kotlin/Int| = R|/|.R|/AmbiguousHasNextIterator.next|() + } + + lval : R|ImproperIterator3| = R|/notRange7|.R|/NotRange7.iterator|() + while(R|/|.R|/ImproperIterator3.hasNext|()) { + lval i: R|kotlin/Int| = R|/|.R|/ImproperIterator3.next|() + } + + lval : R|ImproperIterator5| = R|/notRange8|.R|/NotRange8.iterator|() + while(R|/|.#()) { + lval i: R|kotlin/Int| = R|/|.R|/ImproperIterator5.next|() + } + + lval : R|GoodIterator| = R|/range0|.R|/Range0.iterator|() + while(R|/|.R|/GoodIterator.hasNext|()) { + lval i: R|kotlin/Int| = R|/|.R|/GoodIterator.next|() + } + + lval : R|kotlin/collections/Iterator| = R|/range1|.R|/Range1.iterator|() + while(R|/|.R|kotlin/collections/Iterator.hasNext|()) { + lval i: R|kotlin/Int| = R|/|.R|SubstitutionOverride|() + } + + } diff --git a/compiler/fir/analysis-tests/testData/resolveWithStdlib/diagnostics/forLoopChecker.kt b/compiler/fir/analysis-tests/testData/resolveWithStdlib/diagnostics/forLoopChecker.kt new file mode 100644 index 00000000000..6f0459ccc61 --- /dev/null +++ b/compiler/fir/analysis-tests/testData/resolveWithStdlib/diagnostics/forLoopChecker.kt @@ -0,0 +1,89 @@ +import java.util.*; + +class NotRange1() { + +} + +abstract class NotRange2() { + abstract operator fun iterator() : Unit +} + +abstract class ImproperIterator1 { + abstract operator fun hasNext() : Boolean +} + +abstract class NotRange3() { + abstract operator fun iterator() : ImproperIterator1 +} + +abstract class ImproperIterator2 { + abstract operator fun next() : Boolean +} + +abstract class NotRange4() { + abstract operator fun iterator() : ImproperIterator2 +} + +abstract class ImproperIterator3 { + abstract operator fun hasNext() : Int + abstract operator fun next() : Int +} + +abstract class NotRange5() { + abstract operator fun iterator() : ImproperIterator3 +} + +abstract class AmbiguousHasNextIterator { + abstract operator fun hasNext() : Boolean + val hasNext : Boolean get() = false + abstract operator fun next() : Int +} + +abstract class NotRange6() { + abstract operator fun iterator() : AmbiguousHasNextIterator +} + +abstract class ImproperIterator4 { + val hasNext : Int get() = 1 + abstract operator fun next() : Int +} + +abstract class NotRange7() { + abstract operator fun iterator() : ImproperIterator3 +} + +abstract class GoodIterator { + abstract operator fun hasNext() : Boolean + abstract operator fun next() : Int +} + +abstract class Range0() { + abstract operator fun iterator() : GoodIterator +} + +abstract class Range1() { + abstract operator fun iterator() : Iterator +} + +abstract class ImproperIterator5 { + abstract val String.hasNext : Boolean + abstract operator fun next() : Int +} + +abstract class NotRange8() { + abstract operator fun iterator() : ImproperIterator5 +} + + +fun test(notRange1: NotRange1, notRange2: NotRange2, notRange3: NotRange3, notRange4: NotRange4, notRange5: NotRange5, notRange6: NotRange6, notRange7: NotRange7, notRange8: NotRange8, range0: Range0, range1: Range1) { + for (i in notRange1); + for (i in notRange2); + for (i in notRange3); + for (i in notRange4); + for (i in notRange5); + for (i in notRange6); + for (i in notRange7); + for (i in notRange8); + for (i in range0); + for (i in range1); +} diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirDiagnosticTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirDiagnosticTestGenerated.java index fd77d8d27a9..f685115bdd7 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirDiagnosticTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirDiagnosticTestGenerated.java @@ -4827,6 +4827,12 @@ public class FirDiagnosticTestGenerated extends AbstractFirDiagnosticTest { KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/fir/analysis-tests/testData/resolveWithStdlib/diagnostics"), Pattern.compile("^([^.]+)\\.kt$"), null, true); } + @Test + @TestMetadata("forLoopChecker.kt") + public void testForLoopChecker() throws Exception { + runTest("compiler/fir/analysis-tests/testData/resolveWithStdlib/diagnostics/forLoopChecker.kt"); + } + @Nested @TestMetadata("compiler/fir/analysis-tests/testData/resolveWithStdlib/diagnostics/functionReturnTypeMismatchChecker") @TestDataPath("$PROJECT_ROOT") diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirDiagnosticsWithLightTreeTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirDiagnosticsWithLightTreeTestGenerated.java index 1072ba6c78a..0ba39be874f 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirDiagnosticsWithLightTreeTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirDiagnosticsWithLightTreeTestGenerated.java @@ -4898,6 +4898,12 @@ public class FirDiagnosticsWithLightTreeTestGenerated extends AbstractFirDiagnos KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/fir/analysis-tests/testData/resolveWithStdlib/diagnostics"), Pattern.compile("^([^.]+)\\.kt$"), null, true); } + @Test + @TestMetadata("forLoopChecker.kt") + public void testForLoopChecker() throws Exception { + runTest("compiler/fir/analysis-tests/testData/resolveWithStdlib/diagnostics/forLoopChecker.kt"); + } + @Nested @TestMetadata("compiler/fir/analysis-tests/testData/resolveWithStdlib/diagnostics/functionReturnTypeMismatchChecker") @TestDataPath("$PROJECT_ROOT") diff --git a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt index 8a66765e9a2..abb69d7d3af 100644 --- a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt +++ b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt @@ -256,6 +256,15 @@ object DIAGNOSTICS_LIST : DiagnosticList() { val ASSIGN_OPERATOR_AMBIGUITY by error { parameter>>("candidates") } + val ITERATOR_AMBIGUITY by error(PositioningStrategy.REFERENCE_BY_QUALIFIED) { + parameter>>("candidates") + } + val HAS_NEXT_FUNCTION_AMBIGUITY by error(PositioningStrategy.REFERENCE_BY_QUALIFIED) { + parameter>>("candidates") + } + val NEXT_AMBIGUITY by error(PositioningStrategy.REFERENCE_BY_QUALIFIED) { + parameter>>("candidates") + } } val TYPES_AND_TYPE_PARAMETERS by object : DiagnosticGroup("Types & type parameters") { @@ -612,6 +621,7 @@ object DIAGNOSTICS_LIST : DiagnosticList() { parameter("operator") parameter("rhs") } + val ITERATOR_ON_NULLABLE by error() val UNNECESSARY_SAFE_CALL by warning(PositioningStrategy.SAFE_ACCESS) { parameter("receiverType") } @@ -648,6 +658,15 @@ object DIAGNOSTICS_LIST : DiagnosticList() { val CONVENTIONS by object : DiagnosticGroup("Conventions") { val NO_GET_METHOD by error(PositioningStrategy.ARRAY_ACCESS) val NO_SET_METHOD by error(PositioningStrategy.ARRAY_ACCESS) + val ITERATOR_MISSING by error() + val HAS_NEXT_MISSING by error() + val NEXT_MISSING by error() + val HAS_NEXT_FUNCTION_NONE_APPLICABLE by error { + parameter>>("candidates") + } + val NEXT_NONE_APPLICABLE by error { + parameter>>("candidates") + } } val TYPE_ALIAS by object : DiagnosticGroup("Type alias") { diff --git a/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt b/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt index 952c0085a08..072e90c92dc 100644 --- a/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt +++ b/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt @@ -208,6 +208,9 @@ object FirErrors { // Ambiguity val OVERLOAD_RESOLUTION_AMBIGUITY by error1>>(SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED) val ASSIGN_OPERATOR_AMBIGUITY by error1>>() + val ITERATOR_AMBIGUITY by error1>>(SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED) + val HAS_NEXT_FUNCTION_AMBIGUITY by error1>>(SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED) + val NEXT_AMBIGUITY by error1>>(SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED) // Types & type parameters val TYPE_MISMATCH by error2() @@ -362,6 +365,7 @@ object FirErrors { val UNSAFE_IMPLICIT_INVOKE_CALL by error1(SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED) val UNSAFE_INFIX_CALL by error3(SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED) val UNSAFE_OPERATOR_CALL by error3(SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED) + val ITERATOR_ON_NULLABLE by error0() val UNNECESSARY_SAFE_CALL by warning1(SourceElementPositioningStrategies.SAFE_ACCESS) val UNEXPECTED_SAFE_CALL by error0(SourceElementPositioningStrategies.SAFE_ACCESS) val UNNECESSARY_NOT_NULL_ASSERTION by warning1(SourceElementPositioningStrategies.OPERATOR) @@ -382,6 +386,11 @@ object FirErrors { // Conventions val NO_GET_METHOD by error0(SourceElementPositioningStrategies.ARRAY_ACCESS) val NO_SET_METHOD by error0(SourceElementPositioningStrategies.ARRAY_ACCESS) + val ITERATOR_MISSING by error0() + val HAS_NEXT_MISSING by error0() + val NEXT_MISSING by error0() + val HAS_NEXT_FUNCTION_NONE_APPLICABLE by error1>>() + val NEXT_NONE_APPLICABLE by error1>>() // Type alias val TOPLEVEL_TYPEALIASES_ONLY by error0() diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirForLoopChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirForLoopChecker.kt new file mode 100644 index 00000000000..90ec46ec8b4 --- /dev/null +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirForLoopChecker.kt @@ -0,0 +1,124 @@ +/* + * Copyright 2010-2021 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.fir.analysis.checkers.expression + +import com.intellij.psi.PsiElement +import org.jetbrains.kotlin.fir.FirFakeSourceElementKind +import org.jetbrains.kotlin.fir.FirSourceElement +import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext +import org.jetbrains.kotlin.fir.analysis.diagnostics.* +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.HAS_NEXT_FUNCTION_AMBIGUITY +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.HAS_NEXT_FUNCTION_NONE_APPLICABLE +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.HAS_NEXT_MISSING +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ITERATOR_AMBIGUITY +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ITERATOR_MISSING +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ITERATOR_ON_NULLABLE +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NEXT_AMBIGUITY +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NEXT_MISSING +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NEXT_NONE_APPLICABLE +import org.jetbrains.kotlin.fir.declarations.FirProperty +import org.jetbrains.kotlin.fir.expressions.FirBlock +import org.jetbrains.kotlin.fir.expressions.FirFunctionCall +import org.jetbrains.kotlin.fir.expressions.FirWhileLoop +import org.jetbrains.kotlin.fir.references.FirErrorNamedReference +import org.jetbrains.kotlin.fir.resolve.calls.InapplicableWrongReceiver +import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeAmbiguityError +import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeInapplicableCandidateError +import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeUnresolvedNameError +import org.jetbrains.kotlin.fir.symbols.AbstractFirBasedSymbol +import org.jetbrains.kotlin.psi.KtExpression +import org.jetbrains.kotlin.resolve.calls.tower.isSuccess + +object FirForLoopChecker : FirBlockChecker() { + override fun check(expression: FirBlock, context: CheckerContext, reporter: DiagnosticReporter) { + val statements = expression.statements + for ((iteratorDeclaration, whileLoop) in statements.windowed(2)) { + if (iteratorDeclaration !is FirProperty) continue + if (whileLoop !is FirWhileLoop) continue + if (iteratorDeclaration.source?.kind != FirFakeSourceElementKind.DesugaredForLoop) continue + val iteratorCall = iteratorDeclaration.initializer as FirFunctionCall + val source = iteratorCall.explicitReceiver?.source ?: iteratorCall.source + if (checkSpecialFunctionCall( + iteratorCall, + reporter, + source, + context, + ITERATOR_AMBIGUITY, + ITERATOR_MISSING, + unsafeCallFactory = ITERATOR_ON_NULLABLE + ) + ) { + continue + } + val hasNextCall = whileLoop.condition as FirFunctionCall + if (checkSpecialFunctionCall( + hasNextCall, + reporter, + source, + context, + HAS_NEXT_FUNCTION_AMBIGUITY, + HAS_NEXT_MISSING, + noneApplicableFactory = HAS_NEXT_FUNCTION_NONE_APPLICABLE + ) + ) { + continue + } + val elementDeclaration = whileLoop.block.statements.firstOrNull() as? FirProperty ?: continue + if (elementDeclaration.initializer?.source?.kind != FirFakeSourceElementKind.DesugaredForLoop) continue + val nextCall = elementDeclaration.initializer as FirFunctionCall + checkSpecialFunctionCall( + nextCall, + reporter, + source, + context, + NEXT_AMBIGUITY, + NEXT_MISSING, + noneApplicableFactory = NEXT_NONE_APPLICABLE + ) + } + } + + private fun checkSpecialFunctionCall( + call: FirFunctionCall, + reporter: DiagnosticReporter, + reportSource: FirSourceElement?, + context: CheckerContext, + ambiguityFactory: FirDiagnosticFactory1>>, + missingFactory: FirDiagnosticFactory0, + noneApplicableFactory: FirDiagnosticFactory1>>? = null, + unsafeCallFactory: FirDiagnosticFactory0? = null, + ): Boolean { + val calleeReference = call.calleeReference + if (calleeReference is FirErrorNamedReference) { + when (val diagnostic = calleeReference.diagnostic) { + is ConeAmbiguityError -> if (diagnostic.applicability.isSuccess) { + reporter.reportOn(reportSource, ambiguityFactory, diagnostic.candidates, context) + } else if (noneApplicableFactory != null) { + reporter.reportOn(reportSource, noneApplicableFactory, diagnostic.candidates, context) + } + is ConeUnresolvedNameError -> { + reporter.reportOn(reportSource, missingFactory, context) + } + is ConeInapplicableCandidateError -> { + if (unsafeCallFactory != null || noneApplicableFactory != null) { + diagnostic.candidate.diagnostics.filter { it.applicability == diagnostic.applicability }.forEach { + if (it is InapplicableWrongReceiver) { + if (unsafeCallFactory != null) { + reporter.reportOn(reportSource, unsafeCallFactory, context) + } else { + reporter.reportOn(reportSource, noneApplicableFactory!!, listOf(diagnostic.candidate.symbol), context) + } + return true + } + } + } + } + } + return true + } + return false + } +} \ No newline at end of file diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/collectors/components/ErrorNodeDiagnosticCollectorComponent.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/collectors/components/ErrorNodeDiagnosticCollectorComponent.kt index 935d57711c8..28440f05d57 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/collectors/components/ErrorNodeDiagnosticCollectorComponent.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/collectors/components/ErrorNodeDiagnosticCollectorComponent.kt @@ -16,8 +16,6 @@ import org.jetbrains.kotlin.fir.declarations.FirErrorFunction import org.jetbrains.kotlin.fir.diagnostics.ConeDiagnostic import org.jetbrains.kotlin.fir.expressions.* import org.jetbrains.kotlin.fir.references.FirErrorNamedReference -import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeAmbiguityError -import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeInapplicableCandidateError import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeUnresolvedNameError import org.jetbrains.kotlin.fir.types.ConeClassErrorType import org.jetbrains.kotlin.fir.types.FirErrorTypeRef @@ -92,7 +90,8 @@ class ErrorNodeDiagnosticCollectorComponent(collector: AbstractDiagnosticCollect if (source.elementType == KtNodeTypes.DESTRUCTURING_DECLARATION_ENTRY) { return } - if (source.kind == FirFakeSourceElementKind.ImplicitConstructor) { + if (source.kind == FirFakeSourceElementKind.ImplicitConstructor || source.kind == FirFakeSourceElementKind.DesugaredForLoop) { + // See FirForLoopChecker return } for (coneDiagnostic in diagnostic.toFirDiagnostics(source, qualifiedAccessSource)) { diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDefaultErrorMessages.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDefaultErrorMessages.kt index aead931b6ec..739a5984c82 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDefaultErrorMessages.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDefaultErrorMessages.kt @@ -110,6 +110,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.FORBIDDEN_VARARG_ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.FUNCTION_DECLARATION_WITH_NO_NAME import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.FUN_INTERFACE_CONSTRUCTOR_REFERENCE import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.GENERIC_THROWABLE_SUBCLASS +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.HAS_NEXT_FUNCTION_AMBIGUITY import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.HIDDEN import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ILLEGAL_CONST_EXPRESSION import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ILLEGAL_UNDERSCORE @@ -134,6 +135,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INSTANCE_ACCESS_B import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INTERFACE_WITH_SUPERCLASS import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INVALID_IF_AS_EXPRESSION import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INVALID_TYPE_OF_ANNOTATION_MEMBER +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ITERATOR_AMBIGUITY import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.KCLASS_WITH_NULLABLE_TYPE_PARAMETER_IN_SIGNATURE import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.LEAKED_IN_PLACE_LAMBDA import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.LOCAL_ANNOTATION_CLASS_ERROR @@ -152,6 +154,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NAMED_ARGUMENTS_N import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NAMED_PARAMETER_NOT_FOUND import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NAME_IN_CONSTRAINT_IS_NOT_A_TYPE_PARAMETER import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NESTED_CLASS_NOT_ALLOWED +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NEXT_AMBIGUITY import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NONE_APPLICABLE import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NON_ABSTRACT_FUNCTION_WITH_NO_BODY import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.NON_FINAL_MEMBER_IN_FINAL_CLASS @@ -439,6 +442,9 @@ class FirDefaultErrorMessages : DefaultErrorMessages.Extension { // Ambiguity map.put(OVERLOAD_RESOLUTION_AMBIGUITY, "Overload resolution ambiguity between candidates: {0}", SYMBOLS) map.put(ASSIGN_OPERATOR_AMBIGUITY, "Ambiguity between assign operator candidates: {0}", SYMBOLS) + map.put(ITERATOR_AMBIGUITY, "Method ''iterator()'' is ambiguous for this expression: {0}", SYMBOLS) + map.put(HAS_NEXT_FUNCTION_AMBIGUITY, "Method ''hasNext()'' is ambiguous for this expression: {0}", SYMBOLS) + map.put(NEXT_AMBIGUITY, "Method ''next()'' is ambiguous for this expression: {0}", SYMBOLS) // Types & type parameters map.put(TYPE_MISMATCH, "Type mismatch: inferred type is {1} but {0} was expected", TO_STRING, TO_STRING) diff --git a/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/checkers/CommonExpressionCheckers.kt b/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/checkers/CommonExpressionCheckers.kt index 654ec4921eb..2a7ddd267c2 100644 --- a/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/checkers/CommonExpressionCheckers.kt +++ b/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/checkers/CommonExpressionCheckers.kt @@ -60,4 +60,9 @@ object CommonExpressionCheckers : ExpressionCheckers() { FirReturnAllowedChecker, FirFunctionReturnTypeMismatchChecker ) + + override val blockCheckers: Set + get() = setOf( + FirForLoopChecker + ) } diff --git a/compiler/fir/raw-fir/light-tree2fir/src/org/jetbrains/kotlin/fir/lightTree/converter/ExpressionsConverter.kt b/compiler/fir/raw-fir/light-tree2fir/src/org/jetbrains/kotlin/fir/lightTree/converter/ExpressionsConverter.kt index ece52e140d4..e8b1193426e 100644 --- a/compiler/fir/raw-fir/light-tree2fir/src/org/jetbrains/kotlin/fir/lightTree/converter/ExpressionsConverter.kt +++ b/compiler/fir/raw-fir/light-tree2fir/src/org/jetbrains/kotlin/fir/lightTree/converter/ExpressionsConverter.kt @@ -944,13 +944,19 @@ class ExpressionsConverter( } } + val fakeSource = forLoop.toFirSourceElement(FirFakeSourceElementKind.DesugaredForLoop) val target: FirLoopTarget + // NB: FirForLoopChecker relies on this block existence and structure return buildBlock { - source = forLoop.toFirSourceElement() + source = fakeSource val iteratorVal = generateTemporaryVariable( - this@ExpressionsConverter.baseSession, null, Name.special(""), + this@ExpressionsConverter.baseSession, fakeSource, ITERATOR_NAME, buildFunctionCall { - calleeReference = buildSimpleNamedReference { name = Name.identifier("iterator") } + source = fakeSource + calleeReference = buildSimpleNamedReference { + source = fakeSource + name = OperatorNameConventions.ITERATOR + } explicitReceiver = rangeExpression } ) @@ -958,8 +964,12 @@ class ExpressionsConverter( statements += FirWhileLoopBuilder().apply { source = forLoop.toFirSourceElement() condition = buildFunctionCall { - calleeReference = buildSimpleNamedReference { name = Name.identifier("hasNext") } - explicitReceiver = generateResolvedAccessExpression(null, iteratorVal) + source = fakeSource + calleeReference = buildSimpleNamedReference { + source = fakeSource + name = OperatorNameConventions.HAS_NEXT + } + explicitReceiver = generateResolvedAccessExpression(fakeSource, iteratorVal) } // break/continue in the for loop condition will refer to an outer loop if any. // So, prepare the loop target after building the condition. @@ -975,8 +985,12 @@ class ExpressionsConverter( this@ExpressionsConverter.baseSession, null, if (multiDeclaration != null) DESTRUCTURING_NAME else parameter!!.firValueParameter.name, buildFunctionCall { - calleeReference = buildSimpleNamedReference { name = Name.identifier("next") } - explicitReceiver = generateResolvedAccessExpression(null, iteratorVal) + source = fakeSource + calleeReference = buildSimpleNamedReference { + source = fakeSource + name = OperatorNameConventions.NEXT + } + explicitReceiver = generateResolvedAccessExpression(fakeSource, iteratorVal) }, parameter!!.firValueParameter.returnTypeRef ) diff --git a/compiler/fir/raw-fir/psi2fir/src/org/jetbrains/kotlin/fir/builder/RawFirBuilder.kt b/compiler/fir/raw-fir/psi2fir/src/org/jetbrains/kotlin/fir/builder/RawFirBuilder.kt index c677db88912..72db5a2032d 100644 --- a/compiler/fir/raw-fir/psi2fir/src/org/jetbrains/kotlin/fir/builder/RawFirBuilder.kt +++ b/compiler/fir/raw-fir/psi2fir/src/org/jetbrains/kotlin/fir/builder/RawFirBuilder.kt @@ -1708,16 +1708,17 @@ open class RawFirBuilder( val ktParameter = expression.loopParameter val fakeSource = expression.toFirPsiSourceElement(FirFakeSourceElementKind.DesugaredForLoop) val target: FirLoopTarget + // NB: FirForLoopChecker relies on this block existence and structure return buildBlock { source = fakeSource val rangeSource = expression.loopRange?.toFirSourceElement(FirFakeSourceElementKind.DesugaredForLoop) val iteratorVal = generateTemporaryVariable( - baseSession, rangeSource, Name.special(""), + baseSession, rangeSource, ITERATOR_NAME, buildFunctionCall { source = fakeSource calleeReference = buildSimpleNamedReference { source = fakeSource - name = Name.identifier("iterator") + name = OperatorNameConventions.ITERATOR } explicitReceiver = rangeExpression }, @@ -1729,7 +1730,7 @@ open class RawFirBuilder( source = fakeSource calleeReference = buildSimpleNamedReference { source = fakeSource - name = Name.identifier("hasNext") + name = OperatorNameConventions.HAS_NEXT } explicitReceiver = generateResolvedAccessExpression(fakeSource, iteratorVal) } @@ -1755,7 +1756,7 @@ open class RawFirBuilder( source = fakeSource calleeReference = buildSimpleNamedReference { source = fakeSource - name = Name.identifier("next") + name = OperatorNameConventions.NEXT } explicitReceiver = generateResolvedAccessExpression(fakeSource, iteratorVal) }, diff --git a/compiler/fir/raw-fir/raw-fir.common/src/org/jetbrains/kotlin/fir/builder/BaseFirBuilder.kt b/compiler/fir/raw-fir/raw-fir.common/src/org/jetbrains/kotlin/fir/builder/BaseFirBuilder.kt index ee402ae6837..3062e92ea89 100644 --- a/compiler/fir/raw-fir/raw-fir.common/src/org/jetbrains/kotlin/fir/builder/BaseFirBuilder.kt +++ b/compiler/fir/raw-fir/raw-fir.common/src/org/jetbrains/kotlin/fir/builder/BaseFirBuilder.kt @@ -1207,5 +1207,7 @@ abstract class BaseFirBuilder(val baseSession: FirSession, val context: Conte val ANONYMOUS_OBJECT_NAME = Name.special("") val DESTRUCTURING_NAME = Name.special("") + + val ITERATOR_NAME = Name.special("") } } diff --git a/compiler/testData/diagnostics/tests/ForRangeConventions.fir.kt b/compiler/testData/diagnostics/tests/ForRangeConventions.fir.kt index 87cad73a5f3..68e03f81840 100644 --- a/compiler/testData/diagnostics/tests/ForRangeConventions.fir.kt +++ b/compiler/testData/diagnostics/tests/ForRangeConventions.fir.kt @@ -78,14 +78,14 @@ abstract class NotRange8() { fun test(notRange1: NotRange1, notRange2: NotRange2, notRange3: NotRange3, notRange4: NotRange4, notRange5: NotRange5, notRange6: NotRange6, notRange7: NotRange7, notRange8: NotRange8, range0: Range0, range1: Range1) { - for (i in notRange1); - for (i in notRange2); - for (i in notRange3); - for (i in notRange4); + for (i in notRange1); + for (i in notRange2); + for (i in notRange3); + for (i in notRange4); for (i in notRange5); for (i in notRange6); for (i in notRange7); - for (i in notRange8); + for (i in notRange8); for (i in range0); for (i in range1); diff --git a/compiler/testData/diagnostics/tests/Unresolved.fir.kt b/compiler/testData/diagnostics/tests/Unresolved.fir.kt index 1acaaa7bee4..99eb21fab03 100644 --- a/compiler/testData/diagnostics/tests/Unresolved.fir.kt +++ b/compiler/testData/diagnostics/tests/Unresolved.fir.kt @@ -22,11 +22,11 @@ fun testUnresolved() { is String -> a } - for (j in collection) { + for (j in collection) { var i: Int = j i += 1 foo1(j) - } + } } fun foo1(i: Int) {} diff --git a/compiler/testData/diagnostics/tests/controlStructures/ForLoopWithExtensionIteratorOnNullable.fir.kt b/compiler/testData/diagnostics/tests/controlStructures/ForLoopWithExtensionIteratorOnNullable.fir.kt deleted file mode 100644 index adc7b453dc3..00000000000 --- a/compiler/testData/diagnostics/tests/controlStructures/ForLoopWithExtensionIteratorOnNullable.fir.kt +++ /dev/null @@ -1,22 +0,0 @@ -// !WITH_NEW_INFERENCE -// See also KT-7428 -class Container(val k: K) -// iterator() must be an extension, otherwise code will not compile -operator fun Container.iterator(): Iterator = null!! - -fun test() { - val container: Container? = null - // Error - container.iterator() - // for extension iterator, this code compiles, but should not - for (s in container) {} -} -class OtherContainer(val k: K) { - operator fun iterator(): Iterator = null!! -} - -fun test2() { - val other: OtherContainer? = null - // Error - for (s in other) {} -} diff --git a/compiler/testData/diagnostics/tests/controlStructures/ForLoopWithExtensionIteratorOnNullable.kt b/compiler/testData/diagnostics/tests/controlStructures/ForLoopWithExtensionIteratorOnNullable.kt index 419a961aa80..626cc350a99 100644 --- a/compiler/testData/diagnostics/tests/controlStructures/ForLoopWithExtensionIteratorOnNullable.kt +++ b/compiler/testData/diagnostics/tests/controlStructures/ForLoopWithExtensionIteratorOnNullable.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL // !WITH_NEW_INFERENCE // See also KT-7428 class Container(val k: K) diff --git a/compiler/testData/diagnostics/tests/controlStructures/ForbidStatementAsDirectFunctionBody.fir.kt b/compiler/testData/diagnostics/tests/controlStructures/ForbidStatementAsDirectFunctionBody.fir.kt index e992d0c1e90..08721244579 100644 --- a/compiler/testData/diagnostics/tests/controlStructures/ForbidStatementAsDirectFunctionBody.fir.kt +++ b/compiler/testData/diagnostics/tests/controlStructures/ForbidStatementAsDirectFunctionBody.fir.kt @@ -1,7 +1,7 @@ fun foo1() = while (b()) {} -fun foo2() = for (i in 10) {} +fun foo2() = for (i in 10) {} fun foo3() = when (b()) { true -> 1 diff --git a/compiler/testData/diagnostics/tests/controlStructures/forLoopWithNullableRange.fir.kt b/compiler/testData/diagnostics/tests/controlStructures/forLoopWithNullableRange.fir.kt index 8940c91415f..d43f2741583 100644 --- a/compiler/testData/diagnostics/tests/controlStructures/forLoopWithNullableRange.fir.kt +++ b/compiler/testData/diagnostics/tests/controlStructures/forLoopWithNullableRange.fir.kt @@ -8,7 +8,7 @@ class It { } fun test(c: Coll?) { - for (x in c) {} + for (x in c) {} if (c != null) { for(x in c) {} diff --git a/compiler/testData/diagnostics/tests/controlStructures/forWithNullableIterator.fir.kt b/compiler/testData/diagnostics/tests/controlStructures/forWithNullableIterator.fir.kt index 2daa6c2e4fd..5ec65699826 100644 --- a/compiler/testData/diagnostics/tests/controlStructures/forWithNullableIterator.fir.kt +++ b/compiler/testData/diagnostics/tests/controlStructures/forWithNullableIterator.fir.kt @@ -8,5 +8,5 @@ class It { } fun test() { - for (x in Coll()) {} + for (x in Coll()) {} } \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/declarationChecks/destructuringDeclarations/ForLoopMissingLoopParameter.fir.kt b/compiler/testData/diagnostics/tests/declarationChecks/destructuringDeclarations/ForLoopMissingLoopParameter.fir.kt index 27d22aa856c..12829960c33 100644 --- a/compiler/testData/diagnostics/tests/declarationChecks/destructuringDeclarations/ForLoopMissingLoopParameter.fir.kt +++ b/compiler/testData/diagnostics/tests/declarationChecks/destructuringDeclarations/ForLoopMissingLoopParameter.fir.kt @@ -1,15 +1,15 @@ // !WITH_NEW_INFERENCE fun useDeclaredVariables() { - for ((a, b)) { + for ((a, b)) { a b - } + } } fun checkersShouldRun() { - for ((@A a, _)) { + for ((@A a, _)) { - } + } } annotation class A diff --git a/compiler/testData/diagnostics/tests/incompleteCode/checkNothingIsSubtype.fir.kt b/compiler/testData/diagnostics/tests/incompleteCode/checkNothingIsSubtype.fir.kt index 392bb16b338..f1d324fea07 100644 --- a/compiler/testData/diagnostics/tests/incompleteCode/checkNothingIsSubtype.fir.kt +++ b/compiler/testData/diagnostics/tests/incompleteCode/checkNothingIsSubtype.fir.kt @@ -11,6 +11,6 @@ fun test(nothing: Nothing?) { } fun sum(a : IntArray) : Int { -for (n -return "?" +for (n +return "?" } diff --git a/compiler/testData/diagnostics/tests/incompleteCode/pseudocodeTraverseNextInstructions.fir.kt b/compiler/testData/diagnostics/tests/incompleteCode/pseudocodeTraverseNextInstructions.fir.kt index 4077f374e3c..40f20ac8c18 100644 --- a/compiler/testData/diagnostics/tests/incompleteCode/pseudocodeTraverseNextInstructions.fir.kt +++ b/compiler/testData/diagnostics/tests/incompleteCode/pseudocodeTraverseNextInstructions.fir.kt @@ -1,8 +1,8 @@ package b fun foo() { - for (i in collection) { + for (i in collection) { { break } -} +} diff --git a/compiler/testData/diagnostics/tests/inference/reportingImprovements/typeInferenceFailedOnIteratorCall.fir.kt b/compiler/testData/diagnostics/tests/inference/reportingImprovements/typeInferenceFailedOnIteratorCall.fir.kt index 1ae94dc5341..d0f279d0dcd 100644 --- a/compiler/testData/diagnostics/tests/inference/reportingImprovements/typeInferenceFailedOnIteratorCall.fir.kt +++ b/compiler/testData/diagnostics/tests/inference/reportingImprovements/typeInferenceFailedOnIteratorCall.fir.kt @@ -4,6 +4,6 @@ class X operator fun X.iterator(): Iterable = TODO() fun test() { - for (i in X()) { - } + for (i in X()) { + } } diff --git a/compiler/testData/diagnostics/tests/operatorsOverloading/IteratorAmbiguity.fir.kt b/compiler/testData/diagnostics/tests/operatorsOverloading/IteratorAmbiguity.fir.kt deleted file mode 100644 index 52fe90ad2fb..00000000000 --- a/compiler/testData/diagnostics/tests/operatorsOverloading/IteratorAmbiguity.fir.kt +++ /dev/null @@ -1,27 +0,0 @@ -//KT-1821 Write test for ITERATOR_AMBIGUITY diagnostic - -interface MyCollectionInterface { -} - -interface MyAnotherCollectionInterface { -} - -class MyCollection : MyCollectionInterface, MyAnotherCollectionInterface { -} - -fun MyCollectionInterface.iterator() = MyIterator() - -fun MyAnotherCollectionInterface.iterator() = MyIterator() - -class MyIterator { - fun next() : MyElement = MyElement() - fun hasNext() = true -} - -class MyElement - -fun test1(collection: MyCollection) { - collection.iterator() - for (element in collection) { - } -} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/operatorsOverloading/IteratorAmbiguity.kt b/compiler/testData/diagnostics/tests/operatorsOverloading/IteratorAmbiguity.kt index 82e00331c6d..ab8f1f3fd4a 100644 --- a/compiler/testData/diagnostics/tests/operatorsOverloading/IteratorAmbiguity.kt +++ b/compiler/testData/diagnostics/tests/operatorsOverloading/IteratorAmbiguity.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL //KT-1821 Write test for ITERATOR_AMBIGUITY diagnostic interface MyCollectionInterface { diff --git a/compiler/testData/diagnostics/tests/platformTypes/nullabilityWarnings/for.fir.kt b/compiler/testData/diagnostics/tests/platformTypes/nullabilityWarnings/for.fir.kt deleted file mode 100644 index cddddf5f0d9..00000000000 --- a/compiler/testData/diagnostics/tests/platformTypes/nullabilityWarnings/for.fir.kt +++ /dev/null @@ -1,28 +0,0 @@ -// FILE: J.java - -import org.jetbrains.annotations.*; -import java.util.*; - -public class J { - @NotNull - public static List staticNN; - @Nullable - public static List staticN; - public static List staticJ; -} - -// FILE: k.kt - -fun test() { - // @NotNull platform type - val platformNN = J.staticNN - // @Nullable platform type - val platformN = J.staticN - // platform type with no annotation - val platformJ = J.staticJ - - for (x in platformNN) {} - for (x in platformN) {} - for (x in platformJ) {} -} - diff --git a/compiler/testData/diagnostics/tests/platformTypes/nullabilityWarnings/for.kt b/compiler/testData/diagnostics/tests/platformTypes/nullabilityWarnings/for.kt index ca8c0f7a26a..e0ef2e8a3ca 100644 --- a/compiler/testData/diagnostics/tests/platformTypes/nullabilityWarnings/for.kt +++ b/compiler/testData/diagnostics/tests/platformTypes/nullabilityWarnings/for.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL // FILE: J.java import org.jetbrains.annotations.*; diff --git a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/unnecessaryWithMap.fir.kt b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/unnecessaryWithMap.fir.kt index e7d39fb3d7d..2383d0367d1 100644 --- a/compiler/testData/diagnostics/tests/smartCasts/varnotnull/unnecessaryWithMap.fir.kt +++ b/compiler/testData/diagnostics/tests/smartCasts/varnotnull/unnecessaryWithMap.fir.kt @@ -14,8 +14,8 @@ class MyClass { var res = 0 m = create() // See KT-7428 - for ((k, v) in m) - res += (k.length + v.length) + for ((k, v) in m) + res += (k.length + v.length) return res } } diff --git a/compiler/testData/diagnostics/testsWithStdLib/contracts/dsl/errors/illegalConstructionInContractBlock.fir.kt b/compiler/testData/diagnostics/testsWithStdLib/contracts/dsl/errors/illegalConstructionInContractBlock.fir.kt index c7be0627094..abf4acbac27 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/contracts/dsl/errors/illegalConstructionInContractBlock.fir.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/contracts/dsl/errors/illegalConstructionInContractBlock.fir.kt @@ -25,7 +25,7 @@ fun whenInContract(x: Any?, boolean: Boolean) { fun forInContract(x: Any?) { contract { - for (i in 0..1) { + for (i in 0..1) { returns() implies (x is String) } } diff --git a/compiler/testData/diagnostics/testsWithStdLib/typealias/typeAliasSamAdapterConstructors2.fir.kt b/compiler/testData/diagnostics/testsWithStdLib/typealias/typeAliasSamAdapterConstructors2.fir.kt index 260e9c5bdb0..25041c6bcef 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/typealias/typeAliasSamAdapterConstructors2.fir.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/typealias/typeAliasSamAdapterConstructors2.fir.kt @@ -23,5 +23,5 @@ typealias C2 = JHost.Consumer2 val test1 = R { } val test2 = C { s -> println(s.length) } val test3 = CStr { s -> println(s.length) } -val test4 = CStrList { ss -> for (s in ss) { println(s.length) } } +val test4 = CStrList { ss -> for (s in ss) { println(s.length) } } val test5 = C2 { a, b -> val x: Int = a + b; println(x)} diff --git a/compiler/tests-spec/testData/diagnostics/linked/statements/assignments/p-1/neg/2.1.fir.kt b/compiler/tests-spec/testData/diagnostics/linked/statements/assignments/p-1/neg/2.1.fir.kt index 44616393786..db60c3ca083 100644 --- a/compiler/tests-spec/testData/diagnostics/linked/statements/assignments/p-1/neg/2.1.fir.kt +++ b/compiler/tests-spec/testData/diagnostics/linked/statements/assignments/p-1/neg/2.1.fir.kt @@ -7,7 +7,7 @@ * NOTE: right-hand side of an assignment must be expression */ fun case1() { - val x = for () { } + val x = for () { } val y = for (x in 1..2) { } val a = while () { } @@ -20,7 +20,7 @@ fun case1() { * NOTE: right-hand side of an assignment must be expression */ fun case2() { - var x = for () { } + var x = for () { } var y = for (x in 1..2) { } var a = while () { } @@ -40,7 +40,7 @@ fun case3() { var b :Any? var c :Any? - x = for () { } + x = for () { } y = for (x in 1..2) { } a = while () { } diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDataClassConverters.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDataClassConverters.kt index 561ccce2e0b..9db6d82745c 100644 --- a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDataClassConverters.kt +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDataClassConverters.kt @@ -823,6 +823,33 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert token, ) } + add(FirErrors.ITERATOR_AMBIGUITY) { firDiagnostic -> + IteratorAmbiguityImpl( + firDiagnostic.a.map { abstractFirBasedSymbol -> + firSymbolBuilder.buildSymbol(abstractFirBasedSymbol.fir as FirDeclaration) + }, + firDiagnostic as FirPsiDiagnostic<*>, + token, + ) + } + add(FirErrors.HAS_NEXT_FUNCTION_AMBIGUITY) { firDiagnostic -> + HasNextFunctionAmbiguityImpl( + firDiagnostic.a.map { abstractFirBasedSymbol -> + firSymbolBuilder.buildSymbol(abstractFirBasedSymbol.fir as FirDeclaration) + }, + firDiagnostic as FirPsiDiagnostic<*>, + token, + ) + } + add(FirErrors.NEXT_AMBIGUITY) { firDiagnostic -> + NextAmbiguityImpl( + firDiagnostic.a.map { abstractFirBasedSymbol -> + firSymbolBuilder.buildSymbol(abstractFirBasedSymbol.fir as FirDeclaration) + }, + firDiagnostic as FirPsiDiagnostic<*>, + token, + ) + } add(FirErrors.TYPE_MISMATCH) { firDiagnostic -> TypeMismatchImpl( firSymbolBuilder.typeBuilder.buildKtType(firDiagnostic.a), @@ -1704,6 +1731,12 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert token, ) } + add(FirErrors.ITERATOR_ON_NULLABLE) { firDiagnostic -> + IteratorOnNullableImpl( + firDiagnostic as FirPsiDiagnostic<*>, + token, + ) + } add(FirErrors.UNNECESSARY_SAFE_CALL) { firDiagnostic -> UnnecessarySafeCallImpl( firSymbolBuilder.typeBuilder.buildKtType(firDiagnostic.a), @@ -1784,6 +1817,42 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert token, ) } + add(FirErrors.ITERATOR_MISSING) { firDiagnostic -> + IteratorMissingImpl( + firDiagnostic as FirPsiDiagnostic<*>, + token, + ) + } + add(FirErrors.HAS_NEXT_MISSING) { firDiagnostic -> + HasNextMissingImpl( + firDiagnostic as FirPsiDiagnostic<*>, + token, + ) + } + add(FirErrors.NEXT_MISSING) { firDiagnostic -> + NextMissingImpl( + firDiagnostic as FirPsiDiagnostic<*>, + token, + ) + } + add(FirErrors.HAS_NEXT_FUNCTION_NONE_APPLICABLE) { firDiagnostic -> + HasNextFunctionNoneApplicableImpl( + firDiagnostic.a.map { abstractFirBasedSymbol -> + firSymbolBuilder.buildSymbol(abstractFirBasedSymbol.fir as FirDeclaration) + }, + firDiagnostic as FirPsiDiagnostic<*>, + token, + ) + } + add(FirErrors.NEXT_NONE_APPLICABLE) { firDiagnostic -> + NextNoneApplicableImpl( + firDiagnostic.a.map { abstractFirBasedSymbol -> + firSymbolBuilder.buildSymbol(abstractFirBasedSymbol.fir as FirDeclaration) + }, + firDiagnostic as FirPsiDiagnostic<*>, + token, + ) + } add(FirErrors.TOPLEVEL_TYPEALIASES_ONLY) { firDiagnostic -> ToplevelTypealiasesOnlyImpl( firDiagnostic as FirPsiDiagnostic<*>, diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnostics.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnostics.kt index 8b57a7a4c51..9b52d6d1be4 100644 --- a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnostics.kt +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnostics.kt @@ -588,6 +588,21 @@ sealed class KtFirDiagnostic : KtDiagnosticWithPsi { abstract val candidates: List } + abstract class IteratorAmbiguity : KtFirDiagnostic() { + override val diagnosticClass get() = IteratorAmbiguity::class + abstract val candidates: List + } + + abstract class HasNextFunctionAmbiguity : KtFirDiagnostic() { + override val diagnosticClass get() = HasNextFunctionAmbiguity::class + abstract val candidates: List + } + + abstract class NextAmbiguity : KtFirDiagnostic() { + override val diagnosticClass get() = NextAmbiguity::class + abstract val candidates: List + } + abstract class TypeMismatch : KtFirDiagnostic() { override val diagnosticClass get() = TypeMismatch::class abstract val expectedType: KtType @@ -1203,6 +1218,10 @@ sealed class KtFirDiagnostic : KtDiagnosticWithPsi { abstract val rhs: KtExpression } + abstract class IteratorOnNullable : KtFirDiagnostic() { + override val diagnosticClass get() = IteratorOnNullable::class + } + abstract class UnnecessarySafeCall : KtFirDiagnostic() { override val diagnosticClass get() = UnnecessarySafeCall::class abstract val receiverType: KtType @@ -1257,6 +1276,28 @@ sealed class KtFirDiagnostic : KtDiagnosticWithPsi { override val diagnosticClass get() = NoSetMethod::class } + abstract class IteratorMissing : KtFirDiagnostic() { + override val diagnosticClass get() = IteratorMissing::class + } + + abstract class HasNextMissing : KtFirDiagnostic() { + override val diagnosticClass get() = HasNextMissing::class + } + + abstract class NextMissing : KtFirDiagnostic() { + override val diagnosticClass get() = NextMissing::class + } + + abstract class HasNextFunctionNoneApplicable : KtFirDiagnostic() { + override val diagnosticClass get() = HasNextFunctionNoneApplicable::class + abstract val candidates: List + } + + abstract class NextNoneApplicable : KtFirDiagnostic() { + override val diagnosticClass get() = NextNoneApplicable::class + abstract val candidates: List + } + abstract class ToplevelTypealiasesOnly : KtFirDiagnostic() { override val diagnosticClass get() = ToplevelTypealiasesOnly::class } diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnosticsImpl.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnosticsImpl.kt index 57e615f67d3..b1acc6ba14e 100644 --- a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnosticsImpl.kt +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnosticsImpl.kt @@ -943,6 +943,30 @@ internal class AssignOperatorAmbiguityImpl( override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic) } +internal class IteratorAmbiguityImpl( + override val candidates: List, + firDiagnostic: FirPsiDiagnostic<*>, + override val token: ValidityToken, +) : KtFirDiagnostic.IteratorAmbiguity(), KtAbstractFirDiagnostic { + override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic) +} + +internal class HasNextFunctionAmbiguityImpl( + override val candidates: List, + firDiagnostic: FirPsiDiagnostic<*>, + override val token: ValidityToken, +) : KtFirDiagnostic.HasNextFunctionAmbiguity(), KtAbstractFirDiagnostic { + override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic) +} + +internal class NextAmbiguityImpl( + override val candidates: List, + firDiagnostic: FirPsiDiagnostic<*>, + override val token: ValidityToken, +) : KtFirDiagnostic.NextAmbiguity(), KtAbstractFirDiagnostic { + override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic) +} + internal class TypeMismatchImpl( override val expectedType: KtType, override val actualType: KtType, @@ -1948,6 +1972,13 @@ internal class UnsafeOperatorCallImpl( override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic) } +internal class IteratorOnNullableImpl( + firDiagnostic: FirPsiDiagnostic<*>, + override val token: ValidityToken, +) : KtFirDiagnostic.IteratorOnNullable(), KtAbstractFirDiagnostic { + override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic) +} + internal class UnnecessarySafeCallImpl( override val receiverType: KtType, firDiagnostic: FirPsiDiagnostic<*>, @@ -2038,6 +2069,43 @@ internal class NoSetMethodImpl( override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic) } +internal class IteratorMissingImpl( + firDiagnostic: FirPsiDiagnostic<*>, + override val token: ValidityToken, +) : KtFirDiagnostic.IteratorMissing(), KtAbstractFirDiagnostic { + override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic) +} + +internal class HasNextMissingImpl( + firDiagnostic: FirPsiDiagnostic<*>, + override val token: ValidityToken, +) : KtFirDiagnostic.HasNextMissing(), KtAbstractFirDiagnostic { + override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic) +} + +internal class NextMissingImpl( + firDiagnostic: FirPsiDiagnostic<*>, + override val token: ValidityToken, +) : KtFirDiagnostic.NextMissing(), KtAbstractFirDiagnostic { + override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic) +} + +internal class HasNextFunctionNoneApplicableImpl( + override val candidates: List, + firDiagnostic: FirPsiDiagnostic<*>, + override val token: ValidityToken, +) : KtFirDiagnostic.HasNextFunctionNoneApplicable(), KtAbstractFirDiagnostic { + override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic) +} + +internal class NextNoneApplicableImpl( + override val candidates: List, + firDiagnostic: FirPsiDiagnostic<*>, + override val token: ValidityToken, +) : KtFirDiagnostic.NextNoneApplicable(), KtAbstractFirDiagnostic { + override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic) +} + internal class ToplevelTypealiasesOnlyImpl( firDiagnostic: FirPsiDiagnostic<*>, override val token: ValidityToken,