From dc9523016a9249bbbfc76c0402c88f6623a2fbc0 Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Wed, 13 May 2015 19:29:15 +0200 Subject: [PATCH] report 'trait' keyword as deprecated, provide quickfix for replacing with 'interface' --- .../jetbrains/kotlin/diagnostics/Errors.java | 1 + .../rendering/DefaultErrorMessages.java | 1 + .../kotlin/resolve/DeclarationsChecker.java | 5 ++ .../tests/DeprecatedTraitKeyword.kt | 2 + .../tests/DeprecatedTraitKeyword.txt | 7 +++ .../checkers/JetDiagnosticsTestGenerated.java | 6 ++ .../kotlin/idea/highlighter/JetPsiChecker.kt | 3 +- .../idea/quickfix/DeprecatedTraitSyntaxFix.kt | 57 +++++++++++++++++++ .../idea/quickfix/QuickFixRegistrar.java | 3 + .../traitToInterface/traitToInterface.kt | 5 ++ .../traitToInterface.kt.after | 5 ++ .../QuickFixMultiFileTestGenerated.java | 1 + .../idea/quickfix/QuickFixTestGenerated.java | 14 +++++ 13 files changed, 109 insertions(+), 1 deletion(-) create mode 100644 compiler/testData/diagnostics/tests/DeprecatedTraitKeyword.kt create mode 100644 compiler/testData/diagnostics/tests/DeprecatedTraitKeyword.txt create mode 100644 idea/src/org/jetbrains/kotlin/idea/quickfix/DeprecatedTraitSyntaxFix.kt create mode 100644 idea/testData/quickfix/migration/traitToInterface/traitToInterface.kt create mode 100644 idea/testData/quickfix/migration/traitToInterface/traitToInterface.kt.after diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java index fe6b9c41729..0722cfc15f2 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java @@ -142,6 +142,7 @@ public interface Errors { DiagnosticFactory0 SUPERTYPE_NOT_INITIALIZED = DiagnosticFactory0.create(ERROR); + DiagnosticFactory0 DEPRECATED_TRAIT_KEYWORD = DiagnosticFactory0.create(WARNING); DiagnosticFactory0 DELEGATION_NOT_TO_TRAIT = DiagnosticFactory0.create(ERROR); DiagnosticFactory0 SUPERTYPE_NOT_A_CLASS_OR_TRAIT = DiagnosticFactory0.create(ERROR); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java index 144c0fe4732..459356b0ced 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java @@ -262,6 +262,7 @@ public class DefaultErrorMessages { MAP.put(ENUM_ENTRY_AFTER_ENUM_MEMBER, "Enum entry ''{0}'' is not allowed after a member", NAME); MAP.put(DELEGATION_IN_TRAIT, "Interfaces cannot use delegation"); MAP.put(DELEGATION_NOT_TO_TRAIT, "Only interfaces can be delegated to"); + MAP.put(DEPRECATED_TRAIT_KEYWORD, "'trait' keyword is deprecated, use 'interface' instead"); MAP.put(UNMET_TRAIT_REQUIREMENT, "Super interface ''{0}'' requires subclasses to extend ''{1}''", NAME, NAME); MAP.put(NO_CONSTRUCTOR, "This class does not have a constructor"); MAP.put(NOT_A_CLASS, "Not a class"); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/DeclarationsChecker.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/DeclarationsChecker.java index 9833a32e1cc..c1a71b0f031 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/DeclarationsChecker.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/DeclarationsChecker.java @@ -247,6 +247,11 @@ public class DeclarationsChecker { checkTypeParameters(aClass); if (aClass.isInterface()) { + ASTNode traitKeyword = aClass.getNode().findChildByType(JetTokens.TRAIT_KEYWORD); + if (traitKeyword != null) { + trace.report(Errors.DEPRECATED_TRAIT_KEYWORD.on(traitKeyword.getPsi())); + } + checkTraitModifiers(aClass); checkConstructorInTrait(aClass); } diff --git a/compiler/testData/diagnostics/tests/DeprecatedTraitKeyword.kt b/compiler/testData/diagnostics/tests/DeprecatedTraitKeyword.kt new file mode 100644 index 00000000000..8fedbb55ce2 --- /dev/null +++ b/compiler/testData/diagnostics/tests/DeprecatedTraitKeyword.kt @@ -0,0 +1,2 @@ +trait A { +} diff --git a/compiler/testData/diagnostics/tests/DeprecatedTraitKeyword.txt b/compiler/testData/diagnostics/tests/DeprecatedTraitKeyword.txt new file mode 100644 index 00000000000..d867f9e6a23 --- /dev/null +++ b/compiler/testData/diagnostics/tests/DeprecatedTraitKeyword.txt @@ -0,0 +1,7 @@ +package + +internal interface A { + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java index add7e7467c0..4ef9561bb0c 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java @@ -181,6 +181,12 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest { doTest(fileName); } + @TestMetadata("DeprecatedTraitKeyword.kt") + public void testDeprecatedTraitKeyword() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/DeprecatedTraitKeyword.kt"); + doTest(fileName); + } + @TestMetadata("DiamondFunction.kt") public void testDiamondFunction() throws Exception { String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/DiamondFunction.kt"); diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/highlighter/JetPsiChecker.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/highlighter/JetPsiChecker.kt index 144bc88c7a5..f29786c46b4 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/highlighter/JetPsiChecker.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/highlighter/JetPsiChecker.kt @@ -152,7 +152,8 @@ public open class JetPsiChecker : Annotator, HighlightRangeExtension { when (factory) { Errors.DEPRECATED_SYMBOL, - Errors.DEPRECATED_SYMBOL_WITH_MESSAGE + Errors.DEPRECATED_SYMBOL_WITH_MESSAGE, + Errors.DEPRECATED_TRAIT_KEYWORD -> annotation.setTextAttributes(CodeInsightColors.DEPRECATED_ATTRIBUTES) } diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/DeprecatedTraitSyntaxFix.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/DeprecatedTraitSyntaxFix.kt new file mode 100644 index 00000000000..3ad5f8cd3cb --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/DeprecatedTraitSyntaxFix.kt @@ -0,0 +1,57 @@ +/* + * 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 org.jetbrains.kotlin.diagnostics.Diagnostic +import org.jetbrains.kotlin.idea.quickfix.quickfixUtil.createIntentionFactory +import org.jetbrains.kotlin.lexer.JetTokens +import org.jetbrains.kotlin.psi.JetClass +import org.jetbrains.kotlin.psi.JetFile +import org.jetbrains.kotlin.psi.JetPsiFactory + +public class DeprecatedTraitSyntaxFix(element: PsiElement): JetIntentionAction(element) { + override fun getFamilyName() = "Replace with 'interface'" + override fun getText() = getFamilyName() + + override fun invoke(project: Project, editor: Editor?, file: JetFile?) + = replaceWithInterfaceKeyword(element) + + + companion object : JetSingleIntentionActionFactory() { + fun replaceWithInterfaceKeyword(element: PsiElement) { + val cls = JetPsiFactory(element.getProject()).createClass("interface A {}") + element.replace(cls.getNode().findChildByType(JetTokens.INTERFACE_KEYWORD)!!.getPsi()) + } + + override fun createAction(diagnostic: Diagnostic): IntentionAction = + DeprecatedTraitSyntaxFix(diagnostic.getPsiElement()) + + public fun createWholeProjectFixFactory(): JetSingleIntentionActionFactory = createIntentionFactory { + JetWholeProjectForEachElementOfTypeFix.createByPredicate( + predicate = { it.getNode().findChildByType(JetTokens.TRAIT_KEYWORD) != null }, + taskProcessor = { replaceWithInterfaceKeyword(it.getNode().findChildByType(JetTokens.TRAIT_KEYWORD).getPsi())}, + modalTitle = "Replacing deprecated trait syntax", + name = "Replace with 'interface' in whole project", + familyName = "Replace with 'interface' in whole project" + ) + } + } +} diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.java b/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.java index 8b896695ef8..b5a21e0ce25 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.java +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.java @@ -105,6 +105,9 @@ public class QuickFixRegistrar { true)); QuickFixes.factories.put(OPEN_MODIFIER_IN_TRAIT, RemoveModifierFix.createRemoveModifierFromListOwnerFactory(OPEN_KEYWORD, true)); QuickFixes.factories.put(TRAIT_CAN_NOT_BE_FINAL, removeFinalModifierFactory); + QuickFixes.factories.put(DEPRECATED_TRAIT_KEYWORD, DeprecatedTraitSyntaxFix.Companion); + QuickFixes.factories.put(DEPRECATED_TRAIT_KEYWORD, DeprecatedTraitSyntaxFix.Companion.createWholeProjectFixFactory()); + QuickFixes.factories.put(REDUNDANT_PROJECTION, RemoveModifierFix.createRemoveProjectionFactory(true)); QuickFixes.factories.put(INCOMPATIBLE_MODIFIERS, RemoveModifierFix.createRemoveModifierFactory(false)); QuickFixes.factories.put(VARIANCE_ON_TYPE_PARAMETER_OF_FUNCTION_OR_PROPERTY, RemoveModifierFix.createRemoveVarianceFactory()); diff --git a/idea/testData/quickfix/migration/traitToInterface/traitToInterface.kt b/idea/testData/quickfix/migration/traitToInterface/traitToInterface.kt new file mode 100644 index 00000000000..3d701be7ddb --- /dev/null +++ b/idea/testData/quickfix/migration/traitToInterface/traitToInterface.kt @@ -0,0 +1,5 @@ +// "Replace with 'interface'" "true" + +trait Foo { + +} diff --git a/idea/testData/quickfix/migration/traitToInterface/traitToInterface.kt.after b/idea/testData/quickfix/migration/traitToInterface/traitToInterface.kt.after new file mode 100644 index 00000000000..7792b57fecd --- /dev/null +++ b/idea/testData/quickfix/migration/traitToInterface/traitToInterface.kt.after @@ -0,0 +1,5 @@ +// "Replace with 'interface'" "true" + +interface Foo { + +} diff --git a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiFileTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiFileTestGenerated.java index 9c7e9a2e966..f4c3d279f07 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiFileTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiFileTestGenerated.java @@ -974,6 +974,7 @@ public class QuickFixMultiFileTestGenerated extends AbstractQuickFixMultiFileTes doTestWithExtraFile(fileName); } } + } @TestMetadata("idea/testData/quickfix/modifiers") diff --git a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java index 31088b47dac..04748de3e34 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java @@ -3277,6 +3277,20 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest { } } + @TestMetadata("idea/testData/quickfix/migration/traitToInterface") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class TraitToInterface extends AbstractQuickFixTest { + public void testAllFilesPresentInTraitToInterface() throws Exception { + JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/migration/traitToInterface"), Pattern.compile("^(\\w+)\\.kt$"), true); + } + + @TestMetadata("traitToInterface.kt") + public void testTraitToInterface() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/migration/traitToInterface/traitToInterface.kt"); + doTest(fileName); + } + } } @TestMetadata("idea/testData/quickfix/modifiers")