From 4ac2a46fb44b1429d51012d43418b150e1dbe570 Mon Sep 17 00:00:00 2001 From: svtk Date: Fri, 23 Sep 2011 13:03:14 +0400 Subject: [PATCH] Added 'incompatible modifiers' error; 'remove redundant modifier' fix --- .../jet/lang/diagnostics/Errors.java | 23 ++++++++ .../jet/plugin/annotations/JetPsiChecker.java | 2 +- .../jet/plugin/quickfix/QuickFixes.java | 8 ++- .../plugin/quickfix/RemoveModifierFix.java | 47 +++++++++------- .../quickfix/RemoveRedundantModifierFix.java | 53 +++++++++++++++++++ .../afterRemoveRedundantModifier1.kt | 8 +++ .../afterRemoveRedundantModifier2.kt | 8 +++ .../afterRemoveRedundantModifier3.kt | 8 +++ .../beforeRemoveRedundantModifier1.kt | 8 +++ .../beforeRemoveRedundantModifier2.kt | 8 +++ .../beforeRemoveRedundantModifier3.kt | 8 +++ .../jet/plugin/quickfix/ModifiersTest.java | 26 +++++++++ 12 files changed, 186 insertions(+), 21 deletions(-) create mode 100644 idea/src/org/jetbrains/jet/plugin/quickfix/RemoveRedundantModifierFix.java create mode 100644 idea/testData/quickfix/modifiers/afterRemoveRedundantModifier1.kt create mode 100644 idea/testData/quickfix/modifiers/afterRemoveRedundantModifier2.kt create mode 100644 idea/testData/quickfix/modifiers/afterRemoveRedundantModifier3.kt create mode 100644 idea/testData/quickfix/modifiers/beforeRemoveRedundantModifier1.kt create mode 100644 idea/testData/quickfix/modifiers/beforeRemoveRedundantModifier2.kt create mode 100644 idea/testData/quickfix/modifiers/beforeRemoveRedundantModifier3.kt create mode 100644 idea/tests/org/jetbrains/jet/plugin/quickfix/ModifiersTest.java diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/Errors.java b/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/Errors.java index 73cd701c5f7..0042d1c3c74 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/Errors.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/Errors.java @@ -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 TYPE_MISMATCH = PsiElementOnlyDiagnosticFactory2.create(ERROR, "Type mismatch: inferred type is {1} but {0} was expected"); + ParameterizedDiagnosticFactory1> INCOMPATIBLE_MODIFIERS = new ParameterizedDiagnosticFactory1>(ERROR, "Incompatible modifiers: {0}") { + @Override + protected String makeMessageFor(Collection argument) { + StringBuilder sb = new StringBuilder(); + for (Iterator iterator = argument.iterator(); iterator.hasNext(); ) { + JetKeywordToken modifier = iterator.next(); + sb.append(modifier.getValue()); + if (iterator.hasNext()) { + sb.append(" "); + } + } + return sb.toString(); + } + }; + PsiElementOnlyDiagnosticFactory2 REDUNDANT_MODIFIER = new PsiElementOnlyDiagnosticFactory2(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(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 diff --git a/idea/src/org/jetbrains/jet/plugin/annotations/JetPsiChecker.java b/idea/src/org/jetbrains/jet/plugin/annotations/JetPsiChecker.java index 8350ecd9543..54e7aceec3e 100644 --- a/idea/src/org/jetbrains/jet/plugin/annotations/JetPsiChecker.java +++ b/idea/src/org/jetbrains/jet/plugin/annotations/JetPsiChecker.java @@ -90,7 +90,7 @@ public class JetPsiChecker implements Annotator { DiagnosticWithPsiElement diagnosticWithPsiElement = (DiagnosticWithPsiElement) diagnostic; if (diagnostic.getFactory() instanceof PsiElementOnlyDiagnosticFactory) { PsiElementOnlyDiagnosticFactory factory = (PsiElementOnlyDiagnosticFactory) diagnostic.getFactory(); - Set intentionActionFactories = QuickFixes.get(factory); + Collection intentionActionFactories = QuickFixes.get(factory); for (IntentionActionFactory intentionActionFactory : intentionActionFactories) { IntentionAction action = null; if (intentionActionFactory != null) { diff --git a/idea/src/org/jetbrains/jet/plugin/quickfix/QuickFixes.java b/idea/src/org/jetbrains/jet/plugin/quickfix/QuickFixes.java index 4d5dfa51b46..79b4184183b 100644 --- a/idea/src/org/jetbrains/jet/plugin/quickfix/QuickFixes.java +++ b/idea/src/org/jetbrains/jet/plugin/quickfix/QuickFixes.java @@ -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 actionMap = HashMultimap.create(); + private static final Multimap actionMap = HashMultimap.create(); - public static Set get(PsiElementOnlyDiagnosticFactory diagnosticFactory) { + public static Collection 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()); } } diff --git a/idea/src/org/jetbrains/jet/plugin/quickfix/RemoveModifierFix.java b/idea/src/org/jetbrains/jet/plugin/quickfix/RemoveModifierFix.java index c1e9edfb1e5..99fd068f5d3 100644 --- a/idea/src/org/jetbrains/jet/plugin/quickfix/RemoveModifierFix.java +++ b/idea/src/org/jetbrains/jet/plugin/quickfix/RemoveModifierFix.java @@ -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 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 createFactory(final JetKeywordToken modifier) { diff --git a/idea/src/org/jetbrains/jet/plugin/quickfix/RemoveRedundantModifierFix.java b/idea/src/org/jetbrains/jet/plugin/quickfix/RemoveRedundantModifierFix.java new file mode 100644 index 00000000000..348c0a7886a --- /dev/null +++ b/idea/src/org/jetbrains/jet/plugin/quickfix/RemoveRedundantModifierFix.java @@ -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 { + 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 createFactory() { + return new IntentionActionFactory() { + @Override + public IntentionActionForPsiElement 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); + } + }; + } +} diff --git a/idea/testData/quickfix/modifiers/afterRemoveRedundantModifier1.kt b/idea/testData/quickfix/modifiers/afterRemoveRedundantModifier1.kt new file mode 100644 index 00000000000..38c63050707 --- /dev/null +++ b/idea/testData/quickfix/modifiers/afterRemoveRedundantModifier1.kt @@ -0,0 +1,8 @@ +// "Remove redundant 'open' modifier" "true" +abstract class B() { + abstract fun foo() +} + +abstract class A() : B() { + abstract override fun foo() +} diff --git a/idea/testData/quickfix/modifiers/afterRemoveRedundantModifier2.kt b/idea/testData/quickfix/modifiers/afterRemoveRedundantModifier2.kt new file mode 100644 index 00000000000..38c63050707 --- /dev/null +++ b/idea/testData/quickfix/modifiers/afterRemoveRedundantModifier2.kt @@ -0,0 +1,8 @@ +// "Remove redundant 'open' modifier" "true" +abstract class B() { + abstract fun foo() +} + +abstract class A() : B() { + abstract override fun foo() +} diff --git a/idea/testData/quickfix/modifiers/afterRemoveRedundantModifier3.kt b/idea/testData/quickfix/modifiers/afterRemoveRedundantModifier3.kt new file mode 100644 index 00000000000..38c63050707 --- /dev/null +++ b/idea/testData/quickfix/modifiers/afterRemoveRedundantModifier3.kt @@ -0,0 +1,8 @@ +// "Remove redundant 'open' modifier" "true" +abstract class B() { + abstract fun foo() +} + +abstract class A() : B() { + abstract override fun foo() +} diff --git a/idea/testData/quickfix/modifiers/beforeRemoveRedundantModifier1.kt b/idea/testData/quickfix/modifiers/beforeRemoveRedundantModifier1.kt new file mode 100644 index 00000000000..acec3eb7ca5 --- /dev/null +++ b/idea/testData/quickfix/modifiers/beforeRemoveRedundantModifier1.kt @@ -0,0 +1,8 @@ +// "Remove redundant 'open' modifier" "true" +abstract class B() { + abstract fun foo() +} + +abstract class A() : B() { + open abstract override fun foo() +} diff --git a/idea/testData/quickfix/modifiers/beforeRemoveRedundantModifier2.kt b/idea/testData/quickfix/modifiers/beforeRemoveRedundantModifier2.kt new file mode 100644 index 00000000000..4d4d6440fb0 --- /dev/null +++ b/idea/testData/quickfix/modifiers/beforeRemoveRedundantModifier2.kt @@ -0,0 +1,8 @@ +// "Remove redundant 'open' modifier" "true" +abstract class B() { + abstract fun foo() +} + +abstract class A() : B() { + abstract open override fun foo() +} diff --git a/idea/testData/quickfix/modifiers/beforeRemoveRedundantModifier3.kt b/idea/testData/quickfix/modifiers/beforeRemoveRedundantModifier3.kt new file mode 100644 index 00000000000..53a92e31819 --- /dev/null +++ b/idea/testData/quickfix/modifiers/beforeRemoveRedundantModifier3.kt @@ -0,0 +1,8 @@ +// "Remove redundant 'open' modifier" "true" +abstract class B() { + abstract fun foo() +} + +abstract class A() : B() { + abstract override open fun foo() +} diff --git a/idea/tests/org/jetbrains/jet/plugin/quickfix/ModifiersTest.java b/idea/tests/org/jetbrains/jet/plugin/quickfix/ModifiersTest.java new file mode 100644 index 00000000000..e1199ce2a37 --- /dev/null +++ b/idea/tests/org/jetbrains/jet/plugin/quickfix/ModifiersTest.java @@ -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(); + } +} + +