UNNECESSARY_LATEINIT warning introduced #KT-13295 Fixed

(cherry picked from commit 88f9938)
This commit is contained in:
Mikhail Glukhikh
2016-09-26 11:17:42 +03:00
committed by Mikhail Glukhikh
parent b0ed6adce3
commit 4da9a101cf
27 changed files with 316 additions and 3 deletions
@@ -407,6 +407,7 @@ public interface Errors {
DiagnosticFactory0<KtProperty> MUST_BE_INITIALIZED = DiagnosticFactory0.create(ERROR, DECLARATION_SIGNATURE);
DiagnosticFactory0<KtProperty> MUST_BE_INITIALIZED_OR_BE_ABSTRACT = DiagnosticFactory0.create(ERROR, DECLARATION_SIGNATURE);
DiagnosticFactory0<KtProperty> UNNECESSARY_LATEINIT = DiagnosticFactory0.create(WARNING, LATEINIT_MODIFIER);
DiagnosticFactory0<KtExpression> EXTENSION_PROPERTY_WITH_BACKING_FIELD = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<KtExpression> PROPERTY_INITIALIZER_NO_BACKING_FIELD = DiagnosticFactory0.create(ERROR);
@@ -827,7 +828,7 @@ public interface Errors {
INVISIBLE_MEMBER, NON_PUBLIC_CALL_FROM_PUBLIC_INLINE, INVISIBLE_REFERENCE, INVISIBLE_SETTER);
ImmutableSet<? extends DiagnosticFactory<?>> UNUSED_ELEMENT_DIAGNOSTICS = ImmutableSet.of(
UNUSED_VARIABLE, UNUSED_PARAMETER, ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE, VARIABLE_WITH_REDUNDANT_INITIALIZER,
UNUSED_LAMBDA_EXPRESSION, USELESS_CAST, UNUSED_VALUE, USELESS_ELVIS);
UNUSED_LAMBDA_EXPRESSION, USELESS_CAST, UNUSED_VALUE, USELESS_ELVIS, UNNECESSARY_LATEINIT);
ImmutableSet<? extends DiagnosticFactory<?>> TYPE_INFERENCE_ERRORS = ImmutableSet.of(
TYPE_INFERENCE_NO_INFORMATION_FOR_PARAMETER, TYPE_INFERENCE_CONFLICTING_SUBSTITUTIONS,
TYPE_INFERENCE_PARAMETER_CONSTRAINT_ERROR,
@@ -201,6 +201,8 @@ object PositioningStrategies {
@JvmField val PRIVATE_MODIFIER: PositioningStrategy<KtModifierListOwner> = modifierSetPosition(KtTokens.PRIVATE_KEYWORD)
@JvmField val LATEINIT_MODIFIER: PositioningStrategy<KtModifierListOwner> = modifierSetPosition(KtTokens.LATEINIT_KEYWORD)
@JvmField val VARIANCE_MODIFIER: PositioningStrategy<KtModifierListOwner> = modifierSetPosition(KtTokens.IN_KEYWORD, KtTokens.OUT_KEYWORD)
@JvmField val FOR_REDECLARATION: PositioningStrategy<PsiElement> = object : PositioningStrategy<PsiElement>() {
@@ -229,6 +229,7 @@ public class DefaultErrorMessages {
MAP.put(BACKING_FIELD_IN_INTERFACE, "Property in an interface cannot have a backing field");
MAP.put(MUST_BE_INITIALIZED, "Property must be initialized");
MAP.put(MUST_BE_INITIALIZED_OR_BE_ABSTRACT, "Property must be initialized or be abstract");
MAP.put(UNNECESSARY_LATEINIT, "Lateinit is unnecessary: definitely initialized in constructors");
MAP.put(PROPERTY_INITIALIZER_IN_INTERFACE, "Property initializers are not allowed in interfaces");
MAP.put(PRIVATE_PROPERTY_IN_INTERFACE, "Abstract property in an interface cannot be private");
MAP.put(EXTENSION_PROPERTY_WITH_BACKING_FIELD, "Extension property cannot be initialized because it has no backing field");
@@ -657,8 +657,8 @@ class DeclarationsChecker(
}
}
else {
if (backingFieldRequired && !inTrait && !propertyDescriptor.isLateInit &&
trace.bindingContext.get(BindingContext.IS_UNINITIALIZED, propertyDescriptor) ?: false) {
val isUninitialized = trace.bindingContext.get(BindingContext.IS_UNINITIALIZED, propertyDescriptor) ?: false
if (backingFieldRequired && !inTrait && !propertyDescriptor.isLateInit && isUninitialized) {
if (containingDeclaration !is ClassDescriptor || hasAccessorImplementation) {
trace.report(MUST_BE_INITIALIZED.on(property))
}
@@ -669,6 +669,9 @@ class DeclarationsChecker(
else if (property.typeReference == null) {
trace.report(PROPERTY_WITH_NO_TYPE_NO_INITIALIZER.on(property))
}
if (backingFieldRequired && !inTrait && propertyDescriptor.isLateInit && !isUninitialized) {
trace.report(UNNECESSARY_LATEINIT.on(property))
}
}
}
@@ -0,0 +1,9 @@
// !DIAGNOSTICS: -UNUSED_PARAMETER
class Foo {
<!UNNECESSARY_LATEINIT!>lateinit<!> var bar: String
constructor(baz: Int) {
bar = ""
}
}
@@ -0,0 +1,9 @@
package
public final class Foo {
public constructor Foo(/*0*/ baz: kotlin.Int)
public final lateinit var bar: kotlin.String
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
}
@@ -0,0 +1,11 @@
// !DIAGNOSTICS: -UNUSED_PARAMETER
class Foo {
<!UNNECESSARY_LATEINIT!>lateinit<!> var x: String
constructor() {
x = "Foo"
}
constructor(x: String, y: String): <!NONE_APPLICABLE!>this<!>(y.hashCode())
}
@@ -0,0 +1,10 @@
package
public final class Foo {
public constructor Foo()
public constructor Foo(/*0*/ x: kotlin.String, /*1*/ y: kotlin.String)
public final lateinit var x: kotlin.String
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
}
@@ -0,0 +1,7 @@
class Foo {
<!UNNECESSARY_LATEINIT!>lateinit<!> var bar: String
init {
bar = ""
}
}
@@ -0,0 +1,9 @@
package
public final class Foo {
public constructor Foo()
public final lateinit var bar: kotlin.String
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
}
@@ -0,0 +1,13 @@
// !DIAGNOSTICS: -UNUSED_PARAMETER
class Foo {
<!UNNECESSARY_LATEINIT!>lateinit<!> var bar: String
constructor() {
bar = ""
}
constructor(baz: Int) {
bar = ""
}
}
@@ -0,0 +1,10 @@
package
public final class Foo {
public constructor Foo()
public constructor Foo(/*0*/ baz: kotlin.Int)
public final lateinit var bar: kotlin.String
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
}
@@ -0,0 +1,15 @@
// !DIAGNOSTICS: -UNUSED_PARAMETER
class Foo {
<!UNNECESSARY_LATEINIT!>lateinit<!> var bar: String
constructor() {
bar = ""
}
constructor(a: Int) : this() {
}
constructor(a: Int, b: Int) : this(a) {
}
}
@@ -0,0 +1,11 @@
package
public final class Foo {
public constructor Foo()
public constructor Foo(/*0*/ a: kotlin.Int)
public constructor Foo(/*0*/ a: kotlin.Int, /*1*/ b: kotlin.Int)
public final lateinit var bar: kotlin.String
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
}
@@ -0,0 +1,10 @@
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
<!UNNECESSARY_LATEINIT!>lateinit<!> var bar: String
constructor(baz: Int) {
bar += baz
}
}
@@ -0,0 +1,9 @@
package
public final class Foo {
public constructor Foo(/*0*/ baz: kotlin.Int)
public final lateinit var bar: kotlin.String
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
}
@@ -0,0 +1,9 @@
// !DIAGNOSTICS: -UNUSED_PARAMETER
class Foo() {
lateinit var bar: String
constructor(baz: Int) : this() {
bar = ""
}
}
@@ -0,0 +1,10 @@
package
public final class Foo {
public constructor Foo()
public constructor Foo(/*0*/ baz: kotlin.Int)
public final lateinit var bar: kotlin.String
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
}
@@ -0,0 +1,7 @@
class Foo {
lateinit var bar: String
fun init() {
bar = ""
}
}
@@ -0,0 +1,10 @@
package
public final class Foo {
public constructor Foo()
public final lateinit var bar: kotlin.String
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 final fun init(): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@@ -0,0 +1,9 @@
class Foo {
lateinit var x: String
constructor(y: String) {
x = y
}
constructor()
}
@@ -0,0 +1,10 @@
package
public final class Foo {
public constructor Foo()
public constructor Foo(/*0*/ y: kotlin.String)
public final lateinit var x: kotlin.String
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
}
@@ -0,0 +1,13 @@
// !DIAGNOSTICS: -UNUSED_PARAMETER
class Foo {
<!UNNECESSARY_LATEINIT!>lateinit<!> var bar: String
constructor() {
bar = ""
}
constructor(a: Int) : <!CYCLIC_CONSTRUCTOR_DELEGATION_CALL!>this<!>(a) {
bar = "a"
}
}
@@ -0,0 +1,10 @@
package
public final class Foo {
public constructor Foo()
public constructor Foo(/*0*/ a: kotlin.Int)
public final lateinit var bar: kotlin.String
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
}
@@ -0,0 +1,18 @@
// !DIAGNOSTICS: -UNUSED_PARAMETER
class Foo {
<!UNNECESSARY_LATEINIT!>lateinit<!> var bar: String
constructor() {
bar = ""
}
constructor(a: Int) : <!CYCLIC_CONSTRUCTOR_DELEGATION_CALL!>this<!>(a, 0, 0) {
}
constructor(a: Int, b: Int) : <!CYCLIC_CONSTRUCTOR_DELEGATION_CALL!>this<!>(a) {
}
constructor(a: Int, b: Int, c: Int) : <!CYCLIC_CONSTRUCTOR_DELEGATION_CALL!>this<!>(a, b) {
}
}
@@ -0,0 +1,12 @@
package
public final class Foo {
public constructor Foo()
public constructor Foo(/*0*/ a: kotlin.Int)
public constructor Foo(/*0*/ a: kotlin.Int, /*1*/ b: kotlin.Int)
public constructor Foo(/*0*/ a: kotlin.Int, /*1*/ b: kotlin.Int, /*2*/ c: kotlin.Int)
public final lateinit var bar: kotlin.String
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
}
@@ -3881,6 +3881,81 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
doTest(fileName);
}
}
@TestMetadata("compiler/testData/diagnostics/tests/controlFlowAnalysis/unnecessaryLateinit")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class UnnecessaryLateinit extends AbstractDiagnosticsTest {
public void testAllFilesPresentInUnnecessaryLateinit() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/diagnostics/tests/controlFlowAnalysis/unnecessaryLateinit"), Pattern.compile("^(.+)\\.kt$"), true);
}
@TestMetadata("lateinitWithConstructor.kt")
public void testLateinitWithConstructor() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/controlFlowAnalysis/unnecessaryLateinit/lateinitWithConstructor.kt");
doTest(fileName);
}
@TestMetadata("lateinitWithErroneousDelegation.kt")
public void testLateinitWithErroneousDelegation() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/controlFlowAnalysis/unnecessaryLateinit/lateinitWithErroneousDelegation.kt");
doTest(fileName);
}
@TestMetadata("lateinitWithInit.kt")
public void testLateinitWithInit() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/controlFlowAnalysis/unnecessaryLateinit/lateinitWithInit.kt");
doTest(fileName);
}
@TestMetadata("lateinitWithMultipleConstructors.kt")
public void testLateinitWithMultipleConstructors() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/controlFlowAnalysis/unnecessaryLateinit/lateinitWithMultipleConstructors.kt");
doTest(fileName);
}
@TestMetadata("lateinitWithMultipleConstructorsAndDelegation.kt")
public void testLateinitWithMultipleConstructorsAndDelegation() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/controlFlowAnalysis/unnecessaryLateinit/lateinitWithMultipleConstructorsAndDelegation.kt");
doTest(fileName);
}
@TestMetadata("lateinitWithPlusAssign.kt")
public void testLateinitWithPlusAssign() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/controlFlowAnalysis/unnecessaryLateinit/lateinitWithPlusAssign.kt");
doTest(fileName);
}
@TestMetadata("lateinitWithPrimaryConstructorAndConstructor.kt")
public void testLateinitWithPrimaryConstructorAndConstructor() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/controlFlowAnalysis/unnecessaryLateinit/lateinitWithPrimaryConstructorAndConstructor.kt");
doTest(fileName);
}
@TestMetadata("normalLateinit.kt")
public void testNormalLateinit() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/controlFlowAnalysis/unnecessaryLateinit/normalLateinit.kt");
doTest(fileName);
}
@TestMetadata("normalLateinitWithTwoConstructors.kt")
public void testNormalLateinitWithTwoConstructors() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/controlFlowAnalysis/unnecessaryLateinit/normalLateinitWithTwoConstructors.kt");
doTest(fileName);
}
@TestMetadata("secondaryConstructorDelegateItself.kt")
public void testSecondaryConstructorDelegateItself() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/controlFlowAnalysis/unnecessaryLateinit/secondaryConstructorDelegateItself.kt");
doTest(fileName);
}
@TestMetadata("secondaryConstructorDelegateLoop.kt")
public void testSecondaryConstructorDelegateLoop() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/controlFlowAnalysis/unnecessaryLateinit/secondaryConstructorDelegateLoop.kt");
doTest(fileName);
}
}
}
@TestMetadata("compiler/testData/diagnostics/tests/controlStructures")