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;