Add quickfixes for unnecessary casts and nullability operations to cleanup inspection. Rewrite two quickfixes from Java to Kotlin. Add inspection description. Use marker interface for identifying quickfixes applicable for code cleanup.
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
<html>
|
||||
<body>
|
||||
This inspection is used during the code cleanup operation (Analyze | Code Cleanup) to automatically
|
||||
replace usages of obsolete language features or unnecessarily verbose code constructs with
|
||||
compact and up-to-date syntax.
|
||||
</body>
|
||||
</html>
|
||||
@@ -1009,7 +1009,7 @@
|
||||
|
||||
<localInspection implementationClass="org.jetbrains.kotlin.idea.inspections.KotlinCleanupInspection"
|
||||
shortName="KotlinDeprecation"
|
||||
displayName="Deprecated language feature"
|
||||
displayName="Usage of redundant or deprecated syntax"
|
||||
groupName="Kotlin"
|
||||
enabledByDefault="true"
|
||||
cleanupTool="true"
|
||||
|
||||
@@ -27,6 +27,7 @@ import org.jetbrains.kotlin.diagnostics.Errors
|
||||
import org.jetbrains.kotlin.diagnostics.rendering.DefaultErrorMessages
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.analyzeFullyAndGetResult
|
||||
import org.jetbrains.kotlin.idea.highlighter.JetPsiChecker
|
||||
import org.jetbrains.kotlin.idea.quickfix.CleanupFix
|
||||
import org.jetbrains.kotlin.idea.quickfix.JetWholeProjectModalAction
|
||||
import org.jetbrains.kotlin.idea.quickfix.ReplaceObsoleteLabelSyntaxFix
|
||||
import org.jetbrains.kotlin.idea.quickfix.looksLikeObsoleteLabel
|
||||
@@ -39,7 +40,7 @@ import org.jetbrains.kotlin.resolve.jvm.diagnostics.ErrorsJvm
|
||||
|
||||
public class KotlinCleanupInspection(): LocalInspectionTool(), CleanupLocalInspectionTool {
|
||||
// required to simplify the inspection registration in tests
|
||||
override fun getDisplayName(): String = "Deprecated language feature"
|
||||
override fun getDisplayName(): String = "Usage of redundant or deprecated syntax"
|
||||
|
||||
override fun checkFile(file: PsiFile, manager: InspectionManager, isOnTheFly: Boolean): Array<out ProblemDescriptor>? {
|
||||
if (isOnTheFly || file !is JetFile || !ProjectRootsUtil.isInProjectSource(file)) {
|
||||
@@ -55,29 +56,32 @@ public class KotlinCleanupInspection(): LocalInspectionTool(), CleanupLocalInspe
|
||||
file.acceptChildren(object: JetTreeVisitorVoid() {
|
||||
override fun visitElement(element: PsiElement) {
|
||||
super.visitElement(element)
|
||||
val collection = diagnostics.forElement(element)
|
||||
collection.forEach {
|
||||
if (it.isCleanup()) {
|
||||
problemDescriptors.add(it.toProblemDescriptor(file, manager))
|
||||
}
|
||||
}
|
||||
diagnostics.forElement(element)
|
||||
.filter { it.isCleanup() }
|
||||
.map { it.toProblemDescriptor(file, manager) }
|
||||
.filterNotNullTo(problemDescriptors)
|
||||
}
|
||||
})
|
||||
return problemDescriptors.toTypedArray()
|
||||
}
|
||||
|
||||
private fun Diagnostic.isCleanup() = getFactory().isCleanup() || isObsoleteLabel()
|
||||
private fun Diagnostic.isCleanup() = getFactory() in cleanupDiagnosticsFactories || isObsoleteLabel()
|
||||
|
||||
private fun DiagnosticFactory<*>.isCleanup() =
|
||||
this == Errors.DEPRECATED_TRAIT_KEYWORD ||
|
||||
this == Errors.DEPRECATED_ANNOTATION_SYNTAX ||
|
||||
this == Errors.ENUM_ENTRY_USES_DEPRECATED_OR_NO_DELIMITER ||
|
||||
this == Errors.ENUM_ENTRY_USES_DEPRECATED_SUPER_CONSTRUCTOR ||
|
||||
this == Errors.DEPRECATED_LAMBDA_SYNTAX ||
|
||||
this == Errors.MISSING_CONSTRUCTOR_KEYWORD ||
|
||||
this == Errors.FUNCTION_EXPRESSION_WITH_NAME ||
|
||||
this == Errors.JAVA_LANG_CLASS_PARAMETER_IN_ANNOTATION ||
|
||||
this == ErrorsJvm.JAVA_LANG_CLASS_ARGUMENT_IN_ANNOTATION
|
||||
private val cleanupDiagnosticsFactories = setOf(
|
||||
Errors.DEPRECATED_TRAIT_KEYWORD,
|
||||
Errors.DEPRECATED_ANNOTATION_SYNTAX,
|
||||
Errors.ENUM_ENTRY_USES_DEPRECATED_OR_NO_DELIMITER,
|
||||
Errors.ENUM_ENTRY_USES_DEPRECATED_SUPER_CONSTRUCTOR,
|
||||
Errors.DEPRECATED_LAMBDA_SYNTAX,
|
||||
Errors.MISSING_CONSTRUCTOR_KEYWORD,
|
||||
Errors.FUNCTION_EXPRESSION_WITH_NAME,
|
||||
Errors.JAVA_LANG_CLASS_PARAMETER_IN_ANNOTATION,
|
||||
ErrorsJvm.JAVA_LANG_CLASS_ARGUMENT_IN_ANNOTATION,
|
||||
Errors.UNNECESSARY_NOT_NULL_ASSERTION,
|
||||
Errors.UNNECESSARY_SAFE_CALL,
|
||||
Errors.USELESS_CAST,
|
||||
Errors.USELESS_ELVIS
|
||||
)
|
||||
|
||||
private fun Diagnostic.isObsoleteLabel(): Boolean {
|
||||
val annotationEntry = getPsiElement().getNonStrictParentOfType<JetAnnotationEntry>() ?: return false
|
||||
@@ -86,20 +90,15 @@ public class KotlinCleanupInspection(): LocalInspectionTool(), CleanupLocalInspe
|
||||
|
||||
private fun Diagnostic.toProblemDescriptor(file: JetFile, manager: InspectionManager): ProblemDescriptor? {
|
||||
val quickFixes = JetPsiChecker.createQuickfixes(this)
|
||||
.filter { it.isCleanupFix(this) }
|
||||
.filter { it is CleanupFix }
|
||||
.map { IntentionWrapper(it, file) }
|
||||
|
||||
return manager.createProblemDescriptor(getPsiElement(),
|
||||
if (quickFixes.isEmpty()) return null
|
||||
|
||||
return manager.createProblemDescriptor(getPsiElement(),
|
||||
DefaultErrorMessages.render(this),
|
||||
false,
|
||||
quickFixes.toTypedArray(),
|
||||
ProblemHighlightType.GENERIC_ERROR_OR_WARNING)
|
||||
}
|
||||
|
||||
private fun IntentionAction.isCleanupFix(diagnostic: Diagnostic): Boolean {
|
||||
if (diagnostic.getFactory() == Errors.UNRESOLVED_REFERENCE) {
|
||||
return this is ReplaceObsoleteLabelSyntaxFix
|
||||
}
|
||||
return this !is JetWholeProjectModalAction<*>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.quickfix
|
||||
|
||||
/**
|
||||
* Marker interface for quickfixes that can be used as part of the "Cleanup Code" action. The diagnostics
|
||||
* that produce these quickfixes need to be added to KotlinCleanupInspection.cleanupDiagnosticsFactories.
|
||||
*/
|
||||
public interface CleanupFix
|
||||
// TODO(yole): add isSafeToApply() method here to get rid of filtering by diagnostics factories in
|
||||
// KotlinCleanupInspection
|
||||
@@ -30,7 +30,7 @@ import org.jetbrains.kotlin.psi.JetFile
|
||||
import org.jetbrains.kotlin.psi.JetPrimaryConstructor
|
||||
import org.jetbrains.kotlin.psi.JetPsiFactory
|
||||
|
||||
public class DeprecatedAnnotationSyntaxFix(element: JetAnnotation) : JetIntentionAction<JetAnnotation>(element) {
|
||||
public class DeprecatedAnnotationSyntaxFix(element: JetAnnotation) : JetIntentionAction<JetAnnotation>(element), CleanupFix {
|
||||
override fun getFamilyName(): String = "Replace with '@' annotations"
|
||||
override fun getText(): String = "Replace with '@' annotations"
|
||||
|
||||
|
||||
+2
-2
@@ -32,7 +32,7 @@ import org.jetbrains.kotlin.psi.psiUtil.getNextSiblingIgnoringWhitespaceAndComme
|
||||
import org.jetbrains.kotlin.psi.stubs.elements.JetStubElementTypes
|
||||
import org.jetbrains.kotlin.resolve.DeclarationsChecker
|
||||
|
||||
class DeprecatedEnumEntryDelimiterSyntaxFix(element: JetEnumEntry): JetIntentionAction<JetEnumEntry>(element) {
|
||||
class DeprecatedEnumEntryDelimiterSyntaxFix(element: JetEnumEntry): JetIntentionAction<JetEnumEntry>(element), CleanupFix {
|
||||
|
||||
override fun getFamilyName(): String = getText()
|
||||
|
||||
@@ -41,7 +41,7 @@ class DeprecatedEnumEntryDelimiterSyntaxFix(element: JetEnumEntry): JetIntention
|
||||
override fun invoke(project: Project, editor: Editor?, file: JetFile?) = insertLackingCommaSemicolon(element)
|
||||
|
||||
override fun isAvailable(project: Project, editor: Editor?, file: PsiFile): Boolean
|
||||
= super.isAvailable(project, editor, file) && DeclarationsChecker.enumEntryUsesDeprecatedOrNoDelimiter(element)
|
||||
= super<JetIntentionAction>.isAvailable(project, editor, file) && DeclarationsChecker.enumEntryUsesDeprecatedOrNoDelimiter(element)
|
||||
|
||||
companion object : JetSingleIntentionActionFactory() {
|
||||
override fun createAction(diagnostic: Diagnostic): IntentionAction? =
|
||||
|
||||
+2
-2
@@ -36,7 +36,7 @@ import org.jetbrains.kotlin.psi.psiUtil.getNextSiblingIgnoringWhitespaceAndComme
|
||||
import org.jetbrains.kotlin.psi.stubs.elements.JetStubElementTypes
|
||||
import org.jetbrains.kotlin.resolve.DeclarationsChecker
|
||||
|
||||
class DeprecatedEnumEntrySuperConstructorSyntaxFix(element: JetEnumEntry): JetIntentionAction<JetEnumEntry>(element) {
|
||||
class DeprecatedEnumEntrySuperConstructorSyntaxFix(element: JetEnumEntry): JetIntentionAction<JetEnumEntry>(element), CleanupFix {
|
||||
override fun getFamilyName(): String = getText()
|
||||
|
||||
override fun getText(): String = "Change to short enum entry super constructor"
|
||||
@@ -44,7 +44,7 @@ class DeprecatedEnumEntrySuperConstructorSyntaxFix(element: JetEnumEntry): JetIn
|
||||
override fun invoke(project: Project, editor: Editor?, file: JetFile?) = changeConstructorToShort(element)
|
||||
|
||||
override fun isAvailable(project: Project, editor: Editor?, file: PsiFile): Boolean
|
||||
= super.isAvailable(project, editor, file) && DeclarationsChecker.enumEntryUsesDeprecatedSuperConstructor(element)
|
||||
= super<JetIntentionAction>.isAvailable(project, editor, file) && DeclarationsChecker.enumEntryUsesDeprecatedSuperConstructor(element)
|
||||
|
||||
companion object: JetSingleIntentionActionFactory() {
|
||||
override fun createAction(diagnostic: Diagnostic): IntentionAction? =
|
||||
|
||||
@@ -35,7 +35,7 @@ import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.utils.sure
|
||||
import java.util.ArrayList
|
||||
|
||||
public class DeprecatedLambdaSyntaxFix(element: JetFunctionLiteralExpression) : JetIntentionAction<JetFunctionLiteralExpression>(element) {
|
||||
public class DeprecatedLambdaSyntaxFix(element: JetFunctionLiteralExpression) : JetIntentionAction<JetFunctionLiteralExpression>(element), CleanupFix {
|
||||
override fun getText() = JetBundle.message("migrate.lambda.syntax")
|
||||
override fun getFamilyName() = JetBundle.message("migrate.lambda.syntax.family")
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ import org.jetbrains.kotlin.psi.JetClass
|
||||
import org.jetbrains.kotlin.psi.JetFile
|
||||
import org.jetbrains.kotlin.psi.JetPsiFactory
|
||||
|
||||
public class DeprecatedTraitSyntaxFix(element: PsiElement): JetIntentionAction<PsiElement>(element) {
|
||||
public class DeprecatedTraitSyntaxFix(element: PsiElement): JetIntentionAction<PsiElement>(element), CleanupFix {
|
||||
override fun getFamilyName() = "Replace with 'interface'"
|
||||
override fun getText() = getFamilyName()
|
||||
|
||||
|
||||
@@ -1,179 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.quickfix;
|
||||
|
||||
import com.intellij.codeInsight.FileModificationService;
|
||||
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 com.intellij.psi.impl.source.tree.LeafPsiElement;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.kotlin.idea.JetBundle;
|
||||
import org.jetbrains.kotlin.lexer.JetTokens;
|
||||
import org.jetbrains.kotlin.psi.JetExpression;
|
||||
import org.jetbrains.kotlin.psi.JetFile;
|
||||
import org.jetbrains.kotlin.psi.JetPostfixExpression;
|
||||
import org.jetbrains.kotlin.psi.JetPsiFactory;
|
||||
|
||||
import static org.jetbrains.kotlin.psi.PsiPackage.JetPsiFactory;
|
||||
|
||||
@SuppressWarnings("IntentionDescriptionNotFoundInspection")
|
||||
public class ExclExclCallFix implements IntentionAction {
|
||||
|
||||
private final boolean isRemove;
|
||||
|
||||
private ExclExclCallFix(boolean remove) {
|
||||
isRemove = remove;
|
||||
}
|
||||
|
||||
public static ExclExclCallFix removeExclExclCall() {
|
||||
return new ExclExclCallFix(true);
|
||||
}
|
||||
|
||||
public static ExclExclCallFix introduceExclExclCall() {
|
||||
return new ExclExclCallFix(false);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getText() {
|
||||
return isRemove ? JetBundle.message("remove.unnecessary.non.null.assertion") : JetBundle.message("introduce.non.null.assertion");
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getFamilyName() {
|
||||
return getText();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) {
|
||||
if (file instanceof JetFile) {
|
||||
if (!isRemove) {
|
||||
return isAvailableForIntroduce(editor, file);
|
||||
}
|
||||
else {
|
||||
return isAvailableForRemove(editor, file);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invoke(@NotNull Project project, Editor editor, PsiFile file) throws IncorrectOperationException {
|
||||
if (!FileModificationService.getInstance().prepareFileForWrite(file)) {
|
||||
return;
|
||||
}
|
||||
|
||||
JetPsiFactory psiFactory = JetPsiFactory(project);
|
||||
if (!isRemove) {
|
||||
JetExpression modifiedExpression = getExpressionForIntroduceCall(editor, file);
|
||||
JetExpression exclExclExpression = psiFactory.createExpression(modifiedExpression.getText() + "!!");
|
||||
modifiedExpression.replace(exclExclExpression);
|
||||
}
|
||||
else {
|
||||
JetPostfixExpression postfixExpression = getExclExclPostfixExpression(editor, file);
|
||||
JetExpression expression = psiFactory.createExpression(postfixExpression.getBaseExpression().getText());
|
||||
postfixExpression.replace(expression);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean startInWriteAction() {
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean isAvailableForIntroduce(Editor editor, PsiFile file) {
|
||||
return getExpressionForIntroduceCall(editor, file) != null;
|
||||
}
|
||||
|
||||
private static boolean isAvailableForRemove(Editor editor, PsiFile file) {
|
||||
return getExclExclPostfixExpression(editor, file) != null;
|
||||
}
|
||||
|
||||
private static PsiElement getExclExclElement(Editor editor, PsiFile file) {
|
||||
PsiElement elementAtCaret = file.findElementAt(editor.getCaretModel().getOffset());
|
||||
if (isExclExclLeaf(elementAtCaret)) {
|
||||
return elementAtCaret;
|
||||
}
|
||||
|
||||
if (elementAtCaret != null) {
|
||||
// Case when caret is placed right after !!
|
||||
PsiElement prevLeaf = PsiTreeUtil.prevLeaf(elementAtCaret);
|
||||
if (isExclExclLeaf(prevLeaf)) {
|
||||
return prevLeaf;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static boolean isExclExclLeaf(@Nullable PsiElement element) {
|
||||
return (element instanceof LeafPsiElement) && ((LeafPsiElement) element).getElementType() == JetTokens.EXCLEXCL;
|
||||
}
|
||||
|
||||
private static JetExpression getExpressionForIntroduceCall(Editor editor, PsiFile file) {
|
||||
PsiElement elementAtCaret = file.findElementAt(editor.getCaretModel().getOffset());
|
||||
if (elementAtCaret != null) {
|
||||
JetExpression expression = getExpressionForIntroduceCall(elementAtCaret);
|
||||
if (expression != null) {
|
||||
return expression;
|
||||
}
|
||||
|
||||
// Maybe caret is after error element
|
||||
expression = getExpressionForIntroduceCall(PsiTreeUtil.prevLeaf(elementAtCaret));
|
||||
if (expression != null) {
|
||||
return expression;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static JetExpression getExpressionForIntroduceCall(PsiElement problemElement) {
|
||||
if (problemElement instanceof LeafPsiElement && ((LeafPsiElement) problemElement).getElementType() == JetTokens.DOT) {
|
||||
PsiElement sibling = problemElement.getPrevSibling();
|
||||
if (sibling instanceof JetExpression) {
|
||||
return (JetExpression) sibling;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static JetPostfixExpression getExclExclPostfixExpression(Editor editor, PsiFile file) {
|
||||
PsiElement exclExclElement = getExclExclElement(editor, file);
|
||||
|
||||
if (exclExclElement != null) {
|
||||
PsiElement parent = exclExclElement.getParent();
|
||||
if (parent != null) {
|
||||
PsiElement operationParent = parent.getParent();
|
||||
if (operationParent instanceof JetPostfixExpression && ((JetPostfixExpression) operationParent).getBaseExpression() != null) {
|
||||
return (JetPostfixExpression) operationParent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.quickfix
|
||||
|
||||
import com.intellij.codeInsight.FileModificationService
|
||||
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 com.intellij.psi.impl.source.tree.LeafPsiElement
|
||||
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||
import org.jetbrains.kotlin.idea.JetBundle
|
||||
import org.jetbrains.kotlin.lexer.JetTokens
|
||||
import org.jetbrains.kotlin.psi.JetExpression
|
||||
import org.jetbrains.kotlin.psi.JetFile
|
||||
import org.jetbrains.kotlin.psi.JetPostfixExpression
|
||||
import org.jetbrains.kotlin.psi.JetPsiFactory
|
||||
|
||||
public abstract class ExclExclCallFix : IntentionAction {
|
||||
override fun getFamilyName(): String = getText()
|
||||
|
||||
override fun startInWriteAction(): Boolean = true
|
||||
|
||||
override fun isAvailable(project: Project, editor: Editor?, file: PsiFile) = file is JetFile
|
||||
}
|
||||
|
||||
public class RemoveExclExclCallFix(val psiElement: PsiElement) : ExclExclCallFix(), CleanupFix {
|
||||
override fun getText(): String = JetBundle.message("remove.unnecessary.non.null.assertion")
|
||||
|
||||
override fun isAvailable(project: Project, editor: Editor?, file: PsiFile): Boolean
|
||||
= super<ExclExclCallFix>.isAvailable(project, editor, file) &&
|
||||
getExclExclPostfixExpression() != null
|
||||
|
||||
override fun invoke(project: Project, editor: Editor?, file: PsiFile?) {
|
||||
if (!FileModificationService.getInstance().prepareFileForWrite(file)) return
|
||||
|
||||
val postfixExpression = getExclExclPostfixExpression() ?: return
|
||||
val expression = JetPsiFactory(project).createExpression(postfixExpression.getBaseExpression().getText())
|
||||
postfixExpression.replace(expression)
|
||||
}
|
||||
|
||||
private fun getExclExclPostfixExpression(): JetPostfixExpression? {
|
||||
val operationParent = psiElement.getParent()
|
||||
if (operationParent is JetPostfixExpression && operationParent.getBaseExpression() != null) {
|
||||
return operationParent
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
companion object : JetSingleIntentionActionFactory() {
|
||||
override fun createAction(diagnostic: Diagnostic): IntentionAction
|
||||
= RemoveExclExclCallFix(diagnostic.getPsiElement())
|
||||
}
|
||||
}
|
||||
|
||||
public class AddExclExclCallFix(val psiElement: PsiElement) : ExclExclCallFix() {
|
||||
override fun getText() = JetBundle.message("introduce.non.null.assertion")
|
||||
|
||||
override fun isAvailable(project: Project, editor: Editor?, file: PsiFile): Boolean
|
||||
= super.isAvailable(project, editor, file) &&
|
||||
getExpressionForIntroduceCall() != null
|
||||
|
||||
override fun invoke(project: Project, editor: Editor?, file: PsiFile?) {
|
||||
val modifiedExpression = getExpressionForIntroduceCall() ?: return
|
||||
val exclExclExpression = JetPsiFactory(project).createExpression(modifiedExpression.getText() + "!!")
|
||||
modifiedExpression.replace(exclExclExpression)
|
||||
}
|
||||
|
||||
protected fun getExpressionForIntroduceCall(): JetExpression? {
|
||||
if (psiElement is LeafPsiElement && psiElement.getElementType() == JetTokens.DOT) {
|
||||
val sibling = psiElement.getPrevSibling()
|
||||
if (sibling is JetExpression) {
|
||||
return sibling
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
companion object : JetSingleIntentionActionFactory() {
|
||||
override fun createAction(diagnostic: Diagnostic): IntentionAction
|
||||
= AddExclExclCallFix(diagnostic.getPsiElement())
|
||||
}
|
||||
}
|
||||
@@ -26,7 +26,7 @@ import org.jetbrains.kotlin.psi.JetFile
|
||||
import org.jetbrains.kotlin.psi.JetPrimaryConstructor
|
||||
import org.jetbrains.kotlin.idea.quickfix.quickfixUtil.addConstructorKeyword
|
||||
|
||||
public class MissingConstructorKeywordFix(element: JetPrimaryConstructor) : JetIntentionAction<JetPrimaryConstructor>(element) {
|
||||
public class MissingConstructorKeywordFix(element: JetPrimaryConstructor) : JetIntentionAction<JetPrimaryConstructor>(element), CleanupFix {
|
||||
override fun getFamilyName(): String = getText()
|
||||
override fun getText(): String = "Add 'constructor' keyword"
|
||||
|
||||
|
||||
@@ -173,11 +173,11 @@ public class QuickFixRegistrar {
|
||||
|
||||
QuickFixes.factories.put(UNUSED_VARIABLE, RemovePsiElementSimpleFix.createRemoveVariableFactory());
|
||||
|
||||
QuickFixes.actions.put(UNNECESSARY_SAFE_CALL, ReplaceCallFix.toDotCallFromSafeCall());
|
||||
QuickFixes.actions.put(UNSAFE_CALL, ReplaceCallFix.toSafeCall());
|
||||
QuickFixes.factories.put(UNNECESSARY_SAFE_CALL, ReplaceWithDotCallFix.Companion);
|
||||
QuickFixes.factories.put(UNSAFE_CALL, ReplaceWithSafeCallFix.Companion);
|
||||
|
||||
QuickFixes.actions.put(UNSAFE_CALL, ExclExclCallFix.introduceExclExclCall());
|
||||
QuickFixes.actions.put(UNNECESSARY_NOT_NULL_ASSERTION, ExclExclCallFix.removeExclExclCall());
|
||||
QuickFixes.factories.put(UNSAFE_CALL, AddExclExclCallFix.Companion);
|
||||
QuickFixes.factories.put(UNNECESSARY_NOT_NULL_ASSERTION, RemoveExclExclCallFix.Companion);
|
||||
QuickFixes.factories.put(UNSAFE_INFIX_CALL, ReplaceInfixCallFix.createFactory());
|
||||
|
||||
JetSingleIntentionActionFactory removeProtectedModifierFactory = RemoveModifierFix.createRemoveModifierFromListOwnerFactory(PROTECTED_KEYWORD);
|
||||
|
||||
@@ -30,7 +30,7 @@ import org.jetbrains.kotlin.psi.psiUtil.forEachDescendantsOfType
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getNextSiblingIgnoringWhitespaceAndComments
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
|
||||
public class RemoveNameFromFunctionExpressionFix(element: JetNamedFunction) : JetIntentionAction<JetNamedFunction>(element) {
|
||||
public class RemoveNameFromFunctionExpressionFix(element: JetNamedFunction) : JetIntentionAction<JetNamedFunction>(element), CleanupFix {
|
||||
override fun getText(): String = "Remove identifier from function expression"
|
||||
override fun getFamilyName(): String = getText()
|
||||
|
||||
|
||||
+1
-1
@@ -26,7 +26,7 @@ import org.jetbrains.kotlin.idea.JetBundle;
|
||||
import org.jetbrains.kotlin.idea.core.quickfix.QuickFixUtil;
|
||||
import org.jetbrains.kotlin.psi.*;
|
||||
|
||||
public class RemoveRightPartOfBinaryExpressionFix<T extends JetExpression> extends JetIntentionAction<T> {
|
||||
public class RemoveRightPartOfBinaryExpressionFix<T extends JetExpression> extends JetIntentionAction<T> implements CleanupFix {
|
||||
private final String message;
|
||||
|
||||
public RemoveRightPartOfBinaryExpressionFix(@NotNull T element, String message) {
|
||||
|
||||
@@ -1,101 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.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 com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.kotlin.idea.JetBundle;
|
||||
import org.jetbrains.kotlin.psi.*;
|
||||
|
||||
import static org.jetbrains.kotlin.psi.PsiPackage.JetPsiFactory;
|
||||
|
||||
public class ReplaceCallFix implements IntentionAction {
|
||||
private final boolean toSafe;
|
||||
|
||||
private ReplaceCallFix(boolean safe) {
|
||||
this.toSafe = safe;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return quickfix for replacing dot call with toSafe (?.) call
|
||||
*/
|
||||
public static ReplaceCallFix toSafeCall() {
|
||||
return new ReplaceCallFix(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return quickfix for replacing unnecessary toSafe (?.) call with dot call
|
||||
*/
|
||||
public static ReplaceCallFix toDotCallFromSafeCall() {
|
||||
return new ReplaceCallFix(false);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getText() {
|
||||
return toSafe ? JetBundle.message("replace.with.safe.call") : JetBundle.message("replace.with.dot.call");
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getFamilyName() {
|
||||
return getText();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) {
|
||||
if (file instanceof JetFile) {
|
||||
return getCallExpression(editor, (JetFile) file) != null;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invoke(@NotNull Project project, Editor editor, PsiFile file) throws IncorrectOperationException {
|
||||
JetQualifiedExpression callExpression = getCallExpression(editor, (JetFile) file);
|
||||
assert callExpression != null;
|
||||
|
||||
JetExpression selector = callExpression.getSelectorExpression();
|
||||
if (selector != null) {
|
||||
JetQualifiedExpression newElement = (JetQualifiedExpression) JetPsiFactory(callExpression).createExpression(
|
||||
callExpression.getReceiverExpression().getText() + (toSafe ? "?." : ".") + selector.getText()
|
||||
);
|
||||
|
||||
callExpression.replace(newElement);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean startInWriteAction() {
|
||||
return true;
|
||||
}
|
||||
|
||||
private JetQualifiedExpression getCallExpression(@NotNull Editor editor, @NotNull JetFile file) {
|
||||
PsiElement elementAtCaret = getElementAtCaret(editor, file);
|
||||
return PsiTreeUtil.getParentOfType(elementAtCaret, toSafe ? JetDotQualifiedExpression.class : JetSafeQualifiedExpression.class);
|
||||
}
|
||||
|
||||
private static PsiElement getElementAtCaret(Editor editor, PsiFile file) {
|
||||
return file.findElementAt(editor.getCaretModel().getOffset());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.quickfix
|
||||
|
||||
import com.intellij.codeInsight.intention.IntentionAction
|
||||
import com.intellij.extapi.psi.ASTDelegatePsiElement
|
||||
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.util.PsiTreeUtil
|
||||
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||
import org.jetbrains.kotlin.idea.JetBundle
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
|
||||
public abstract class ReplaceCallFix protected (val psiElement: PsiElement) : IntentionAction {
|
||||
|
||||
override fun getFamilyName(): String = getText()
|
||||
|
||||
override fun isAvailable(project: Project, editor: Editor?, file: PsiFile): Boolean {
|
||||
if (file is JetFile) {
|
||||
return getCallExpression() != null
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
override fun invoke(project: Project, editor: Editor?, file: PsiFile) {
|
||||
val callExpression = getCallExpression() ?: return
|
||||
|
||||
val selector = callExpression.getSelectorExpression()
|
||||
if (selector != null) {
|
||||
val newElement = JetPsiFactory(callExpression).createExpression(
|
||||
callExpression.getReceiverExpression().getText() + operation + selector.getText()) as JetQualifiedExpression
|
||||
|
||||
callExpression.replace(newElement)
|
||||
}
|
||||
}
|
||||
|
||||
override fun startInWriteAction(): Boolean = true
|
||||
|
||||
private fun getCallExpression(): JetQualifiedExpression? {
|
||||
return PsiTreeUtil.getParentOfType(psiElement, classToReplace)
|
||||
}
|
||||
|
||||
abstract val operation: String
|
||||
abstract val classToReplace: Class<out JetQualifiedExpression>
|
||||
}
|
||||
|
||||
public class ReplaceWithSafeCallFix(psiElement: PsiElement): ReplaceCallFix(psiElement) {
|
||||
override fun getText(): String = JetBundle.message("replace.with.safe.call")
|
||||
override val operation: String get() = "?."
|
||||
override val classToReplace: Class<out JetQualifiedExpression> get() = javaClass<JetDotQualifiedExpression>()
|
||||
|
||||
companion object : JetSingleIntentionActionFactory() {
|
||||
override fun createAction(diagnostic: Diagnostic): IntentionAction
|
||||
= ReplaceWithSafeCallFix(diagnostic.getPsiElement())
|
||||
}
|
||||
}
|
||||
|
||||
public class ReplaceWithDotCallFix(psiElement: PsiElement): ReplaceCallFix(psiElement), CleanupFix {
|
||||
override fun getText(): String = JetBundle.message("replace.with.dot.call")
|
||||
override val operation: String get() = "."
|
||||
override val classToReplace: Class<out JetQualifiedExpression> get() = javaClass<JetSafeQualifiedExpression>()
|
||||
|
||||
companion object : JetSingleIntentionActionFactory() {
|
||||
override fun createAction(diagnostic: Diagnostic): IntentionAction
|
||||
= ReplaceWithDotCallFix(diagnostic.getPsiElement())
|
||||
}
|
||||
}
|
||||
@@ -30,7 +30,7 @@ import org.jetbrains.kotlin.psi.psiUtil.getNextSiblingIgnoringWhitespaceAndComme
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
|
||||
public class ReplaceObsoleteLabelSyntaxFix(element: JetAnnotationEntry?) : JetIntentionAction<JetAnnotationEntry>(element) {
|
||||
public class ReplaceObsoleteLabelSyntaxFix(element: JetAnnotationEntry?) : JetIntentionAction<JetAnnotationEntry>(element), CleanupFix {
|
||||
override fun getFamilyName(): String = "Update obsolete label syntax"
|
||||
override fun getText(): String = "Replace with label ${element.getCalleeExpression()?.getText() ?: ""}@"
|
||||
|
||||
|
||||
+1
-1
@@ -33,7 +33,7 @@ import java.util.ArrayList
|
||||
|
||||
public class ReplaceJavaClassAsAnnotationArgumentFix(
|
||||
annotationEntry: JetAnnotationEntry
|
||||
) : JetIntentionAction<JetAnnotationEntry>(annotationEntry) {
|
||||
) : JetIntentionAction<JetAnnotationEntry>(annotationEntry), CleanupFix {
|
||||
|
||||
override fun getText() = JetBundle.message("replace.java.class.argument")
|
||||
override fun getFamilyName() = JetBundle.message("replace.java.class.argument.family")
|
||||
|
||||
+4
-9
@@ -16,27 +16,22 @@
|
||||
|
||||
package org.jetbrains.kotlin.idea.quickfix.replaceJavaClass
|
||||
|
||||
import com.intellij.codeInsight.intention.IntentionAction
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.PsiFile
|
||||
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||
import org.jetbrains.kotlin.idea.JetBundle
|
||||
import org.jetbrains.kotlin.idea.quickfix.CleanupFix
|
||||
import org.jetbrains.kotlin.idea.quickfix.JetIntentionAction
|
||||
import org.jetbrains.kotlin.idea.quickfix.JetSingleIntentionActionFactory
|
||||
import org.jetbrains.kotlin.idea.quickfix.JetWholeProjectForEachElementOfTypeFix
|
||||
import org.jetbrains.kotlin.idea.quickfix.JetWholeProjectModalAction
|
||||
import org.jetbrains.kotlin.idea.quickfix.quickfixUtil.createIntentionFactory
|
||||
import org.jetbrains.kotlin.idea.quickfix.quickfixUtil.createIntentionForFirstParentOfType
|
||||
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
|
||||
import org.jetbrains.kotlin.idea.util.ShortenReferences
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType
|
||||
import java.util.ArrayList
|
||||
import org.jetbrains.kotlin.psi.JetClass
|
||||
import org.jetbrains.kotlin.psi.JetFile
|
||||
|
||||
public class ReplaceJavaClassAsAnnotationParameterFix(
|
||||
annotationClass: JetClass
|
||||
) : JetIntentionAction<JetClass>(annotationClass) {
|
||||
) : JetIntentionAction<JetClass>(annotationClass), CleanupFix {
|
||||
|
||||
override fun getText() = JetBundle.message("replace.java.class.parameter")
|
||||
override fun getFamilyName() = JetBundle.message("replace.java.class.parameter.family")
|
||||
|
||||
@@ -30,3 +30,15 @@ fun foo() {
|
||||
continue@loop
|
||||
}
|
||||
}
|
||||
|
||||
fun unnecessarySafeCall(x: String) {
|
||||
x?.length()
|
||||
}
|
||||
|
||||
fun unnecessaryExclExcl(x: String) {
|
||||
x!!.length()
|
||||
}
|
||||
|
||||
fun unnecessaryCast(x: String) = x as String
|
||||
|
||||
fun unnecessaryElvis(x: String) = x ?: ""
|
||||
|
||||
@@ -32,3 +32,15 @@ fun foo() {
|
||||
continue@loop
|
||||
}
|
||||
}
|
||||
|
||||
fun unnecessarySafeCall(x: String) {
|
||||
x.length()
|
||||
}
|
||||
|
||||
fun unnecessaryExclExcl(x: String) {
|
||||
x.length()
|
||||
}
|
||||
|
||||
fun unnecessaryCast(x: String) = x
|
||||
|
||||
fun unnecessaryElvis(x: String) = x
|
||||
|
||||
Reference in New Issue
Block a user