Check componentN functions for override conflicts
Check if newly generated component functions happen to override something which they're not supposed to. Create new slice to store mapping from annotation descriptors to corresponding PSI elements, which is used by override checker to report errors in data classes on.
This commit is contained in:
@@ -145,6 +145,8 @@ public interface Errors {
|
||||
DiagnosticFactory3.create(ERROR, NAMED_ELEMENT);
|
||||
DiagnosticFactory3<JetModifierListOwner, CallableMemberDescriptor, CallableDescriptor, DeclarationDescriptor> CANNOT_OVERRIDE_INVISIBLE_MEMBER =
|
||||
DiagnosticFactory3.create(ERROR, OVERRIDE_MODIFIER);
|
||||
DiagnosticFactory2<JetAnnotationEntry, CallableMemberDescriptor, DeclarationDescriptor> DATA_CLASS_OVERRIDE_CONFLICT =
|
||||
DiagnosticFactory2.create(ERROR);
|
||||
SimpleDiagnosticFactory<JetDeclaration> CANNOT_INFER_VISIBILITY = SimpleDiagnosticFactory.create(ERROR, DECLARATION);
|
||||
|
||||
DiagnosticFactory1<JetClass, ClassDescriptor> ENUM_ENTRY_SHOULD_BE_INITIALIZED = DiagnosticFactory1.create(ERROR, NAME_IDENTIFIER);
|
||||
|
||||
+2
@@ -156,6 +156,8 @@ public class DefaultErrorMessages {
|
||||
MAP.put(NOTHING_TO_OVERRIDE, "''{0}'' overrides nothing", NAME);
|
||||
MAP.put(VIRTUAL_MEMBER_HIDDEN, "''{0}'' hides member of supertype ''{2}'' and needs ''override'' modifier", NAME, NAME, NAME);
|
||||
|
||||
MAP.put(DATA_CLASS_OVERRIDE_CONFLICT, "Function ''{0}'' generated for the data class conflicts with member of supertype ''{1}''", NAME, NAME);
|
||||
|
||||
MAP.put(CANNOT_OVERRIDE_INVISIBLE_MEMBER, "''{0}'' cannot has no access to ''{1}'' in class {2}, so it cannot override it",
|
||||
DescriptorRenderer.TEXT, DescriptorRenderer.TEXT, DescriptorRenderer.TEXT);
|
||||
MAP.put(CANNOT_INFER_VISIBILITY, "Cannot infer visibility. Please specify it explicitly");
|
||||
|
||||
@@ -61,7 +61,9 @@ public interface BindingContext {
|
||||
}
|
||||
};
|
||||
|
||||
WritableSlice<JetAnnotationEntry, AnnotationDescriptor> ANNOTATION = Slices.createSimpleSlice();
|
||||
WritableSlice<AnnotationDescriptor, JetAnnotationEntry> ANNOTATION_DESCRIPTOR_TO_PSI_ELEMENT = Slices.createSimpleSlice();
|
||||
WritableSlice<JetAnnotationEntry, AnnotationDescriptor> ANNOTATION =
|
||||
Slices.<JetAnnotationEntry, AnnotationDescriptor>sliceBuilder().setOpposite(ANNOTATION_DESCRIPTOR_TO_PSI_ELEMENT).build();
|
||||
|
||||
WritableSlice<JetExpression, CompileTimeConstant<?>> COMPILE_TIME_VALUE = Slices.createSimpleSlice();
|
||||
WritableSlice<JetTypeReference, JetType> TYPE = Slices.createSimpleSlice();
|
||||
|
||||
@@ -26,12 +26,14 @@ import com.intellij.util.containers.MultiMap;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.jet.lang.descriptors.*;
|
||||
import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor;
|
||||
import org.jetbrains.jet.lang.diagnostics.Errors;
|
||||
import org.jetbrains.jet.lang.psi.*;
|
||||
import org.jetbrains.jet.lang.resolve.name.Name;
|
||||
import org.jetbrains.jet.lang.resolve.scopes.JetScope;
|
||||
import org.jetbrains.jet.lang.types.JetType;
|
||||
import org.jetbrains.jet.lang.types.checker.JetTypeChecker;
|
||||
import org.jetbrains.jet.lang.types.lang.JetStandardLibrary;
|
||||
import org.jetbrains.jet.lexer.JetTokens;
|
||||
import org.jetbrains.jet.util.CommonSuppliers;
|
||||
|
||||
@@ -564,6 +566,14 @@ public class OverrideResolver {
|
||||
}
|
||||
|
||||
private void checkOverrideForMember(@NotNull final CallableMemberDescriptor declared) {
|
||||
if (declared.getKind() == CallableMemberDescriptor.Kind.SYNTHESIZED) {
|
||||
// TODO: this should be replaced soon by a framework of synthesized member generation tools
|
||||
if (declared.getName().getName().startsWith(DescriptorResolver.COMPONENT_FUNCTION_NAME_PREFIX)) {
|
||||
checkOverrideForComponentFunction(declared);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (declared.getKind() != CallableMemberDescriptor.Kind.DECLARATION) {
|
||||
return;
|
||||
}
|
||||
@@ -578,7 +588,7 @@ public class OverrideResolver {
|
||||
Set<? extends CallableMemberDescriptor> overriddenDescriptors = declared.getOverriddenDescriptors();
|
||||
|
||||
if (overrideNode != null) {
|
||||
checkOverridesForMemberMarkedOverride(declared, new CheckOverrideReportStrategy() {
|
||||
checkOverridesForMemberMarkedOverride(declared, true, new CheckOverrideReportStrategy() {
|
||||
private boolean finalOverriddenError = false;
|
||||
private boolean typeMismatchError = false;
|
||||
private boolean kindMismatchError = false;
|
||||
@@ -624,7 +634,11 @@ public class OverrideResolver {
|
||||
}
|
||||
}
|
||||
|
||||
private void checkOverridesForMemberMarkedOverride(@NotNull CallableMemberDescriptor declared, @NotNull CheckOverrideReportStrategy reportError) {
|
||||
private void checkOverridesForMemberMarkedOverride(
|
||||
@NotNull CallableMemberDescriptor declared,
|
||||
boolean checkIfOverridesNothing,
|
||||
@NotNull CheckOverrideReportStrategy reportError
|
||||
) {
|
||||
Set<? extends CallableMemberDescriptor> overriddenDescriptors = declared.getOverriddenDescriptors();
|
||||
|
||||
for (CallableMemberDescriptor overridden : overriddenDescriptors) {
|
||||
@@ -643,7 +657,7 @@ public class OverrideResolver {
|
||||
}
|
||||
}
|
||||
|
||||
if (overriddenDescriptors.isEmpty()) {
|
||||
if (checkIfOverridesNothing && overriddenDescriptors.isEmpty()) {
|
||||
DeclarationDescriptor containingDeclaration = declared.getContainingDeclaration();
|
||||
assert containingDeclaration instanceof ClassDescriptor : "Overrides may only be resolved in a class, but " + declared + " comes from " + containingDeclaration;
|
||||
ClassDescriptor declaringClass = (ClassDescriptor) containingDeclaration;
|
||||
@@ -658,6 +672,58 @@ public class OverrideResolver {
|
||||
}
|
||||
}
|
||||
|
||||
private void checkOverrideForComponentFunction(@NotNull final CallableMemberDescriptor componentFunction) {
|
||||
final JetAnnotationEntry dataAnnotation = findDataAnnotationForDataClass(componentFunction.getContainingDeclaration());
|
||||
|
||||
checkOverridesForMemberMarkedOverride(componentFunction, false, new CheckOverrideReportStrategy() {
|
||||
private boolean overrideConflict = false;
|
||||
|
||||
@Override
|
||||
public void overridingFinalMember(@NotNull CallableMemberDescriptor overridden) {
|
||||
if (!overrideConflict) {
|
||||
overrideConflict = true;
|
||||
trace.report(DATA_CLASS_OVERRIDE_CONFLICT.on(dataAnnotation, componentFunction, overridden.getContainingDeclaration()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void returnTypeMismatchOnOverride(@NotNull CallableMemberDescriptor overridden) {
|
||||
if (!overrideConflict) {
|
||||
overrideConflict = true;
|
||||
trace.report(DATA_CLASS_OVERRIDE_CONFLICT.on(dataAnnotation, componentFunction, overridden.getContainingDeclaration()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void varOverriddenByVal(@NotNull CallableMemberDescriptor overridden) {
|
||||
throw new IllegalStateException("Component functions are not properties");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cannotOverrideInvisibleMember(@NotNull CallableMemberDescriptor invisibleOverridden) {
|
||||
throw new IllegalStateException("CANNOT_OVERRIDE_INVISIBLE_MEMBER should be reported on the corresponding property");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void nothingToOverride() {
|
||||
throw new IllegalStateException("Component functions are OK to override nothing");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private JetAnnotationEntry findDataAnnotationForDataClass(@NotNull DeclarationDescriptor dataClass) {
|
||||
ClassDescriptor stdDataClassAnnotation = JetStandardLibrary.getInstance().getDataClassAnnotation();
|
||||
for (AnnotationDescriptor annotation : dataClass.getAnnotations()) {
|
||||
if (stdDataClassAnnotation.equals(annotation.getType().getConstructor().getDeclarationDescriptor())) {
|
||||
return BindingContextUtils.getNotNull(trace.getBindingContext(),
|
||||
BindingContext.ANNOTATION_DESCRIPTOR_TO_PSI_ELEMENT,
|
||||
annotation);
|
||||
}
|
||||
}
|
||||
throw new IllegalStateException("No data annotation is found for data class");
|
||||
}
|
||||
|
||||
private CallableMemberDescriptor findInvisibleOverriddenDescriptor(CallableMemberDescriptor declared, ClassDescriptor declaringClass) {
|
||||
CallableMemberDescriptor invisibleOverride = null;
|
||||
outer:
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
open data class A(val x: Int, val y: String)
|
||||
|
||||
<!DATA_CLASS_OVERRIDE_CONFLICT!>data<!> class B(val z: String) : A(42, "")
|
||||
@@ -0,0 +1,5 @@
|
||||
trait T {
|
||||
fun component1(): Int
|
||||
}
|
||||
|
||||
data class A(val x: Int) : T
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
trait T {
|
||||
final fun component1(): Int = 42
|
||||
}
|
||||
|
||||
<!DATA_CLASS_OVERRIDE_CONFLICT!>data<!> class A(val x: Int) : T
|
||||
@@ -0,0 +1,3 @@
|
||||
open data class D(private final val x: Int)
|
||||
|
||||
data class E(internal <!CANNOT_OVERRIDE_INVISIBLE_MEMBER!>override<!> val x: Int) : D(42)
|
||||
@@ -977,6 +977,11 @@ public class JetDiagnosticsTestGenerated extends AbstractDiagnosticsTestWithEage
|
||||
doTest("compiler/testData/diagnostics/tests/dataClasses/componentNamedComponent1.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("dataClassOverrideConflict.kt")
|
||||
public void testDataClassOverrideConflict() throws Exception {
|
||||
doTest("compiler/testData/diagnostics/tests/dataClasses/dataClassOverrideConflict.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("emptyConstructor.kt")
|
||||
public void testEmptyConstructor() throws Exception {
|
||||
doTest("compiler/testData/diagnostics/tests/dataClasses/emptyConstructor.kt");
|
||||
@@ -992,6 +997,16 @@ public class JetDiagnosticsTestGenerated extends AbstractDiagnosticsTestWithEage
|
||||
doTest("compiler/testData/diagnostics/tests/dataClasses/firstParamIsVar.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("implementTraitWhichHasComponent1.kt")
|
||||
public void testImplementTraitWhichHasComponent1() throws Exception {
|
||||
doTest("compiler/testData/diagnostics/tests/dataClasses/implementTraitWhichHasComponent1.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("implementTraitWhichHasFinalComponent1.kt")
|
||||
public void testImplementTraitWhichHasFinalComponent1() throws Exception {
|
||||
doTest("compiler/testData/diagnostics/tests/dataClasses/implementTraitWhichHasFinalComponent1.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("multiDeclaration.kt")
|
||||
public void testMultiDeclaration() throws Exception {
|
||||
doTest("compiler/testData/diagnostics/tests/dataClasses/multiDeclaration.kt");
|
||||
@@ -1012,6 +1027,11 @@ public class JetDiagnosticsTestGenerated extends AbstractDiagnosticsTestWithEage
|
||||
doTest("compiler/testData/diagnostics/tests/dataClasses/oneValParam.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("overrideInvisibleMember.kt")
|
||||
public void testOverrideInvisibleMember() throws Exception {
|
||||
doTest("compiler/testData/diagnostics/tests/dataClasses/overrideInvisibleMember.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("secondParamIsVal.kt")
|
||||
public void testSecondParamIsVal() throws Exception {
|
||||
doTest("compiler/testData/diagnostics/tests/dataClasses/secondParamIsVal.kt");
|
||||
|
||||
Reference in New Issue
Block a user