'Add primary constructor' quick fix added

This commit is contained in:
svtk
2011-09-28 13:50:17 +04:00
parent 3cc9740057
commit 7b2c5132f7
14 changed files with 190 additions and 60 deletions
@@ -1,6 +1,6 @@
package org.jetbrains.jet.lang.diagnostics; 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.lang.types.JetType;
import org.jetbrains.jet.lexer.JetKeywordToken; import org.jetbrains.jet.lexer.JetKeywordToken;
@@ -9,6 +9,6 @@ import org.jetbrains.jet.lexer.JetKeywordToken;
*/ */
public interface DiagnosticParameters { public interface DiagnosticParameters {
DiagnosticParameter<JetKeywordToken> MODIFIER = new DiagnosticParameterImpl<JetKeywordToken>("MODIFIER"); DiagnosticParameter<JetKeywordToken> MODIFIER = new DiagnosticParameterImpl<JetKeywordToken>("MODIFIER");
DiagnosticParameter<JetModifierListOwner> CLASS = new DiagnosticParameterImpl<JetModifierListOwner>("CLASS"); DiagnosticParameter<JetClass> CLASS = new DiagnosticParameterImpl<JetClass>("CLASS");
DiagnosticParameter<JetType> TYPE = new DiagnosticParameterImpl<JetType>("TYPE"); DiagnosticParameter<JetType> TYPE = new DiagnosticParameterImpl<JetType>("TYPE");
} }
@@ -7,14 +7,14 @@ import org.jetbrains.annotations.NotNull;
/** /**
* @author svtk * @author svtk
*/ */
public class DiagnosticWithParametersFactory<T extends PsiElement, A> extends PsiElementOnlyDiagnosticFactory1<T, A> { public class DiagnosticWithParameterFactory<T extends PsiElement, A> extends PsiElementOnlyDiagnosticFactory1<T, A> {
public static <T extends PsiElement, A> DiagnosticWithParametersFactory<T, A> create(Severity severity, String messageStub, DiagnosticParameter<A> diagnosticParameter) { public static <T extends PsiElement, A> DiagnosticWithParameterFactory<T, A> create(Severity severity, String messageStub, DiagnosticParameter<A> diagnosticParameter) {
return new DiagnosticWithParametersFactory<T, A>(severity, messageStub, diagnosticParameter); return new DiagnosticWithParameterFactory<T, A>(severity, messageStub, diagnosticParameter);
} }
private final DiagnosticParameter<A> diagnosticParameter; private final DiagnosticParameter<A> diagnosticParameter;
protected DiagnosticWithParametersFactory(Severity severity, String message, DiagnosticParameter<A> diagnosticParameter) { protected DiagnosticWithParameterFactory(Severity severity, String message, DiagnosticParameter<A> diagnosticParameter) {
super(severity, message); super(severity, message);
this.diagnosticParameter = diagnosticParameter; this.diagnosticParameter = diagnosticParameter;
} }
@@ -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"); SimpleDiagnosticFactory FUNCTION_WITH_NO_TYPE_NO_BODY = SimpleDiagnosticFactory.create(ERROR, "This function must either declare a return type or have a body element");
SimplePsiElementOnlyDiagnosticFactory<JetModifierListOwner> ABSTRACT_PROPERTY_IN_PRIMARY_CONSTRUCTOR_PARAMETERS = SimplePsiElementOnlyDiagnosticFactory.create(ERROR, "This property cannot be declared abstract"); SimplePsiElementOnlyDiagnosticFactory<JetModifierListOwner> ABSTRACT_PROPERTY_IN_PRIMARY_CONSTRUCTOR_PARAMETERS = SimplePsiElementOnlyDiagnosticFactory.create(ERROR, "This property cannot be declared abstract");
SimplePsiElementOnlyDiagnosticFactory<JetModifierListOwner> ABSTRACT_PROPERTY_NOT_IN_CLASS = SimplePsiElementOnlyDiagnosticFactory.create(ERROR, "A property may be abstract only when defined in a class or trait"); SimplePsiElementOnlyDiagnosticFactory<JetModifierListOwner> ABSTRACT_PROPERTY_NOT_IN_CLASS = SimplePsiElementOnlyDiagnosticFactory.create(ERROR, "A property may be abstract only when defined in a class or trait");
DiagnosticWithParametersFactory<JetProperty, JetType> ABSTRACT_PROPERTY_WITH_INITIALIZER = DiagnosticWithParametersFactory.create(ERROR, "Property with initializer cannot be abstract", DiagnosticParameters.TYPE); DiagnosticWithParameterFactory<JetProperty, JetType> ABSTRACT_PROPERTY_WITH_INITIALIZER = DiagnosticWithParameterFactory.create(ERROR, "Property with initializer cannot be abstract", DiagnosticParameters.TYPE);
DiagnosticWithParametersFactory<JetProperty, JetType> ABSTRACT_PROPERTY_WITH_GETTER = DiagnosticWithParametersFactory.create(ERROR, "Property with getter implementation cannot be abstract", DiagnosticParameters.TYPE); DiagnosticWithParameterFactory<JetProperty, JetType> ABSTRACT_PROPERTY_WITH_GETTER = DiagnosticWithParameterFactory.create(ERROR, "Property with getter implementation cannot be abstract", DiagnosticParameters.TYPE);
DiagnosticWithParametersFactory<JetProperty, JetType> ABSTRACT_PROPERTY_WITH_SETTER = DiagnosticWithParametersFactory.create(ERROR, "Property with setter implementation cannot be abstract", DiagnosticParameters.TYPE); DiagnosticWithParameterFactory<JetProperty, JetType> 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 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"); SimpleDiagnosticFactory MUST_BE_INITIALIZED = SimpleDiagnosticFactory.create(ERROR, "Property must be initialized");
SimplePsiElementOnlyDiagnosticFactory<JetModifierListOwner> MUST_BE_INITIALIZED_OR_BE_ABSTRACT = SimplePsiElementOnlyDiagnosticFactory.create(ERROR, "Property must be initialized or be abstract"); SimplePsiElementOnlyDiagnosticFactory<JetModifierListOwner> MUST_BE_INITIALIZED_OR_BE_ABSTRACT = SimplePsiElementOnlyDiagnosticFactory.create(ERROR, "Property must be initialized or be abstract");
DiagnosticWithParametersFactory<JetProperty, JetType> PROPERTY_INITIALIZER_IN_TRAIT = DiagnosticWithParametersFactory.create(ERROR, "Property initializers are not allowed in traits", DiagnosticParameters.TYPE); DiagnosticWithParameterFactory<JetProperty, JetType> 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_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<JetProperty, JetClass> PROPERTY_INITIALIZER_NO_PRIMARY_CONSTRUCTOR = DiagnosticWithParameterFactory.create(ERROR, "Property initializers are not allowed when no primary constructor is present", DiagnosticParameters.CLASS);
SimplePsiElementOnlyDiagnosticFactory<JetModifierListOwner> REDUNDANT_ABSTRACT = SimplePsiElementOnlyDiagnosticFactory.create(WARNING, "Abstract modifier is redundant in traits"); SimplePsiElementOnlyDiagnosticFactory<JetModifierListOwner> REDUNDANT_ABSTRACT = SimplePsiElementOnlyDiagnosticFactory.create(WARNING, "Abstract modifier is redundant in traits");
PsiElementOnlyDiagnosticFactory3<JetModifierListOwner, String, ClassDescriptor, JetModifierListOwner> ABSTRACT_PROPERTY_IN_NON_ABSTRACT_CLASS = new PsiElementOnlyDiagnosticFactory3<JetModifierListOwner, String, ClassDescriptor, JetModifierListOwner>(ERROR, "Abstract property {0} in non-abstract class {1}") { PsiElementOnlyDiagnosticFactory3<JetModifierListOwner, String, ClassDescriptor, JetClass> ABSTRACT_PROPERTY_IN_NON_ABSTRACT_CLASS = new PsiElementOnlyDiagnosticFactory3<JetModifierListOwner, String, ClassDescriptor, JetClass>(ERROR, "Abstract property {0} in non-abstract class {1}") {
@NotNull @NotNull
protected DiagnosticWithPsiElement<JetModifierListOwner> on(@NotNull JetModifierListOwner elementToBlame, @NotNull TextRange textRangeToMark, @NotNull String s, @NotNull ClassDescriptor classDescriptor, @NotNull JetModifierListOwner aClass) { protected DiagnosticWithPsiElement<JetModifierListOwner> 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); return super.on(elementToBlame, textRangeToMark, s, classDescriptor, aClass).add(DiagnosticParameters.CLASS, aClass);
} }
}; };
PsiElementOnlyDiagnosticFactory3<JetFunctionOrPropertyAccessor, String, ClassDescriptor, JetModifierListOwner> ABSTRACT_FUNCTION_IN_NON_ABSTRACT_CLASS = new PsiElementOnlyDiagnosticFactory3<JetFunctionOrPropertyAccessor, String, ClassDescriptor, JetModifierListOwner>(ERROR, "Abstract function {0} in non-abstract class {1}") { PsiElementOnlyDiagnosticFactory3<JetFunctionOrPropertyAccessor, String, ClassDescriptor, JetClass> ABSTRACT_FUNCTION_IN_NON_ABSTRACT_CLASS = new PsiElementOnlyDiagnosticFactory3<JetFunctionOrPropertyAccessor, String, ClassDescriptor, JetClass>(ERROR, "Abstract function {0} in non-abstract class {1}") {
@NotNull @NotNull
public DiagnosticWithPsiElement<JetFunctionOrPropertyAccessor> on(@NotNull JetFunctionOrPropertyAccessor elementToBlame, @NotNull ASTNode nodeToMark, @NotNull String s, @NotNull ClassDescriptor classDescriptor, @NotNull JetModifierListOwner modifierListOwner) { public DiagnosticWithPsiElement<JetFunctionOrPropertyAccessor> 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); 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 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"); SimpleDiagnosticFactory MANY_CALLS_TO_THIS = SimpleDiagnosticFactory.create(ERROR, "Only one call to 'this(...)' is allowed");
PsiElementOnlyDiagnosticFactory1<JetModifierListOwner, CallableMemberDescriptor> NOTHING_TO_OVERRIDE = PsiElementOnlyDiagnosticFactory1.create(ERROR, "{0} overrides nothing", DescriptorRenderer.TEXT); PsiElementOnlyDiagnosticFactory1<JetModifierListOwner, CallableMemberDescriptor> NOTHING_TO_OVERRIDE = PsiElementOnlyDiagnosticFactory1.create(ERROR, "{0} overrides nothing", DescriptorRenderer.TEXT);
ParameterizedDiagnosticFactory1<PropertyDescriptor> PRIMARY_CONSTRUCTOR_MISSING_STATEFUL_PROPERTY = ParameterizedDiagnosticFactory1.create(ERROR, "This class must have a primary constructor, because property {0} has a backing field"); PsiElementOnlyDiagnosticFactory1<JetClass, PropertyDescriptor> PRIMARY_CONSTRUCTOR_MISSING_STATEFUL_PROPERTY = PsiElementOnlyDiagnosticFactory1.create(ERROR, "This class must have a primary constructor, because property {0} has a backing field");
ParameterizedDiagnosticFactory1<JetClassOrObject> PRIMARY_CONSTRUCTOR_MISSING_SUPER_CONSTRUCTOR_CALL = new ParameterizedDiagnosticFactory1<JetClassOrObject>(ERROR, "Class {0} must have a constructor in order to be able to initialize supertypes") { ParameterizedDiagnosticFactory1<JetClassOrObject> PRIMARY_CONSTRUCTOR_MISSING_SUPER_CONSTRUCTOR_CALL = new ParameterizedDiagnosticFactory1<JetClassOrObject>(ERROR, "Class {0} must have a constructor in order to be able to initialize supertypes") {
@Override @Override
protected String makeMessageFor(@NotNull JetClassOrObject argument) { protected String makeMessageFor(@NotNull JetClassOrObject argument) {
@@ -162,8 +162,8 @@ public interface Errors {
SimplePsiElementOnlyDiagnosticFactory<JetBinaryExpressionWithTypeRHS> USELESS_CAST_STATIC_ASSERT_IS_FINE = SimplePsiElementOnlyDiagnosticFactory.create(WARNING, "No cast needed, use ':' instead"); SimplePsiElementOnlyDiagnosticFactory<JetBinaryExpressionWithTypeRHS> USELESS_CAST_STATIC_ASSERT_IS_FINE = SimplePsiElementOnlyDiagnosticFactory.create(WARNING, "No cast needed, use ':' instead");
SimplePsiElementOnlyDiagnosticFactory<JetBinaryExpressionWithTypeRHS> USELESS_CAST = SimplePsiElementOnlyDiagnosticFactory.create(WARNING, "No cast needed"); SimplePsiElementOnlyDiagnosticFactory<JetBinaryExpressionWithTypeRHS> USELESS_CAST = SimplePsiElementOnlyDiagnosticFactory.create(WARNING, "No cast needed");
SimpleDiagnosticFactory CAST_NEVER_SUCCEEDS = SimpleDiagnosticFactory.create(WARNING, "This cast can never succeed"); SimpleDiagnosticFactory CAST_NEVER_SUCCEEDS = SimpleDiagnosticFactory.create(WARNING, "This cast can never succeed");
DiagnosticWithParametersFactory<JetPropertyAccessor, JetType> WRONG_SETTER_PARAMETER_TYPE = DiagnosticWithParametersFactory.create(ERROR, "Setter parameter type must be equal to the type of the property, i.e. {0}", DiagnosticParameters.TYPE); DiagnosticWithParameterFactory<JetPropertyAccessor, JetType> WRONG_SETTER_PARAMETER_TYPE = DiagnosticWithParameterFactory.create(ERROR, "Setter parameter type must be equal to the type of the property, i.e. {0}", DiagnosticParameters.TYPE);
DiagnosticWithParametersFactory<JetPropertyAccessor, JetType> 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<JetPropertyAccessor, JetType> 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<ClassifierDescriptor> NO_CLASS_OBJECT = ParameterizedDiagnosticFactory1.create(ERROR, "Classifier {0} does not have a class object", NAME); ParameterizedDiagnosticFactory1<ClassifierDescriptor> 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"); SimpleDiagnosticFactory NO_GENERICS_IN_SUPERTYPE_SPECIFIER = SimpleDiagnosticFactory.create(ERROR, "Generic arguments of the base type must be specified");
@@ -1,6 +1,7 @@
package org.jetbrains.jet.lang.psi; package org.jetbrains.jet.lang.psi;
import com.intellij.openapi.project.Project; import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Pair;
import com.intellij.psi.PsiElement; import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFileFactory; import com.intellij.psi.PsiFileFactory;
import com.intellij.util.LocalTimeCounter; import com.intellij.util.LocalTimeCounter;
@@ -24,9 +25,10 @@ public class JetPsiFactory {
return property.getPropertyTypeRef(); return property.getPropertyTypeRef();
} }
public static PsiElement[] createColon(Project project) { //the pair contains the first and the last elements of a range
public static Pair<PsiElement, PsiElement> createColon(Project project) {
JetProperty property = createProperty(project, "val x : Int"); 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) { public static PsiElement createWhiteSpace(Project project) {
@@ -38,11 +40,6 @@ public class JetPsiFactory {
return property.findElementAt(3); 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) { public static JetClass createClass(Project project, String text) {
return createDeclaration(project, text, JetClass.class); return createDeclaration(project, text, JetClass.class);
} }
@@ -99,4 +96,9 @@ public class JetPsiFactory {
JetNamespace namespace = createNamespace(project, "import " + classPath); JetNamespace namespace = createNamespace(project, "import " + classPath);
return namespace.getImportDirectives().iterator().next(); return namespace.getImportDirectives().iterator().next();
} }
public static PsiElement createPrimaryConstructor(Project project) {
JetClass aClass = createClass(project, "class A()");
return aClass.findElementAt(7).getParent();
}
} }
@@ -95,7 +95,7 @@ public class BodyResolver {
if (nameIdentifier != null) { if (nameIdentifier != null) {
// context.getTrace().getErrorHandler().genericError(nameIdentifier.getNode(), // context.getTrace().getErrorHandler().genericError(nameIdentifier.getNode(),
// "This class must have a primary constructor, because property " + propertyDescriptor.getName() + " has a backing field"); // "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; break;
} }
@@ -590,7 +590,10 @@ public class BodyResolver {
} }
else if (classDescriptor != null && classDescriptor.getUnsubstitutedPrimaryConstructor() == null) { 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().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 inTrait = classDescriptor.getKind() == ClassKind.TRAIT;
boolean inEnum = classDescriptor.getKind() == ClassKind.ENUM_CLASS; boolean inEnum = classDescriptor.getKind() == ClassKind.ENUM_CLASS;
boolean inAbstractClass = classDescriptor.getModality() == Modality.ABSTRACT; 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) { 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) { if (hasAbstractModifier && inTrait && !isPropertyAccessor) {
context.getTrace().report(REDUNDANT_ABSTRACT.on(functionOrPropertyAccessor, abstractNode)); context.getTrace().report(REDUNDANT_ABSTRACT.on(functionOrPropertyAccessor, abstractNode));
@@ -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<JetClass> {
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<JetClass> createFactory() {
return new JetIntentionActionFactory<JetClass>() {
@Override
public JetIntentionAction<JetClass> createAction(DiagnosticWithPsiElement diagnostic) {
assert diagnostic.getPsiElement() instanceof JetClass;
return new AddPrimaryConstructorFix((JetClass) diagnostic.getPsiElement());
}
};
}
}
@@ -12,14 +12,14 @@ import org.jetbrains.jet.lang.diagnostics.DiagnosticWithPsiElementImpl;
public class QuickFixUtil { public class QuickFixUtil {
private QuickFixUtil() {} private QuickFixUtil() {}
public static <T extends PsiElement> JetIntentionActionFactory<T> createFactoryRedirectingAdditionalInfoToAnotherFactory(final JetIntentionActionFactory<T> factory, final DiagnosticParameter<T> parameter) { public static <T extends PsiElement, P extends T> JetIntentionActionFactory<PsiElement> createFactoryRedirectingAdditionalInfoToAnotherFactory(final JetIntentionActionFactory<T> factory, final DiagnosticParameter<P> parameter) {
return new JetIntentionActionFactory<T>() { return new JetIntentionActionFactory<PsiElement>() {
@Override @Override
public JetIntentionAction<T> createAction(DiagnosticWithPsiElement diagnostic) { public JetIntentionAction<PsiElement> createAction(DiagnosticWithPsiElement diagnostic) {
DiagnosticWithParameters<PsiElement> diagnosticWithParameters = JetIntentionAction.assertAndCastToDiagnosticWithParameters(diagnostic, parameter); DiagnosticWithParameters<PsiElement> diagnosticWithParameters = JetIntentionAction.assertAndCastToDiagnosticWithParameters(diagnostic, parameter);
T element = diagnosticWithParameters.getParameter(parameter); T element = diagnosticWithParameters.getParameter(parameter);
return factory.createAction(new DiagnosticWithPsiElementImpl<T>(diagnostic.getFactory(), diagnostic.getSeverity(), diagnostic.getMessage(), element)); return (JetIntentionAction<PsiElement>) factory.createAction(new DiagnosticWithPsiElementImpl<T>(diagnostic.getFactory(), diagnostic.getSeverity(), diagnostic.getMessage(), element));
} }
}; };
} }
@@ -6,10 +6,7 @@ import com.intellij.psi.PsiElement;
import org.jetbrains.jet.lang.diagnostics.DiagnosticParameters; import org.jetbrains.jet.lang.diagnostics.DiagnosticParameters;
import org.jetbrains.jet.lang.diagnostics.Errors; import org.jetbrains.jet.lang.diagnostics.Errors;
import org.jetbrains.jet.lang.diagnostics.PsiElementOnlyDiagnosticFactory; import org.jetbrains.jet.lang.diagnostics.PsiElementOnlyDiagnosticFactory;
import org.jetbrains.jet.lang.psi.JetFunctionOrPropertyAccessor; import org.jetbrains.jet.lang.psi.*;
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.lexer.JetToken; import org.jetbrains.jet.lexer.JetToken;
import org.jetbrains.jet.lexer.JetTokens; import org.jetbrains.jet.lexer.JetTokens;
@@ -53,7 +50,7 @@ public class QuickFixes {
add(Errors.MUST_BE_INITIALIZED_OR_BE_ABSTRACT, addAbstractModifierFactory); add(Errors.MUST_BE_INITIALIZED_OR_BE_ABSTRACT, addAbstractModifierFactory);
add(Errors.REDUNDANT_ABSTRACT, removeAbstractModifierFactory); add(Errors.REDUNDANT_ABSTRACT, removeAbstractModifierFactory);
JetIntentionActionFactory<JetModifierListOwner> addAbstractToClassFactory = QuickFixUtil.createFactoryRedirectingAdditionalInfoToAnotherFactory(addAbstractModifierFactory, DiagnosticParameters.CLASS); JetIntentionActionFactory<PsiElement> addAbstractToClassFactory = QuickFixUtil.createFactoryRedirectingAdditionalInfoToAnotherFactory(addAbstractModifierFactory, DiagnosticParameters.CLASS);
add(Errors.ABSTRACT_PROPERTY_IN_NON_ABSTRACT_CLASS, removeAbstractModifierFactory); add(Errors.ABSTRACT_PROPERTY_IN_NON_ABSTRACT_CLASS, removeAbstractModifierFactory);
add(Errors.ABSTRACT_PROPERTY_IN_NON_ABSTRACT_CLASS, addAbstractToClassFactory); 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.UNNECESSARY_SAFE_CALL, ReplaceSafeCallToDotCall.createFactory());
add(Errors.REDUNDANT_MODIFIER, RemoveRedundantModifierFix.createFactory()); add(Errors.REDUNDANT_MODIFIER, RemoveRedundantModifierFix.createFactory());
add(Errors.PROPERTY_INITIALIZER_NO_PRIMARY_CONSTRUCTOR, RemovePartsFromPropertyFix.createRemoveInitializerFactory());
JetIntentionActionFactory<JetClass> addPrimaryConstructorFactory = AddPrimaryConstructorFix.createFactory();
add(Errors.PROPERTY_INITIALIZER_NO_PRIMARY_CONSTRUCTOR, QuickFixUtil.createFactoryRedirectingAdditionalInfoToAnotherFactory(addPrimaryConstructorFactory, DiagnosticParameters.CLASS));
add(Errors.PRIMARY_CONSTRUCTOR_MISSING_STATEFUL_PROPERTY, addPrimaryConstructorFactory);
} }
} }
@@ -2,10 +2,12 @@ package org.jetbrains.jet.plugin.quickfix;
import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project; import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Pair;
import com.intellij.psi.PsiElement; import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile; import com.intellij.psi.PsiFile;
import com.intellij.util.IncorrectOperationException; import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.diagnostics.DiagnosticParameters; import org.jetbrains.jet.lang.diagnostics.DiagnosticParameters;
import org.jetbrains.jet.lang.diagnostics.DiagnosticWithParameters; import org.jetbrains.jet.lang.diagnostics.DiagnosticWithParameters;
import org.jetbrains.jet.lang.diagnostics.DiagnosticWithPsiElement; import org.jetbrains.jet.lang.diagnostics.DiagnosticWithPsiElement;
@@ -17,34 +19,46 @@ import org.jetbrains.jet.lang.types.JetType;
*/ */
public class RemovePartsFromPropertyFix extends JetIntentionAction<JetProperty> { public class RemovePartsFromPropertyFix extends JetIntentionAction<JetProperty> {
private final JetType type; 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); super(element);
this.type = type; this.type = type;
partsToRemove = partsToRemove(element.getGetter() != null && element.getGetter().getBodyExpression() != null, this.removeInitializer = removeInitializer;
element.getSetter() != null && element.getSetter().getBodyExpression() != null, this.removeGetter = removeGetter;
element.getInitializer() != null); 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(); StringBuilder sb = new StringBuilder();
if (hasGetter) { if (getter) {
sb.append("getter"); sb.append("getter");
if (hasSetter && hasInitializer) { if (setter && initializer) {
sb.append(", "); sb.append(", ");
} }
else if (hasSetter || hasInitializer) { else if (setter || initializer) {
sb.append(" and "); sb.append(" and ");
} }
} }
if (hasSetter) { if (setter) {
sb.append("setter"); sb.append("setter");
if (hasInitializer) { if (initializer) {
sb.append(" and "); sb.append(" and ");
} }
} }
if (hasInitializer) { if (initializer) {
sb.append("initializer"); sb.append("initializer");
} }
return sb.toString(); return sb.toString();
@@ -53,7 +67,7 @@ public class RemovePartsFromPropertyFix extends JetIntentionAction<JetProperty>
@NotNull @NotNull
@Override @Override
public String getText() { public String getText() {
return "Remove " + partsToRemove + " from property"; return "Remove " + partsToRemove(removeGetter, removeSetter, removeInitializer) + " from property";
} }
@NotNull @NotNull
@@ -71,23 +85,23 @@ public class RemovePartsFromPropertyFix extends JetIntentionAction<JetProperty>
public void invoke(@NotNull Project project, Editor editor, PsiFile file) throws IncorrectOperationException { public void invoke(@NotNull Project project, Editor editor, PsiFile file) throws IncorrectOperationException {
JetProperty newElement = (JetProperty) element.copy(); JetProperty newElement = (JetProperty) element.copy();
JetPropertyAccessor getter = newElement.getGetter(); JetPropertyAccessor getter = newElement.getGetter();
if (getter != null) { if (removeGetter && getter != null) {
newElement.deleteChildInternal(getter.getNode()); newElement.deleteChildInternal(getter.getNode());
} }
JetPropertyAccessor setter = newElement.getSetter(); JetPropertyAccessor setter = newElement.getSetter();
if (setter != null) { if (removeSetter && setter != null) {
newElement.deleteChildInternal(setter.getNode()); newElement.deleteChildInternal(setter.getNode());
} }
JetExpression initializer = newElement.getInitializer(); JetExpression initializer = newElement.getInitializer();
boolean needImport = false; boolean needImport = false;
if (initializer != null) { if (removeInitializer && initializer != null) {
PsiElement nameIdentifier = newElement.getNameIdentifier(); PsiElement nameIdentifier = newElement.getNameIdentifier();
assert nameIdentifier != null; assert nameIdentifier != null;
PsiElement nextSibling = nameIdentifier.getNextSibling(); PsiElement nextSibling = nameIdentifier.getNextSibling();
assert nextSibling != null; assert nextSibling != null;
newElement.deleteChildRange(nextSibling, initializer); newElement.deleteChildRange(nextSibling, initializer);
if (newElement.getPropertyTypeRef() == null) { if (newElement.getPropertyTypeRef() == null && type != null) {
newElement = addPropertyType(project, newElement, type); newElement = addPropertyType(project, newElement, type);
needImport = true; needImport = true;
} }
@@ -102,13 +116,11 @@ public class RemovePartsFromPropertyFix extends JetIntentionAction<JetProperty>
public static JetProperty addPropertyType(Project project, JetProperty property, JetType type) { public static JetProperty addPropertyType(Project project, JetProperty property, JetType type) {
JetProperty newProperty = (JetProperty) property.copy(); JetProperty newProperty = (JetProperty) property.copy();
JetTypeReference typeReference = JetPsiFactory.createType(project, type.toString()); JetTypeReference typeReference = JetPsiFactory.createType(project, type.toString());
PsiElement[] colon = JetPsiFactory.createColon(project); Pair<PsiElement, PsiElement> colon = JetPsiFactory.createColon(project);
PsiElement nameIdentifier = newProperty.getNameIdentifier(); PsiElement nameIdentifier = newProperty.getNameIdentifier();
assert nameIdentifier != null; assert nameIdentifier != null;
newProperty.addAfter(typeReference, nameIdentifier); newProperty.addAfter(typeReference, nameIdentifier);
for (int i = colon.length - 1; i >= 0; i--) { newProperty.addRangeAfter(colon.getFirst(), colon.getSecond(), nameIdentifier);
newProperty.addAfter(colon[i], nameIdentifier);
}
return newProperty; return newProperty;
} }
@@ -123,4 +135,14 @@ public class RemovePartsFromPropertyFix extends JetIntentionAction<JetProperty>
} }
}; };
} }
public static JetIntentionActionFactory<JetProperty> createRemoveInitializerFactory() {
return new JetIntentionActionFactory<JetProperty>() {
@Override
public JetIntentionAction<JetProperty> createAction(DiagnosticWithPsiElement diagnostic) {
assert diagnostic.getPsiElement() instanceof JetProperty;
return new RemovePartsFromPropertyFix((JetProperty) diagnostic.getPsiElement(), true, false, false);
}
};
}
} }
@@ -0,0 +1,6 @@
// "Add primary constructor to A" "true"
namespace a
class A() {
var i : Int = <caret>1
}
@@ -0,0 +1,6 @@
// "Add primary constructor to A" "true"
namespace a
class A<caret>() {
var i : Int = 1
}
@@ -0,0 +1,6 @@
// "Add primary constructor to A" "true"
namespace a
class A {
var i : Int = <caret>1
}
@@ -0,0 +1,6 @@
// "Add primary constructor to A" "true"
namespace a
class A<caret> {
var i : Int = 1
}
@@ -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();
}
}