From 8a927d70d7bc88da66cd503c365defe6e87d3076 Mon Sep 17 00:00:00 2001 From: Alex Tkachman Date: Wed, 28 Sep 2011 10:54:45 +0300 Subject: [PATCH 1/3] array codegen bugs --- .../jetbrains/jet/codegen/CodegenUtil.java | 11 +++-- .../jet/codegen/ExpressionCodegen.java | 19 +++++-- .../jetbrains/jet/codegen/JetTypeMapper.java | 16 ++++++ .../jet/codegen/NamespaceCodegen.java | 2 +- .../org/jetbrains/jet/codegen/StackValue.java | 3 +- idea/testData/codegen/regressions/kt326.jet | 25 ++++++++++ .../jetbrains/jet/codegen/ArrayGenTest.java | 49 +++++++++++++++++++ .../jet/codegen/ArrayGenTestCase.java | 7 --- stdlib/src/jet/typeinfo/TypeInfo.java | 5 ++ 9 files changed, 120 insertions(+), 17 deletions(-) create mode 100644 idea/testData/codegen/regressions/kt326.jet create mode 100644 idea/tests/org/jetbrains/jet/codegen/ArrayGenTest.java delete mode 100644 idea/tests/org/jetbrains/jet/codegen/ArrayGenTestCase.java diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/CodegenUtil.java b/compiler/backend/src/org/jetbrains/jet/codegen/CodegenUtil.java index 205d116fda9..562dcd33014 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/CodegenUtil.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/CodegenUtil.java @@ -1,13 +1,12 @@ package org.jetbrains.jet.codegen; -import com.intellij.psi.PsiClass; -import com.intellij.psi.PsiElement; import org.jetbrains.jet.lang.descriptors.ClassDescriptor; import org.jetbrains.jet.lang.descriptors.ClassKind; import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor; -import org.jetbrains.jet.lang.psi.JetClass; -import org.jetbrains.jet.lang.resolve.BindingContext; +import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor; import org.jetbrains.jet.lang.types.JetType; +import org.jetbrains.jet.lang.types.TypeProjection; +import org.objectweb.asm.Type; /** * @author abreslav @@ -54,4 +53,8 @@ public class CodegenUtil { } return (ClassDescriptor) outerDescriptor; } + + public static Type arrayElementType(Type type) { + return Type.getType(type.getDescriptor().substring(1)); + } } diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/ExpressionCodegen.java b/compiler/backend/src/org/jetbrains/jet/codegen/ExpressionCodegen.java index e93acaccf81..096e1b3aa51 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/ExpressionCodegen.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/ExpressionCodegen.java @@ -378,8 +378,8 @@ public class ExpressionCodegen extends JetVisitor { gen(expression.getLoopRange(), loopRangeType); // array v.load(myIndexVar, Type.INT_TYPE); - v.aload(loopRangeType.getElementType()); - StackValue.onStack(loopRangeType.getElementType()).put(asmParamType, v); + v.aload(CodegenUtil.arrayElementType(loopRangeType)); + StackValue.onStack(CodegenUtil.arrayElementType(loopRangeType)).put(asmParamType, v); v.store(lookupLocal(parameterDescriptor), asmParamType); } @@ -1683,12 +1683,23 @@ public class ExpressionCodegen extends JetVisitor { } private void generateNewArray(JetCallExpression expression, Type type) { + JetType arrayType = bindingContext.get(BindingContext.EXPRESSION_TYPE, expression); + List args = expression.getValueArguments(); if (args.size() != 1) { throw new CompilationException("array constructor requires one value argument"); } gen(args.get(0).getArgumentExpression(), Type.INT_TYPE); - v.newarray(type.getElementType()); + + JetType elementType = typeMapper.getGenericsElementType(arrayType); + if(elementType != null) { + generateTypeInfo(elementType); + v.invokestatic("jet/typeinfo/TypeInfo", "newArray", "(ILjet/typeinfo/TypeInfo;)[Ljava/lang/Object;"); + v.checkcast(type); + } + else { + v.newarray(CodegenUtil.arrayElementType(type)); + } } @Override @@ -1698,7 +1709,7 @@ public class ExpressionCodegen extends JetVisitor { gen(array, arrayType); generateArrayIndex(expression); if (arrayType.getSort() == Type.ARRAY) { - final Type elementType = arrayType.getElementType(); + final Type elementType = CodegenUtil.arrayElementType(arrayType); return StackValue.arrayElement(elementType); } else { diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/JetTypeMapper.java b/compiler/backend/src/org/jetbrains/jet/codegen/JetTypeMapper.java index 91ad14a4f3f..92f504660c3 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/JetTypeMapper.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/JetTypeMapper.java @@ -670,4 +670,20 @@ public class JetTypeMapper { public String isKnownTypeInfo(JetType jetType) { return knowTypes.get(jetType); } + + public boolean isGenericsArray(JetType type) { + DeclarationDescriptor declarationDescriptor = type.getConstructor().getDeclarationDescriptor(); + if(declarationDescriptor instanceof TypeParameterDescriptor) + return true; + + if(standardLibrary.getArray().equals(declarationDescriptor)) + return isGenericsArray(type.getArguments().get(0).getType()); + + return false; + } + + public JetType getGenericsElementType(JetType arrayType) { + JetType type = arrayType.getArguments().get(0).getType(); + return isGenericsArray(type) ? type : null; + } } diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/NamespaceCodegen.java b/compiler/backend/src/org/jetbrains/jet/codegen/NamespaceCodegen.java index 005cb7c422f..4974917fb30 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/NamespaceCodegen.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/NamespaceCodegen.java @@ -152,7 +152,7 @@ public class NamespaceCodegen { v.aconst(jvmType); v.iconst(jetType.isNullable() ? 1 : 0); List arguments = jetType.getArguments(); - if (arguments.size() > 0) { + if (arguments.size() > 0 && !(jvmType.getSort() == Type.ARRAY && jvmType.getElementType().getSort() != Type.OBJECT)) { v.iconst(arguments.size()); v.newarray(JetTypeMapper.TYPE_TYPEINFOPROJECTION); diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/StackValue.java b/compiler/backend/src/org/jetbrains/jet/codegen/StackValue.java index 29dbf0c7914..94b0504eeba 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/StackValue.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/StackValue.java @@ -186,7 +186,7 @@ public abstract class StackValue { else v.iconst(0); } - else if (type.getSort() == Type.OBJECT && this.type.equals(JetTypeMapper.TYPE_OBJECT)) { + else if (type.getSort() == Type.OBJECT && this.type.equals(JetTypeMapper.TYPE_OBJECT) || type.getSort() == Type.ARRAY) { v.checkcast(type); } else if (type.getSort() == Type.OBJECT) { @@ -431,6 +431,7 @@ public abstract class StackValue { @Override public void put(Type type, InstructionAdapter v) { v.aload(type); // assumes array and index are on the stack + coerce(type, v); } @Override diff --git a/idea/testData/codegen/regressions/kt326.jet b/idea/testData/codegen/regressions/kt326.jet new file mode 100644 index 00000000000..cfd171f11e0 --- /dev/null +++ b/idea/testData/codegen/regressions/kt326.jet @@ -0,0 +1,25 @@ +namespace test + +class List() { + val a : Array = Array(1) +} + +fun box() : String { + val a = List() + a.a[0] = "1" + println(a.a[0]) + + val b = List() + b.a[0] = 10 + println(b.a[0]) + + val c = List>() + c.a[0] = Array(4) + println(c.a[0].size) + + return "OK" +} + +fun println(s : Any?) { + System.out?.println(s); +} diff --git a/idea/tests/org/jetbrains/jet/codegen/ArrayGenTest.java b/idea/tests/org/jetbrains/jet/codegen/ArrayGenTest.java new file mode 100644 index 00000000000..372ba4214d1 --- /dev/null +++ b/idea/tests/org/jetbrains/jet/codegen/ArrayGenTest.java @@ -0,0 +1,49 @@ +package org.jetbrains.jet.codegen; + +import java.lang.reflect.Method; + +public class ArrayGenTest extends CodegenTestCase { + public void testKt238 () throws Exception { + blackBoxFile("regressions/kt238.jet"); + } + + public void testKt326 () throws Exception { + blackBoxFile("regressions/kt326.jet"); + } + + public void testCreateMultiInt () throws Exception { + loadText("fun foo() = Array> (5)"); + Method foo = generateFunction(); + Object invoke = foo.invoke(null); + System.out.println(invoke.getClass()); + assertTrue(invoke instanceof int[][]); + } + + public void testCreateMultiString () throws Exception { + loadText("fun foo() = Array> (5)"); + Method foo = generateFunction(); + Object invoke = foo.invoke(null); + System.out.println(invoke.getClass()); + assertTrue(invoke instanceof String[][]); + } + + public void testCreateMultiGenerics () throws Exception { + /* + loadText("class L() { val a = Array(5) } fun foo() = L.a"); + System.out.println(generateToText()); + Method foo = generateFunction(); + Object invoke = foo.invoke(null); + System.out.println(invoke.getClass()); + assertTrue(invoke instanceof Integer[]); + */ + } + + public void testIntGenerics () throws Exception { + loadText("class L(var a : T) {} fun foo() = L(5).a"); + System.out.println(generateToText()); + Method foo = generateFunction(); + Object invoke = foo.invoke(null); + System.out.println(invoke.getClass()); + assertTrue(invoke instanceof Integer); + } +} diff --git a/idea/tests/org/jetbrains/jet/codegen/ArrayGenTestCase.java b/idea/tests/org/jetbrains/jet/codegen/ArrayGenTestCase.java deleted file mode 100644 index 36153e29568..00000000000 --- a/idea/tests/org/jetbrains/jet/codegen/ArrayGenTestCase.java +++ /dev/null @@ -1,7 +0,0 @@ -package org.jetbrains.jet.codegen; - -public class ArrayGenTestCase extends CodegenTestCase { - public void testKt238 () throws Exception { - blackBoxFile("regressions/kt238.jet"); - } -} diff --git a/stdlib/src/jet/typeinfo/TypeInfo.java b/stdlib/src/jet/typeinfo/TypeInfo.java index e4a971e90fe..4a49915511f 100644 --- a/stdlib/src/jet/typeinfo/TypeInfo.java +++ b/stdlib/src/jet/typeinfo/TypeInfo.java @@ -3,6 +3,7 @@ package jet.typeinfo; import jet.JetObject; import jet.Tuple0; +import java.lang.reflect.Array; import java.lang.reflect.Field; import java.lang.reflect.TypeVariable; import java.util.*; @@ -37,6 +38,10 @@ public abstract class TypeInfo implements JetObject { public static final TypeInfo NULLABLE_DOUBLE_TYPE_INFO = getTypeInfo(Double.class, true); public static final TypeInfo NULLABLE_STRING_TYPE_INFO = getTypeInfo(String.class, true); public static final TypeInfo NULLABLE_TUPLE0_TYPE_INFO = getTypeInfo(Tuple0.class, true); + + public static Object [] newArray(int length, TypeInfo typeInfo) { + return (Object[]) Array.newInstance(((TypeInfoImpl) typeInfo).signature.klazz, length); + } public static TypeInfoProjection invariantProjection(final TypeInfo typeInfo) { return (TypeInfoProjection) typeInfo; From 70d48263177138591bae4e1cd019b74d86fc5390 Mon Sep 17 00:00:00 2001 From: svtk Date: Wed, 28 Sep 2011 11:58:55 +0400 Subject: [PATCH 2/3] Tests for import class action added --- .../jet/lang/resolve/BodyResolver.java | 9 +++++- .../jetbrains/jet/lang/types/ErrorUtils.java | 2 +- .../jetbrains/jet/plugin/JetPluginUtil.java | 3 +- .../quickfix/ChangeAccessorTypeFix.java | 3 +- .../plugin/quickfix/ImportClassHelper.java | 3 +- .../quickfix/RemovePartsFromPropertyFix.java | 8 +---- .../quickfix/ReplaceSafeCallToDotCall.java | 1 - .../classImport/afterHasThisImport.kt | 15 +++++++++ .../classImport/afterNoImportJavaLang.kt | 8 +++++ .../classImport/afterNoImportJetStandard.kt | 8 +++++ .../quickfix/classImport/afterToImport1.kt | 11 +++++++ .../quickfix/classImport/afterToImport2.kt | 15 +++++++++ .../classImport/beforeHasThisImport.kt | 15 +++++++++ .../classImport/beforeNoImportJavaLang.kt | 8 +++++ .../classImport/beforeNoImportJetStandard.kt | 8 +++++ .../quickfix/classImport/beforeToImport1.kt | 10 ++++++ .../quickfix/classImport/beforeToImport2.kt | 13 ++++++++ .../jet/plugin/quickfix/ClassImportTests.java | 32 +++++++++++++++++++ 18 files changed, 158 insertions(+), 14 deletions(-) create mode 100644 idea/testData/quickfix/classImport/afterHasThisImport.kt create mode 100644 idea/testData/quickfix/classImport/afterNoImportJavaLang.kt create mode 100644 idea/testData/quickfix/classImport/afterNoImportJetStandard.kt create mode 100644 idea/testData/quickfix/classImport/afterToImport1.kt create mode 100644 idea/testData/quickfix/classImport/afterToImport2.kt create mode 100644 idea/testData/quickfix/classImport/beforeHasThisImport.kt create mode 100644 idea/testData/quickfix/classImport/beforeNoImportJavaLang.kt create mode 100644 idea/testData/quickfix/classImport/beforeNoImportJetStandard.kt create mode 100644 idea/testData/quickfix/classImport/beforeToImport1.kt create mode 100644 idea/testData/quickfix/classImport/beforeToImport2.kt create mode 100644 idea/tests/org/jetbrains/jet/plugin/quickfix/ClassImportTests.java diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/BodyResolver.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/BodyResolver.java index 24d8ea9f5dd..1d9d9265e94 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/BodyResolver.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/BodyResolver.java @@ -526,6 +526,9 @@ public class BodyResolver { if (propertyDescriptor.getModality() == Modality.ABSTRACT) { JetType returnType = propertyDescriptor.getReturnType(); + if (returnType instanceof DeferredType) { + returnType = ((DeferredType) returnType).getActualType(); + } JetExpression initializer = property.getInitializer(); if (initializer != null) { @@ -575,7 +578,11 @@ public class BodyResolver { } if (inTrait) { // context.getTrace().getErrorHandler().genericError(initializer.getNode(), "Property initializers are not allowed in traits"); - context.getTrace().report(PROPERTY_INITIALIZER_IN_TRAIT.on(property, initializer, propertyDescriptor.getReturnType())); + JetType returnType = propertyDescriptor.getReturnType(); + if (returnType instanceof DeferredType) { + returnType = ((DeferredType) returnType).getActualType(); + } + context.getTrace().report(PROPERTY_INITIALIZER_IN_TRAIT.on(property, initializer, returnType)); } else if (!backingFieldRequired) { // context.getTrace().getErrorHandler().genericError(initializer.getNode(), "Initializer is not allowed here because this property has no backing field"); diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/types/ErrorUtils.java b/compiler/frontend/src/org/jetbrains/jet/lang/types/ErrorUtils.java index b30f40e2aa2..6e5761683d0 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/types/ErrorUtils.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/types/ErrorUtils.java @@ -177,7 +177,7 @@ public class ErrorUtils { } public static boolean isError(@NotNull DeclarationDescriptor candidate) { - return candidate.getContainingDeclaration() == getErrorClass(); + return candidate.getContainingDeclaration() == getErrorClass() || candidate == ERROR_MODULE; } private static class ErrorTypeImpl implements JetType { diff --git a/idea/src/org/jetbrains/jet/plugin/JetPluginUtil.java b/idea/src/org/jetbrains/jet/plugin/JetPluginUtil.java index 2bbac86b500..37cc3071f59 100644 --- a/idea/src/org/jetbrains/jet/plugin/JetPluginUtil.java +++ b/idea/src/org/jetbrains/jet/plugin/JetPluginUtil.java @@ -9,6 +9,7 @@ import org.jetbrains.jet.lang.resolve.java.JavaClassDescriptor; import org.jetbrains.jet.lang.resolve.java.JavaDescriptorResolver; import org.jetbrains.jet.lang.resolve.scopes.JetScope; import org.jetbrains.jet.lang.types.DeferredType; +import org.jetbrains.jet.lang.types.ErrorUtils; import org.jetbrains.jet.lang.types.JetStandardLibrary; import org.jetbrains.jet.lang.types.JetType; @@ -61,7 +62,7 @@ public class JetPluginUtil { JetScope libraryScope = standardLibrary.getLibraryScope(); DeclarationDescriptor declaration = type.getMemberScope().getContainingDeclaration(); - if (declaration instanceof JavaClassDescriptor) { + if (declaration instanceof JavaClassDescriptor || ErrorUtils.isError(declaration)) { return false; } while (!(declaration instanceof NamespaceDescriptor)) { diff --git a/idea/src/org/jetbrains/jet/plugin/quickfix/ChangeAccessorTypeFix.java b/idea/src/org/jetbrains/jet/plugin/quickfix/ChangeAccessorTypeFix.java index dfab9fc0a0e..17590360691 100644 --- a/idea/src/org/jetbrains/jet/plugin/quickfix/ChangeAccessorTypeFix.java +++ b/idea/src/org/jetbrains/jet/plugin/quickfix/ChangeAccessorTypeFix.java @@ -48,7 +48,6 @@ public class ChangeAccessorTypeFix extends JetIntentionAction createFactory() { diff --git a/idea/src/org/jetbrains/jet/plugin/quickfix/ImportClassHelper.java b/idea/src/org/jetbrains/jet/plugin/quickfix/ImportClassHelper.java index 09c5891de8f..a8471ab2e96 100644 --- a/idea/src/org/jetbrains/jet/plugin/quickfix/ImportClassHelper.java +++ b/idea/src/org/jetbrains/jet/plugin/quickfix/ImportClassHelper.java @@ -3,6 +3,7 @@ package org.jetbrains.jet.plugin.quickfix; import com.intellij.psi.PsiElement; import org.jetbrains.annotations.NotNull; import org.jetbrains.jet.lang.psi.*; +import org.jetbrains.jet.lang.types.ErrorUtils; import org.jetbrains.jet.lang.types.JetType; import org.jetbrains.jet.plugin.JetPluginUtil; @@ -21,7 +22,7 @@ public class ImportClassHelper { JetNamespace namespace = (JetNamespace) parent; List importDirectives = namespace.getImportDirectives(); - if (JetPluginUtil.checkTypeIsStandard(type, element.getProject())) { + if (JetPluginUtil.checkTypeIsStandard(type, element.getProject()) || ErrorUtils.isError(type.getMemberScope().getContainingDeclaration())) { element.replace(newElement); return; } diff --git a/idea/src/org/jetbrains/jet/plugin/quickfix/RemovePartsFromPropertyFix.java b/idea/src/org/jetbrains/jet/plugin/quickfix/RemovePartsFromPropertyFix.java index 2b4675c931c..55429205dff 100644 --- a/idea/src/org/jetbrains/jet/plugin/quickfix/RemovePartsFromPropertyFix.java +++ b/idea/src/org/jetbrains/jet/plugin/quickfix/RemovePartsFromPropertyFix.java @@ -10,7 +10,6 @@ import org.jetbrains.jet.lang.diagnostics.DiagnosticParameters; import org.jetbrains.jet.lang.diagnostics.DiagnosticWithParameters; import org.jetbrains.jet.lang.diagnostics.DiagnosticWithPsiElement; import org.jetbrains.jet.lang.psi.*; -import org.jetbrains.jet.lang.types.DeferredType; import org.jetbrains.jet.lang.types.JetType; /** @@ -22,12 +21,7 @@ public class RemovePartsFromPropertyFix extends JetIntentionAction private RemovePartsFromPropertyFix(@NotNull JetProperty element, JetType type) { super(element); - if (type instanceof DeferredType) { - this.type = ((DeferredType) type).getActualType(); - } - else { - this.type = type; - } + this.type = type; partsToRemove = partsToRemove(element.getGetter() != null && element.getGetter().getBodyExpression() != null, element.getSetter() != null && element.getSetter().getBodyExpression() != null, element.getInitializer() != null); diff --git a/idea/src/org/jetbrains/jet/plugin/quickfix/ReplaceSafeCallToDotCall.java b/idea/src/org/jetbrains/jet/plugin/quickfix/ReplaceSafeCallToDotCall.java index 9f6f3016f8e..61a8cc6e0ad 100644 --- a/idea/src/org/jetbrains/jet/plugin/quickfix/ReplaceSafeCallToDotCall.java +++ b/idea/src/org/jetbrains/jet/plugin/quickfix/ReplaceSafeCallToDotCall.java @@ -36,7 +36,6 @@ public class ReplaceSafeCallToDotCall extends JetIntentionAction { JetSafeQualifiedExpression safeQualifiedExpression = (JetSafeQualifiedExpression) element; JetDotQualifiedExpression newElement = (JetDotQualifiedExpression) JetPsiFactory.createExpression(project, "x.foo"); - //TODO check for null CodeEditUtil.replaceChild(newElement.getNode(), newElement.getSelectorExpression().getNode(), safeQualifiedExpression.getSelectorExpression().getNode()); CodeEditUtil.replaceChild(newElement.getNode(), newElement.getReceiverExpression().getNode(), safeQualifiedExpression.getReceiverExpression().getNode()); diff --git a/idea/testData/quickfix/classImport/afterHasThisImport.kt b/idea/testData/quickfix/classImport/afterHasThisImport.kt new file mode 100644 index 00000000000..097dd252598 --- /dev/null +++ b/idea/testData/quickfix/classImport/afterHasThisImport.kt @@ -0,0 +1,15 @@ +// "Remove initializer from property" "true" +namespace a + +import java.util.Collections + +namespace b { + +import java.util.List + +class M { + trait A { + val l : List? + } +} +} \ No newline at end of file diff --git a/idea/testData/quickfix/classImport/afterNoImportJavaLang.kt b/idea/testData/quickfix/classImport/afterNoImportJavaLang.kt new file mode 100644 index 00000000000..1ffb2769998 --- /dev/null +++ b/idea/testData/quickfix/classImport/afterNoImportJavaLang.kt @@ -0,0 +1,8 @@ +// "Remove initializer from property" "true" +namespace a + +class M { + trait A { + abstract val e : Exception + } +} \ No newline at end of file diff --git a/idea/testData/quickfix/classImport/afterNoImportJetStandard.kt b/idea/testData/quickfix/classImport/afterNoImportJetStandard.kt new file mode 100644 index 00000000000..dc3f621c67e --- /dev/null +++ b/idea/testData/quickfix/classImport/afterNoImportJetStandard.kt @@ -0,0 +1,8 @@ +// "Remove initializer from property" "true" +namespace a + +class M { + trait A { + abstract val i : Int + } +} \ No newline at end of file diff --git a/idea/testData/quickfix/classImport/afterToImport1.kt b/idea/testData/quickfix/classImport/afterToImport1.kt new file mode 100644 index 00000000000..60d9ffb0c1b --- /dev/null +++ b/idea/testData/quickfix/classImport/afterToImport1.kt @@ -0,0 +1,11 @@ +// "Remove initializer from property" "true" +namespace a + +import java.util.Collections +import java.util.List + +class M { + trait A { + abstract val l : List? + } +} \ No newline at end of file diff --git a/idea/testData/quickfix/classImport/afterToImport2.kt b/idea/testData/quickfix/classImport/afterToImport2.kt new file mode 100644 index 00000000000..60556ccd964 --- /dev/null +++ b/idea/testData/quickfix/classImport/afterToImport2.kt @@ -0,0 +1,15 @@ +// "Remove initializer from property" "true" +namespace a + +import java.util.Collections + +namespace b { + +import java.util.List + +class M { + trait A { + abstract val l : List? + } +} +} \ No newline at end of file diff --git a/idea/testData/quickfix/classImport/beforeHasThisImport.kt b/idea/testData/quickfix/classImport/beforeHasThisImport.kt new file mode 100644 index 00000000000..bfcc05e9b9c --- /dev/null +++ b/idea/testData/quickfix/classImport/beforeHasThisImport.kt @@ -0,0 +1,15 @@ +// "Remove initializer from property" "true" +namespace a + +import java.util.Collections + +namespace b { + +import java.util.List + +class M { + trait A { + val l = Collections.emptyList() + } +} +} \ No newline at end of file diff --git a/idea/testData/quickfix/classImport/beforeNoImportJavaLang.kt b/idea/testData/quickfix/classImport/beforeNoImportJavaLang.kt new file mode 100644 index 00000000000..d81efa4eaa8 --- /dev/null +++ b/idea/testData/quickfix/classImport/beforeNoImportJavaLang.kt @@ -0,0 +1,8 @@ +// "Remove initializer from property" "true" +namespace a + +class M { + trait A { + abstract val e = Exception("") + } +} \ No newline at end of file diff --git a/idea/testData/quickfix/classImport/beforeNoImportJetStandard.kt b/idea/testData/quickfix/classImport/beforeNoImportJetStandard.kt new file mode 100644 index 00000000000..320776e668a --- /dev/null +++ b/idea/testData/quickfix/classImport/beforeNoImportJetStandard.kt @@ -0,0 +1,8 @@ +// "Remove initializer from property" "true" +namespace a + +class M { + trait A { + abstract val i = 10 + } +} \ No newline at end of file diff --git a/idea/testData/quickfix/classImport/beforeToImport1.kt b/idea/testData/quickfix/classImport/beforeToImport1.kt new file mode 100644 index 00000000000..075adebf27d --- /dev/null +++ b/idea/testData/quickfix/classImport/beforeToImport1.kt @@ -0,0 +1,10 @@ +// "Remove initializer from property" "true" +namespace a + +import java.util.Collections + +class M { + trait A { + abstract val l = Collections.emptyList() + } +} \ No newline at end of file diff --git a/idea/testData/quickfix/classImport/beforeToImport2.kt b/idea/testData/quickfix/classImport/beforeToImport2.kt new file mode 100644 index 00000000000..c99cd4ebe4d --- /dev/null +++ b/idea/testData/quickfix/classImport/beforeToImport2.kt @@ -0,0 +1,13 @@ +// "Remove initializer from property" "true" +namespace a + +import java.util.Collections + +namespace b { + +class M { + trait A { + abstract val l = Collections.emptyList() + } +} +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/jet/plugin/quickfix/ClassImportTests.java b/idea/tests/org/jetbrains/jet/plugin/quickfix/ClassImportTests.java new file mode 100644 index 00000000000..2b29637e9fa --- /dev/null +++ b/idea/tests/org/jetbrains/jet/plugin/quickfix/ClassImportTests.java @@ -0,0 +1,32 @@ +package org.jetbrains.jet.plugin.quickfix; + +import com.intellij.codeInsight.daemon.quickFix.LightQuickFixTestCase; +import com.intellij.openapi.projectRoots.Sdk; +import org.jetbrains.jet.JetTestCaseBase; + +/** + * @author svtk + */ +public class ClassImportTests extends LightQuickFixTestCase { + + public void test() throws Exception { + doAllTests(); + } + + @Override + protected String getBasePath() { + return "/quickfix/classImport"; + } + + @Override + protected String getTestDataPath() { + return JetTestCaseBase.getTestDataPathBase(); + } + + @Override + protected Sdk getProjectJDK() { + return JetTestCaseBase.jdkFromIdeaHome(); + } + +} + From 7b2c5132f7d232bd2ed7f18e4cef2f1b2f1e2cfe Mon Sep 17 00:00:00 2001 From: svtk Date: Wed, 28 Sep 2011 13:50:17 +0400 Subject: [PATCH 3/3] 'Add primary constructor' quick fix added --- .../diagnostics/DiagnosticParameters.java | 4 +- ...va => DiagnosticWithParameterFactory.java} | 8 +-- .../jet/lang/diagnostics/Errors.java | 24 +++---- .../jetbrains/jet/lang/psi/JetPsiFactory.java | 16 +++-- .../jet/lang/resolve/BodyResolver.java | 13 ++-- .../quickfix/AddPrimaryConstructorFix.java | 52 +++++++++++++++ .../jet/plugin/quickfix/QuickFixUtil.java | 8 +-- .../jet/plugin/quickfix/QuickFixes.java | 13 ++-- .../quickfix/RemovePartsFromPropertyFix.java | 64 +++++++++++++------ .../afterAddPrimaryConstructor1.kt | 6 ++ .../afterAddPrimaryConstructor2.kt | 6 ++ .../beforeAddPrimaryConstructor1.kt | 6 ++ .../beforeAddPrimaryConstructor2.kt | 6 ++ .../quickfix/AddPrimaryConstructorTest.java | 24 +++++++ 14 files changed, 190 insertions(+), 60 deletions(-) rename compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/{DiagnosticWithParametersFactory.java => DiagnosticWithParameterFactory.java} (60%) create mode 100644 idea/src/org/jetbrains/jet/plugin/quickfix/AddPrimaryConstructorFix.java create mode 100644 idea/testData/quickfix/addPrimaryConstructor/afterAddPrimaryConstructor1.kt create mode 100644 idea/testData/quickfix/addPrimaryConstructor/afterAddPrimaryConstructor2.kt create mode 100644 idea/testData/quickfix/addPrimaryConstructor/beforeAddPrimaryConstructor1.kt create mode 100644 idea/testData/quickfix/addPrimaryConstructor/beforeAddPrimaryConstructor2.kt create mode 100644 idea/tests/org/jetbrains/jet/plugin/quickfix/AddPrimaryConstructorTest.java diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/DiagnosticParameters.java b/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/DiagnosticParameters.java index b0bd0d82fa7..edbb89e2dc7 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/DiagnosticParameters.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/DiagnosticParameters.java @@ -1,6 +1,6 @@ package org.jetbrains.jet.lang.diagnostics; -import org.jetbrains.jet.lang.psi.JetModifierListOwner; +import org.jetbrains.jet.lang.psi.JetClass; import org.jetbrains.jet.lang.types.JetType; import org.jetbrains.jet.lexer.JetKeywordToken; @@ -9,6 +9,6 @@ import org.jetbrains.jet.lexer.JetKeywordToken; */ public interface DiagnosticParameters { DiagnosticParameter MODIFIER = new DiagnosticParameterImpl("MODIFIER"); - DiagnosticParameter CLASS = new DiagnosticParameterImpl("CLASS"); + DiagnosticParameter CLASS = new DiagnosticParameterImpl("CLASS"); DiagnosticParameter TYPE = new DiagnosticParameterImpl("TYPE"); } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/DiagnosticWithParametersFactory.java b/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/DiagnosticWithParameterFactory.java similarity index 60% rename from compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/DiagnosticWithParametersFactory.java rename to compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/DiagnosticWithParameterFactory.java index e51f936cb73..341f26f8c8c 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/DiagnosticWithParametersFactory.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/DiagnosticWithParameterFactory.java @@ -7,14 +7,14 @@ import org.jetbrains.annotations.NotNull; /** * @author svtk */ -public class DiagnosticWithParametersFactory extends PsiElementOnlyDiagnosticFactory1 { - public static DiagnosticWithParametersFactory create(Severity severity, String messageStub, DiagnosticParameter diagnosticParameter) { - return new DiagnosticWithParametersFactory(severity, messageStub, diagnosticParameter); +public class DiagnosticWithParameterFactory extends PsiElementOnlyDiagnosticFactory1 { + public static DiagnosticWithParameterFactory create(Severity severity, String messageStub, DiagnosticParameter diagnosticParameter) { + return new DiagnosticWithParameterFactory(severity, messageStub, diagnosticParameter); } private final DiagnosticParameter diagnosticParameter; - protected DiagnosticWithParametersFactory(Severity severity, String message, DiagnosticParameter diagnosticParameter) { + protected DiagnosticWithParameterFactory(Severity severity, String message, DiagnosticParameter diagnosticParameter) { super(severity, message); this.diagnosticParameter = diagnosticParameter; } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/Errors.java b/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/Errors.java index 95abe0c2195..3ac77dc4ebb 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/Errors.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/Errors.java @@ -88,25 +88,25 @@ public interface Errors { SimpleDiagnosticFactory FUNCTION_WITH_NO_TYPE_NO_BODY = SimpleDiagnosticFactory.create(ERROR, "This function must either declare a return type or have a body element"); SimplePsiElementOnlyDiagnosticFactory ABSTRACT_PROPERTY_IN_PRIMARY_CONSTRUCTOR_PARAMETERS = SimplePsiElementOnlyDiagnosticFactory.create(ERROR, "This property cannot be declared abstract"); SimplePsiElementOnlyDiagnosticFactory ABSTRACT_PROPERTY_NOT_IN_CLASS = SimplePsiElementOnlyDiagnosticFactory.create(ERROR, "A property may be abstract only when defined in a class or trait"); - DiagnosticWithParametersFactory ABSTRACT_PROPERTY_WITH_INITIALIZER = DiagnosticWithParametersFactory.create(ERROR, "Property with initializer cannot be abstract", DiagnosticParameters.TYPE); - DiagnosticWithParametersFactory ABSTRACT_PROPERTY_WITH_GETTER = DiagnosticWithParametersFactory.create(ERROR, "Property with getter implementation cannot be abstract", DiagnosticParameters.TYPE); - DiagnosticWithParametersFactory ABSTRACT_PROPERTY_WITH_SETTER = DiagnosticWithParametersFactory.create(ERROR, "Property with setter implementation cannot be abstract", DiagnosticParameters.TYPE); + DiagnosticWithParameterFactory ABSTRACT_PROPERTY_WITH_INITIALIZER = DiagnosticWithParameterFactory.create(ERROR, "Property with initializer cannot be abstract", DiagnosticParameters.TYPE); + DiagnosticWithParameterFactory ABSTRACT_PROPERTY_WITH_GETTER = DiagnosticWithParameterFactory.create(ERROR, "Property with getter implementation cannot be abstract", DiagnosticParameters.TYPE); + DiagnosticWithParameterFactory ABSTRACT_PROPERTY_WITH_SETTER = DiagnosticWithParameterFactory.create(ERROR, "Property with setter implementation cannot be abstract", DiagnosticParameters.TYPE); SimpleDiagnosticFactory BACKING_FIELD_IN_TRAIT = SimpleDiagnosticFactory.create(ERROR, "Property in a trait cannot have a backing field"); SimpleDiagnosticFactory MUST_BE_INITIALIZED = SimpleDiagnosticFactory.create(ERROR, "Property must be initialized"); SimplePsiElementOnlyDiagnosticFactory MUST_BE_INITIALIZED_OR_BE_ABSTRACT = SimplePsiElementOnlyDiagnosticFactory.create(ERROR, "Property must be initialized or be abstract"); - DiagnosticWithParametersFactory PROPERTY_INITIALIZER_IN_TRAIT = DiagnosticWithParametersFactory.create(ERROR, "Property initializers are not allowed in traits", DiagnosticParameters.TYPE); + DiagnosticWithParameterFactory PROPERTY_INITIALIZER_IN_TRAIT = DiagnosticWithParameterFactory.create(ERROR, "Property initializers are not allowed in traits", DiagnosticParameters.TYPE); SimpleDiagnosticFactory PROPERTY_INITIALIZER_NO_BACKING_FIELD = SimpleDiagnosticFactory.create(ERROR, "Initializer is not allowed here because this property has no backing field"); - SimpleDiagnosticFactory PROPERTY_INITIALIZER_NO_PRIMARY_CONSTRUCTOR = SimpleDiagnosticFactory.create(ERROR, "Property initializers are not allowed when no primary constructor is present"); + DiagnosticWithParameterFactory PROPERTY_INITIALIZER_NO_PRIMARY_CONSTRUCTOR = DiagnosticWithParameterFactory.create(ERROR, "Property initializers are not allowed when no primary constructor is present", DiagnosticParameters.CLASS); SimplePsiElementOnlyDiagnosticFactory REDUNDANT_ABSTRACT = SimplePsiElementOnlyDiagnosticFactory.create(WARNING, "Abstract modifier is redundant in traits"); - PsiElementOnlyDiagnosticFactory3 ABSTRACT_PROPERTY_IN_NON_ABSTRACT_CLASS = new PsiElementOnlyDiagnosticFactory3(ERROR, "Abstract property {0} in non-abstract class {1}") { + PsiElementOnlyDiagnosticFactory3 ABSTRACT_PROPERTY_IN_NON_ABSTRACT_CLASS = new PsiElementOnlyDiagnosticFactory3(ERROR, "Abstract property {0} in non-abstract class {1}") { @NotNull - protected DiagnosticWithPsiElement on(@NotNull JetModifierListOwner elementToBlame, @NotNull TextRange textRangeToMark, @NotNull String s, @NotNull ClassDescriptor classDescriptor, @NotNull JetModifierListOwner aClass) { + protected DiagnosticWithPsiElement on(@NotNull JetModifierListOwner elementToBlame, @NotNull TextRange textRangeToMark, @NotNull String s, @NotNull ClassDescriptor classDescriptor, @NotNull JetClass aClass) { return super.on(elementToBlame, textRangeToMark, s, classDescriptor, aClass).add(DiagnosticParameters.CLASS, aClass); } }; - PsiElementOnlyDiagnosticFactory3 ABSTRACT_FUNCTION_IN_NON_ABSTRACT_CLASS = new PsiElementOnlyDiagnosticFactory3(ERROR, "Abstract function {0} in non-abstract class {1}") { + PsiElementOnlyDiagnosticFactory3 ABSTRACT_FUNCTION_IN_NON_ABSTRACT_CLASS = new PsiElementOnlyDiagnosticFactory3(ERROR, "Abstract function {0} in non-abstract class {1}") { @NotNull - public DiagnosticWithPsiElement on(@NotNull JetFunctionOrPropertyAccessor elementToBlame, @NotNull ASTNode nodeToMark, @NotNull String s, @NotNull ClassDescriptor classDescriptor, @NotNull JetModifierListOwner modifierListOwner) { + public DiagnosticWithPsiElement on(@NotNull JetFunctionOrPropertyAccessor elementToBlame, @NotNull ASTNode nodeToMark, @NotNull String s, @NotNull ClassDescriptor classDescriptor, @NotNull JetClass modifierListOwner) { return super.on(elementToBlame, nodeToMark, s, classDescriptor, modifierListOwner).add(DiagnosticParameters.CLASS, modifierListOwner); } }; @@ -125,7 +125,7 @@ public interface Errors { SimpleDiagnosticFactory INITIALIZER_WITH_NO_ARGUMENTS = SimpleDiagnosticFactory.create(ERROR, "Constructor arguments required"); SimpleDiagnosticFactory MANY_CALLS_TO_THIS = SimpleDiagnosticFactory.create(ERROR, "Only one call to 'this(...)' is allowed"); PsiElementOnlyDiagnosticFactory1 NOTHING_TO_OVERRIDE = PsiElementOnlyDiagnosticFactory1.create(ERROR, "{0} overrides nothing", DescriptorRenderer.TEXT); - ParameterizedDiagnosticFactory1 PRIMARY_CONSTRUCTOR_MISSING_STATEFUL_PROPERTY = ParameterizedDiagnosticFactory1.create(ERROR, "This class must have a primary constructor, because property {0} has a backing field"); + PsiElementOnlyDiagnosticFactory1 PRIMARY_CONSTRUCTOR_MISSING_STATEFUL_PROPERTY = PsiElementOnlyDiagnosticFactory1.create(ERROR, "This class must have a primary constructor, because property {0} has a backing field"); ParameterizedDiagnosticFactory1 PRIMARY_CONSTRUCTOR_MISSING_SUPER_CONSTRUCTOR_CALL = new ParameterizedDiagnosticFactory1(ERROR, "Class {0} must have a constructor in order to be able to initialize supertypes") { @Override protected String makeMessageFor(@NotNull JetClassOrObject argument) { @@ -162,8 +162,8 @@ public interface Errors { SimplePsiElementOnlyDiagnosticFactory USELESS_CAST_STATIC_ASSERT_IS_FINE = SimplePsiElementOnlyDiagnosticFactory.create(WARNING, "No cast needed, use ':' instead"); SimplePsiElementOnlyDiagnosticFactory USELESS_CAST = SimplePsiElementOnlyDiagnosticFactory.create(WARNING, "No cast needed"); SimpleDiagnosticFactory CAST_NEVER_SUCCEEDS = SimpleDiagnosticFactory.create(WARNING, "This cast can never succeed"); - DiagnosticWithParametersFactory WRONG_SETTER_PARAMETER_TYPE = DiagnosticWithParametersFactory.create(ERROR, "Setter parameter type must be equal to the type of the property, i.e. {0}", DiagnosticParameters.TYPE); - DiagnosticWithParametersFactory WRONG_GETTER_RETURN_TYPE = DiagnosticWithParametersFactory.create(ERROR, "Getter return type must be equal to the type of the property, i.e. {0}", DiagnosticParameters.TYPE); + DiagnosticWithParameterFactory WRONG_SETTER_PARAMETER_TYPE = DiagnosticWithParameterFactory.create(ERROR, "Setter parameter type must be equal to the type of the property, i.e. {0}", DiagnosticParameters.TYPE); + DiagnosticWithParameterFactory WRONG_GETTER_RETURN_TYPE = DiagnosticWithParameterFactory.create(ERROR, "Getter return type must be equal to the type of the property, i.e. {0}", DiagnosticParameters.TYPE); ParameterizedDiagnosticFactory1 NO_CLASS_OBJECT = ParameterizedDiagnosticFactory1.create(ERROR, "Classifier {0} does not have a class object", NAME); SimpleDiagnosticFactory NO_GENERICS_IN_SUPERTYPE_SPECIFIER = SimpleDiagnosticFactory.create(ERROR, "Generic arguments of the base type must be specified"); diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetPsiFactory.java b/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetPsiFactory.java index c1081a1bcf5..ab9ad23aa7c 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetPsiFactory.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetPsiFactory.java @@ -1,6 +1,7 @@ package org.jetbrains.jet.lang.psi; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Pair; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFileFactory; import com.intellij.util.LocalTimeCounter; @@ -24,9 +25,10 @@ public class JetPsiFactory { return property.getPropertyTypeRef(); } - public static PsiElement[] createColon(Project project) { + //the pair contains the first and the last elements of a range + public static Pair createColon(Project project) { JetProperty property = createProperty(project, "val x : Int"); - return new PsiElement[] { property.findElementAt(5), property.findElementAt(6), property.findElementAt(7) }; + return Pair.create(property.findElementAt(5), property.findElementAt(7)); } public static PsiElement createWhiteSpace(Project project) { @@ -38,11 +40,6 @@ public class JetPsiFactory { return property.findElementAt(3); } -// public static PsiElement createEndOfLine(Project project) { -// JetNamedFunction function = createFunction(project, "fun f { \n }"); -// return function.findElementAt(8); -// } - public static JetClass createClass(Project project, String text) { return createDeclaration(project, text, JetClass.class); } @@ -99,4 +96,9 @@ public class JetPsiFactory { JetNamespace namespace = createNamespace(project, "import " + classPath); return namespace.getImportDirectives().iterator().next(); } + + public static PsiElement createPrimaryConstructor(Project project) { + JetClass aClass = createClass(project, "class A()"); + return aClass.findElementAt(7).getParent(); + } } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/BodyResolver.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/BodyResolver.java index 1d9d9265e94..37dad7b2ce4 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/BodyResolver.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/BodyResolver.java @@ -95,7 +95,7 @@ public class BodyResolver { if (nameIdentifier != null) { // context.getTrace().getErrorHandler().genericError(nameIdentifier.getNode(), // "This class must have a primary constructor, because property " + propertyDescriptor.getName() + " has a backing field"); - context.getTrace().report(PRIMARY_CONSTRUCTOR_MISSING_STATEFUL_PROPERTY.on(nameIdentifier, propertyDescriptor)); + context.getTrace().report(PRIMARY_CONSTRUCTOR_MISSING_STATEFUL_PROPERTY.on(jetClass, nameIdentifier, propertyDescriptor)); } break; } @@ -590,7 +590,10 @@ public class BodyResolver { } else if (classDescriptor != null && classDescriptor.getUnsubstitutedPrimaryConstructor() == null) { // context.getTrace().getErrorHandler().genericError(initializer.getNode(), "Property initializers are not allowed when no primary constructor is present"); - context.getTrace().report(PROPERTY_INITIALIZER_NO_PRIMARY_CONSTRUCTOR.on(initializer)); + PsiElement classElement = context.getTrace().get(BindingContext.DESCRIPTOR_TO_DECLARATION, classDescriptor); + assert classElement instanceof JetClass; + + context.getTrace().report(PROPERTY_INITIALIZER_NO_PRIMARY_CONSTRUCTOR.on(property, initializer, (JetClass) classElement)); } } @@ -697,10 +700,10 @@ public class BodyResolver { boolean inTrait = classDescriptor.getKind() == ClassKind.TRAIT; boolean inEnum = classDescriptor.getKind() == ClassKind.ENUM_CLASS; boolean inAbstractClass = classDescriptor.getModality() == Modality.ABSTRACT; - PsiElement classElement = context.getTrace().get(BindingContext.DESCRIPTOR_TO_DECLARATION, classDescriptor); - assert classElement instanceof JetModifierListOwner; if (hasAbstractModifier && !inAbstractClass && !inTrait && !inEnum) { - context.getTrace().report(ABSTRACT_FUNCTION_IN_NON_ABSTRACT_CLASS.on(functionOrPropertyAccessor, abstractNode, functionDescriptor.getName(), classDescriptor, (JetModifierListOwner) classElement)); + PsiElement classElement = context.getTrace().get(BindingContext.DESCRIPTOR_TO_DECLARATION, classDescriptor); + assert classElement instanceof JetClass; + context.getTrace().report(ABSTRACT_FUNCTION_IN_NON_ABSTRACT_CLASS.on(functionOrPropertyAccessor, abstractNode, functionDescriptor.getName(), classDescriptor, (JetClass) classElement)); } if (hasAbstractModifier && inTrait && !isPropertyAccessor) { context.getTrace().report(REDUNDANT_ABSTRACT.on(functionOrPropertyAccessor, abstractNode)); diff --git a/idea/src/org/jetbrains/jet/plugin/quickfix/AddPrimaryConstructorFix.java b/idea/src/org/jetbrains/jet/plugin/quickfix/AddPrimaryConstructorFix.java new file mode 100644 index 00000000000..ec8e3ee1a50 --- /dev/null +++ b/idea/src/org/jetbrains/jet/plugin/quickfix/AddPrimaryConstructorFix.java @@ -0,0 +1,52 @@ +package org.jetbrains.jet.plugin.quickfix; + +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.project.Project; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import com.intellij.util.IncorrectOperationException; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.lang.diagnostics.DiagnosticWithPsiElement; +import org.jetbrains.jet.lang.psi.JetClass; +import org.jetbrains.jet.lang.psi.JetPsiFactory; + +/** + * @author svtk + */ +public class AddPrimaryConstructorFix extends JetIntentionAction { + + public AddPrimaryConstructorFix(@NotNull JetClass element) { + super(element); + } + + @NotNull + @Override + public String getText() { + return "Add primary constructor to " + element.getName(); + } + + @NotNull + @Override + public String getFamilyName() { + return "Add primary constructor"; + } + + @Override + public void invoke(@NotNull Project project, Editor editor, PsiFile file) throws IncorrectOperationException { + JetClass newClass = (JetClass) element.copy(); + assert !newClass.hasPrimaryConstructor(); + PsiElement primaryConstructor = JetPsiFactory.createPrimaryConstructor(project); + newClass.addAfter(primaryConstructor, newClass.getNameIdentifier()); + element.replace(newClass); + } + + public static JetIntentionActionFactory createFactory() { + return new JetIntentionActionFactory() { + @Override + public JetIntentionAction createAction(DiagnosticWithPsiElement diagnostic) { + assert diagnostic.getPsiElement() instanceof JetClass; + return new AddPrimaryConstructorFix((JetClass) diagnostic.getPsiElement()); + } + }; + } +} diff --git a/idea/src/org/jetbrains/jet/plugin/quickfix/QuickFixUtil.java b/idea/src/org/jetbrains/jet/plugin/quickfix/QuickFixUtil.java index 31cd3fa058d..40bb408a736 100644 --- a/idea/src/org/jetbrains/jet/plugin/quickfix/QuickFixUtil.java +++ b/idea/src/org/jetbrains/jet/plugin/quickfix/QuickFixUtil.java @@ -12,14 +12,14 @@ import org.jetbrains.jet.lang.diagnostics.DiagnosticWithPsiElementImpl; public class QuickFixUtil { private QuickFixUtil() {} - public static JetIntentionActionFactory createFactoryRedirectingAdditionalInfoToAnotherFactory(final JetIntentionActionFactory factory, final DiagnosticParameter parameter) { - return new JetIntentionActionFactory() { + public static JetIntentionActionFactory createFactoryRedirectingAdditionalInfoToAnotherFactory(final JetIntentionActionFactory factory, final DiagnosticParameter

parameter) { + return new JetIntentionActionFactory() { @Override - public JetIntentionAction createAction(DiagnosticWithPsiElement diagnostic) { + public JetIntentionAction createAction(DiagnosticWithPsiElement diagnostic) { DiagnosticWithParameters diagnosticWithParameters = JetIntentionAction.assertAndCastToDiagnosticWithParameters(diagnostic, parameter); T element = diagnosticWithParameters.getParameter(parameter); - return factory.createAction(new DiagnosticWithPsiElementImpl(diagnostic.getFactory(), diagnostic.getSeverity(), diagnostic.getMessage(), element)); + return (JetIntentionAction) factory.createAction(new DiagnosticWithPsiElementImpl(diagnostic.getFactory(), diagnostic.getSeverity(), diagnostic.getMessage(), element)); } }; } diff --git a/idea/src/org/jetbrains/jet/plugin/quickfix/QuickFixes.java b/idea/src/org/jetbrains/jet/plugin/quickfix/QuickFixes.java index 33704a2da42..07c3da654ab 100644 --- a/idea/src/org/jetbrains/jet/plugin/quickfix/QuickFixes.java +++ b/idea/src/org/jetbrains/jet/plugin/quickfix/QuickFixes.java @@ -6,10 +6,7 @@ import com.intellij.psi.PsiElement; import org.jetbrains.jet.lang.diagnostics.DiagnosticParameters; import org.jetbrains.jet.lang.diagnostics.Errors; import org.jetbrains.jet.lang.diagnostics.PsiElementOnlyDiagnosticFactory; -import org.jetbrains.jet.lang.psi.JetFunctionOrPropertyAccessor; -import org.jetbrains.jet.lang.psi.JetModifierListOwner; -import org.jetbrains.jet.lang.psi.JetProperty; -import org.jetbrains.jet.lang.psi.JetPropertyAccessor; +import org.jetbrains.jet.lang.psi.*; import org.jetbrains.jet.lexer.JetToken; import org.jetbrains.jet.lexer.JetTokens; @@ -53,7 +50,7 @@ public class QuickFixes { add(Errors.MUST_BE_INITIALIZED_OR_BE_ABSTRACT, addAbstractModifierFactory); add(Errors.REDUNDANT_ABSTRACT, removeAbstractModifierFactory); - JetIntentionActionFactory addAbstractToClassFactory = QuickFixUtil.createFactoryRedirectingAdditionalInfoToAnotherFactory(addAbstractModifierFactory, DiagnosticParameters.CLASS); + JetIntentionActionFactory addAbstractToClassFactory = QuickFixUtil.createFactoryRedirectingAdditionalInfoToAnotherFactory(addAbstractModifierFactory, DiagnosticParameters.CLASS); add(Errors.ABSTRACT_PROPERTY_IN_NON_ABSTRACT_CLASS, removeAbstractModifierFactory); add(Errors.ABSTRACT_PROPERTY_IN_NON_ABSTRACT_CLASS, addAbstractToClassFactory); @@ -89,6 +86,12 @@ public class QuickFixes { add(Errors.UNNECESSARY_SAFE_CALL, ReplaceSafeCallToDotCall.createFactory()); add(Errors.REDUNDANT_MODIFIER, RemoveRedundantModifierFix.createFactory()); + + add(Errors.PROPERTY_INITIALIZER_NO_PRIMARY_CONSTRUCTOR, RemovePartsFromPropertyFix.createRemoveInitializerFactory()); + + JetIntentionActionFactory addPrimaryConstructorFactory = AddPrimaryConstructorFix.createFactory(); + add(Errors.PROPERTY_INITIALIZER_NO_PRIMARY_CONSTRUCTOR, QuickFixUtil.createFactoryRedirectingAdditionalInfoToAnotherFactory(addPrimaryConstructorFactory, DiagnosticParameters.CLASS)); + add(Errors.PRIMARY_CONSTRUCTOR_MISSING_STATEFUL_PROPERTY, addPrimaryConstructorFactory); } } diff --git a/idea/src/org/jetbrains/jet/plugin/quickfix/RemovePartsFromPropertyFix.java b/idea/src/org/jetbrains/jet/plugin/quickfix/RemovePartsFromPropertyFix.java index 55429205dff..faa272f521d 100644 --- a/idea/src/org/jetbrains/jet/plugin/quickfix/RemovePartsFromPropertyFix.java +++ b/idea/src/org/jetbrains/jet/plugin/quickfix/RemovePartsFromPropertyFix.java @@ -2,10 +2,12 @@ package org.jetbrains.jet.plugin.quickfix; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Pair; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.util.IncorrectOperationException; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.lang.diagnostics.DiagnosticParameters; import org.jetbrains.jet.lang.diagnostics.DiagnosticWithParameters; import org.jetbrains.jet.lang.diagnostics.DiagnosticWithPsiElement; @@ -17,34 +19,46 @@ import org.jetbrains.jet.lang.types.JetType; */ public class RemovePartsFromPropertyFix extends JetIntentionAction { private final JetType type; - private final String partsToRemove; + private final boolean removeInitializer; + private final boolean removeGetter; + private final boolean removeSetter; - private RemovePartsFromPropertyFix(@NotNull JetProperty element, JetType type) { + private RemovePartsFromPropertyFix(@NotNull JetProperty element, @Nullable JetType type, boolean removeInitializer, boolean removeGetter, boolean removeSetter) { super(element); this.type = type; - partsToRemove = partsToRemove(element.getGetter() != null && element.getGetter().getBodyExpression() != null, - element.getSetter() != null && element.getSetter().getBodyExpression() != null, - element.getInitializer() != null); + this.removeInitializer = removeInitializer; + this.removeGetter = removeGetter; + this.removeSetter = removeSetter; + } + + private RemovePartsFromPropertyFix(@NotNull JetProperty element, @Nullable JetType type) { + this(element, type, element.getInitializer() != null, + element.getGetter() != null && element.getGetter().getBodyExpression() != null, + element.getSetter() != null && element.getSetter().getBodyExpression() != null); } - private static String partsToRemove(boolean hasGetter, boolean hasSetter, boolean hasInitializer) { + private RemovePartsFromPropertyFix(@NotNull JetProperty element, boolean removeInitializer, boolean removeGetter, boolean removeSetter) { + this(element, null, removeInitializer, removeGetter, removeSetter); + } + + private static String partsToRemove(boolean getter, boolean setter, boolean initializer) { StringBuilder sb = new StringBuilder(); - if (hasGetter) { + if (getter) { sb.append("getter"); - if (hasSetter && hasInitializer) { + if (setter && initializer) { sb.append(", "); } - else if (hasSetter || hasInitializer) { + else if (setter || initializer) { sb.append(" and "); } } - if (hasSetter) { + if (setter) { sb.append("setter"); - if (hasInitializer) { + if (initializer) { sb.append(" and "); } } - if (hasInitializer) { + if (initializer) { sb.append("initializer"); } return sb.toString(); @@ -53,7 +67,7 @@ public class RemovePartsFromPropertyFix extends JetIntentionAction @NotNull @Override public String getText() { - return "Remove " + partsToRemove + " from property"; + return "Remove " + partsToRemove(removeGetter, removeSetter, removeInitializer) + " from property"; } @NotNull @@ -71,23 +85,23 @@ public class RemovePartsFromPropertyFix extends JetIntentionAction public void invoke(@NotNull Project project, Editor editor, PsiFile file) throws IncorrectOperationException { JetProperty newElement = (JetProperty) element.copy(); JetPropertyAccessor getter = newElement.getGetter(); - if (getter != null) { + if (removeGetter && getter != null) { newElement.deleteChildInternal(getter.getNode()); } JetPropertyAccessor setter = newElement.getSetter(); - if (setter != null) { + if (removeSetter && setter != null) { newElement.deleteChildInternal(setter.getNode()); } JetExpression initializer = newElement.getInitializer(); boolean needImport = false; - if (initializer != null) { + if (removeInitializer && initializer != null) { PsiElement nameIdentifier = newElement.getNameIdentifier(); assert nameIdentifier != null; PsiElement nextSibling = nameIdentifier.getNextSibling(); assert nextSibling != null; newElement.deleteChildRange(nextSibling, initializer); - if (newElement.getPropertyTypeRef() == null) { + if (newElement.getPropertyTypeRef() == null && type != null) { newElement = addPropertyType(project, newElement, type); needImport = true; } @@ -102,13 +116,11 @@ public class RemovePartsFromPropertyFix extends JetIntentionAction public static JetProperty addPropertyType(Project project, JetProperty property, JetType type) { JetProperty newProperty = (JetProperty) property.copy(); JetTypeReference typeReference = JetPsiFactory.createType(project, type.toString()); - PsiElement[] colon = JetPsiFactory.createColon(project); + Pair colon = JetPsiFactory.createColon(project); PsiElement nameIdentifier = newProperty.getNameIdentifier(); assert nameIdentifier != null; newProperty.addAfter(typeReference, nameIdentifier); - for (int i = colon.length - 1; i >= 0; i--) { - newProperty.addAfter(colon[i], nameIdentifier); - } + newProperty.addRangeAfter(colon.getFirst(), colon.getSecond(), nameIdentifier); return newProperty; } @@ -123,4 +135,14 @@ public class RemovePartsFromPropertyFix extends JetIntentionAction } }; } + + public static JetIntentionActionFactory createRemoveInitializerFactory() { + return new JetIntentionActionFactory() { + @Override + public JetIntentionAction createAction(DiagnosticWithPsiElement diagnostic) { + assert diagnostic.getPsiElement() instanceof JetProperty; + return new RemovePartsFromPropertyFix((JetProperty) diagnostic.getPsiElement(), true, false, false); + } + }; + } } diff --git a/idea/testData/quickfix/addPrimaryConstructor/afterAddPrimaryConstructor1.kt b/idea/testData/quickfix/addPrimaryConstructor/afterAddPrimaryConstructor1.kt new file mode 100644 index 00000000000..bf7a9a22a5d --- /dev/null +++ b/idea/testData/quickfix/addPrimaryConstructor/afterAddPrimaryConstructor1.kt @@ -0,0 +1,6 @@ +// "Add primary constructor to A" "true" +namespace a + +class A() { + var i : Int = 1 +} diff --git a/idea/testData/quickfix/addPrimaryConstructor/afterAddPrimaryConstructor2.kt b/idea/testData/quickfix/addPrimaryConstructor/afterAddPrimaryConstructor2.kt new file mode 100644 index 00000000000..71ddc338e46 --- /dev/null +++ b/idea/testData/quickfix/addPrimaryConstructor/afterAddPrimaryConstructor2.kt @@ -0,0 +1,6 @@ +// "Add primary constructor to A" "true" +namespace a + +class A() { + var i : Int = 1 +} diff --git a/idea/testData/quickfix/addPrimaryConstructor/beforeAddPrimaryConstructor1.kt b/idea/testData/quickfix/addPrimaryConstructor/beforeAddPrimaryConstructor1.kt new file mode 100644 index 00000000000..8d4c44c292f --- /dev/null +++ b/idea/testData/quickfix/addPrimaryConstructor/beforeAddPrimaryConstructor1.kt @@ -0,0 +1,6 @@ +// "Add primary constructor to A" "true" +namespace a + +class A { + var i : Int = 1 +} diff --git a/idea/testData/quickfix/addPrimaryConstructor/beforeAddPrimaryConstructor2.kt b/idea/testData/quickfix/addPrimaryConstructor/beforeAddPrimaryConstructor2.kt new file mode 100644 index 00000000000..dd6ce1b5889 --- /dev/null +++ b/idea/testData/quickfix/addPrimaryConstructor/beforeAddPrimaryConstructor2.kt @@ -0,0 +1,6 @@ +// "Add primary constructor to A" "true" +namespace a + +class A { + var i : Int = 1 +} diff --git a/idea/tests/org/jetbrains/jet/plugin/quickfix/AddPrimaryConstructorTest.java b/idea/tests/org/jetbrains/jet/plugin/quickfix/AddPrimaryConstructorTest.java new file mode 100644 index 00000000000..e575bc659bd --- /dev/null +++ b/idea/tests/org/jetbrains/jet/plugin/quickfix/AddPrimaryConstructorTest.java @@ -0,0 +1,24 @@ +package org.jetbrains.jet.plugin.quickfix; + +import com.intellij.codeInsight.daemon.quickFix.LightQuickFixTestCase; +import org.jetbrains.jet.JetTestCaseBase; + +/** + * @author svtk + */ +public class AddPrimaryConstructorTest extends LightQuickFixTestCase { + + public void test() throws Exception { + doAllTests(); + } + + @Override + protected String getBasePath() { + return "/quickfix/addPrimaryConstructor"; + } + + @Override + protected String getTestDataPath() { + return JetTestCaseBase.getTestDataPathBase(); + } +}