diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/DiagnosticWithPsiElement.java b/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/DiagnosticWithPsiElement.java index 3c046115de9..034ef8de822 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/DiagnosticWithPsiElement.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/DiagnosticWithPsiElement.java @@ -1,6 +1,8 @@ package org.jetbrains.jet.lang.diagnostics; +import com.intellij.openapi.util.TextRange; import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; import org.jetbrains.annotations.NotNull; /** @@ -10,7 +12,11 @@ public class DiagnosticWithPsiElement extends GenericDiagn private final T psiElement; public DiagnosticWithPsiElement(DiagnosticFactory factory, Severity severity, String message, T psiElement) { - super(factory, severity, message, psiElement.getContainingFile(), psiElement.getTextRange()); + this(factory, severity, message, psiElement, psiElement.getTextRange()); + } + + public DiagnosticWithPsiElement(DiagnosticFactory factory, Severity severity, String message, T psiElement, @NotNull TextRange textRange) { + super(factory, severity, message, psiElement.getContainingFile(), textRange); this.psiElement = psiElement; } 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 e61b3fe4fd2..3d3dfdbae9b 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/Errors.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/Errors.java @@ -53,7 +53,7 @@ public interface Errors { 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"); - SimpleDiagnosticFactory REDUNDANT_ABSTRACT = SimpleDiagnosticFactory.create(WARNING, "Abstract modifier is redundant in traits"); + SimplePsiElementOnlyDiagnosticFactory REDUNDANT_ABSTRACT = SimplePsiElementOnlyDiagnosticFactory.create(WARNING, "Abstract modifier is redundant in traits"); ParameterizedDiagnosticFactory2 ABSTRACT_PROPERTY_IN_NON_ABSTRACT_CLASS = ParameterizedDiagnosticFactory2.create(ERROR, "Abstract property {0} in non-abstract class {1}"); ParameterizedDiagnosticFactory2 ABSTRACT_FUNCTION_IN_NON_ABSTRACT_CLASS = ParameterizedDiagnosticFactory2.create(ERROR, "Abstract function {0} in non-abstract class {1}"); ParameterizedDiagnosticFactory1 ABSTRACT_FUNCTION_WITH_BODY = ParameterizedDiagnosticFactory1.create(ERROR, "A function {0} with body cannot be abstract"); diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/PsiElementOnlyDiagnosticFactory.java b/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/PsiElementOnlyDiagnosticFactory.java new file mode 100644 index 00000000000..6cf335b2a35 --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/PsiElementOnlyDiagnosticFactory.java @@ -0,0 +1,9 @@ +package org.jetbrains.jet.lang.diagnostics; + +import com.intellij.psi.PsiElement; + +/** + * @author svtk + */ +public interface PsiElementOnlyDiagnosticFactory extends DiagnosticFactory { +} diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/SimplePsiElementOnlyDiagnosticFactory.java b/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/SimplePsiElementOnlyDiagnosticFactory.java new file mode 100644 index 00000000000..63ff839fe0c --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/SimplePsiElementOnlyDiagnosticFactory.java @@ -0,0 +1,34 @@ +package org.jetbrains.jet.lang.diagnostics; + +import com.intellij.lang.ASTNode; +import com.intellij.openapi.util.TextRange; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import org.jetbrains.annotations.NotNull; + +/** + * @author svtk + */ +public class SimplePsiElementOnlyDiagnosticFactory extends DiagnosticFactoryWithSeverity implements PsiElementOnlyDiagnosticFactory { + + public static SimplePsiElementOnlyDiagnosticFactory create(Severity severity, String message) { + return new SimplePsiElementOnlyDiagnosticFactory(severity, message); + } + + protected final String message; + + public SimplePsiElementOnlyDiagnosticFactory(Severity severity, String message) { + super(severity); + this.message = message; + } + + @NotNull + public Diagnostic on(@NotNull T element, @NotNull ASTNode node) { + return new DiagnosticWithPsiElement(this, severity, message, element, node.getTextRange()); + } + + @NotNull + public Diagnostic on(@NotNull T element) { + return on(element, element.getNode()); + } +} diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/BodyResolver.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/BodyResolver.java index 900e06fffef..8e5ce72a814 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/BodyResolver.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/BodyResolver.java @@ -621,7 +621,7 @@ public class BodyResolver { } if (classDescriptor.getKind() == ClassKind.TRAIT) { // context.getTrace().getErrorHandler().genericWarning(abstractNode, "Abstract modifier is redundant in traits"); - context.getTrace().report(REDUNDANT_ABSTRACT.on(abstractNode)); + context.getTrace().report(REDUNDANT_ABSTRACT.on(property, abstractNode)); } } @@ -799,7 +799,7 @@ public class BodyResolver { } if (hasAbstractModifier && inTrait && !isPropertyAccessor) { // context.getTrace().getErrorHandler().genericWarning(abstractNode, "Abstract modifier is redundant in trait"); - context.getTrace().report(REDUNDANT_ABSTRACT.on(abstractNode)); + context.getTrace().report(REDUNDANT_ABSTRACT.on((JetDeclaration)function, abstractNode)); //TODO } if (function.getBodyExpression() != null && hasAbstractModifier) { // context.getTrace().getErrorHandler().genericError(abstractNode, "Method " + methodName + "with body cannot be abstract"); diff --git a/idea/src/org/jetbrains/jet/plugin/annotations/JetPsiChecker.java b/idea/src/org/jetbrains/jet/plugin/annotations/JetPsiChecker.java index 175370d4059..6cce61fbae0 100644 --- a/idea/src/org/jetbrains/jet/plugin/annotations/JetPsiChecker.java +++ b/idea/src/org/jetbrains/jet/plugin/annotations/JetPsiChecker.java @@ -1,6 +1,8 @@ package org.jetbrains.jet.plugin.annotations; +import com.intellij.codeInsight.intention.IntentionAction; import com.intellij.codeInspection.ProblemHighlightType; +import com.intellij.lang.annotation.Annotation; import com.intellij.lang.annotation.AnnotationHolder; import com.intellij.lang.annotation.Annotator; import com.intellij.openapi.progress.ProcessCanceledException; @@ -18,6 +20,7 @@ import org.jetbrains.jet.lang.resolve.BindingContext; import org.jetbrains.jet.lang.types.JetType; import org.jetbrains.jet.plugin.AnalyzerFacade; import org.jetbrains.jet.plugin.JetHighlighter; +import org.jetbrains.jet.plugin.quickfix.QuickFixes; import java.util.Collection; import java.util.HashSet; @@ -49,6 +52,7 @@ public class JetPsiChecker implements Annotator { Collection diagnostics = bindingContext.getDiagnostics(); Set redeclarations = new HashSet(); for (Diagnostic diagnostic : diagnostics) { + Annotation annotation = null; if (diagnostic.getSeverity() == Severity.ERROR) { if (diagnostic instanceof UnresolvedReferenceDiagnostic) { UnresolvedReferenceDiagnostic unresolvedReferenceDiagnostic = (UnresolvedReferenceDiagnostic) diagnostic; @@ -70,11 +74,25 @@ public class JetPsiChecker implements Annotator { markRedeclaration(redeclarations, redeclarationDiagnostic.getB(), bindingContext, holder); } else { - holder.createErrorAnnotation(diagnostic.getFactory().getTextRange(diagnostic), diagnostic.getMessage()); + annotation = holder.createErrorAnnotation(diagnostic.getFactory().getTextRange(diagnostic), diagnostic.getMessage()); } } else if (diagnostic.getSeverity() == Severity.WARNING) { - holder.createWarningAnnotation(diagnostic.getFactory().getTextRange(diagnostic), diagnostic.getMessage()); + annotation = holder.createWarningAnnotation(diagnostic.getFactory().getTextRange(diagnostic), diagnostic.getMessage()); + } + if (annotation != null && diagnostic instanceof DiagnosticWithPsiElement) { + DiagnosticWithPsiElement diagnosticWithPsiElement = (DiagnosticWithPsiElement) diagnostic; + if (diagnostic.getFactory() instanceof PsiElementOnlyDiagnosticFactory) { + PsiElementOnlyDiagnosticFactory factory = (PsiElementOnlyDiagnosticFactory) diagnostic.getFactory(); + QuickFixes.IntentionActionFactory intentionActionFactory = QuickFixes.get(factory); + IntentionAction action = null; + if (intentionActionFactory != null) { + action = intentionActionFactory.createAction(diagnosticWithPsiElement); + } + if (action != null) { + annotation.registerFix(action); + } + } } } } diff --git a/idea/src/org/jetbrains/jet/plugin/quickfix/QuickFixes.java b/idea/src/org/jetbrains/jet/plugin/quickfix/QuickFixes.java new file mode 100644 index 00000000000..a0541b40ba1 --- /dev/null +++ b/idea/src/org/jetbrains/jet/plugin/quickfix/QuickFixes.java @@ -0,0 +1,47 @@ +package org.jetbrains.jet.plugin.quickfix; + +import com.google.common.collect.Maps; +import com.intellij.codeInsight.intention.IntentionAction; +import com.intellij.psi.PsiElement; +import org.jetbrains.jet.lang.diagnostics.Diagnostic; +import org.jetbrains.jet.lang.diagnostics.DiagnosticFactory; +import org.jetbrains.jet.lang.diagnostics.DiagnosticWithPsiElement; +import org.jetbrains.jet.lang.diagnostics.Errors; +import org.jetbrains.jet.lang.diagnostics.PsiElementOnlyDiagnosticFactory; + +import java.util.Map; + +/** +* @author svtk +*/ +public class QuickFixes { + private static Map actionMap = Maps.newHashMap(); + + public static IntentionActionFactory get(PsiElementOnlyDiagnosticFactory f) { + return actionMap.get(f); + } + + private QuickFixes() {} + + public static abstract class IntentionActionForPsiElement implements IntentionAction { + protected T psiElement; + + public IntentionActionForPsiElement(T element) { + this.psiElement = element; + } + } + + public interface IntentionActionFactory { + IntentionActionForPsiElement createAction(DiagnosticWithPsiElement diagnostic); + } + + private static void add(PsiElementOnlyDiagnosticFactory diagnosticFactory, IntentionActionFactory actionFactory) { + actionMap.put(diagnosticFactory, actionFactory); + } + + + static { + add(Errors.REDUNDANT_ABSTRACT, RemoveAbstractModifierFix.factory); + } +} + diff --git a/idea/src/org/jetbrains/jet/plugin/quickfix/RemoveAbstractModifierFix.java b/idea/src/org/jetbrains/jet/plugin/quickfix/RemoveAbstractModifierFix.java new file mode 100644 index 00000000000..d86be2b1baf --- /dev/null +++ b/idea/src/org/jetbrains/jet/plugin/quickfix/RemoveAbstractModifierFix.java @@ -0,0 +1,67 @@ +package org.jetbrains.jet.plugin.quickfix; + +import com.intellij.lang.ASTNode; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.project.Project; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import com.intellij.util.IncorrectOperationException; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.lang.diagnostics.DiagnosticWithPsiElement; +import org.jetbrains.jet.lang.psi.JetDeclaration; +import org.jetbrains.jet.lexer.JetTokens; + +/** +* @author svtk +*/ +public class RemoveAbstractModifierFix extends QuickFixes.IntentionActionForPsiElement { + public RemoveAbstractModifierFix(JetDeclaration declaration) { + super(declaration); + } + + @NotNull + @Override + public String getText() { + return "remove.abstract.modifier.fix"; + } + + @NotNull + @Override + public String getFamilyName() { + return "remove.abstract.modifier.family"; + } + + @Override + public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) { + return psiElement.isValid(); + } + + @Override + public void invoke(@NotNull Project project, Editor editor, PsiFile file) throws IncorrectOperationException { + JetDeclaration declaration = removeAbstractModifier(psiElement); + psiElement.replace(declaration); + } + + public static JetDeclaration removeAbstractModifier(PsiElement element) { + assert element instanceof JetDeclaration; + JetDeclaration declaration = (JetDeclaration) (element.copy()); + assert declaration.hasModifier(JetTokens.ABSTRACT_KEYWORD); + ASTNode abstractNode = declaration.getModifierList().getModifierNode(JetTokens.ABSTRACT_KEYWORD); + declaration.deleteChildInternal(abstractNode); + return declaration; + } + + @Override + public boolean startInWriteAction() { + return true; + } + + public static QuickFixes.IntentionActionFactory factory = + new QuickFixes.IntentionActionFactory() { + @Override + public QuickFixes.IntentionActionForPsiElement createAction(DiagnosticWithPsiElement diagnostic) { + assert diagnostic.getPsiElement() instanceof JetDeclaration; + return new RemoveAbstractModifierFix((JetDeclaration) diagnostic.getPsiElement()); + } + }; +}