Diagnostic on lateinit

This commit is contained in:
Yan Zhulanow
2015-09-03 01:32:58 +03:00
parent add13cae82
commit e3967b9fa0
6 changed files with 176 additions and 5 deletions
@@ -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);
@@ -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
View File
@@ -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
}
+61
View File
@@ -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");