diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/BindingContext.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/BindingContext.java index 549d3b485c0..22c390d4794 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/BindingContext.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/BindingContext.java @@ -220,8 +220,11 @@ public interface BindingContext { .setFurtherLookupSlices(DECLARATIONS_TO_DESCRIPTORS).build(); WritableSlice LABEL_TARGET = Slices.sliceBuilder().build(); - WritableSlice VALUE_PARAMETER_AS_PROPERTY = - Slices.sliceBuilder().build(); + WritableSlice VALUE_PARAMETER_AS_PROPERTY = + Slices.sliceBuilder().build(); + + WritableSlice DATA_CLASS_COMPONENT_FUNCTION = + Slices.sliceBuilder().build(); WritableSlice FQNAME_TO_CLASS_DESCRIPTOR = new BasicWritableSlice(DO_NOTHING, true); WritableSlice FQNAME_TO_NAMESPACE_DESCRIPTOR = diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/DeclarationResolver.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/DeclarationResolver.java index 15b5e2b4cc4..f756a3d4970 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/DeclarationResolver.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/DeclarationResolver.java @@ -28,6 +28,8 @@ 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.resolve.scopes.WritableScope; +import org.jetbrains.jet.lang.types.ErrorUtils; +import org.jetbrains.jet.lang.types.lang.JetStandardLibrary; import javax.inject.Inject; import java.util.*; @@ -88,6 +90,7 @@ public class DeclarationResolver { resolveConstructorHeaders(); resolveAnnotationStubsOnClassesAndConstructors(); resolveFunctionAndPropertyHeaders(); + createComponentFunctionsForDataClasses(); importsResolver.processMembersImports(rootScope); checkRedeclarationsInNamespaces(); checkRedeclarationsInInnerClassNames(); @@ -210,6 +213,38 @@ public class DeclarationResolver { } } + private void createComponentFunctionsForDataClasses() { + for (Map.Entry entry : context.getClasses().entrySet()) { + JetClass jetClass = entry.getKey(); + MutableClassDescriptor classDescriptor = entry.getValue(); + + if (jetClass.hasPrimaryConstructor() && JetStandardLibrary.isData(classDescriptor)) { + createComponentFunctions(classDescriptor); + } + } + } + + private void createComponentFunctions(MutableClassDescriptor classDescriptor) { + Set constructors = classDescriptor.getConstructors(); + assert constructors.size() == 1 : "Data class hasn't a single constructor: " + constructors.size(); + ConstructorDescriptor constructor = constructors.iterator().next(); + + int parameterIndex = 0; + for (ValueParameterDescriptor parameter : constructor.getValueParameters()) { + if (!ErrorUtils.isErrorType(parameter.getType())) { + PropertyDescriptor property = trace.get(BindingContext.VALUE_PARAMETER_AS_PROPERTY, parameter); + if (property != null) { + ++parameterIndex; + + SimpleFunctionDescriptor functionDescriptor = + DescriptorResolver.createComponentFunctionDescriptor(parameterIndex, property, parameter, classDescriptor, trace); + + classDescriptor.getBuilder().addFunctionDescriptor(functionDescriptor); + } + } + } + } + private void processPrimaryConstructor(MutableClassDescriptor classDescriptor, JetClass klass) { if (classDescriptor.getKind() == ClassKind.TRAIT) { JetParameterList primaryConstructorParameterList = klass.getPrimaryConstructorParameterList(); diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/DescriptorResolver.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/DescriptorResolver.java index 852554f2201..863b22e4edd 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/DescriptorResolver.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/DescriptorResolver.java @@ -58,6 +58,7 @@ import static org.jetbrains.jet.lexer.JetTokens.OVERRIDE_KEYWORD; public class DescriptorResolver { public static final Name VALUE_OF_METHOD_NAME = Name.identifier("valueOf"); public static final Name VALUES_METHOD_NAME = Name.identifier("values"); + public static final String COMPONENT_FUNCTION_NAME_PREFIX = "component"; @NotNull private TypeResolver typeResolver; @@ -295,6 +296,40 @@ public class DescriptorResolver { return functionDescriptor; } + @NotNull + public static SimpleFunctionDescriptor createComponentFunctionDescriptor( + int parameterIndex, + @NotNull PropertyDescriptor property, + @NotNull ValueParameterDescriptor parameter, + @NotNull ClassDescriptor classDescriptor, + @NotNull BindingTrace trace + ) { + String functionName = COMPONENT_FUNCTION_NAME_PREFIX + parameterIndex; + JetType returnType = property.getType(); + + SimpleFunctionDescriptorImpl functionDescriptor = new SimpleFunctionDescriptorImpl( + classDescriptor, + Collections.emptyList(), + Name.identifier(functionName), + CallableMemberDescriptor.Kind.SYNTHESIZED + ); + + functionDescriptor.initialize( + null, + classDescriptor.getImplicitReceiver(), + Collections.emptyList(), + Collections.emptyList(), + returnType, + Modality.FINAL, + property.getVisibility(), + true + ); + + trace.record(BindingContext.DATA_CLASS_COMPONENT_FUNCTION, parameter, functionDescriptor); + + return functionDescriptor; + } + public static Visibility getDefaultVisibility(JetModifierListOwner modifierListOwner, DeclarationDescriptor containingDescriptor) { Visibility defaultVisibility; if (containingDescriptor instanceof ClassDescriptor) { @@ -1076,6 +1111,7 @@ public class DescriptorResolver { getter.initialize(propertyDescriptor.getType()); trace.record(BindingContext.PRIMARY_CONSTRUCTOR_PARAMETER, parameter, propertyDescriptor); + trace.record(BindingContext.VALUE_PARAMETER_AS_PROPERTY, valueParameter, propertyDescriptor); return propertyDescriptor; } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ExpressionTypingUtils.java b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ExpressionTypingUtils.java index ab576cb2ad0..271b264cd85 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ExpressionTypingUtils.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ExpressionTypingUtils.java @@ -277,7 +277,7 @@ public class ExpressionTypingUtils { ) { int componentIndex = 1; for (JetMultiDeclarationEntry entry : multiDeclaration.getEntries()) { - final Name componentName = Name.identifier("component" + componentIndex); + final Name componentName = Name.identifier(DescriptorResolver.COMPONENT_FUNCTION_NAME_PREFIX + componentIndex); componentIndex++; JetType expectedType = getExpectedTypeForComponent(context, entry); diff --git a/compiler/testData/diagnostics/tests/dataClasses/bothParamsAreNotProperties.kt b/compiler/testData/diagnostics/tests/dataClasses/bothParamsAreNotProperties.kt new file mode 100644 index 00000000000..67293d1f847 --- /dev/null +++ b/compiler/testData/diagnostics/tests/dataClasses/bothParamsAreNotProperties.kt @@ -0,0 +1,6 @@ +data class A(x: Int, y: String) + +fun foo(a: A) { + a.component1() + a.component2() +} diff --git a/compiler/testData/diagnostics/tests/dataClasses/componentFunctionInSubClass.kt b/compiler/testData/diagnostics/tests/dataClasses/componentFunctionInSubClass.kt new file mode 100644 index 00000000000..c57b6469d6f --- /dev/null +++ b/compiler/testData/diagnostics/tests/dataClasses/componentFunctionInSubClass.kt @@ -0,0 +1,11 @@ +open data class A(private val x: Int) + +class B : A(1) { + fun component1(): String = "" +} + +fun foo() { + val b = B() + b.component1() : String + (b : A).component1() : Int +} diff --git a/compiler/testData/diagnostics/tests/dataClasses/componentFunctionVisibility.kt b/compiler/testData/diagnostics/tests/dataClasses/componentFunctionVisibility.kt new file mode 100644 index 00000000000..07c92e8b60e --- /dev/null +++ b/compiler/testData/diagnostics/tests/dataClasses/componentFunctionVisibility.kt @@ -0,0 +1,15 @@ +open data class A(private val x: Int, protected val y: String, public val z: Any) + +fun foo(a: A) { + a.component1() + a.component2() + a.component3() +} + +class B : A(42, "", "") { + fun foo() { + this.component1() + this.component2() + this.component3() + } +} diff --git a/compiler/testData/diagnostics/tests/dataClasses/componentFunctionsAreFinal.kt b/compiler/testData/diagnostics/tests/dataClasses/componentFunctionsAreFinal.kt new file mode 100644 index 00000000000..601b4132b46 --- /dev/null +++ b/compiler/testData/diagnostics/tests/dataClasses/componentFunctionsAreFinal.kt @@ -0,0 +1,11 @@ +open data class A(val x: Int, val y: String) + +class B : A(42, "OK") { + override fun component1(): Int = 21 + override fun component2(): Int = 21 +} + +fun foo(b: B) { + b.component1() + b.component2() +} diff --git a/compiler/testData/diagnostics/tests/dataClasses/componentNamedComponent1.kt b/compiler/testData/diagnostics/tests/dataClasses/componentNamedComponent1.kt new file mode 100644 index 00000000000..045ee011574 --- /dev/null +++ b/compiler/testData/diagnostics/tests/dataClasses/componentNamedComponent1.kt @@ -0,0 +1,6 @@ +data class A(val component1: Int) + +fun foo(a: A) { + a.component1() + a.component1 +} diff --git a/compiler/testData/diagnostics/tests/dataClasses/emptyConstructor.kt b/compiler/testData/diagnostics/tests/dataClasses/emptyConstructor.kt new file mode 100644 index 00000000000..ef979927e6d --- /dev/null +++ b/compiler/testData/diagnostics/tests/dataClasses/emptyConstructor.kt @@ -0,0 +1,6 @@ +data class A() + +fun foo(a: A) { + a.component1() + a.component2() +} diff --git a/compiler/testData/diagnostics/tests/dataClasses/firstParamIsVal.kt b/compiler/testData/diagnostics/tests/dataClasses/firstParamIsVal.kt new file mode 100644 index 00000000000..1d1e1f213ce --- /dev/null +++ b/compiler/testData/diagnostics/tests/dataClasses/firstParamIsVal.kt @@ -0,0 +1,6 @@ +data class A(val x: Int, y: String) + +fun foo(a: A) { + a.component1() : Int + a.component2() +} diff --git a/compiler/testData/diagnostics/tests/dataClasses/firstParamIsVar.kt b/compiler/testData/diagnostics/tests/dataClasses/firstParamIsVar.kt new file mode 100644 index 00000000000..f7c23fa80e2 --- /dev/null +++ b/compiler/testData/diagnostics/tests/dataClasses/firstParamIsVar.kt @@ -0,0 +1,6 @@ +data class A(var x: Int, y: String) + +fun foo(a: A) { + a.component1() : Int + a.component2() +} diff --git a/compiler/testData/diagnostics/tests/dataClasses/multiDeclaration.kt b/compiler/testData/diagnostics/tests/dataClasses/multiDeclaration.kt new file mode 100644 index 00000000000..c9a8f39c3d0 --- /dev/null +++ b/compiler/testData/diagnostics/tests/dataClasses/multiDeclaration.kt @@ -0,0 +1,7 @@ +data class A(val x: Int, val y: String) + +fun foo(a: A) { + val (b, c) = a + b : Int + c : String +} diff --git a/compiler/testData/diagnostics/tests/dataClasses/multiDeclarationFor.kt b/compiler/testData/diagnostics/tests/dataClasses/multiDeclarationFor.kt new file mode 100644 index 00000000000..415335c5ca8 --- /dev/null +++ b/compiler/testData/diagnostics/tests/dataClasses/multiDeclarationFor.kt @@ -0,0 +1,8 @@ +data class A(val x: Int, val y: String) + +fun foo(arr: Array) { + for ((b, c) in arr) { + b : Int + c : String + } +} diff --git a/compiler/testData/diagnostics/tests/dataClasses/notADataClass.kt b/compiler/testData/diagnostics/tests/dataClasses/notADataClass.kt new file mode 100644 index 00000000000..2561937f3e4 --- /dev/null +++ b/compiler/testData/diagnostics/tests/dataClasses/notADataClass.kt @@ -0,0 +1,6 @@ +class A(val x: Int, val y: String) + +fun foo(a: A) { + a.component1() + a.component2() +} diff --git a/compiler/testData/diagnostics/tests/dataClasses/oneValParam.kt b/compiler/testData/diagnostics/tests/dataClasses/oneValParam.kt new file mode 100644 index 00000000000..db488601a98 --- /dev/null +++ b/compiler/testData/diagnostics/tests/dataClasses/oneValParam.kt @@ -0,0 +1,6 @@ +data class A(val x: Int) + +fun foo(a: A) { + a.component1() : Int + a.component2() +} diff --git a/compiler/testData/diagnostics/tests/dataClasses/secondParamIsVal.kt b/compiler/testData/diagnostics/tests/dataClasses/secondParamIsVal.kt new file mode 100644 index 00000000000..a56bea06d21 --- /dev/null +++ b/compiler/testData/diagnostics/tests/dataClasses/secondParamIsVal.kt @@ -0,0 +1,6 @@ +data class A(x: Int, val y: String) + +fun foo(a: A) { + a.component1() : String + a.component2() +} diff --git a/compiler/testData/diagnostics/tests/dataClasses/secondParamIsVar.kt b/compiler/testData/diagnostics/tests/dataClasses/secondParamIsVar.kt new file mode 100644 index 00000000000..d3a951fdf8c --- /dev/null +++ b/compiler/testData/diagnostics/tests/dataClasses/secondParamIsVar.kt @@ -0,0 +1,6 @@ +data class A(x: Int, var y: String) + +fun foo(a: A) { + a.component1() : String + a.component2() +} diff --git a/compiler/testData/diagnostics/tests/dataClasses/twoValParams.kt b/compiler/testData/diagnostics/tests/dataClasses/twoValParams.kt new file mode 100644 index 00000000000..f6a1910fbc7 --- /dev/null +++ b/compiler/testData/diagnostics/tests/dataClasses/twoValParams.kt @@ -0,0 +1,6 @@ +data class A(val x: Int, val y: String) + +fun foo(a: A) { + a.component1() : Int + a.component2() : String +} diff --git a/compiler/testData/diagnostics/tests/dataClasses/twoVarParams.kt b/compiler/testData/diagnostics/tests/dataClasses/twoVarParams.kt new file mode 100644 index 00000000000..459e9803734 --- /dev/null +++ b/compiler/testData/diagnostics/tests/dataClasses/twoVarParams.kt @@ -0,0 +1,6 @@ +data class A(var x: Int, var y: String) + +fun foo(a: A) { + a.component1() : Int + a.component2() : String +} diff --git a/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java index c14beb98bcd..b6ee8f84b70 100644 --- a/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java @@ -27,7 +27,7 @@ import java.io.File; @InnerTestClasses({JetDiagnosticsTestGenerated.Tests.class, JetDiagnosticsTestGenerated.Script.class}) public class JetDiagnosticsTestGenerated extends AbstractDiagnosticsTestWithEagerResolve { @TestMetadata("compiler/testData/diagnostics/tests") - @InnerTestClasses({Tests.Annotations.class, Tests.BackingField.class, Tests.Cast.class, Tests.CheckArguments.class, Tests.ControlFlowAnalysis.class, Tests.ControlStructures.class, Tests.DataFlow.class, Tests.DataFlowInfoTraversal.class, Tests.DeclarationChecks.class, Tests.Enum.class, Tests.Extensions.class, Tests.Generics.class, Tests.IncompleteCode.class, Tests.Inference.class, Tests.Infos.class, Tests.J_k.class, Tests.Jdk_annotations.class, Tests.Library.class, Tests.NullabilityAndAutoCasts.class, Tests.Objects.class, Tests.OperatorsOverloading.class, Tests.Overload.class, Tests.Override.class, Tests.Redeclarations.class, Tests.Regressions.class, Tests.Scopes.class, Tests.Shadowing.class, Tests.Substitutions.class, Tests.Subtyping.class, Tests.Tuples.class, Tests.Varargs.class}) + @InnerTestClasses({Tests.Annotations.class, Tests.BackingField.class, Tests.Cast.class, Tests.CheckArguments.class, Tests.ControlFlowAnalysis.class, Tests.ControlStructures.class, Tests.DataClasses.class, Tests.DataFlow.class, Tests.DataFlowInfoTraversal.class, Tests.DeclarationChecks.class, Tests.Enum.class, Tests.Extensions.class, Tests.Generics.class, Tests.IncompleteCode.class, Tests.Inference.class, Tests.Infos.class, Tests.J_k.class, Tests.Jdk_annotations.class, Tests.Library.class, Tests.NullabilityAndAutoCasts.class, Tests.Objects.class, Tests.OperatorsOverloading.class, Tests.Overload.class, Tests.Override.class, Tests.Redeclarations.class, Tests.Regressions.class, Tests.Scopes.class, Tests.Shadowing.class, Tests.Substitutions.class, Tests.Subtyping.class, Tests.Tuples.class, Tests.Varargs.class}) public static class Tests extends AbstractDiagnosticsTestWithEagerResolve { @TestMetadata("Abstract.kt") public void testAbstract() throws Exception { @@ -946,6 +946,94 @@ public class JetDiagnosticsTestGenerated extends AbstractDiagnosticsTestWithEage } + @TestMetadata("compiler/testData/diagnostics/tests/dataClasses") + public static class DataClasses extends AbstractDiagnosticsTestWithEagerResolve { + public void testAllFilesPresentInDataClasses() throws Exception { + JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.checkers.AbstractDiagnosticsTestWithEagerResolve", new File("compiler/testData/diagnostics/tests/dataClasses"), "kt", true); + } + + @TestMetadata("bothParamsAreNotProperties.kt") + public void testBothParamsAreNotProperties() throws Exception { + doTest("compiler/testData/diagnostics/tests/dataClasses/bothParamsAreNotProperties.kt"); + } + + @TestMetadata("componentFunctionInSubClass.kt") + public void testComponentFunctionInSubClass() throws Exception { + doTest("compiler/testData/diagnostics/tests/dataClasses/componentFunctionInSubClass.kt"); + } + + @TestMetadata("componentFunctionVisibility.kt") + public void testComponentFunctionVisibility() throws Exception { + doTest("compiler/testData/diagnostics/tests/dataClasses/componentFunctionVisibility.kt"); + } + + @TestMetadata("componentFunctionsAreFinal.kt") + public void testComponentFunctionsAreFinal() throws Exception { + doTest("compiler/testData/diagnostics/tests/dataClasses/componentFunctionsAreFinal.kt"); + } + + @TestMetadata("componentNamedComponent1.kt") + public void testComponentNamedComponent1() throws Exception { + doTest("compiler/testData/diagnostics/tests/dataClasses/componentNamedComponent1.kt"); + } + + @TestMetadata("emptyConstructor.kt") + public void testEmptyConstructor() throws Exception { + doTest("compiler/testData/diagnostics/tests/dataClasses/emptyConstructor.kt"); + } + + @TestMetadata("firstParamIsVal.kt") + public void testFirstParamIsVal() throws Exception { + doTest("compiler/testData/diagnostics/tests/dataClasses/firstParamIsVal.kt"); + } + + @TestMetadata("firstParamIsVar.kt") + public void testFirstParamIsVar() throws Exception { + doTest("compiler/testData/diagnostics/tests/dataClasses/firstParamIsVar.kt"); + } + + @TestMetadata("multiDeclaration.kt") + public void testMultiDeclaration() throws Exception { + doTest("compiler/testData/diagnostics/tests/dataClasses/multiDeclaration.kt"); + } + + @TestMetadata("multiDeclarationFor.kt") + public void testMultiDeclarationFor() throws Exception { + doTest("compiler/testData/diagnostics/tests/dataClasses/multiDeclarationFor.kt"); + } + + @TestMetadata("notADataClass.kt") + public void testNotADataClass() throws Exception { + doTest("compiler/testData/diagnostics/tests/dataClasses/notADataClass.kt"); + } + + @TestMetadata("oneValParam.kt") + public void testOneValParam() throws Exception { + doTest("compiler/testData/diagnostics/tests/dataClasses/oneValParam.kt"); + } + + @TestMetadata("secondParamIsVal.kt") + public void testSecondParamIsVal() throws Exception { + doTest("compiler/testData/diagnostics/tests/dataClasses/secondParamIsVal.kt"); + } + + @TestMetadata("secondParamIsVar.kt") + public void testSecondParamIsVar() throws Exception { + doTest("compiler/testData/diagnostics/tests/dataClasses/secondParamIsVar.kt"); + } + + @TestMetadata("twoValParams.kt") + public void testTwoValParams() throws Exception { + doTest("compiler/testData/diagnostics/tests/dataClasses/twoValParams.kt"); + } + + @TestMetadata("twoVarParams.kt") + public void testTwoVarParams() throws Exception { + doTest("compiler/testData/diagnostics/tests/dataClasses/twoVarParams.kt"); + } + + } + @TestMetadata("compiler/testData/diagnostics/tests/dataFlow") public static class DataFlow extends AbstractDiagnosticsTestWithEagerResolve { public void testAllFilesPresentInDataFlow() throws Exception { @@ -3189,6 +3277,7 @@ public class JetDiagnosticsTestGenerated extends AbstractDiagnosticsTestWithEage suite.addTestSuite(CheckArguments.class); suite.addTestSuite(ControlFlowAnalysis.class); suite.addTestSuite(ControlStructures.class); + suite.addTestSuite(DataClasses.class); suite.addTestSuite(DataFlow.class); suite.addTestSuite(DataFlowInfoTraversal.class); suite.addTest(DeclarationChecks.innerSuite());