diff --git a/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowInformationProvider.kt b/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowInformationProvider.kt index def819a534a..deb53e970d5 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowInformationProvider.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowInformationProvider.kt @@ -302,7 +302,6 @@ class ControlFlowInformationProvider private constructor( } private fun PropertyDescriptor.isDefinitelyInitialized(): Boolean { - if (isLateInit) return true if (trace.get(BACKING_FIELD_REQUIRED, this) ?: false) return false val property = DescriptorToSourceUtils.descriptorToDeclaration(this) if (property is KtProperty && property.hasDelegate()) return false @@ -325,6 +324,10 @@ class ControlFlowInformationProvider private constructor( if (variableDescriptor !is PropertyDescriptor) { variableDescriptor?.let { varWithUninitializedErrorGenerated.add(it) } } + else if (variableDescriptor.isLateInit) { + trace.record(MUST_BE_LATEINIT, variableDescriptor) + return + } when (variableDescriptor) { is ValueParameterDescriptor -> report(Errors.UNINITIALIZED_PARAMETER.on(element, variableDescriptor), ctxt) diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/BindingContext.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/BindingContext.java index c999ece3325..2d95d8a4a58 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/BindingContext.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/BindingContext.java @@ -204,6 +204,7 @@ public interface BindingContext { } }; WritableSlice IS_UNINITIALIZED = Slices.createSimpleSetSlice(); + WritableSlice MUST_BE_LATEINIT = Slices.createSimpleSetSlice(); WritableSlice BLOCK = new SetSlice(DO_NOTHING) { @Override diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/DeclarationsChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/DeclarationsChecker.kt index 4eeb316c76a..f975b1e2d27 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/DeclarationsChecker.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/DeclarationsChecker.kt @@ -29,8 +29,7 @@ import org.jetbrains.kotlin.diagnostics.Errors.* import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.visibilityModifier -import org.jetbrains.kotlin.resolve.BindingContext.TYPE -import org.jetbrains.kotlin.resolve.BindingContext.TYPE_PARAMETER +import org.jetbrains.kotlin.resolve.BindingContext.* import org.jetbrains.kotlin.resolve.DescriptorUtils.classCanHaveAbstractMembers import org.jetbrains.kotlin.resolve.DescriptorUtils.classCanHaveOpenMembers import org.jetbrains.kotlin.types.* @@ -670,7 +669,10 @@ class DeclarationsChecker( trace.report(PROPERTY_WITH_NO_TYPE_NO_INITIALIZER.on(property)) } if (backingFieldRequired && !inTrait && propertyDescriptor.isLateInit && !isUninitialized) { - trace.report(UNNECESSARY_LATEINIT.on(property)) + if (trace[MUST_BE_LATEINIT, propertyDescriptor] ?: false) {} + else { + trace.report(UNNECESSARY_LATEINIT.on(property)) + } } } } diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/unnecessaryLateinit/lateinitRecursiveInLambda.kt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/unnecessaryLateinit/lateinitRecursiveInLambda.kt new file mode 100644 index 00000000000..718684da9b5 --- /dev/null +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/unnecessaryLateinit/lateinitRecursiveInLambda.kt @@ -0,0 +1,6 @@ +class Test { + lateinit var someRunnable: Runnable + init { + someRunnable = Runnable { someRunnable.run() } + } +} diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/unnecessaryLateinit/lateinitRecursiveInLambda.txt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/unnecessaryLateinit/lateinitRecursiveInLambda.txt new file mode 100644 index 00000000000..ad0e3aed30c --- /dev/null +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/unnecessaryLateinit/lateinitRecursiveInLambda.txt @@ -0,0 +1,9 @@ +package + +public final class Test { + public constructor Test() + public final lateinit var someRunnable: java.lang.Runnable + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/unnecessaryLateinit/lateinitWithPlusAssign.kt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/unnecessaryLateinit/lateinitWithPlusAssign.kt index 1a7b5dd5573..332526497b9 100644 --- a/compiler/testData/diagnostics/tests/controlFlowAnalysis/unnecessaryLateinit/lateinitWithPlusAssign.kt +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/unnecessaryLateinit/lateinitWithPlusAssign.kt @@ -1,10 +1,8 @@ class Foo { - // Erroneous case: after lateinit removal, we'll have error at 'bar += baz', not here - // However, looks like we must have error at 'bar += baz' even with lateinit - // because nobody can initialize this 'bar' before constructor is called - lateinit var bar: String + lateinit var bar: String constructor(baz: Int) { + // At best, we should have error here despite of lateinit bar += baz } } diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java index c39697add0d..9d72fc44285 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java @@ -3890,6 +3890,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest { KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/diagnostics/tests/controlFlowAnalysis/unnecessaryLateinit"), Pattern.compile("^(.+)\\.kt$"), true); } + @TestMetadata("lateinitRecursiveInLambda.kt") + public void testLateinitRecursiveInLambda() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/controlFlowAnalysis/unnecessaryLateinit/lateinitRecursiveInLambda.kt"); + doTest(fileName); + } + @TestMetadata("lateinitWithConstructor.kt") public void testLateinitWithConstructor() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/controlFlowAnalysis/unnecessaryLateinit/lateinitWithConstructor.kt");