Added more quickfixes; added tests

This commit is contained in:
svtk
2011-09-21 17:58:44 +04:00
parent e6c871aecc
commit 7f815176f6
96 changed files with 1357 additions and 147 deletions
@@ -1,6 +1,7 @@
package org.jetbrains.jet.lang.diagnostics;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.NotNull;
@@ -22,16 +23,21 @@ public abstract class DiagnosticFactoryWithPsiElement1<T extends PsiElement, A>
@NotNull
public Diagnostic on(@NotNull T element, @NotNull A argument) {
return on(element, element.getNode(), argument);
return on(element, element.getTextRange(), argument);
}
@NotNull
public Diagnostic on(@NotNull T element, @NotNull ASTNode node, @NotNull A argument) {
return new DiagnosticWithPsiElementImpl<T>(this, severity, makeMessage(argument), element, node.getTextRange());
return on(element, node.getTextRange(), argument);
}
@NotNull
public Diagnostic on(@NotNull T element, @NotNull PsiElement psiElement, @NotNull A argument) {
return new DiagnosticWithPsiElementImpl<T>(this, severity, makeMessage(argument), element, psiElement.getTextRange());
return on(element, psiElement.getTextRange(), argument);
}
@NotNull
protected Diagnostic on(@NotNull T element, @NotNull TextRange textRange, @NotNull A argument) {
return new DiagnosticWithPsiElementImpl<T>(this, severity, makeMessage(argument), element, textRange);
}
}
@@ -0,0 +1,52 @@
package org.jetbrains.jet.lang.diagnostics;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.NotNull;
/**
* @author svtk
*/
public class DiagnosticFactoryWithPsiElement3<T extends PsiElement, A, B, C> extends DiagnosticFactoryWithMessageFormat {
protected DiagnosticFactoryWithPsiElement3(Severity severity, String messageStub) {
super(severity, messageStub);
}
protected String makeMessage(@NotNull A a, @NotNull B b, @NotNull C c) {
return messageFormat.format(new Object[]{makeMessageForA(a), makeMessageForB(b), makeMessageForC(c)});
}
protected String makeMessageForA(@NotNull A a) {
return a.toString();
}
protected String makeMessageForB(@NotNull B b) {
return b.toString();
}
protected String makeMessageForC(@NotNull C c) {
return c.toString();
}
@NotNull
public Diagnostic on(@NotNull T element, @NotNull A a, @NotNull B b, @NotNull C c) {
return on(element, element, a, b, c);
}
@NotNull
public Diagnostic on(@NotNull T element, @NotNull PsiElement psiElement, @NotNull A a, @NotNull B b, @NotNull C c) {
return on(element, psiElement.getTextRange(), a, b, c);
}
@NotNull
public Diagnostic on(@NotNull T element, @NotNull ASTNode node, @NotNull A a, @NotNull B b, @NotNull C c) {
return on(element, node.getTextRange(), a, b, c);
}
@NotNull
protected Diagnostic on(@NotNull T element, @NotNull TextRange textRange, @NotNull A a, @NotNull B b, @NotNull C c) {
return new DiagnosticWithPsiElementImpl<T>(this, severity, makeMessage(a, b, c), element, textRange);
}
}
@@ -0,0 +1,26 @@
package org.jetbrains.jet.lang.diagnostics;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.NotNull;
/**
* @author svtk
*/
public class DiagnosticWithAdditionalInfo<T extends PsiElement, I> extends DiagnosticWithPsiElementImpl<T> {
private final I info;
public DiagnosticWithAdditionalInfo(DiagnosticFactory factory, Severity severity, String message, T psiElement, I info) {
super(factory, severity, message, psiElement);
this.info = info;
}
public DiagnosticWithAdditionalInfo(DiagnosticFactory factory, Severity severity, String message, T psiElement, @NotNull TextRange textRange, I info) {
super(factory, severity, message, psiElement, textRange);
this.info = info;
}
public I getInfo() {
return info;
}
}
@@ -0,0 +1,24 @@
package org.jetbrains.jet.lang.diagnostics;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.NotNull;
/**
* @author svtk
*/
public class DiagnosticWithAdditionalInfoFactory1<T extends PsiElement, A> extends PsiElementOnlyDiagnosticFactory1<T, A> {
public static <T extends PsiElement, A> DiagnosticWithAdditionalInfoFactory1<T, A> create(Severity severity, String messageStub) {
return new DiagnosticWithAdditionalInfoFactory1<T, A>(severity, messageStub);
}
protected DiagnosticWithAdditionalInfoFactory1(Severity severity, String message) {
super(severity, message);
}
@NotNull
@Override
protected Diagnostic on(@NotNull T element, @NotNull TextRange textRange, @NotNull A argument) {
return new DiagnosticWithAdditionalInfo<T, A>(this, severity, makeMessage(argument), element, textRange, argument);
}
}
@@ -1,5 +1,7 @@
package org.jetbrains.jet.lang.diagnostics;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.descriptors.*;
@@ -43,25 +45,36 @@ public interface Errors {
SimpleDiagnosticFactory PROPERTY_WITH_NO_TYPE_NO_INITIALIZER = SimpleDiagnosticFactory.create(ERROR, "This property must either have a type annotation or be initialized");
SimpleDiagnosticFactory FUNCTION_WITH_NO_TYPE_NO_BODY = SimpleDiagnosticFactory.create(ERROR, "This function must either declare a return type or have a body element");
SimpleDiagnosticFactory ABSTRACT_PROPERTY_IN_PRIMARY_CONSTRUCTOR_PARAMETERS = SimpleDiagnosticFactory.create(ERROR, "This property cannot be declared abstract");
SimpleDiagnosticFactory ABSTRACT_PROPERTY_NOT_IN_CLASS = SimpleDiagnosticFactory.create(ERROR, "A property may be abstract only when defined in a class or trait");
SimpleDiagnosticFactory ABSTRACT_PROPERTY_WITH_INITIALIZER = SimpleDiagnosticFactory.create(ERROR, "Property with initializer cannot be abstract");
SimpleDiagnosticFactory ABSTRACT_PROPERTY_WITH_GETTER = SimpleDiagnosticFactory.create(ERROR, "Property with getter implementation cannot be abstract");
SimpleDiagnosticFactory ABSTRACT_PROPERTY_WITH_SETTER = SimpleDiagnosticFactory.create(ERROR, "Property with setter implementation cannot be 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");
//TODO pass String instead of JetType or ensure JetType's value is computed
DiagnosticWithAdditionalInfoFactory1<JetProperty, JetType> ABSTRACT_PROPERTY_WITH_INITIALIZER = DiagnosticWithAdditionalInfoFactory1.create(ERROR, "Property with initializer cannot be abstract");
DiagnosticWithAdditionalInfoFactory1<JetProperty, JetType> ABSTRACT_PROPERTY_WITH_GETTER = DiagnosticWithAdditionalInfoFactory1.create(ERROR, "Property with getter implementation cannot be abstract");
DiagnosticWithAdditionalInfoFactory1<JetProperty, JetType> ABSTRACT_PROPERTY_WITH_SETTER = DiagnosticWithAdditionalInfoFactory1.create(ERROR, "Property with setter implementation cannot be abstract");
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_OR_BE_ABSTRACT = SimpleDiagnosticFactory.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");
SimpleDiagnosticFactory PROPERTY_INITIALIZER_IN_TRAIT = SimpleDiagnosticFactory.create(ERROR, "Property initializers are not allowed in traits");
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");
SimplePsiElementOnlyDiagnosticFactory<JetModifierListOwner> REDUNDANT_ABSTRACT = SimplePsiElementOnlyDiagnosticFactory.create(WARNING, "Abstract modifier is redundant in traits");
PsiElementOnlyDiagnosticFactory2<JetModifierListOwner, String, ClassDescriptor> ABSTRACT_PROPERTY_IN_NON_ABSTRACT_CLASS = PsiElementOnlyDiagnosticFactory2.create(ERROR, "Abstract property {0} in non-abstract class {1}");
ParameterizedDiagnosticFactory2<String, ClassDescriptor> ABSTRACT_FUNCTION_IN_NON_ABSTRACT_CLASS = ParameterizedDiagnosticFactory2.create(ERROR, "Abstract function {0} in non-abstract class {1}");
ParameterizedDiagnosticFactory1<FunctionDescriptor> ABSTRACT_FUNCTION_WITH_BODY = ParameterizedDiagnosticFactory1.create(ERROR, "A function {0} with body cannot be abstract");
PsiElementOnlyDiagnosticFactory1<JetModifierListOwner, FunctionDescriptor> NON_ABSTRACT_FUNCTION_WITH_NO_BODY = PsiElementOnlyDiagnosticFactory1.create(ERROR, "Method {0} without a body must be abstract");
ParameterizedDiagnosticFactory1<FunctionDescriptor> NON_MEMBER_ABSTRACT_FUNCTION = ParameterizedDiagnosticFactory1.create(ERROR, "Function {0} is not a class or trait member and cannot be abstract");
SimpleDiagnosticFactory NON_MEMBER_ABSTRACT_ACCESSOR = SimpleDiagnosticFactory.create(ERROR, "This property is not a class or trait member and thus cannot have abstract accessors"); // TODO : Better message
ParameterizedDiagnosticFactory1<FunctionDescriptor> NON_MEMBER_FUNCTION_NO_BODY = ParameterizedDiagnosticFactory1.create(ERROR, "Function {0} must have a body");
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
protected Diagnostic on(@NotNull JetModifierListOwner element, @NotNull TextRange textRange, @NotNull String s, @NotNull ClassDescriptor classDescriptor, @NotNull JetClass jetClass) {
return new DiagnosticWithAdditionalInfo<JetModifierListOwner, JetClass>(this, severity, makeMessage(s, classDescriptor, jetClass), element, textRange, jetClass);
}
};
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}") {
@NotNull
public Diagnostic on(@NotNull JetFunctionOrPropertyAccessor element, @NotNull ASTNode node, @NotNull String s, @NotNull ClassDescriptor classDescriptor, @NotNull JetModifierListOwner jetClass) {
return new DiagnosticWithAdditionalInfo<JetFunctionOrPropertyAccessor, JetModifierListOwner>(this, severity, makeMessage(s, classDescriptor, jetClass), element, node.getTextRange(), jetClass);
}
};
PsiElementOnlyDiagnosticFactory1<JetFunctionOrPropertyAccessor, FunctionDescriptor> ABSTRACT_FUNCTION_WITH_BODY = PsiElementOnlyDiagnosticFactory1.create(ERROR, "A function {0} with body cannot be abstract");
PsiElementOnlyDiagnosticFactory1<JetFunctionOrPropertyAccessor, FunctionDescriptor> NON_ABSTRACT_FUNCTION_WITH_NO_BODY = PsiElementOnlyDiagnosticFactory1.create(ERROR, "Method {0} without a body must be abstract");
PsiElementOnlyDiagnosticFactory1<JetModifierListOwner, FunctionDescriptor> NON_MEMBER_ABSTRACT_FUNCTION = PsiElementOnlyDiagnosticFactory1.create(ERROR, "Function {0} is not a class or trait member and cannot be abstract");
SimplePsiElementOnlyDiagnosticFactory<JetModifierListOwner> NON_MEMBER_ABSTRACT_ACCESSOR = SimplePsiElementOnlyDiagnosticFactory.create(ERROR, "This property is not a class or trait member and thus cannot have abstract accessors"); // TODO : Better message
PsiElementOnlyDiagnosticFactory1<JetFunctionOrPropertyAccessor, FunctionDescriptor> NON_MEMBER_FUNCTION_NO_BODY = PsiElementOnlyDiagnosticFactory1.create(ERROR, "Function {0} must have a body");
SimpleDiagnosticFactory PROJECTION_ON_NON_CLASS_TYPE_ARGUMENT = SimpleDiagnosticFactory.create(ERROR, "Projections are not allowed on type arguments of functions and properties"); // TODO : better positioning
@@ -71,7 +84,7 @@ public interface Errors {
SimpleDiagnosticFactory BY_IN_SECONDARY_CONSTRUCTOR = SimpleDiagnosticFactory.create(ERROR, "'by'-clause is only supported for primary constructors");
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");
ParameterizedDiagnosticFactory1<FunctionDescriptor> NOTHING_TO_OVERRIDE = ParameterizedDiagnosticFactory1.create(ERROR, "Function {0} overrides nothing");
PsiElementOnlyDiagnosticFactory1<JetFunction, FunctionDescriptor> NOTHING_TO_OVERRIDE = PsiElementOnlyDiagnosticFactory1.create(ERROR, "Function {0} overrides nothing");
ParameterizedDiagnosticFactory1<PropertyDescriptor> PRIMARY_CONSTRUCTOR_MISSING_STATEFUL_PROPERTY = ParameterizedDiagnosticFactory1.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") {
@Override
@@ -85,7 +98,7 @@ public interface Errors {
return e.getClass().getSimpleName() + ": " + e.getMessage();
}
};
ParameterizedDiagnosticFactory3<FunctionDescriptor, FunctionDescriptor, DeclarationDescriptor> VIRTUAL_METHOD_HIDDEN = ParameterizedDiagnosticFactory3.create(ERROR, "Function '{0}' hides '{1}' in class {2} and needs 'override' modifier");
PsiElementOnlyDiagnosticFactory3<JetFunction, FunctionDescriptor, FunctionDescriptor, DeclarationDescriptor> VIRTUAL_METHOD_HIDDEN = PsiElementOnlyDiagnosticFactory3.create(ERROR, "Function '{0}' hides '{1}' in class {2} and needs 'override' modifier");
SimpleDiagnosticFactory UNREACHABLE_CODE = SimpleDiagnosticFactory.create(ERROR, "Unreachable code");
ParameterizedDiagnosticFactory1<String> UNREACHABLE_BECAUSE_OF_NOTHING = ParameterizedDiagnosticFactory1.create(ERROR, "This code is unreachable, because '{0}' never terminates normally");
@@ -100,7 +113,7 @@ public interface Errors {
SimpleDiagnosticFactory LOCAL_EXTENSION_PROPERTY = SimpleDiagnosticFactory.create(ERROR, "Local extension properties are not allowed");
SimpleDiagnosticFactory LOCAL_VARIABLE_WITH_GETTER = SimpleDiagnosticFactory.create(ERROR, "Local variables are not allowed to have getters");
SimpleDiagnosticFactory LOCAL_VARIABLE_WITH_SETTER = SimpleDiagnosticFactory.create(ERROR, "Local variables are not allowed to have setters");
SimpleDiagnosticFactory VAL_WITH_SETTER = SimpleDiagnosticFactory.create(ERROR, "A 'val'-property cannot have a setter");
SimplePsiElementOnlyDiagnosticFactory<JetProperty> VAL_WITH_SETTER = SimplePsiElementOnlyDiagnosticFactory.create(ERROR, "A 'val'-property cannot have a setter");
SimpleDiagnosticFactory EQUALS_MISSING = SimpleDiagnosticFactory.create(ERROR, "No method 'equals(Any?) : Boolean' available");
SimpleDiagnosticFactory ASSIGNMENT_IN_EXPRESSION_CONTEXT = SimpleDiagnosticFactory.create(ERROR, "Assignments are not expressions, and only expressions are allowed in this context");
@@ -111,11 +124,11 @@ public interface Errors {
SimpleDiagnosticFactory NO_THIS = SimpleDiagnosticFactory.create(ERROR, "'this' is not defined in this context");
SimpleDiagnosticFactory NOT_A_SUPERTYPE = SimpleDiagnosticFactory.create(ERROR, "Not a supertype");
SimpleDiagnosticFactory NO_WHEN_ENTRIES = SimpleDiagnosticFactory.create(ERROR, "Entries are required for when-expression"); // TODO : Scope, and maybe this should not be an error
SimpleDiagnosticFactory USELESS_CAST_STATIC_ASSERT_IS_FINE = SimpleDiagnosticFactory.create(WARNING, "No cast needed, use ':' instead");
SimpleDiagnosticFactory USELESS_CAST = SimpleDiagnosticFactory.create(WARNING, "No cast needed");
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");
SimpleDiagnosticFactory CAST_NEVER_SUCCEEDS = SimpleDiagnosticFactory.create(WARNING, "This cast can never succeed");
ParameterizedDiagnosticFactory1<JetType> WRONG_SETTER_PARAMETER_TYPE = ParameterizedDiagnosticFactory1.create(ERROR, "Setter parameter type must be equal to the type of the property, i.e. {0}");
ParameterizedDiagnosticFactory1<JetType> WRONG_GETTER_RETURN_TYPE = ParameterizedDiagnosticFactory1.create(ERROR, "Getter return type must be equal to the type of the property, i.e. {0}");
DiagnosticWithAdditionalInfoFactory1<JetPropertyAccessor, JetType> WRONG_SETTER_PARAMETER_TYPE = DiagnosticWithAdditionalInfoFactory1.create(ERROR, "Setter parameter type must be equal to the type of the property, i.e. {0}");
DiagnosticWithAdditionalInfoFactory1<JetPropertyAccessor, JetType> WRONG_GETTER_RETURN_TYPE = DiagnosticWithAdditionalInfoFactory1.create(ERROR, "Getter return type must be equal to the type of the property, i.e. {0}");
ParameterizedDiagnosticFactory1<ClassifierDescriptor> NO_CLASS_OBJECT = new ParameterizedDiagnosticFactory1<ClassifierDescriptor>(ERROR, "Classifier {0} does not have a class object") {
@Override
protected String makeMessageFor(@NotNull ClassifierDescriptor argument) {
@@ -145,7 +158,7 @@ public interface Errors {
ParameterizedDiagnosticFactory1<JetType> UPPER_BOUND_VIOLATED = ParameterizedDiagnosticFactory1.create(ERROR, "An upper bound {0} is violated"); // TODO : Message
ParameterizedDiagnosticFactory1<JetType> FINAL_CLASS_OBJECT_UPPER_BOUND = ParameterizedDiagnosticFactory1.create(ERROR, "{0} is a final type, and thus a class object cannot extend it");
ParameterizedDiagnosticFactory1<JetType> FINAL_UPPER_BOUND = ParameterizedDiagnosticFactory1.create(WARNING, "{0} is a final type, and thus a value of the type parameter is predetermined");
ParameterizedDiagnosticFactory1<JetType> USELESS_ELVIS = ParameterizedDiagnosticFactory1.create(WARNING, "Elvis operator (?:) always returns the left operand of non-nullable type {0}");
PsiElementOnlyDiagnosticFactory1<JetBinaryExpression, JetType> USELESS_ELVIS = PsiElementOnlyDiagnosticFactory1.create(WARNING, "Elvis operator (?:) always returns the left operand of non-nullable type {0}");
ParameterizedDiagnosticFactory1<TypeParameterDescriptor> CONFLICTING_UPPER_BOUNDS = new ParameterizedDiagnosticFactory1<TypeParameterDescriptor>(ERROR, "Upper bounds of {0} have empty intersection") {
@Override
protected String makeMessageFor(@NotNull TypeParameterDescriptor argument) {
@@ -182,7 +195,7 @@ public interface Errors {
ParameterizedDiagnosticFactory1<JetType> UNSAFE_CALL = ParameterizedDiagnosticFactory1.create(ERROR, "Only safe calls (?.) are allowed on a nullable receiver of type {0}");
SimpleDiagnosticFactory AMBIGUOUS_LABEL = SimpleDiagnosticFactory.create(ERROR, "Ambiguous label");
ParameterizedDiagnosticFactory1<String> UNSUPPORTED = ParameterizedDiagnosticFactory1.create(ERROR, "Unsupported [{0}]");
ParameterizedDiagnosticFactory1<JetType> UNNECESSARY_SAFE_CALL = ParameterizedDiagnosticFactory1.create(WARNING, "Unnecessary safe call on a non-null receiver of type {0}");
PsiElementOnlyDiagnosticFactory1<JetElement, JetType> UNNECESSARY_SAFE_CALL = PsiElementOnlyDiagnosticFactory1.create(WARNING, "Unnecessary safe call on a non-null receiver of type {0}");
ParameterizedDiagnosticFactory2<JetTypeConstraint, JetTypeParameterListOwner> NAME_IN_CONSTRAINT_IS_NOT_A_TYPE_PARAMETER = new ParameterizedDiagnosticFactory2<JetTypeConstraint, JetTypeParameterListOwner>(ERROR, "{0} does not refer to a type parameter of {1}") {
@Override
protected String makeMessageForA(@NotNull JetTypeConstraint jetTypeConstraint) {
@@ -9,31 +9,15 @@ import org.jetbrains.annotations.NotNull;
/**
* @author abreslav
*/
public class ParameterizedDiagnosticFactory3<A, B, C> extends DiagnosticFactoryWithMessageFormat {
public class ParameterizedDiagnosticFactory3<A, B, C> extends DiagnosticFactoryWithPsiElement3<PsiElement, A, B, C> {
public static <A, B, C> ParameterizedDiagnosticFactory3<A, B, C> create(Severity severity, String messageStub) {
return new ParameterizedDiagnosticFactory3<A, B, C>(severity, messageStub);
}
public ParameterizedDiagnosticFactory3(Severity severity, String messageStub) {
protected ParameterizedDiagnosticFactory3(Severity severity, String messageStub) {
super(severity, messageStub);
}
protected String makeMessage(@NotNull A a, @NotNull B b, @NotNull C c) {
return messageFormat.format(new Object[]{makeMessageForA(a), makeMessageForB(b), makeMessageForC(c)});
}
protected String makeMessageForA(@NotNull A a) {
return a.toString();
}
protected String makeMessageForB(@NotNull B b) {
return b.toString();
}
protected String makeMessageForC(@NotNull C c) {
return c.toString();
}
@NotNull
public Diagnostic on(@NotNull PsiFile psiFile, @NotNull TextRange range, @NotNull A a, @NotNull B b, @NotNull C c) {
return new GenericDiagnostic(this, severity, makeMessage(a, b, c), psiFile, range);
@@ -43,9 +27,4 @@ public class ParameterizedDiagnosticFactory3<A, B, C> extends DiagnosticFactoryW
public Diagnostic on(@NotNull ASTNode node, @NotNull A a, @NotNull B b, @NotNull C c) {
return on(DiagnosticUtils.getContainingFile(node), node.getTextRange(), a, b, c);
}
@NotNull
public Diagnostic on(@NotNull PsiElement element, @NotNull A a, @NotNull B b, @NotNull C c) {
return new DiagnosticWithPsiElementImpl<PsiElement>(this, severity, makeMessage(a, b, c), element);
}
}
@@ -0,0 +1,15 @@
package org.jetbrains.jet.lang.diagnostics;
import com.intellij.psi.PsiElement;
/**
* @author svtk
*/
public class PsiElementOnlyDiagnosticFactory3<T extends PsiElement, A, B, C> extends DiagnosticFactoryWithPsiElement3<T, A, B, C> implements PsiElementOnlyDiagnosticFactory<T> {
protected PsiElementOnlyDiagnosticFactory3(Severity severity, String messageStub) {
super(severity, messageStub);
}
public static <T extends PsiElement, A, B, C> PsiElementOnlyDiagnosticFactory3<T, A, B, C> create(Severity severity, String messageStub) {
return new PsiElementOnlyDiagnosticFactory3<T, A, B, C>(severity, messageStub);
}
}
@@ -7,22 +7,27 @@ import org.jetbrains.annotations.NotNull;
/**
* @author svtk
*/
public abstract class SimpleDiagnosticFactoryWithPsiElement<T extends PsiElement> extends DiagnosticFactoryWithSeverity {
public abstract class SimpleDiagnosticFactoryWithPsiElement<T extends PsiElement> extends DiagnosticFactoryWithSeverity {
protected final String message;
protected final String message;
protected SimpleDiagnosticFactoryWithPsiElement(Severity severity, String message) {
super(severity);
this.message = message;
}
protected SimpleDiagnosticFactoryWithPsiElement(Severity severity, String message) {
super(severity);
this.message = message;
}
@NotNull
public Diagnostic on(@NotNull T element, @NotNull ASTNode node) {
return new DiagnosticWithPsiElementImpl<T>(this, severity, message, element, node.getTextRange());
}
@NotNull
public Diagnostic on(@NotNull T element, @NotNull ASTNode node) {
return new DiagnosticWithPsiElementImpl<T>(this, severity, message, element, node.getTextRange());
}
@NotNull
public Diagnostic on(@NotNull T element) {
return on(element, element.getNode());
}
@NotNull
public Diagnostic on(@NotNull T element, @NotNull PsiElement psiElement) {
return new DiagnosticWithPsiElementImpl<T>(this, severity, message, element, psiElement.getTextRange());
}
@NotNull
public Diagnostic on(@NotNull T element) {
return on(element, element.getNode());
}
}
@@ -13,7 +13,7 @@ import java.util.List;
/**
* @author max
*/
public class JetClass extends JetTypeParameterListOwner implements JetClassOrObject {
public class JetClass extends JetTypeParameterListOwner implements JetClassOrObject, JetModifierListOwner {
public JetClass(@NotNull ASTNode node) {
super(node);
}
@@ -14,7 +14,7 @@ import java.util.List;
/**
* @author abreslav
*/
abstract public class JetFunction extends JetTypeParameterListOwner implements JetDeclarationWithBody, JetModifierListOwner {
abstract public class JetFunction extends JetTypeParameterListOwner implements JetFunctionOrPropertyAccessor {
public JetFunction(@NotNull ASTNode node) {
super(node);
}
@@ -0,0 +1,7 @@
package org.jetbrains.jet.lang.psi;
/**
* @author svtk
*/
public interface JetFunctionOrPropertyAccessor extends JetDeclarationWithBody, JetModifierListOwner {
}
@@ -12,7 +12,7 @@ import java.util.List;
/**
* @author max
*/
public class JetPropertyAccessor extends JetDeclaration implements JetDeclarationWithBody, JetModifierListOwner {
public class JetPropertyAccessor extends JetDeclaration implements JetFunctionOrPropertyAccessor {
public JetPropertyAccessor(@NotNull ASTNode node) {
super(node);
}
@@ -43,8 +43,8 @@ public class JetPsiFactory {
return (JetFile) PsiFileFactory.getInstance(project).createFileFromText("dummy.jet", JetFileType.INSTANCE, text, LocalTimeCounter.currentTime(), true);
}
public static JetProperty createProperty(Project project, String name, String type) {
String text = "val " + name + (type != null ? ":" + type : "");
public static JetProperty createProperty(Project project, String name, String type, boolean isVar) {
String text = (isVar ? "var " : "val ") + name + (type != null ? ":" + type : "");
return createProperty(project, text);
}
@@ -63,7 +63,7 @@ public class JetPsiFactory {
}
public static PsiElement createNameIdentifier(Project project, String name) {
return createProperty(project, name, null).getNameIdentifier();
return createProperty(project, name, null, false).getNameIdentifier();
}
public static JetNamedFunction createFunction(Project project, String funDecl) {
@@ -75,4 +75,9 @@ public class JetPsiFactory {
JetProperty property = createProperty(project, text);
return property.getModifierList();
}
public static JetExpression createEmptyBody(Project project) {
JetNamedFunction function = createFunction(project, "fun foo() {}");
return function.getBodyExpression();
}
}
@@ -13,7 +13,6 @@ import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.resolve.scopes.JetScope;
import org.jetbrains.jet.lang.resolve.scopes.WritableScope;
import org.jetbrains.jet.lang.resolve.scopes.WritableScopeImpl;
import org.jetbrains.jet.lang.resolve.scopes.receivers.ClassReceiver;
import org.jetbrains.jet.lang.resolve.scopes.receivers.ExtensionCallableReceiver;
import org.jetbrains.jet.lang.types.*;
import org.jetbrains.jet.lexer.JetTokens;
@@ -188,7 +187,7 @@ public class BodyResolver {
}
if (hasOverrideModifier && declaredFunction.getOverriddenDescriptors().size() == 0) {
// context.getTrace().getErrorHandler().genericError(overrideNode, "Method " + declaredFunction.getName() + " overrides nothing");
context.getTrace().report(NOTHING_TO_OVERRIDE.on(overrideNode, declaredFunction));
context.getTrace().report(NOTHING_TO_OVERRIDE.on(function, overrideNode, declaredFunction));
}
PsiElement nameIdentifier = function.getNameIdentifier();
if (!hasOverrideModifier && declaredFunction.getOverriddenDescriptors().size() > 0 && nameIdentifier != null) {
@@ -196,7 +195,7 @@ public class BodyResolver {
// context.getTrace().getErrorHandler().genericError(nameIdentifier.getNode(),
// "Method '" + declaredFunction.getName() + "' overrides method '" + overriddenFunction.getName() + "' in class " +
// overriddenFunction.getContainingDeclaration().getName() + " and needs 'override' modifier");
context.getTrace().report(VIRTUAL_METHOD_HIDDEN.on(nameIdentifier, declaredFunction, overriddenFunction, overriddenFunction.getContainingDeclaration()));
context.getTrace().report(VIRTUAL_METHOD_HIDDEN.on(function, nameIdentifier, declaredFunction, overriddenFunction, overriddenFunction.getContainingDeclaration()));
}
}
@@ -616,12 +615,14 @@ public class BodyResolver {
if (abstractNode != null) { //has abstract modifier
if (classDescriptor == null) {
// context.getTrace().getErrorHandler().genericError(abstractNode, "A property may be abstract only when defined in a class or trait");
context.getTrace().report(ABSTRACT_PROPERTY_NOT_IN_CLASS.on(abstractNode));
context.getTrace().report(ABSTRACT_PROPERTY_NOT_IN_CLASS.on(property, abstractNode));
return;
}
if (!(classDescriptor.getModality() == Modality.ABSTRACT) && classDescriptor.getKind() != ClassKind.ENUM_CLASS) {
// context.getTrace().getErrorHandler().genericError(abstractNode, "Abstract property " + property.getName() + " in non-abstract class " + classDescriptor.getName());
context.getTrace().report(ABSTRACT_PROPERTY_IN_NON_ABSTRACT_CLASS.on(property, abstractNode, property.getName(), classDescriptor));
PsiElement classElement = context.getTrace().get(BindingContext.DESCRIPTOR_TO_DECLARATION, classDescriptor);
assert classElement instanceof JetClass;
context.getTrace().report(ABSTRACT_PROPERTY_IN_NON_ABSTRACT_CLASS.on(property, abstractNode, property.getName(), classDescriptor, (JetClass) classElement));
return;
}
if (classDescriptor.getKind() == ClassKind.TRAIT) {
@@ -631,18 +632,22 @@ public class BodyResolver {
}
if (propertyDescriptor.getModality() == Modality.ABSTRACT) {
JetType returnType = propertyDescriptor.getReturnType();
//TODO We need ensure here that jet type is computed or pass it's computed name to quick fix instead
returnType.equals(new Object());
JetExpression initializer = property.getInitializer();
if (initializer != null) {
// context.getTrace().getErrorHandler().genericError(initializer.getNode(), "Property with initializer cannot be abstract");
context.getTrace().report(ABSTRACT_PROPERTY_WITH_INITIALIZER.on(initializer));
context.getTrace().report(ABSTRACT_PROPERTY_WITH_INITIALIZER.on(property, initializer, returnType));
}
if (getter != null && getter.getBodyExpression() != null) {
// context.getTrace().getErrorHandler().genericError(getter.getNode(), "Property with getter implementation cannot be abstract");
context.getTrace().report(ABSTRACT_PROPERTY_WITH_GETTER.on(getter));
context.getTrace().report(ABSTRACT_PROPERTY_WITH_GETTER.on(property, getter, returnType));
}
if (setter != null && setter.getBodyExpression() != null) {
// context.getTrace().getErrorHandler().genericError(setter.getNode(), "Property with setter implementation cannot be abstract");
context.getTrace().report(ABSTRACT_PROPERTY_WITH_SETTER.on(setter));
context.getTrace().report(ABSTRACT_PROPERTY_WITH_SETTER.on(property, setter, returnType));
}
}
}
@@ -672,7 +677,7 @@ public class BodyResolver {
context.getTrace().report(MUST_BE_INITIALIZED.on(nameNode));
} else {
// context.getTrace().getErrorHandler().genericError(nameNode, "Property must be initialized or be abstract");
context.getTrace().report(MUST_BE_INITIALIZED_OR_BE_ABSTRACT.on(nameNode));
context.getTrace().report(MUST_BE_INITIALIZED_OR_BE_ABSTRACT.on(property, nameNode));
}
}
return;
@@ -785,8 +790,8 @@ public class BodyResolver {
else {
throw new UnsupportedOperationException();
}
JetModifierListOwner modifierListOwner = (JetModifierListOwner) function;
JetModifierList modifierList = modifierListOwner.getModifierList();
JetFunctionOrPropertyAccessor functionOrPropertyAccessor = (JetFunctionOrPropertyAccessor) function;
JetModifierList modifierList = functionOrPropertyAccessor.getModifierList();
ASTNode abstractNode = modifierList != null ? modifierList.getModifierNode(JetTokens.ABSTRACT_KEYWORD) : null;
boolean hasAbstractModifier = abstractNode != null;
if (containingDescriptor instanceof ClassDescriptor) {
@@ -794,38 +799,32 @@ public class BodyResolver {
boolean inTrait = classDescriptor.getKind() == ClassKind.TRAIT;
boolean inEnum = classDescriptor.getKind() == ClassKind.ENUM_CLASS;
boolean inAbstractClass = classDescriptor.getModality() == Modality.ABSTRACT;
// String methodName = function.getName() != null ? function.getName() + " " : "";
if (hasAbstractModifier && !inAbstractClass && !inTrait && !inEnum) {
// context.getTrace().getErrorHandler().genericError(abstractNode, "Abstract method " + methodName + " in non-abstract class " + classDescriptor.getName());
context.getTrace().report(ABSTRACT_FUNCTION_IN_NON_ABSTRACT_CLASS.on(abstractNode, functionDescriptor.getName(), classDescriptor));
PsiElement classElement = context.getTrace().get(BindingContext.DESCRIPTOR_TO_DECLARATION, classDescriptor);
assert classElement instanceof JetModifierListOwner;
context.getTrace().report(ABSTRACT_FUNCTION_IN_NON_ABSTRACT_CLASS.on(functionOrPropertyAccessor, abstractNode, functionDescriptor.getName(), classDescriptor, (JetModifierListOwner) classElement));
}
if (hasAbstractModifier && inTrait && !isPropertyAccessor) {
// context.getTrace().getErrorHandler().genericWarning(abstractNode, "Abstract modifier is redundant in trait");
context.getTrace().report(REDUNDANT_ABSTRACT.on(modifierListOwner, abstractNode));
context.getTrace().report(REDUNDANT_ABSTRACT.on(functionOrPropertyAccessor, abstractNode));
}
if (function.getBodyExpression() != null && hasAbstractModifier) {
// context.getTrace().getErrorHandler().genericError(abstractNode, "Method " + methodName + "with body cannot be abstract");
context.getTrace().report(ABSTRACT_FUNCTION_WITH_BODY.on(abstractNode, functionDescriptor));
if (function.getBodyExpression() != null && hasAbstractModifier) { //TODO
context.getTrace().report(ABSTRACT_FUNCTION_WITH_BODY.on(functionOrPropertyAccessor, abstractNode, functionDescriptor));
}
if (function.getBodyExpression() == null && !hasAbstractModifier && !inTrait && nameIdentifier != null && !isPropertyAccessor) {
// context.getTrace().getErrorHandler().genericError(nameIdentifier.getNode(), "Method " + function.getName() + " without body must be abstract");
context.getTrace().report(NON_ABSTRACT_FUNCTION_WITH_NO_BODY.on(modifierListOwner, nameIdentifier, functionDescriptor));
context.getTrace().report(NON_ABSTRACT_FUNCTION_WITH_NO_BODY.on(functionOrPropertyAccessor, nameIdentifier, functionDescriptor));
}
return;
}
if (hasAbstractModifier) {
if (!isPropertyAccessor) {
// context.getTrace().getErrorHandler().genericError(abstractNode, "Function " + function.getName() + " is not a member and cannot be abstract");
context.getTrace().report(NON_MEMBER_ABSTRACT_FUNCTION.on(abstractNode, functionDescriptor));
context.getTrace().report(NON_MEMBER_ABSTRACT_FUNCTION.on(functionOrPropertyAccessor, abstractNode, functionDescriptor));
}
else {
// context.getTrace().getErrorHandler().genericError(abstractNode, "Property {0} is not a class or trait member and thus cannot have be abstract accessors");
context.getTrace().report(NON_MEMBER_ABSTRACT_ACCESSOR.on(abstractNode));
context.getTrace().report(NON_MEMBER_ABSTRACT_ACCESSOR.on(functionOrPropertyAccessor, abstractNode));
}
}
if (function.getBodyExpression() == null && !hasAbstractModifier && nameIdentifier != null && !isPropertyAccessor) {
// context.getTrace().getErrorHandler().genericError(nameIdentifier.getNode(), "Function " + function.getName() + " must have a body");
context.getTrace().report(NON_MEMBER_FUNCTION_NO_BODY.on(nameIdentifier, functionDescriptor));
context.getTrace().report(NON_MEMBER_FUNCTION_NO_BODY.on(functionOrPropertyAccessor, nameIdentifier, functionDescriptor));
}
}
@@ -580,7 +580,7 @@ public class ClassDescriptorResolver {
if (inType != null) {
if (!semanticServices.getTypeChecker().equalTypes(type, inType)) {
// trace.getErrorHandler().genericError(typeReference.getNode(), "Setter parameter type must be equal to the type of the property, i.e. " + inType);
trace.report(WRONG_SETTER_PARAMETER_TYPE.on(typeReference, inType));
trace.report(WRONG_SETTER_PARAMETER_TYPE.on(setter, typeReference, inType));
}
}
else {
@@ -602,7 +602,7 @@ public class ClassDescriptorResolver {
if (! property.isVar()) {
if (setter != null) {
// trace.getErrorHandler().genericError(setter.asElement().getNode(), "A 'val'-property cannot have a setter");
trace.report(VAL_WITH_SETTER.on(setter));
trace.report(VAL_WITH_SETTER.on(property, setter));
}
}
return setterDescriptor;
@@ -622,7 +622,7 @@ public class ClassDescriptorResolver {
returnType = typeResolver.resolveType(scope, returnTypeReference);
if (outType != null && !semanticServices.getTypeChecker().equalTypes(returnType, outType)) {
// trace.getErrorHandler().genericError(returnTypeReference.getNode(), "Getter return type must be equal to the type of the property, i.e. " + propertyDescriptor.getReturnType());
trace.report(WRONG_GETTER_RETURN_TYPE.on(returnTypeReference, propertyDescriptor.getReturnType()));
trace.report(WRONG_GETTER_RETURN_TYPE.on(getter, returnTypeReference, propertyDescriptor.getReturnType()));
}
}
@@ -693,7 +693,7 @@ public class ClassDescriptorResolver {
ASTNode abstractNode = modifierList.getModifierNode(JetTokens.ABSTRACT_KEYWORD);
if (abstractNode != null) {
// trace.getErrorHandler().genericError(abstractNode, "This property cannot be declared abstract");
trace.report(ABSTRACT_PROPERTY_IN_PRIMARY_CONSTRUCTOR_PARAMETERS.on(abstractNode));
trace.report(ABSTRACT_PROPERTY_IN_PRIMARY_CONSTRUCTOR_PARAMETERS.on(parameter, abstractNode));
}
}
@@ -217,7 +217,8 @@ public class JetTypeInferrer {
return callResolver;
}
private void checkNullSafety(@Nullable JetType receiverType, @NotNull ASTNode operationTokenNode, @Nullable FunctionDescriptor callee) {
//TODO JetElement -> JetWhenConditionCall || JetQualifiedExpression
private void checkNullSafety(@Nullable JetType receiverType, @NotNull ASTNode operationTokenNode, @Nullable FunctionDescriptor callee, @NotNull JetElement element) {
if (receiverType != null && callee != null) {
boolean namespaceType = receiverType instanceof NamespaceType;
JetType calleeReceiverType = callee.getReceiverType();
@@ -237,7 +238,7 @@ public class JetTypeInferrer {
else {
// trace.getErrorHandler().genericWarning(operationTokenNode, "Unnecessary safe call on a non-null receiver of type " + receiverType);
trace.report(UNNECESSARY_SAFE_CALL.on(operationTokenNode, receiverType));
trace.report(UNNECESSARY_SAFE_CALL.on(element, operationTokenNode, receiverType));
}
}
@@ -1180,7 +1181,7 @@ public class JetTypeInferrer {
if (!typeChecker.isSubtypeOf(targetType, actualType)) {
if (typeChecker.isSubtypeOf(actualType, targetType)) {
// context.trace.getErrorHandler().genericWarning(expression.getOperationSign().getNode(), "No cast needed, use ':' instead");
context.trace.report(USELESS_CAST_STATIC_ASSERT_IS_FINE.on(expression.getOperationSign()));
context.trace.report(USELESS_CAST_STATIC_ASSERT_IS_FINE.on(expression, expression.getOperationSign()));
}
else {
// See JET-58 Make 'as never succeeds' a warning, or even never check for Java (external) types
@@ -1191,7 +1192,7 @@ public class JetTypeInferrer {
else {
if (typeChecker.isSubtypeOf(actualType, targetType)) {
// context.trace.getErrorHandler().genericWarning(expression.getOperationSign().getNode(), "No cast needed");
context.trace.report(USELESS_CAST.on(expression.getOperationSign()));
context.trace.report(USELESS_CAST.on(expression, expression.getOperationSign()));
}
}
}
@@ -1397,7 +1398,7 @@ public class JetTypeInferrer {
// JetType selectorReturnType = getType(compositeScope, callSuffixExpression, false, context);
JetType selectorReturnType = getSelectorReturnType(subjectType, callSuffixExpression, context);//getType(compositeScope, callSuffixExpression, false, context);
ensureBooleanResultWithCustomSubject(callSuffixExpression, selectorReturnType, "This expression", context);
context.services.checkNullSafety(subjectType, condition.getOperationTokenNode(), getCalleeFunctionDescriptor(callSuffixExpression, context));
context.services.checkNullSafety(subjectType, condition.getOperationTokenNode(), getCalleeFunctionDescriptor(callSuffixExpression, context), condition);
}
}
@@ -2060,7 +2061,7 @@ public class JetTypeInferrer {
if (selectorExpression != null) {
receiverType = context.services.enrichOutType(receiverExpression, receiverType, context);
context.services.checkNullSafety(receiverType, expression.getOperationTokenNode(), getCalleeFunctionDescriptor(selectorExpression, context));
context.services.checkNullSafety(receiverType, expression.getOperationTokenNode(), getCalleeFunctionDescriptor(selectorExpression, context), expression);
}
}
return context.services.checkType(result, expression, contextWithExpectedType);
@@ -2362,7 +2363,7 @@ public class JetTypeInferrer {
if (leftType != null) {
if (!leftType.isNullable()) {
// context.trace.getErrorHandler().genericWarning(left.getNode(), "Elvis operator (?:) always returns the left operand of non-nullable type " + leftType);
context.trace.report(USELESS_ELVIS.on(left, leftType));
context.trace.report(USELESS_ELVIS.on(expression, left, leftType));
}
if (rightType != null) {
context.services.checkType(TypeUtils.makeNullableAsSpecified(leftType, rightType.isNullable()), left, contextWithExpectedType);
@@ -90,13 +90,15 @@ public class JetPsiChecker implements Annotator {
DiagnosticWithPsiElement diagnosticWithPsiElement = (DiagnosticWithPsiElement) diagnostic;
if (diagnostic.getFactory() instanceof PsiElementOnlyDiagnosticFactory) {
PsiElementOnlyDiagnosticFactory factory = (PsiElementOnlyDiagnosticFactory) diagnostic.getFactory();
IntentionActionFactory intentionActionFactory = QuickFixes.get(factory);
IntentionAction action = null;
if (intentionActionFactory != null) {
action = intentionActionFactory.createAction(diagnosticWithPsiElement);
}
if (action != null) {
annotation.registerFix(action);
Set<IntentionActionFactory> intentionActionFactories = QuickFixes.get(factory);
for (IntentionActionFactory intentionActionFactory : intentionActionFactories) {
IntentionAction action = null;
if (intentionActionFactory != null) {
action = intentionActionFactory.createAction(diagnosticWithPsiElement);
}
if (action != null) {
annotation.registerFix(action);
}
}
}
}
@@ -0,0 +1,61 @@
package org.jetbrains.jet.plugin.quickfix;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiWhiteSpace;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.diagnostics.DiagnosticWithPsiElement;
import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lexer.JetTokens;
/**
* @author svtk
*/
public class AddFunctionBodyFix extends IntentionActionForPsiElement<JetFunctionOrPropertyAccessor> {
public AddFunctionBodyFix(@NotNull JetFunctionOrPropertyAccessor element) {
super(element);
}
@NotNull
@Override
public String getText() {
return "Add function body";
}
@NotNull
@Override
public String getFamilyName() {
return "Add function body";
}
@Override
public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) {
return super.isAvailable(project, editor, file) &&
element.getBodyExpression() == null;
}
@Override
public void invoke(@NotNull Project project, Editor editor, PsiFile file) throws IncorrectOperationException {
JetFunctionOrPropertyAccessor newElement = (JetFunctionOrPropertyAccessor) element.copy();
JetExpression bodyExpression = newElement.getBodyExpression();
if (!(newElement.getLastChild() instanceof PsiWhiteSpace)) {
newElement.add(JetPsiFactory.createWhiteSpace(project));
}
if (bodyExpression == null) {
newElement.add(JetPsiFactory.createEmptyBody(project));
}
element.replace(newElement);
}
public static IntentionActionFactory<JetFunctionOrPropertyAccessor> createFactory() {
return new IntentionActionFactory<JetFunctionOrPropertyAccessor>() {
@Override
public IntentionActionForPsiElement<JetFunctionOrPropertyAccessor> createAction(DiagnosticWithPsiElement diagnostic) {
assert diagnostic.getPsiElement() instanceof JetFunctionOrPropertyAccessor;
return new AddFunctionBodyFix((JetFunctionOrPropertyAccessor) diagnostic.getPsiElement());
}
};
}
}
@@ -7,7 +7,9 @@ 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.*;
import org.jetbrains.jet.lang.psi.JetModifierList;
import org.jetbrains.jet.lang.psi.JetModifierListOwner;
import org.jetbrains.jet.lang.psi.JetPsiFactory;
import org.jetbrains.jet.lexer.JetKeywordToken;
import org.jetbrains.jet.lexer.JetToken;
import org.jetbrains.jet.lexer.JetTokens;
@@ -17,27 +19,41 @@ import org.jetbrains.jet.lexer.JetTokens;
*/
public class AddModifierFix extends ModifierFix {
private final JetToken[] modifiersThanCanBeReplaced;
private final JetToken[] conflictedModifiers;
private AddModifierFix(@NotNull JetModifierListOwner element, JetKeywordToken modifier, JetToken[] modifiersThanCanBeReplaced) {
private AddModifierFix(@NotNull JetModifierListOwner element, JetKeywordToken modifier, JetToken[] modifiersThanCanBeReplaced, JetToken[] conflictedModifiers) {
super(element, modifier);
this.modifiersThanCanBeReplaced = modifiersThanCanBeReplaced;
this.conflictedModifiers = conflictedModifiers;
}
private static boolean checkConflictModifiers(JetModifierListOwner element, JetToken[] conflictedModifiers) {
for (JetToken conflictedModifier : conflictedModifiers) {
if (element.hasModifier(conflictedModifier)) {
return true;
}
}
return false;
}
@NotNull
@Override
public String getText() {
return "add." + modifier.getValue() + ".modifier.fix";
if (modifier == JetTokens.ABSTRACT_KEYWORD) {
return "Make " + getElementName() + " " + modifier.getValue();
}
return "Add '" + modifier.getValue() + "' modifier";
}
@NotNull
@Override
public String getFamilyName() {
return "add." + modifier.getValue() + ".modifier.family";
return "Add modifier";
}
@Override
public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) {
return super.isAvailable(project, editor, file) && !element.hasModifier(JetTokens.FINAL_KEYWORD);
return element.isValid() && !checkConflictModifiers(element, conflictedModifiers);
}
@Override
@@ -81,17 +97,17 @@ public class AddModifierFix extends ModifierFix {
return true;
}
public static IntentionActionFactory<JetModifierListOwner> createFactory(final JetKeywordToken modifier, final JetToken[] modifiersThatCanBeReplaced) {
public static IntentionActionFactory<JetModifierListOwner> createFactory(final JetKeywordToken modifier, final JetToken[] modifiersThatCanBeReplaced, final JetToken[] conflictedModifiers) {
return new IntentionActionFactory<JetModifierListOwner>() {
@Override
public IntentionActionForPsiElement<JetModifierListOwner> createAction(DiagnosticWithPsiElement diagnostic) {
assert diagnostic.getPsiElement() instanceof JetModifierListOwner;
return new AddModifierFix((JetModifierListOwner) diagnostic.getPsiElement(), modifier, modifiersThatCanBeReplaced);
return new AddModifierFix((JetModifierListOwner) diagnostic.getPsiElement(), modifier, modifiersThatCanBeReplaced, conflictedModifiers);
}
};
}
public static IntentionActionFactory<JetModifierListOwner> createFactory(final JetKeywordToken modifier) {
return createFactory(modifier, new JetToken[] {});
return createFactory(modifier, new JetToken[0], new JetToken[0]);
}
}
@@ -0,0 +1,72 @@
package org.jetbrains.jet.plugin.quickfix;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiFile;
import com.intellij.psi.impl.source.codeStyle.CodeEditUtil;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.diagnostics.DiagnosticWithAdditionalInfo;
import org.jetbrains.jet.lang.diagnostics.DiagnosticWithPsiElement;
import org.jetbrains.jet.lang.psi.JetParameter;
import org.jetbrains.jet.lang.psi.JetPropertyAccessor;
import org.jetbrains.jet.lang.psi.JetPsiFactory;
import org.jetbrains.jet.lang.psi.JetTypeReference;
import org.jetbrains.jet.lang.types.JetType;
/**
* @author svtk
*/
public class ChangeAccessorTypeFix extends IntentionActionForPsiElement<JetPropertyAccessor> {
private final JetType type;
public ChangeAccessorTypeFix(@NotNull JetPropertyAccessor element, JetType type) {
super(element);
this.type = type;
}
@NotNull
@Override
public String getText() {
return (element.isGetter() ? "Change getter " : "Change setter parameter ") + "type to " + type;
}
@NotNull
@Override
public String getFamilyName() {
return "Change accessor type";
}
@Override
public void invoke(@NotNull Project project, Editor editor, PsiFile file) throws IncorrectOperationException {
JetPropertyAccessor newElement = (JetPropertyAccessor) element.copy();
JetTypeReference newTypeReference = JetPsiFactory.createType(project, type.toString());
if (element.isGetter()) {
JetTypeReference returnTypeReference = newElement.getReturnTypeReference();
assert returnTypeReference != null;
CodeEditUtil.replaceChild(newElement.getNode(), returnTypeReference.getNode(), newTypeReference.getNode());
element.replace(newElement);
}
else {
JetParameter parameter = newElement.getParameter();
assert parameter != null;
JetTypeReference typeReference = parameter.getTypeReference();
assert typeReference != null;
CodeEditUtil.replaceChild(parameter.getNode(), typeReference.getNode(), newTypeReference.getNode());
element.replace(newElement);
}
}
public static IntentionActionFactory<JetPropertyAccessor> createFactory() {
return new IntentionActionFactory<JetPropertyAccessor>() {
@Override
public IntentionActionForPsiElement<JetPropertyAccessor> createAction(DiagnosticWithPsiElement diagnostic) {
assert diagnostic instanceof DiagnosticWithAdditionalInfo;
assert diagnostic.getPsiElement() instanceof JetPropertyAccessor;
assert ((DiagnosticWithAdditionalInfo) diagnostic).getInfo() instanceof JetType;
return new ChangeAccessorTypeFix((JetPropertyAccessor) diagnostic.getPsiElement(), (JetType) ((DiagnosticWithAdditionalInfo) diagnostic).getInfo());
}
};
}
}
@@ -0,0 +1,64 @@
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.psi.impl.source.codeStyle.CodeEditUtil;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.diagnostics.DiagnosticWithPsiElement;
import org.jetbrains.jet.lang.psi.JetProperty;
import org.jetbrains.jet.lang.psi.JetPsiFactory;
import org.jetbrains.jet.lexer.JetTokens;
/**
* @author svtk
*/
public class ChangeVariableMutabilityFix extends IntentionActionForPsiElement<JetProperty> {
public ChangeVariableMutabilityFix(@NotNull JetProperty element) {
super(element);
}
@NotNull
@Override
public String getText() {
return "Make variable " + (element.isVar() ? "immutable" : "mutable");
}
@NotNull
@Override
public String getFamilyName() {
return "Change variable mutability";
}
@Override
public void invoke(@NotNull Project project, Editor editor, PsiFile file) throws IncorrectOperationException {
JetProperty newElement = (JetProperty) element.copy();
if (newElement.isVar()) {
PsiElement varElement = newElement.getNode().findChildByType(JetTokens.VAR_KEYWORD).getPsi();
JetProperty valProperty = JetPsiFactory.createProperty(project, "x", "Any", false);
PsiElement valElement = valProperty.getNode().findChildByType(JetTokens.VAL_KEYWORD).getPsi();
CodeEditUtil.replaceChild(newElement.getNode(), varElement.getNode(), valElement.getNode());
}
else {
PsiElement valElement = newElement.getNode().findChildByType(JetTokens.VAL_KEYWORD).getPsi();
JetProperty varProperty = JetPsiFactory.createProperty(project, "x", "Any", true);
PsiElement varElement = varProperty.getNode().findChildByType(JetTokens.VAR_KEYWORD).getPsi();
CodeEditUtil.replaceChild(newElement.getNode(), valElement.getNode(), varElement.getNode());
}
element.replace(newElement);
}
public static IntentionActionFactory<JetProperty> createFactory() {
return new IntentionActionFactory<JetProperty>() {
@Override
public IntentionActionForPsiElement<JetProperty> createAction(DiagnosticWithPsiElement diagnostic) {
assert diagnostic.getPsiElement() instanceof JetProperty;
return new ChangeVariableMutabilityFix((JetProperty) diagnostic.getPsiElement());
}
};
}
}
@@ -8,6 +8,6 @@ import org.jetbrains.jet.lang.diagnostics.DiagnosticWithPsiElement;
*/
public interface IntentionActionFactory<T extends PsiElement> {
IntentionActionForPsiElement createAction(DiagnosticWithPsiElement diagnostic);
IntentionActionForPsiElement<T> createAction(DiagnosticWithPsiElement diagnostic);
}
@@ -1,7 +1,10 @@
package org.jetbrains.jet.plugin.quickfix;
import com.intellij.codeInsight.intention.IntentionAction;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import org.jetbrains.annotations.NotNull;
/**
@@ -13,4 +16,14 @@ public abstract class IntentionActionForPsiElement<T extends PsiElement> impleme
public IntentionActionForPsiElement(@NotNull T element) {
this.element = element;
}
@Override
public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) {
return element.isValid();
}
@Override
public boolean startInWriteAction() {
return true;
}
}
@@ -1,8 +1,7 @@
package org.jetbrains.jet.plugin.quickfix;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiNameIdentifierOwner;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.psi.JetModifierListOwner;
import org.jetbrains.jet.lexer.JetKeywordToken;
@@ -18,14 +17,14 @@ public abstract class ModifierFix extends IntentionActionForPsiElement<JetModifi
this.modifier = modifier;
}
@Override
public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) {
return element.isValid();
@NotNull
protected String getElementName() {
if (element instanceof PsiNameIdentifierOwner) {
PsiElement nameIdentifier = ((PsiNameIdentifierOwner) element).getNameIdentifier();
if (nameIdentifier != null) {
return "'" + nameIdentifier.getText() + "'";
}
}
return "'" + element.getText() + "'";
}
@Override
public boolean startInWriteAction() {
return true;
}
}
@@ -0,0 +1,33 @@
package org.jetbrains.jet.plugin.quickfix;
import com.intellij.psi.PsiElement;
import org.jetbrains.jet.lang.diagnostics.DiagnosticWithAdditionalInfo;
import org.jetbrains.jet.lang.diagnostics.DiagnosticWithPsiElement;
import org.jetbrains.jet.lang.diagnostics.DiagnosticWithPsiElementImpl;
/**
* @author svtk
*/
public class QuickFixUtil {
private QuickFixUtil() {}
public static <T extends PsiElement> IntentionActionFactory<T> createFactoryRedirectingAdditionalInfoToAnotherFactory(final IntentionActionFactory<T> factory) {
return new IntentionActionFactory<T>() {
@Override
public IntentionActionForPsiElement<T> createAction(DiagnosticWithPsiElement diagnostic) {
//no type check; should be followed manually
assert diagnostic instanceof DiagnosticWithAdditionalInfo;
Object info = ((DiagnosticWithAdditionalInfo) diagnostic).getInfo();
T element = null;
try {
element = (T) info;
}
catch (ClassCastException ex) {
assert false : ex;
}
return factory.createAction(new DiagnosticWithPsiElementImpl<T>(diagnostic.getFactory(), diagnostic.getSeverity(), diagnostic.getMessage(), element));
}
};
}
}
@@ -1,39 +1,88 @@
package org.jetbrains.jet.plugin.quickfix;
import com.google.common.collect.Maps;
import com.google.common.collect.HashMultimap;
import com.intellij.psi.PsiElement;
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.lexer.JetToken;
import org.jetbrains.jet.lexer.JetTokens;
import java.util.Map;
import java.util.Set;
/**
* @author svtk
*/
public class QuickFixes {
private static Map<PsiElementOnlyDiagnosticFactory, IntentionActionFactory> actionMap = Maps.newHashMap();
private static HashMultimap<PsiElementOnlyDiagnosticFactory, IntentionActionFactory> actionMap = HashMultimap.create();
public static IntentionActionFactory get(PsiElementOnlyDiagnosticFactory diagnosticFactory) {
public static Set<IntentionActionFactory> get(PsiElementOnlyDiagnosticFactory diagnosticFactory) {
return actionMap.get(diagnosticFactory);
}
private QuickFixes() {}
private static <T extends PsiElement> void add(PsiElementOnlyDiagnosticFactory<T> diagnosticFactory, IntentionActionFactory<T> actionFactory) {
private static <T extends PsiElement> void add(PsiElementOnlyDiagnosticFactory<? extends T> diagnosticFactory, IntentionActionFactory<T> actionFactory) {
actionMap.put(diagnosticFactory, actionFactory);
}
static {
IntentionActionFactory<JetModifierListOwner> removeAbstractModifierFactory = RemoveModifierFix.createFactory(JetTokens.ABSTRACT_KEYWORD);
IntentionActionFactory<JetModifierListOwner> addAbstractModifierFactory = AddModifierFix.createFactory(JetTokens.ABSTRACT_KEYWORD, new JetToken[]{JetTokens.OPEN_KEYWORD});
IntentionActionFactory<JetModifierListOwner> addAbstractModifierFactory = AddModifierFix.createFactory(JetTokens.ABSTRACT_KEYWORD, new JetToken[]{JetTokens.OPEN_KEYWORD}, new JetToken[] {JetTokens.FINAL_KEYWORD});
add(Errors.ABSTRACT_PROPERTY_IN_PRIMARY_CONSTRUCTOR_PARAMETERS, removeAbstractModifierFactory);
add(Errors.ABSTRACT_PROPERTY_NOT_IN_CLASS, removeAbstractModifierFactory);
IntentionActionFactory<JetProperty> removePartsFromPropertyFactory = RemovePartsFromPropertyFix.createFactory();
add(Errors.ABSTRACT_PROPERTY_WITH_INITIALIZER, removeAbstractModifierFactory);
add(Errors.ABSTRACT_PROPERTY_WITH_INITIALIZER, removePartsFromPropertyFactory);
add(Errors.ABSTRACT_PROPERTY_WITH_GETTER, removeAbstractModifierFactory);
add(Errors.ABSTRACT_PROPERTY_WITH_GETTER, removePartsFromPropertyFactory);
add(Errors.ABSTRACT_PROPERTY_WITH_SETTER, removeAbstractModifierFactory);
add(Errors.ABSTRACT_PROPERTY_WITH_SETTER, removePartsFromPropertyFactory);
add(Errors.MUST_BE_INITIALIZED_OR_BE_ABSTRACT, addAbstractModifierFactory);
add(Errors.REDUNDANT_ABSTRACT, removeAbstractModifierFactory);
IntentionActionFactory<JetModifierListOwner> addAbstractToClassFactory = QuickFixUtil.createFactoryRedirectingAdditionalInfoToAnotherFactory(addAbstractModifierFactory);
add(Errors.ABSTRACT_PROPERTY_IN_NON_ABSTRACT_CLASS, removeAbstractModifierFactory);
add(Errors.ABSTRACT_PROPERTY_IN_NON_ABSTRACT_CLASS, addAbstractToClassFactory);
IntentionActionFactory<JetFunctionOrPropertyAccessor> removeFunctionBodyFactory = RemoveFunctionBodyFix.createFactory();
add(Errors.ABSTRACT_FUNCTION_IN_NON_ABSTRACT_CLASS, removeAbstractModifierFactory);
add(Errors.ABSTRACT_FUNCTION_IN_NON_ABSTRACT_CLASS, addAbstractToClassFactory);
add(Errors.ABSTRACT_FUNCTION_WITH_BODY, removeAbstractModifierFactory);
add(Errors.ABSTRACT_FUNCTION_WITH_BODY, removeFunctionBodyFactory);
IntentionActionFactory<JetFunctionOrPropertyAccessor> addFunctionBodyFactory = AddFunctionBodyFix.createFactory();
add(Errors.NON_ABSTRACT_FUNCTION_WITH_NO_BODY, addAbstractModifierFactory);
add(Errors.NON_ABSTRACT_FUNCTION_WITH_NO_BODY, addFunctionBodyFactory);
add(Errors.NON_MEMBER_ABSTRACT_FUNCTION, removeAbstractModifierFactory);
add(Errors.NON_MEMBER_ABSTRACT_ACCESSOR, removeAbstractModifierFactory);
add(Errors.NON_MEMBER_FUNCTION_NO_BODY, addFunctionBodyFactory);
add(Errors.NOTHING_TO_OVERRIDE, RemoveModifierFix.createFactory(JetTokens.OVERRIDE_KEYWORD));
add(Errors.VIRTUAL_METHOD_HIDDEN, AddModifierFix.createFactory(JetTokens.OVERRIDE_KEYWORD));
add(Errors.VAL_WITH_SETTER, ChangeVariableMutabilityFix.createFactory());
add(Errors.USELESS_CAST_STATIC_ASSERT_IS_FINE, ReplaceOperationInBinaryExpressionFix.createChangeCastToStaticAssertFactory());
add(Errors.USELESS_CAST, RemoveRightPartOfBinaryExpressionFix.createRemoveCastFactory());
IntentionActionFactory<JetPropertyAccessor> changeAccessorTypeFactory = ChangeAccessorTypeFix.createFactory();
add(Errors.WRONG_SETTER_PARAMETER_TYPE, changeAccessorTypeFactory);
add(Errors.WRONG_GETTER_RETURN_TYPE, changeAccessorTypeFactory);
add(Errors.USELESS_ELVIS, RemoveRightPartOfBinaryExpressionFix.createRemoveElvisOperatorFactory());
add(Errors.UNNECESSARY_SAFE_CALL, ReplaceSafeCallToDotCall.createFactory());
}
}
@@ -0,0 +1,64 @@
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.psi.PsiWhiteSpace;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.diagnostics.DiagnosticWithPsiElement;
import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lexer.JetTokens;
/**
* @author svtk
*/
public class RemoveFunctionBodyFix extends IntentionActionForPsiElement<JetFunctionOrPropertyAccessor> {
public RemoveFunctionBodyFix(@NotNull JetFunctionOrPropertyAccessor element) {
super(element);
}
@NotNull
@Override
public String getText() {
return "Remove function body";
}
@NotNull
@Override
public String getFamilyName() {
return "Remove function body";
}
@Override
public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) {
return super.isAvailable(project, editor, file) &&
element.getBodyExpression() != null;
}
@Override
public void invoke(@NotNull Project project, Editor editor, PsiFile file) throws IncorrectOperationException {
JetFunctionOrPropertyAccessor newElement = (JetFunctionOrPropertyAccessor) element.copy();
JetExpression bodyExpression = newElement.getBodyExpression();
if (bodyExpression != null) {
PsiElement prevSibling = bodyExpression.getPrevSibling();
if (prevSibling instanceof PsiWhiteSpace) {
((JetElement)newElement).deleteChildInternal(prevSibling.getNode());
}
((JetElement)newElement).deleteChildInternal(bodyExpression.getNode());
}
element.replace(newElement);
}
public static IntentionActionFactory<JetFunctionOrPropertyAccessor> createFactory() {
return new IntentionActionFactory<JetFunctionOrPropertyAccessor>() {
@Override
public IntentionActionForPsiElement<JetFunctionOrPropertyAccessor> createAction(DiagnosticWithPsiElement diagnostic) {
assert diagnostic.getPsiElement() instanceof JetFunctionOrPropertyAccessor;
return new RemoveFunctionBodyFix((JetFunctionOrPropertyAccessor) diagnostic.getPsiElement());
}
};
}
}
@@ -5,13 +5,16 @@ 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.psi.PsiWhiteSpace;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.diagnostics.DiagnosticWithPsiElement;
import org.jetbrains.jet.lang.psi.JetElement;
import org.jetbrains.jet.lang.psi.JetModifierList;
import org.jetbrains.jet.lang.psi.JetModifierListOwner;
import org.jetbrains.jet.lexer.JetKeywordToken;
import org.jetbrains.jet.lexer.JetToken;
import org.jetbrains.jet.lexer.JetTokens;
/**
* @author svtk
@@ -25,13 +28,21 @@ public class RemoveModifierFix extends ModifierFix {
@NotNull
@Override
public String getText() {
return "remove." + modifier.getValue() + ".modifier.fix";
if (modifier == JetTokens.ABSTRACT_KEYWORD) {
return "Make " + getElementName() + " not " + modifier.getValue();
}
return "Remove '" + modifier.getValue() + "' modifier";
}
@NotNull
@Override
public String getFamilyName() {
return "remove." + modifier.getValue() + "abstract.modifier.family";
return "Remove modifier";
}
@Override
public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) {
return element.isValid();
}
@Override
@@ -40,14 +51,29 @@ public class RemoveModifierFix extends ModifierFix {
}
@NotNull
private static JetModifierListOwner removeModifier(PsiElement element, JetToken modifier) {
JetModifierListOwner newElement = (JetModifierListOwner) (element.copy());
/*package*/ static <T extends JetModifierListOwner> T removeModifier(T element, JetToken modifier) {
T newElement = (T) (element.copy());
assert newElement.hasModifier(modifier);
ASTNode modifierNode = newElement.getModifierList().getModifierNode(modifier);
JetModifierList modifierList = newElement.getModifierList();
ASTNode modifierNode = modifierList.getModifierNode(modifier);
PsiElement whiteSpace = modifierNode.getPsi().getNextSibling();
((JetElement)newElement).deleteChildInternal(modifierNode);
if (modifierList.getChildren().length == 0) {
whiteSpace = modifierList.getNextSibling();
((JetElement) newElement).deleteChildInternal(modifierList.getNode());
removeWhiteSpace(newElement, whiteSpace);
}
else {
removeWhiteSpace(newElement, whiteSpace);
}
return newElement;
}
private static void removeWhiteSpace(PsiElement element, PsiElement subElement) {
if (subElement instanceof PsiWhiteSpace) {
((JetElement) element).deleteChildInternal(subElement.getNode());
}
}
public static IntentionActionFactory<JetModifierListOwner> createFactory(final JetKeywordToken modifier) {
return new IntentionActionFactory<JetModifierListOwner>() {
@@ -0,0 +1,121 @@
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.DiagnosticWithAdditionalInfo;
import org.jetbrains.jet.lang.diagnostics.DiagnosticWithPsiElement;
import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.types.JetType;
/**
* @author svtk
*/
public class RemovePartsFromPropertyFix extends IntentionActionForPsiElement<JetProperty> {
private final JetType type;
private final String partsToRemove;
private RemovePartsFromPropertyFix(@NotNull JetProperty element, JetType type) {
super(element);
this.type = type;
partsToRemove = partsToRemove(element.getGetter() != null && element.getGetter().getBodyExpression() != null,
element.getSetter() != null && element.getSetter().getBodyExpression() != null,
element.getInitializer() != null);
}
private static String partsToRemove(boolean hasGetter, boolean hasSetter, boolean hasInitializer) {
StringBuilder sb = new StringBuilder();
if (hasGetter) {
sb.append("getter");
if (hasSetter && hasInitializer) {
sb.append(", ");
}
else if (hasSetter || hasInitializer) {
sb.append(" and ");
}
}
if (hasSetter) {
sb.append("setter");
if (hasInitializer) {
sb.append(" and ");
}
}
if (hasInitializer) {
sb.append("initializer");
}
return sb.toString();
}
@NotNull
@Override
public String getText() {
return "Remove " + partsToRemove + " from property";
}
@NotNull
@Override
public String getFamilyName() {
return "Remove parts from property to make it abstract";
}
@Override
public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) {
return element.isValid();
}
@Override
public void invoke(@NotNull Project project, Editor editor, PsiFile file) throws IncorrectOperationException {
JetProperty newElement = (JetProperty) element.copy();
JetPropertyAccessor getter = newElement.getGetter();
if (getter != null) {
newElement.deleteChildInternal(getter.getNode());
}
JetPropertyAccessor setter = newElement.getSetter();
if (setter != null) {
newElement.deleteChildInternal(setter.getNode());
}
JetExpression initializer = newElement.getInitializer();
if (initializer != null) {
PsiElement nameIdentifier = newElement.getNameIdentifier();
assert nameIdentifier != null;
PsiElement nextSibling = nameIdentifier.getNextSibling();
assert nextSibling != null;
newElement.deleteChildRange(nextSibling, initializer);
if (newElement.getPropertyTypeRef() == null) {
newElement = addPropertyType(project, newElement, type);
}
}
element.replace(newElement);
}
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);
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);
}
return newProperty;
}
public static IntentionActionFactory<JetProperty> createFactory() {
return new IntentionActionFactory<JetProperty>() {
@Override
public IntentionActionForPsiElement<JetProperty> createAction(DiagnosticWithPsiElement diagnostic) {
assert diagnostic.getPsiElement() instanceof JetProperty;
assert diagnostic instanceof DiagnosticWithAdditionalInfo;
Object info = ((DiagnosticWithAdditionalInfo) diagnostic).getInfo();
assert info instanceof JetType;
return new RemovePartsFromPropertyFix((JetProperty) diagnostic.getPsiElement(), (JetType) info);
}
};
}
}
@@ -0,0 +1,71 @@
package org.jetbrains.jet.plugin.quickfix;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
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.JetBinaryExpression;
import org.jetbrains.jet.lang.psi.JetBinaryExpressionWithTypeRHS;
import org.jetbrains.jet.lang.psi.JetExpression;
/**
* @author svtk
*/
public abstract class RemoveRightPartOfBinaryExpressionFix<T extends JetExpression> extends IntentionActionForPsiElement<T> {
public RemoveRightPartOfBinaryExpressionFix(@NotNull T element) {
super(element);
}
@NotNull
@Override
public String getFamilyName() {
return "Remove right part of a binary expression";
}
@Override
public void invoke(@NotNull Project project, Editor editor, PsiFile file) throws IncorrectOperationException {
if (element instanceof JetBinaryExpression) {
JetBinaryExpression newElement = (JetBinaryExpression) element.copy();
element.replace(newElement.getLeft());
}
else if (element instanceof JetBinaryExpressionWithTypeRHS) {
JetBinaryExpressionWithTypeRHS newElement = (JetBinaryExpressionWithTypeRHS) element.copy();
element.replace(newElement.getLeft());
}
}
public static IntentionActionFactory<JetBinaryExpressionWithTypeRHS> createRemoveCastFactory() {
return new IntentionActionFactory<JetBinaryExpressionWithTypeRHS>() {
@Override
public IntentionActionForPsiElement<JetBinaryExpressionWithTypeRHS> createAction(DiagnosticWithPsiElement diagnostic) {
assert diagnostic.getPsiElement() instanceof JetBinaryExpressionWithTypeRHS;
return new RemoveRightPartOfBinaryExpressionFix<JetBinaryExpressionWithTypeRHS>((JetBinaryExpressionWithTypeRHS) diagnostic.getPsiElement()) {
@NotNull
@Override
public String getText() {
return "Remove cast";
}
};
}
};
}
public static IntentionActionFactory<JetBinaryExpression> createRemoveElvisOperatorFactory() {
return new IntentionActionFactory<JetBinaryExpression>() {
@Override
public IntentionActionForPsiElement<JetBinaryExpression> createAction(DiagnosticWithPsiElement diagnostic) {
assert diagnostic.getPsiElement() instanceof JetBinaryExpression;
return new RemoveRightPartOfBinaryExpressionFix<JetBinaryExpression>((JetBinaryExpression) diagnostic.getPsiElement()) {
@NotNull
@Override
public String getText() {
return "Remove elvis operator";
}
};
}
};
}
}
@@ -0,0 +1,57 @@
package org.jetbrains.jet.plugin.quickfix;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiFile;
import com.intellij.psi.impl.source.codeStyle.CodeEditUtil;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.diagnostics.DiagnosticWithPsiElement;
import org.jetbrains.jet.lang.psi.JetBinaryExpressionWithTypeRHS;
import org.jetbrains.jet.lang.psi.JetExpression;
import org.jetbrains.jet.lang.psi.JetPsiFactory;
/**
* @author svtk
*/
public abstract class ReplaceOperationInBinaryExpressionFix<T extends JetExpression> extends IntentionActionForPsiElement<T> {
private final String expressionWithNecessaryOperation;
public ReplaceOperationInBinaryExpressionFix(@NotNull T element, String expressionWithNecessaryOperation) {
super(element);
this.expressionWithNecessaryOperation = expressionWithNecessaryOperation;
}
@NotNull
@Override
public String getFamilyName() {
return "Replace operation in a binary expression";
}
@Override
public void invoke(@NotNull Project project, Editor editor, PsiFile file) throws IncorrectOperationException {
if (element instanceof JetBinaryExpressionWithTypeRHS) {
JetBinaryExpressionWithTypeRHS expression = (JetBinaryExpressionWithTypeRHS) JetPsiFactory.createExpression(project, expressionWithNecessaryOperation);
JetBinaryExpressionWithTypeRHS newElement = (JetBinaryExpressionWithTypeRHS) element.copy();
CodeEditUtil.replaceChild(newElement.getNode(), newElement.getOperationSign().getNode(), expression.getOperationSign().getNode());
element.replace(newElement);
}
}
public static IntentionActionFactory<JetBinaryExpressionWithTypeRHS> createChangeCastToStaticAssertFactory() {
return new IntentionActionFactory<JetBinaryExpressionWithTypeRHS>() {
@Override
public IntentionActionForPsiElement<JetBinaryExpressionWithTypeRHS> createAction(DiagnosticWithPsiElement diagnostic) {
assert diagnostic.getPsiElement() instanceof JetBinaryExpressionWithTypeRHS;
return new ReplaceOperationInBinaryExpressionFix<JetBinaryExpressionWithTypeRHS>((JetBinaryExpressionWithTypeRHS) diagnostic.getPsiElement(), "2 : Int") {
@NotNull
@Override
public String getText() {
return "Replace a cast with a static assert";
}
};
}
};
}
}
@@ -0,0 +1,62 @@
package org.jetbrains.jet.plugin.quickfix;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiFile;
import com.intellij.psi.impl.source.codeStyle.CodeEditUtil;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.diagnostics.DiagnosticWithPsiElement;
import org.jetbrains.jet.lang.psi.*;
/**
* @author svtk
*/
public class ReplaceSafeCallToDotCall extends IntentionActionForPsiElement<JetElement> {
public ReplaceSafeCallToDotCall(@NotNull JetElement element) {
super(element);
}
@NotNull
@Override
public String getText() {
return "Replace to dot call";
}
@NotNull
@Override
public String getFamilyName() {
return "Replace safe call to dot call";
}
@Override
public void invoke(@NotNull Project project, Editor editor, PsiFile file) throws IncorrectOperationException {
if (element instanceof JetSafeQualifiedExpression) {
JetSafeQualifiedExpression safeQualifiedExpression = (JetSafeQualifiedExpression) element;
JetDotQualifiedExpression newElement = (JetDotQualifiedExpression) JetPsiFactory.createExpression(project, "x.foo");
CodeEditUtil.replaceChild(newElement.getNode(), newElement.getSelectorExpression().getNode(), safeQualifiedExpression.getSelectorExpression().getNode());
CodeEditUtil.replaceChild(newElement.getNode(), newElement.getReceiverExpression().getNode(), safeQualifiedExpression.getReceiverExpression().getNode());
element.replace(newElement);
}
else if (element instanceof JetWhenConditionCall) {
JetWhenConditionCall newElement = (JetWhenConditionCall) element;
JetDotQualifiedExpression callExpression = (JetDotQualifiedExpression) JetPsiFactory.createExpression(project, "x.foo");
CodeEditUtil.replaceChild(newElement.getNode(), newElement.getOperationTokenNode(), callExpression.getOperationTokenNode());
element.replace(newElement);
}
}
public static IntentionActionFactory<JetElement> createFactory() {
return new IntentionActionFactory<JetElement>() {
@Override
public IntentionActionForPsiElement<JetElement> createAction(DiagnosticWithPsiElement diagnostic) {
assert diagnostic.getPsiElement() instanceof JetElement;
return new ReplaceSafeCallToDotCall((JetElement) diagnostic.getPsiElement());
}
};
}
}
@@ -0,0 +1,4 @@
// "Make 'foo' not abstract" "true"
class A() {
<caret>fun foo()
}
@@ -0,0 +1,4 @@
// "Remove function body" "true"
abstract class A() {
<caret>abstract fun foo()
}
@@ -0,0 +1,4 @@
// "Make 'A' abstract" "true"
abstract class A() {
<caret>abstract var i : Int
}
@@ -0,0 +1,4 @@
// "Make 'i' not abstract" "true"
class A() {
<caret>var i : Int
}
@@ -0,0 +1,2 @@
// "Make 'i' not abstract" "true"
class A(<caret>val i: Int) {}
@@ -0,0 +1,2 @@
// "Make 'i' not abstract" "true"
<caret>val i: Int = 1
@@ -0,0 +1,4 @@
// "Remove initializer from property" "true"
abstract class A {
abstract var i : Int<caret>
}
@@ -0,0 +1,4 @@
// "Remove initializer from property" "true"
abstract class A {
abstract var i : Int<caret>
}
@@ -0,0 +1,5 @@
// "Make 'i' not abstract" "true"
class B {
<caret>val i: Int
get() = $i
}
@@ -0,0 +1,5 @@
// "Remove getter and initializer from property" "true"
abstract class B {
abstract val i : <caret>Int
}
@@ -0,0 +1,4 @@
// "Make 'i' not abstract" "true"
class A {
<caret>var i = 0
}
@@ -0,0 +1,5 @@
// "Make 'j' not abstract" "true"
class B {
<caret>var j: Int
set(v: Int) {}
}
@@ -0,0 +1,4 @@
// "Make 'i' abstract" "true"
abstract class A() {
abstract var <caret>i : Int
}
@@ -0,0 +1,4 @@
// "Add function body" "true"
class A() {
fun <caret>foo() {}
}
@@ -0,0 +1,2 @@
// "Make 'abstract get' not abstract" "true"
val i : Int = 0; <caret>get
@@ -0,0 +1,2 @@
// "Make 'foo' not abstract" "true"
<caret>fun foo()
@@ -0,0 +1,4 @@
// "Add function body" "true"
namespace a {
fun <caret>foo() {}
}
@@ -0,0 +1,4 @@
// "Make 'foo' not abstract" "true"
trait A {
<caret>fun foo()
}
@@ -0,0 +1,4 @@
// "Make 'foo' abstract" "true"
abstract class B() {
abstract fun <caret>foo()
}
@@ -0,0 +1,4 @@
// "Make 'foo' not abstract" "true"
class A() {
<caret>abstract fun foo()
}
@@ -0,0 +1,4 @@
// "Remove function body" "true"
abstract class A() {
<caret>abstract fun foo() {}
}
@@ -0,0 +1,4 @@
// "Make 'A' abstract" "true"
class A() {
<caret>abstract var i : Int
}
@@ -0,0 +1,4 @@
// "Make 'i' not abstract" "true"
class A() {
<caret>abstract var i : Int
}
@@ -0,0 +1,2 @@
// "Make 'i' not abstract" "true"
class A(<caret>abstract val i: Int) {}
@@ -0,0 +1,2 @@
// "Make 'i' not abstract" "true"
<caret>abstract val i: Int = 1
@@ -0,0 +1,4 @@
// "Remove initializer from property" "true"
abstract class A {
abstract var i = 0<caret>
}
@@ -0,0 +1,4 @@
// "Remove initializer from property" "true"
abstract class A {
abstract var i : Int = 0<caret>
}
@@ -0,0 +1,5 @@
// "Make 'i' not abstract" "true"
class B {
<caret>abstract val i: Int
get() = $i
}
@@ -0,0 +1,5 @@
// "Remove getter and initializer from property" "true"
abstract class B {
abstract val i = <caret>0
get() = $i
}
@@ -0,0 +1,4 @@
// "Make 'i' not abstract" "true"
class A {
<caret>abstract var i = 0
}
@@ -0,0 +1,5 @@
// "Make 'j' not abstract" "true"
class B {
abstract<caret> var j: Int
set(v: Int) {}
}
@@ -0,0 +1,4 @@
// "Make 'foo' abstract" "false"
class B() {
final fun <caret>foo()
}
@@ -0,0 +1,4 @@
// "Make 'i' abstract" "true"
abstract class A() {
var <caret>i : Int
}
@@ -0,0 +1,4 @@
// "Add function body" "true"
class A() {
fun <caret>foo()
}
@@ -0,0 +1,2 @@
// "Make 'abstract get' not abstract" "true"
val i : Int = 0; <caret>abstract get
@@ -0,0 +1,2 @@
// "Make 'foo' not abstract" "true"
<caret>abstract fun foo()
@@ -0,0 +1,4 @@
// "Add function body" "true"
namespace a {
fun <caret>foo()
}
@@ -0,0 +1,4 @@
// "Make 'foo' not abstract" "true"
trait A {
<caret>abstract fun foo()
}
@@ -0,0 +1,4 @@
// "Make 'foo' abstract" "true"
abstract class B() {
open fun <caret>foo()
}
@@ -0,0 +1,5 @@
// "Make variable mutable" "true"
class A() {
var a: Int = 0
<caret>set(v: Int) {}
}
@@ -0,0 +1,5 @@
// "Make variable mutable" "true"
class A() {
val a: Int = 0
<caret>set(v: Int) {}
}
@@ -0,0 +1,4 @@
// "Replace to dot call" "true"
fun foo(a: Any) {
<caret>a.equals(0)
}
@@ -0,0 +1,7 @@
// "Replace to dot call" "true"
fun foo(a: Any) {
when (a) {
.equals(0) => true
else => false
}
}
@@ -0,0 +1,4 @@
// "Replace a cast with a static assert" "true"
fun foo(a: String) {
val b = a : Any
}
@@ -0,0 +1,4 @@
// "Remove cast" "true"
fun foo(a: Any) {
val b = a<caret>
}
@@ -0,0 +1,4 @@
// "Remove elvis operator" "true"
fun foo(a: String) {
val b : String = <caret>a
}
@@ -0,0 +1,4 @@
// "Replace to dot call" "true"
fun foo(a: Any) {
a<caret>?.equals(0)
}
@@ -0,0 +1,7 @@
// "Replace to dot call" "true"
fun foo(a: Any) {
when (a) {
<caret>?.equals(0) => true
else => false
}
}
@@ -0,0 +1,4 @@
// "Replace a cast with a static assert" "true"
fun foo(a: String) {
val b = a <caret>as Any
}
@@ -0,0 +1,4 @@
// "Remove cast" "true"
fun foo(a: Any) {
val b = a <caret>as Any
}
@@ -0,0 +1,4 @@
// "Remove elvis operator" "true"
fun foo(a: String) {
val b : String = <caret>a ?: "s"
}
@@ -0,0 +1,4 @@
// "Remove 'override' modifier" "true"
class A() {
<caret>fun foo() {}
}
@@ -0,0 +1,8 @@
// "Add 'override' modifier" "true"
open class A() {
fun foo() {}
}
class B() : A() {
override fun <caret>foo() {}
}
@@ -0,0 +1,4 @@
// "Remove 'override' modifier" "true"
class A() {
<caret>override fun foo() {}
}
@@ -0,0 +1,8 @@
// "Add 'override' modifier" "true"
open class A() {
fun foo() {}
}
class B() : A() {
fun <caret>foo() {}
}
@@ -0,0 +1,5 @@
// "Change getter type to Int" "true"
class A() {
val i: Int
get(): Int = 1
}
@@ -0,0 +1,5 @@
// "Change setter parameter type to Int" "true"
class A() {
var i: Int = 0
set(v: Int) {}
}
@@ -0,0 +1,5 @@
// "Change getter type to Int" "true"
class A() {
val i: Int
get(): <caret>Any = 1
}
@@ -0,0 +1,5 @@
// "Change setter parameter type to Int" "true"
class A() {
var i: Int = 0
set(v: <caret>Any) {}
}
@@ -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 AbstractModifierTest extends LightQuickFixTestCase {
public void test() throws Exception {
doAllTests();
}
@Override
protected String getBasePath() {
return "/quickfix/abstract";
}
@Override
protected String getTestDataPath() {
return JetTestCaseBase.getTestDataPathBase();
}
}
@@ -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 ChangeVariableMutabilityTest extends LightQuickFixTestCase {
public void test() throws Exception {
doAllTests();
}
@Override
protected String getBasePath() {
return "/quickfix/changeVariableMutability";
}
@Override
protected String getTestDataPath() {
return JetTestCaseBase.getTestDataPathBase();
}
}
@@ -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 ExpressionsTest extends LightQuickFixTestCase {
public void test() throws Exception {
doAllTests();
}
@Override
protected String getBasePath() {
return "/quickfix/expressions";
}
@Override
protected String getTestDataPath() {
return JetTestCaseBase.getTestDataPathBase();
}
}
@@ -0,0 +1,25 @@
package org.jetbrains.jet.plugin.quickfix;
import com.intellij.codeInsight.daemon.quickFix.LightQuickFixTestCase;
import org.jetbrains.jet.JetTestCaseBase;
/**
* @author svtk
*/
public class OverrideModifierTest extends LightQuickFixTestCase {
public void test() throws Exception {
doAllTests();
}
@Override
protected String getBasePath() {
return "/quickfix/override";
}
@Override
protected String getTestDataPath() {
return JetTestCaseBase.getTestDataPathBase();
}
}
@@ -0,0 +1,25 @@
package org.jetbrains.jet.plugin.quickfix;
import com.intellij.codeInsight.daemon.quickFix.LightQuickFixTestCase;
import org.jetbrains.jet.JetTestCaseBase;
/**
* @author svtk
*/
public class TypeAdditionTests extends LightQuickFixTestCase {
public void test() throws Exception {
doAllTests();
}
@Override
protected String getBasePath() {
return "/quickfix/typeAddition";
}
@Override
protected String getTestDataPath() {
return JetTestCaseBase.getTestDataPathBase();
}
}