Diagnostic on lateinit
This commit is contained in:
@@ -315,6 +315,7 @@ public interface Errors {
|
||||
DiagnosticFactory0<JetExpression> PROPERTY_INITIALIZER_NO_BACKING_FIELD = DiagnosticFactory0.create(ERROR);
|
||||
DiagnosticFactory0<JetExpression> PROPERTY_INITIALIZER_IN_TRAIT = DiagnosticFactory0.create(ERROR);
|
||||
DiagnosticFactory0<JetProperty> FINAL_PROPERTY_IN_TRAIT = DiagnosticFactory0.create(ERROR, FINAL_MODIFIER);
|
||||
DiagnosticFactory0<PsiElement> INAPPLICABLE_LATEINIT_MODIFIER = DiagnosticFactory0.create(ERROR);
|
||||
DiagnosticFactory0<JetProperty> BACKING_FIELD_IN_TRAIT = DiagnosticFactory0.create(ERROR, DECLARATION_SIGNATURE);
|
||||
|
||||
DiagnosticFactory2<JetModifierListOwner, String, ClassDescriptor> ABSTRACT_PROPERTY_IN_NON_ABSTRACT_CLASS = DiagnosticFactory2.create(ERROR, ABSTRACT_MODIFIER);
|
||||
|
||||
+1
@@ -199,6 +199,7 @@ public class DefaultErrorMessages {
|
||||
MAP.put(LOCAL_VARIABLE_WITH_DELEGATE, "Local variables are not allowed to have delegates");
|
||||
|
||||
MAP.put(GETTER_VISIBILITY_DIFFERS_FROM_PROPERTY_VISIBILITY, "Getter visibility must be the same as property visibility");
|
||||
MAP.put(INAPPLICABLE_LATEINIT_MODIFIER, "''lateinit'' modifier is allowed only on non-null member properties with a backing field");
|
||||
MAP.put(BACKING_FIELD_IN_TRAIT, "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");
|
||||
|
||||
@@ -22,15 +22,13 @@ import com.intellij.lang.ASTNode;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
|
||||
import org.jetbrains.kotlin.descriptors.*;
|
||||
import org.jetbrains.kotlin.diagnostics.Errors;
|
||||
import org.jetbrains.kotlin.lexer.JetModifierKeywordToken;
|
||||
import org.jetbrains.kotlin.lexer.JetTokens;
|
||||
import org.jetbrains.kotlin.psi.*;
|
||||
import org.jetbrains.kotlin.types.JetType;
|
||||
import org.jetbrains.kotlin.types.SubstitutionUtils;
|
||||
import org.jetbrains.kotlin.types.TypeConstructor;
|
||||
import org.jetbrains.kotlin.types.TypeProjection;
|
||||
import org.jetbrains.kotlin.types.*;
|
||||
import org.jetbrains.kotlin.types.checker.JetTypeChecker;
|
||||
|
||||
import java.util.Collection;
|
||||
@@ -260,6 +258,7 @@ public class DeclarationsChecker {
|
||||
PropertyDescriptor propertyDescriptor = trace.get(BindingContext.PRIMARY_CONSTRUCTOR_PARAMETER, parameter);
|
||||
if (propertyDescriptor != null) {
|
||||
modifiersChecker.checkModifiersForDeclaration(parameter, propertyDescriptor);
|
||||
checkPropertyLateInit(parameter, propertyDescriptor);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -325,10 +324,56 @@ public class DeclarationsChecker {
|
||||
if (containingDeclaration instanceof ClassDescriptor) {
|
||||
checkPropertyAbstractness(property, propertyDescriptor, (ClassDescriptor) containingDeclaration);
|
||||
}
|
||||
checkPropertyLateInit(property, propertyDescriptor);
|
||||
checkPropertyInitializer(property, propertyDescriptor);
|
||||
checkAccessors(property, propertyDescriptor);
|
||||
}
|
||||
|
||||
private void checkPropertyLateInit(@NotNull JetCallableDeclaration property, @NotNull PropertyDescriptor propertyDescriptor) {
|
||||
JetModifierList modifierList = property.getModifierList();
|
||||
if (modifierList == null) return;
|
||||
PsiElement modifier = modifierList.getModifier(JetTokens.LATE_INIT_KEYWORD);
|
||||
if (modifier == null) return;
|
||||
|
||||
boolean hasBackingField =
|
||||
Boolean.TRUE.equals(trace.getBindingContext().get(BindingContext.BACKING_FIELD_REQUIRED, propertyDescriptor));
|
||||
|
||||
boolean hasDelegateOrInitializer = false;
|
||||
boolean hasCorrespondingValueParameter = false;
|
||||
|
||||
if (property instanceof JetProperty) {
|
||||
hasDelegateOrInitializer = ((JetProperty) property).hasDelegateExpressionOrInitializer();
|
||||
}
|
||||
else if (property instanceof JetParameter) {
|
||||
hasCorrespondingValueParameter = true;
|
||||
}
|
||||
|
||||
PropertyGetterDescriptor getter = propertyDescriptor.getGetter();
|
||||
PropertySetterDescriptor setter = propertyDescriptor.getSetter();
|
||||
|
||||
boolean customGetterOrSetter = false;
|
||||
if (getter != null) {
|
||||
customGetterOrSetter = getter.hasBody();
|
||||
}
|
||||
if (setter != null) {
|
||||
customGetterOrSetter |= setter.hasBody();
|
||||
}
|
||||
|
||||
boolean returnTypeIsNullable = true;
|
||||
boolean returnTypeIsPrimitive = true;
|
||||
|
||||
JetType returnType = propertyDescriptor.getReturnType();
|
||||
if (returnType != null) {
|
||||
returnTypeIsNullable = TypeUtils.isNullableType(returnType);
|
||||
returnTypeIsPrimitive = KotlinBuiltIns.isPrimitiveType(returnType);
|
||||
}
|
||||
|
||||
if (!hasBackingField || hasCorrespondingValueParameter || hasDelegateOrInitializer || customGetterOrSetter
|
||||
|| returnTypeIsNullable || returnTypeIsPrimitive || propertyDescriptor.getExtensionReceiverParameter() != null) {
|
||||
trace.report(INAPPLICABLE_LATEINIT_MODIFIER.on(modifier));
|
||||
}
|
||||
}
|
||||
|
||||
private void checkPropertyAbstractness(
|
||||
@NotNull JetProperty property,
|
||||
@NotNull PropertyDescriptor propertyDescriptor,
|
||||
@@ -392,7 +437,7 @@ public class DeclarationsChecker {
|
||||
|
||||
if (initializer == null && delegate == null) {
|
||||
boolean error = false;
|
||||
if (backingFieldRequired && !inTrait &&
|
||||
if (backingFieldRequired && !inTrait && !propertyDescriptor.isLateInit() &&
|
||||
Boolean.TRUE.equals(trace.getBindingContext().get(BindingContext.IS_UNINITIALIZED, propertyDescriptor))) {
|
||||
if (!(containingDeclaration instanceof ClassDescriptor) || hasAccessorImplementation) {
|
||||
error = true;
|
||||
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
class CustomDelegate {
|
||||
public fun get(thisRef: Any?, prop: PropertyMetadata): String = prop.name
|
||||
}
|
||||
|
||||
public abstract class A<T: Any, V: String?>(<!INAPPLICABLE_LATEINIT_MODIFIER!>lateinit<!> val p2: String) {
|
||||
|
||||
public lateinit val a: String
|
||||
lateinit val b: T
|
||||
private lateinit var c: CharSequence
|
||||
|
||||
lateinit val d: String
|
||||
get
|
||||
|
||||
public lateinit var e: String
|
||||
get
|
||||
private set
|
||||
|
||||
fun a() {
|
||||
<!WRONG_MODIFIER_TARGET!>lateinit<!> val <!UNUSED_VARIABLE!>a<!>: String
|
||||
}
|
||||
|
||||
<!INAPPLICABLE_LATEINIT_MODIFIER!>lateinit<!> val e1: V
|
||||
<!INAPPLICABLE_LATEINIT_MODIFIER!>lateinit<!> val e2: String?
|
||||
<!INAPPLICABLE_LATEINIT_MODIFIER!>lateinit<!> val e3: Int
|
||||
<!INAPPLICABLE_LATEINIT_MODIFIER!>lateinit<!> val e4: Int?
|
||||
<!INAPPLICABLE_LATEINIT_MODIFIER!>lateinit<!> var e5 = "A"
|
||||
<!INAPPLICABLE_LATEINIT_MODIFIER!>lateinit<!> var e6 = 3
|
||||
|
||||
<!INAPPLICABLE_LATEINIT_MODIFIER!>lateinit<!> val e7 by CustomDelegate()
|
||||
|
||||
<!INAPPLICABLE_LATEINIT_MODIFIER!>lateinit<!> val e8: String
|
||||
get() = "A"
|
||||
|
||||
<!INAPPLICABLE_LATEINIT_MODIFIER!>lateinit<!> var e9: String
|
||||
set(v) { $e9 = v }
|
||||
|
||||
abstract <!INAPPLICABLE_LATEINIT_MODIFIER!>lateinit<!> val e10: String
|
||||
|
||||
<!INAPPLICABLE_LATEINIT_MODIFIER!>lateinit<!> val String.e11: String
|
||||
|
||||
<!INAPPLICABLE_LATEINIT_MODIFIER!>lateinit<!> var String.e12: String
|
||||
}
|
||||
|
||||
<!WRONG_MODIFIER_TARGET!>lateinit<!> val topLevel: String
|
||||
<!WRONG_MODIFIER_TARGET!>lateinit<!> var topLevelMutable: String
|
||||
|
||||
public interface Intf {
|
||||
<!INAPPLICABLE_LATEINIT_MODIFIER!>lateinit<!> val str: String
|
||||
}
|
||||
|
||||
public abstract class AbstractClass {
|
||||
abstract val str: String
|
||||
}
|
||||
|
||||
public class AbstractClassImpl : AbstractClass() {
|
||||
override lateinit val str: String
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package
|
||||
|
||||
internal lateinit val topLevel: kotlin.String
|
||||
internal lateinit var topLevelMutable: kotlin.String
|
||||
|
||||
public abstract class A</*0*/ T : kotlin.Any, /*1*/ V : kotlin.String?> {
|
||||
public constructor A</*0*/ T : kotlin.Any, /*1*/ V : kotlin.String?>(/*0*/ p2: kotlin.String)
|
||||
public final lateinit val a: kotlin.String
|
||||
internal final lateinit val b: T
|
||||
private final lateinit var c: kotlin.CharSequence
|
||||
internal final lateinit val d: kotlin.String
|
||||
public final lateinit var e: kotlin.String
|
||||
internal final lateinit val e1: V
|
||||
internal abstract lateinit val e10: kotlin.String
|
||||
internal final lateinit val e2: kotlin.String?
|
||||
internal final lateinit val e3: kotlin.Int
|
||||
internal final lateinit val e4: kotlin.Int?
|
||||
internal final lateinit var e5: kotlin.String
|
||||
internal final lateinit var e6: kotlin.Int
|
||||
internal final lateinit val e7: kotlin.String
|
||||
internal final lateinit val e8: kotlin.String
|
||||
internal final lateinit var e9: kotlin.String
|
||||
internal final val p2: kotlin.String
|
||||
internal final lateinit val kotlin.String.e11: kotlin.String
|
||||
internal final lateinit var kotlin.String.e12: kotlin.String
|
||||
internal final fun a(): kotlin.Unit
|
||||
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
|
||||
}
|
||||
|
||||
public abstract class AbstractClass {
|
||||
public constructor AbstractClass()
|
||||
internal abstract val str: 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
|
||||
}
|
||||
|
||||
public final class AbstractClassImpl : AbstractClass {
|
||||
public constructor AbstractClassImpl()
|
||||
internal open override /*1*/ lateinit val str: 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
|
||||
}
|
||||
|
||||
internal final class CustomDelegate {
|
||||
public constructor CustomDelegate()
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public final fun get(/*0*/ thisRef: kotlin.Any?, /*1*/ prop: kotlin.PropertyMetadata): kotlin.String
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
|
||||
public interface Intf {
|
||||
internal abstract lateinit val str: 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
|
||||
}
|
||||
@@ -301,6 +301,12 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest {
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("LateInit.kt")
|
||||
public void testLateInit() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/LateInit.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("LocalClassAndShortSubpackageNames.kt")
|
||||
public void testLocalClassAndShortSubpackageNames() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/LocalClassAndShortSubpackageNames.kt");
|
||||
|
||||
Reference in New Issue
Block a user