Added 'incompatible modifiers' error; 'remove redundant modifier' fix

This commit is contained in:
svtk
2011-09-23 13:03:14 +04:00
parent 67fdbbd230
commit 4ac2a46fb4
12 changed files with 186 additions and 21 deletions
@@ -1,5 +1,6 @@
package org.jetbrains.jet.lang.diagnostics;
import com.google.common.collect.Lists;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.PsiElement;
@@ -7,6 +8,7 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.descriptors.*;
import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.types.JetType;
import org.jetbrains.jet.lexer.JetKeywordToken;
import org.jetbrains.jet.resolve.DescriptorRenderer;
import java.lang.reflect.Field;
@@ -25,6 +27,27 @@ public interface Errors {
UnresolvedReferenceDiagnosticFactory UNRESOLVED_REFERENCE = UnresolvedReferenceDiagnosticFactory.INSTANCE;
RedeclarationDiagnosticFactory REDECLARATION = RedeclarationDiagnosticFactory.INSTANCE;
PsiElementOnlyDiagnosticFactory2<PsiElement, JetType, JetType> TYPE_MISMATCH = PsiElementOnlyDiagnosticFactory2.create(ERROR, "Type mismatch: inferred type is {1} but {0} was expected");
ParameterizedDiagnosticFactory1<Collection<JetKeywordToken>> INCOMPATIBLE_MODIFIERS = new ParameterizedDiagnosticFactory1<Collection<JetKeywordToken>>(ERROR, "Incompatible modifiers: {0}") {
@Override
protected String makeMessageFor(Collection<JetKeywordToken> argument) {
StringBuilder sb = new StringBuilder();
for (Iterator<JetKeywordToken> iterator = argument.iterator(); iterator.hasNext(); ) {
JetKeywordToken modifier = iterator.next();
sb.append(modifier.getValue());
if (iterator.hasNext()) {
sb.append(" ");
}
}
return sb.toString();
}
};
PsiElementOnlyDiagnosticFactory2<JetModifierList, JetKeywordToken, JetKeywordToken> REDUNDANT_MODIFIER = new PsiElementOnlyDiagnosticFactory2<JetModifierList, JetKeywordToken, JetKeywordToken>(Severity.WARNING, "Modifier {0} is redundant because {1} is present") {
@NotNull
@Override
public Diagnostic on(@NotNull JetModifierList element, @NotNull ASTNode node, @NotNull JetKeywordToken redundantModifier, @NotNull JetKeywordToken presentModifier) {
return new DiagnosticWithAdditionalInfo<JetModifierList, JetKeywordToken>(this, severity, makeMessage(redundantModifier, presentModifier), element, node.getTextRange(), redundantModifier);
}
};
SimpleDiagnosticFactory SAFE_CALLS_ARE_NOT_ALLOWED_ON_NAMESPACES = SimpleDiagnosticFactory.create(ERROR, "Safe calls are not allowed on namespaces");
SimpleDiagnosticFactory TYPECHECKER_HAS_RUN_INTO_RECURSIVE_PROBLEM = SimpleDiagnosticFactory.create(ERROR, "Type checking has run into a recursive problem"); // TODO: message
@@ -90,7 +90,7 @@ public class JetPsiChecker implements Annotator {
DiagnosticWithPsiElement diagnosticWithPsiElement = (DiagnosticWithPsiElement) diagnostic;
if (diagnostic.getFactory() instanceof PsiElementOnlyDiagnosticFactory) {
PsiElementOnlyDiagnosticFactory factory = (PsiElementOnlyDiagnosticFactory) diagnostic.getFactory();
Set<IntentionActionFactory> intentionActionFactories = QuickFixes.get(factory);
Collection<IntentionActionFactory> intentionActionFactories = QuickFixes.get(factory);
for (IntentionActionFactory intentionActionFactory : intentionActionFactories) {
IntentionAction action = null;
if (intentionActionFactory != null) {
@@ -1,6 +1,7 @@
package org.jetbrains.jet.plugin.quickfix;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
import com.intellij.psi.PsiElement;
import org.jetbrains.jet.lang.diagnostics.Errors;
import org.jetbrains.jet.lang.diagnostics.PsiElementOnlyDiagnosticFactory;
@@ -11,15 +12,16 @@ import org.jetbrains.jet.lang.psi.JetPropertyAccessor;
import org.jetbrains.jet.lexer.JetToken;
import org.jetbrains.jet.lexer.JetTokens;
import java.util.Collection;
import java.util.Set;
/**
* @author svtk
*/
public class QuickFixes {
private static HashMultimap<PsiElementOnlyDiagnosticFactory, IntentionActionFactory> actionMap = HashMultimap.create();
private static final Multimap<PsiElementOnlyDiagnosticFactory, IntentionActionFactory> actionMap = HashMultimap.create();
public static Set<IntentionActionFactory> get(PsiElementOnlyDiagnosticFactory diagnosticFactory) {
public static Collection<IntentionActionFactory> get(PsiElementOnlyDiagnosticFactory diagnosticFactory) {
return actionMap.get(diagnosticFactory);
}
@@ -83,6 +85,8 @@ public class QuickFixes {
add(Errors.USELESS_ELVIS, RemoveRightPartOfBinaryExpressionFix.createRemoveElvisOperatorFactory());
add(Errors.UNNECESSARY_SAFE_CALL, ReplaceSafeCallToDotCall.createFactory());
add(Errors.REDUNDANT_MODIFIER, RemoveRedundantModifierFix.createFactory());
}
}
@@ -1,5 +1,6 @@
package org.jetbrains.jet.plugin.quickfix;
import com.intellij.extapi.psi.ASTDelegatePsiElement;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
@@ -47,32 +48,42 @@ public class RemoveModifierFix extends ModifierFix {
@Override
public void invoke(@NotNull Project project, Editor editor, PsiFile file) throws IncorrectOperationException {
element.replace(removeModifier(element, modifier));
JetModifierListOwner newElement = (JetModifierListOwner) element.copy();
element.replace(removeModifier(newElement, modifier));
}
@NotNull
/*package*/ static <T extends JetModifierListOwner> T removeModifier(T element, JetToken modifier) {
T newElement = (T) (element.copy());
assert newElement.hasModifier(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);
JetModifierList modifierList = element.getModifierList();
assert modifierList != null;
removeModifierFromList(modifierList, modifier);
if (modifierList.getFirstChild() == null) {
PsiElement whiteSpace = modifierList.getNextSibling();
assert element instanceof JetElement;
((JetElement) element).deleteChildInternal(modifierList.getNode());
removeWhiteSpace((JetElement) element, whiteSpace);
}
else {
removeWhiteSpace(newElement, whiteSpace);
}
return newElement;
return element;
}
private static void removeWhiteSpace(PsiElement element, PsiElement subElement) {
if (subElement instanceof PsiWhiteSpace) {
((JetElement) element).deleteChildInternal(subElement.getNode());
/*package*/ static JetModifierList removeModifierFromList(@NotNull JetModifierList modifierList, JetToken modifier) {
assert modifierList.hasModifier(modifier);
ASTNode modifierNode = modifierList.getModifierNode(modifier);
PsiElement whiteSpace = modifierNode.getPsi().getNextSibling();
boolean wsRemoved = removeWhiteSpace(modifierList, whiteSpace);
modifierList.deleteChildInternal(modifierNode);
if (!wsRemoved) {
removeWhiteSpace(modifierList, modifierList.getLastChild());
}
return modifierList;
}
private static boolean removeWhiteSpace(ASTDelegatePsiElement element, PsiElement subElement) {
if (subElement instanceof PsiWhiteSpace) {
element.deleteChildInternal(subElement.getNode());
return true;
}
return false;
}
public static IntentionActionFactory<JetModifierListOwner> createFactory(final JetKeywordToken modifier) {
@@ -0,0 +1,53 @@
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.DiagnosticWithAdditionalInfo;
import org.jetbrains.jet.lang.diagnostics.DiagnosticWithPsiElement;
import org.jetbrains.jet.lang.psi.JetModifierList;
import org.jetbrains.jet.lexer.JetKeywordToken;
/**
* @author svtk
*/
public class RemoveRedundantModifierFix extends IntentionActionForPsiElement<JetModifierList> {
private JetKeywordToken redundantModifier;
public RemoveRedundantModifierFix(@NotNull JetModifierList element, @NotNull JetKeywordToken redundantModifier) {
super(element);
this.redundantModifier = redundantModifier;
}
@NotNull
@Override
public String getText() {
return "Remove redundant '" + redundantModifier + "' modifier";
}
@NotNull
@Override
public String getFamilyName() {
return "Remove redundant modifier";
}
@Override
public void invoke(@NotNull Project project, Editor editor, PsiFile file) throws IncorrectOperationException {
JetModifierList newElement = (JetModifierList) element.copy();
element.replace(RemoveModifierFix.removeModifierFromList(newElement, redundantModifier));
}
public static IntentionActionFactory<JetModifierList> createFactory() {
return new IntentionActionFactory<JetModifierList>() {
@Override
public IntentionActionForPsiElement<JetModifierList> createAction(DiagnosticWithPsiElement diagnostic) {
assert diagnostic.getPsiElement() instanceof JetModifierList;
assert diagnostic instanceof DiagnosticWithAdditionalInfo;
Object info = ((DiagnosticWithAdditionalInfo) diagnostic).getInfo();
assert info instanceof JetKeywordToken;
return new RemoveRedundantModifierFix((JetModifierList) diagnostic.getPsiElement(), (JetKeywordToken) info);
}
};
}
}
@@ -0,0 +1,8 @@
// "Remove redundant 'open' modifier" "true"
abstract class B() {
abstract fun foo()
}
abstract class A() : B() {
<caret>abstract override fun foo()
}
@@ -0,0 +1,8 @@
// "Remove redundant 'open' modifier" "true"
abstract class B() {
abstract fun foo()
}
abstract class A() : B() {
<caret>abstract override fun foo()
}
@@ -0,0 +1,8 @@
// "Remove redundant 'open' modifier" "true"
abstract class B() {
abstract fun foo()
}
abstract class A() : B() {
<caret>abstract override fun foo()
}
@@ -0,0 +1,8 @@
// "Remove redundant 'open' modifier" "true"
abstract class B() {
abstract fun foo()
}
abstract class A() : B() {
<caret>open abstract override fun foo()
}
@@ -0,0 +1,8 @@
// "Remove redundant 'open' modifier" "true"
abstract class B() {
abstract fun foo()
}
abstract class A() : B() {
<caret>abstract open override fun foo()
}
@@ -0,0 +1,8 @@
// "Remove redundant 'open' modifier" "true"
abstract class B() {
abstract fun foo()
}
abstract class A() : B() {
<caret>abstract override open fun foo()
}
@@ -0,0 +1,26 @@
package org.jetbrains.jet.plugin.quickfix;
import com.intellij.codeInsight.daemon.quickFix.LightQuickFixTestCase;
import org.jetbrains.jet.JetTestCaseBase;
/**
* @author svtk
*/
public class ModifiersTest extends LightQuickFixTestCase {
public void test() throws Exception {
doAllTests();
}
@Override
protected String getBasePath() {
return "/quickfix/modifiers";
}
@Override
protected String getTestDataPath() {
return JetTestCaseBase.getTestDataPathBase();
}
}