Fix diagnostic for uninitialized extension property without accessors
#KT-8612 Fixed
This commit is contained in:
@@ -462,6 +462,8 @@ public interface Errors {
|
|||||||
|
|
||||||
DiagnosticFactory0<KtProperty> MUST_BE_INITIALIZED = DiagnosticFactory0.create(ERROR, DECLARATION_SIGNATURE);
|
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> MUST_BE_INITIALIZED_OR_BE_ABSTRACT = DiagnosticFactory0.create(ERROR, DECLARATION_SIGNATURE);
|
||||||
|
DiagnosticFactory0<KtProperty> EXTENSION_PROPERTY_MUST_HAVE_ACCESSORS_OR_BE_ABSTRACT =
|
||||||
|
DiagnosticFactory0.create(ERROR, DECLARATION_SIGNATURE);
|
||||||
DiagnosticFactory0<KtProperty> UNNECESSARY_LATEINIT = DiagnosticFactory0.create(WARNING, LATEINIT_MODIFIER);
|
DiagnosticFactory0<KtProperty> UNNECESSARY_LATEINIT = DiagnosticFactory0.create(WARNING, LATEINIT_MODIFIER);
|
||||||
|
|
||||||
DiagnosticFactory0<KtExpression> EXTENSION_PROPERTY_WITH_BACKING_FIELD = DiagnosticFactory0.create(ERROR);
|
DiagnosticFactory0<KtExpression> EXTENSION_PROPERTY_WITH_BACKING_FIELD = DiagnosticFactory0.create(ERROR);
|
||||||
|
|||||||
+1
@@ -231,6 +231,7 @@ public class DefaultErrorMessages {
|
|||||||
MAP.put(BACKING_FIELD_IN_INTERFACE, "Property in an interface cannot have a backing field");
|
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, "Property must be initialized");
|
||||||
MAP.put(MUST_BE_INITIALIZED_OR_BE_ABSTRACT, "Property must be initialized or be abstract");
|
MAP.put(MUST_BE_INITIALIZED_OR_BE_ABSTRACT, "Property must be initialized or be abstract");
|
||||||
|
MAP.put(EXTENSION_PROPERTY_MUST_HAVE_ACCESSORS_OR_BE_ABSTRACT, "Extension property must have accessors or be abstract");
|
||||||
MAP.put(UNNECESSARY_LATEINIT, "Lateinit is unnecessary: definitely initialized in constructors");
|
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(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(PRIVATE_PROPERTY_IN_INTERFACE, "Abstract property in an interface cannot be private");
|
||||||
|
|||||||
@@ -668,19 +668,19 @@ class DeclarationsChecker(
|
|||||||
val hasAccessorImplementation = propertyDescriptor.hasAccessorImplementation()
|
val hasAccessorImplementation = propertyDescriptor.hasAccessorImplementation()
|
||||||
|
|
||||||
val containingDeclaration = propertyDescriptor.containingDeclaration
|
val containingDeclaration = propertyDescriptor.containingDeclaration
|
||||||
val inTrait = containingDeclaration is ClassDescriptor && containingDeclaration.kind == ClassKind.INTERFACE
|
val inInterface = DescriptorUtils.isInterface(containingDeclaration)
|
||||||
if (propertyDescriptor.modality == Modality.ABSTRACT) {
|
if (propertyDescriptor.modality == Modality.ABSTRACT) {
|
||||||
if (!property.hasDelegateExpressionOrInitializer() && property.typeReference == null) {
|
if (!property.hasDelegateExpressionOrInitializer() && property.typeReference == null) {
|
||||||
trace.report(PROPERTY_WITH_NO_TYPE_NO_INITIALIZER.on(property))
|
trace.report(PROPERTY_WITH_NO_TYPE_NO_INITIALIZER.on(property))
|
||||||
}
|
}
|
||||||
if (inTrait && property.hasModifier(KtTokens.PRIVATE_KEYWORD) && !property.hasModifier(KtTokens.ABSTRACT_KEYWORD)) {
|
if (inInterface && property.hasModifier(KtTokens.PRIVATE_KEYWORD) && !property.hasModifier(KtTokens.ABSTRACT_KEYWORD)) {
|
||||||
trace.report(PRIVATE_PROPERTY_IN_INTERFACE.on(property))
|
trace.report(PRIVATE_PROPERTY_IN_INTERFACE.on(property))
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
val backingFieldRequired = trace.bindingContext.get(BindingContext.BACKING_FIELD_REQUIRED, propertyDescriptor) ?: false
|
val backingFieldRequired = trace.bindingContext.get(BindingContext.BACKING_FIELD_REQUIRED, propertyDescriptor) ?: false
|
||||||
if (inTrait && backingFieldRequired && hasAccessorImplementation) {
|
if (inInterface && backingFieldRequired && hasAccessorImplementation) {
|
||||||
trace.report(BACKING_FIELD_IN_INTERFACE.on(property))
|
trace.report(BACKING_FIELD_IN_INTERFACE.on(property))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -688,7 +688,7 @@ class DeclarationsChecker(
|
|||||||
val delegate = property.delegate
|
val delegate = property.delegate
|
||||||
val isHeader = propertyDescriptor.isHeader
|
val isHeader = propertyDescriptor.isHeader
|
||||||
if (initializer != null) {
|
if (initializer != null) {
|
||||||
if (inTrait) {
|
if (inInterface) {
|
||||||
trace.report(PROPERTY_INITIALIZER_IN_INTERFACE.on(initializer))
|
trace.report(PROPERTY_INITIALIZER_IN_INTERFACE.on(initializer))
|
||||||
}
|
}
|
||||||
else if (isHeader) {
|
else if (isHeader) {
|
||||||
@@ -702,15 +702,18 @@ class DeclarationsChecker(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (delegate != null) {
|
else if (delegate != null) {
|
||||||
if (inTrait) {
|
if (inInterface) {
|
||||||
trace.report(DELEGATED_PROPERTY_IN_INTERFACE.on(delegate))
|
trace.report(DELEGATED_PROPERTY_IN_INTERFACE.on(delegate))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
val isUninitialized = trace.bindingContext.get(BindingContext.IS_UNINITIALIZED, propertyDescriptor) ?: false
|
val isUninitialized = trace.bindingContext.get(BindingContext.IS_UNINITIALIZED, propertyDescriptor) ?: false
|
||||||
val isExternal = propertyDescriptor.isEffectivelyExternal()
|
val isExternal = propertyDescriptor.isEffectivelyExternal()
|
||||||
if (backingFieldRequired && !inTrait && !propertyDescriptor.isLateInit && !isHeader && isUninitialized && !isExternal) {
|
if (backingFieldRequired && !inInterface && !propertyDescriptor.isLateInit && !isHeader && isUninitialized && !isExternal) {
|
||||||
if (containingDeclaration !is ClassDescriptor || hasAccessorImplementation) {
|
if (propertyDescriptor.extensionReceiverParameter != null && !hasAccessorImplementation) {
|
||||||
|
trace.report(EXTENSION_PROPERTY_MUST_HAVE_ACCESSORS_OR_BE_ABSTRACT.on(property))
|
||||||
|
}
|
||||||
|
else if (containingDeclaration !is ClassDescriptor || hasAccessorImplementation) {
|
||||||
trace.report(MUST_BE_INITIALIZED.on(property))
|
trace.report(MUST_BE_INITIALIZED.on(property))
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -723,7 +726,7 @@ class DeclarationsChecker(
|
|||||||
else if (noExplicitTypeOrGetterType(property)) {
|
else if (noExplicitTypeOrGetterType(property)) {
|
||||||
trace.report(PROPERTY_WITH_NO_TYPE_NO_INITIALIZER.on(property))
|
trace.report(PROPERTY_WITH_NO_TYPE_NO_INITIALIZER.on(property))
|
||||||
}
|
}
|
||||||
if (backingFieldRequired && !inTrait && propertyDescriptor.isLateInit && !isUninitialized &&
|
if (backingFieldRequired && !inInterface && propertyDescriptor.isLateInit && !isUninitialized &&
|
||||||
trace[MUST_BE_LATEINIT, propertyDescriptor] != true) {
|
trace[MUST_BE_LATEINIT, propertyDescriptor] != true) {
|
||||||
trace.report(UNNECESSARY_LATEINIT.on(property))
|
trace.report(UNNECESSARY_LATEINIT.on(property))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ fun test() {
|
|||||||
val Int.abs : Int
|
val Int.abs : Int
|
||||||
get() = if (this > 0) this else -this;
|
get() = if (this > 0) this else -this;
|
||||||
|
|
||||||
<!MUST_BE_INITIALIZED!>val <T> T.foo : T<!>
|
<!EXTENSION_PROPERTY_MUST_HAVE_ACCESSORS_OR_BE_ABSTRACT!>val <T> T.foo : T<!>
|
||||||
|
|
||||||
fun Int.foo() = this
|
fun Int.foo() = this
|
||||||
|
|
||||||
|
|||||||
Vendored
+25
@@ -0,0 +1,25 @@
|
|||||||
|
<!EXTENSION_PROPERTY_MUST_HAVE_ACCESSORS_OR_BE_ABSTRACT!>val String.test1: Int<!>
|
||||||
|
<!EXTENSION_PROPERTY_MUST_HAVE_ACCESSORS_OR_BE_ABSTRACT!>var String.test2: Int<!>
|
||||||
|
|
||||||
|
<!EXTENSION_PROPERTY_MUST_HAVE_ACCESSORS_OR_BE_ABSTRACT!>var String.test3: Int<!>; public set
|
||||||
|
|
||||||
|
class C {
|
||||||
|
<!EXTENSION_PROPERTY_MUST_HAVE_ACCESSORS_OR_BE_ABSTRACT!>val String.test1: Int<!>
|
||||||
|
<!EXTENSION_PROPERTY_MUST_HAVE_ACCESSORS_OR_BE_ABSTRACT!>var String.test2: Int<!>
|
||||||
|
<!EXTENSION_PROPERTY_MUST_HAVE_ACCESSORS_OR_BE_ABSTRACT!>var String.test3: Int<!>; public set
|
||||||
|
}
|
||||||
|
|
||||||
|
interface I {
|
||||||
|
val String.test1: Int
|
||||||
|
var String.test2: Int
|
||||||
|
var String.test3: Int; public set
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class A {
|
||||||
|
<!EXTENSION_PROPERTY_MUST_HAVE_ACCESSORS_OR_BE_ABSTRACT!>val String.test1: Int<!>
|
||||||
|
<!EXTENSION_PROPERTY_MUST_HAVE_ACCESSORS_OR_BE_ABSTRACT!>var String.test2: Int<!>
|
||||||
|
<!EXTENSION_PROPERTY_MUST_HAVE_ACCESSORS_OR_BE_ABSTRACT!>var String.test3: Int<!>; public set
|
||||||
|
|
||||||
|
abstract val String.testA1: Int
|
||||||
|
abstract var String.testA2: Int
|
||||||
|
}
|
||||||
Vendored
+36
@@ -0,0 +1,36 @@
|
|||||||
|
package
|
||||||
|
|
||||||
|
public val kotlin.String.test1: kotlin.Int
|
||||||
|
public var kotlin.String.test2: kotlin.Int
|
||||||
|
public var kotlin.String.test3: kotlin.Int
|
||||||
|
|
||||||
|
public abstract class A {
|
||||||
|
public constructor A()
|
||||||
|
public final val kotlin.String.test1: kotlin.Int
|
||||||
|
public final var kotlin.String.test2: kotlin.Int
|
||||||
|
public final var kotlin.String.test3: kotlin.Int
|
||||||
|
public abstract val kotlin.String.testA1: kotlin.Int
|
||||||
|
public abstract var kotlin.String.testA2: kotlin.Int
|
||||||
|
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 C {
|
||||||
|
public constructor C()
|
||||||
|
public final val kotlin.String.test1: kotlin.Int
|
||||||
|
public final var kotlin.String.test2: kotlin.Int
|
||||||
|
public final var kotlin.String.test3: kotlin.Int
|
||||||
|
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 interface I {
|
||||||
|
public abstract val kotlin.String.test1: kotlin.Int
|
||||||
|
public abstract var kotlin.String.test2: kotlin.Int
|
||||||
|
public abstract var kotlin.String.test3: kotlin.Int
|
||||||
|
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
|
||||||
|
}
|
||||||
+1
-1
@@ -34,6 +34,6 @@ set(value) {}
|
|||||||
<!UNRESOLVED_REFERENCE!>get<!>() = 42
|
<!UNRESOLVED_REFERENCE!>get<!>() = 42
|
||||||
<!UNRESOLVED_REFERENCE!>set<!>(<!UNRESOLVED_REFERENCE!>value<!>) {}
|
<!UNRESOLVED_REFERENCE!>set<!>(<!UNRESOLVED_REFERENCE!>value<!>) {}
|
||||||
|
|
||||||
<!MUST_BE_INITIALIZED_OR_BE_ABSTRACT!>var String.testExtVarLineBreakSemi: Int<!>;
|
<!EXTENSION_PROPERTY_MUST_HAVE_ACCESSORS_OR_BE_ABSTRACT!>var String.testExtVarLineBreakSemi: Int<!>;
|
||||||
<!UNRESOLVED_REFERENCE!>get<!>() = 42
|
<!UNRESOLVED_REFERENCE!>get<!>() = 42
|
||||||
<!UNRESOLVED_REFERENCE!>set<!>(<!UNRESOLVED_REFERENCE!>value<!>) {}
|
<!UNRESOLVED_REFERENCE!>set<!>(<!UNRESOLVED_REFERENCE!>value<!>) {}
|
||||||
|
|||||||
@@ -15838,6 +15838,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
|
|||||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/diagnostics/tests/properties"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true);
|
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/diagnostics/tests/properties"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("extensionPropertyMustHaveAccessorsOrBeAbstract.kt")
|
||||||
|
public void testExtensionPropertyMustHaveAccessorsOrBeAbstract() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/properties/extensionPropertyMustHaveAccessorsOrBeAbstract.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("compiler/testData/diagnostics/tests/properties/inferenceFromGetters")
|
@TestMetadata("compiler/testData/diagnostics/tests/properties/inferenceFromGetters")
|
||||||
@TestDataPath("$PROJECT_ROOT")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
@RunWith(JUnit3RunnerWithInners.class)
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
|||||||
Reference in New Issue
Block a user