From 141ffa27dc635946d9cde4f11824a072029ddf9d Mon Sep 17 00:00:00 2001 From: Anton Sukhonosenko Date: Mon, 26 Oct 2015 01:47:16 +0300 Subject: [PATCH] val / var are now forbidden on a loop multi declaration parameter #KT-6274 Fixed VAL_OR_VAR_ON_LOOP_MULTI_PARAMETER error added KtValVarKeywordOwner interface added for PSIs that have getValOrVarKeyword method --- .../jetbrains/kotlin/diagnostics/Errors.java | 1 + .../rendering/DefaultErrorMessages.java | 1 + .../kotlin/psi/KtMultiDeclaration.java | 2 +- .../org/jetbrains/kotlin/psi/KtParameter.java | 2 +- .../kotlin/psi/KtValVarKeywordOwner.java | 25 ++++++++++++++++++ .../kotlin/psi/KtVariableDeclaration.java | 8 +----- .../kotlin/resolve/ModifiersChecker.java | 2 +- .../ControlStructureTypingVisitor.java | 1 + .../controlStructures/valVarLoopParameter.kt | 23 ++++++++++++++++ .../controlStructures/valVarLoopParameter.txt | 26 +++++++++++++++++++ .../DataFlowInMultiDeclInFor.kt | 2 +- .../kotlin/idea/quickfix/QuickFixRegistrar.kt | 1 + .../RemoveValVarFromParameterFix.java | 8 +++--- .../rename/AutomaticVariableRenamer.kt | 7 ++--- .../loopMultiParameter.kt | 12 +++++++++ .../loopMultiParameter.kt.after | 12 +++++++++ .../idea/quickfix/QuickFixTestGenerated.java | 6 +++++ 17 files changed, 119 insertions(+), 20 deletions(-) create mode 100644 compiler/frontend/src/org/jetbrains/kotlin/psi/KtValVarKeywordOwner.java create mode 100644 idea/testData/quickfix/variables/removeValVarFromParameter/loopMultiParameter.kt create mode 100644 idea/testData/quickfix/variables/removeValVarFromParameter/loopMultiParameter.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 ceaad1bf434..a7970d92f0a 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java @@ -667,6 +667,7 @@ public interface Errors { DiagnosticFactory3 INVISIBLE_SETTER = DiagnosticFactory3.create(ERROR); DiagnosticFactory1 VAL_OR_VAR_ON_LOOP_PARAMETER = DiagnosticFactory1.create(ERROR); + DiagnosticFactory1 VAL_OR_VAR_ON_LOOP_MULTI_PARAMETER = DiagnosticFactory1.create(ERROR); DiagnosticFactory1 VAL_OR_VAR_ON_FUN_PARAMETER = DiagnosticFactory1.create(ERROR); DiagnosticFactory1 VAL_OR_VAR_ON_CATCH_PARAMETER = DiagnosticFactory1.create(ERROR); DiagnosticFactory1 VAL_OR_VAR_ON_SECONDARY_CONSTRUCTOR_PARAMETER = DiagnosticFactory1.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 e667e423abb..23f1f17f249 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java @@ -264,6 +264,7 @@ public class DefaultErrorMessages { MAP.put(VARIABLE_EXPECTED, "Variable expected"); MAP.put(VAL_OR_VAR_ON_LOOP_PARAMETER, "''{0}'' on loop parameter is not allowed", TO_STRING); + MAP.put(VAL_OR_VAR_ON_LOOP_MULTI_PARAMETER, "''{0}'' on loop multi parameter is not allowed", TO_STRING); MAP.put(VAL_OR_VAR_ON_FUN_PARAMETER, "''{0}'' on function parameter is not allowed", TO_STRING); MAP.put(VAL_OR_VAR_ON_CATCH_PARAMETER, "''{0}'' on catch parameter is not allowed", TO_STRING); MAP.put(VAL_OR_VAR_ON_SECONDARY_CONSTRUCTOR_PARAMETER, "''{0}'' on secondary constructor parameter is not allowed", TO_STRING); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/psi/KtMultiDeclaration.java b/compiler/frontend/src/org/jetbrains/kotlin/psi/KtMultiDeclaration.java index d36705b2cb8..5cc99c77438 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/psi/KtMultiDeclaration.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/psi/KtMultiDeclaration.java @@ -29,7 +29,7 @@ import java.util.List; import static org.jetbrains.kotlin.lexer.KtTokens.*; -public class KtMultiDeclaration extends KtDeclarationImpl { +public class KtMultiDeclaration extends KtDeclarationImpl implements KtValVarKeywordOwner { public KtMultiDeclaration(@NotNull ASTNode node) { super(node); } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/psi/KtParameter.java b/compiler/frontend/src/org/jetbrains/kotlin/psi/KtParameter.java index 33ced1cf3cd..726a270f9f5 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/psi/KtParameter.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/psi/KtParameter.java @@ -31,7 +31,7 @@ import org.jetbrains.kotlin.psi.typeRefHelpers.TypeRefHelpersKt; import java.util.Collections; import java.util.List; -public class KtParameter extends KtNamedDeclarationStub implements KtCallableDeclaration { +public class KtParameter extends KtNamedDeclarationStub implements KtCallableDeclaration, KtValVarKeywordOwner { public KtParameter(@NotNull ASTNode node) { super(node); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/psi/KtValVarKeywordOwner.java b/compiler/frontend/src/org/jetbrains/kotlin/psi/KtValVarKeywordOwner.java new file mode 100644 index 00000000000..96b04f05a6f --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/kotlin/psi/KtValVarKeywordOwner.java @@ -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.psi; + +import com.intellij.psi.PsiElement; +import org.jetbrains.annotations.Nullable; + +public interface KtValVarKeywordOwner extends PsiElement { + @Nullable + PsiElement getValOrVarKeyword(); +} diff --git a/compiler/frontend/src/org/jetbrains/kotlin/psi/KtVariableDeclaration.java b/compiler/frontend/src/org/jetbrains/kotlin/psi/KtVariableDeclaration.java index 572b19bedbe..2a3c060ac66 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/psi/KtVariableDeclaration.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/psi/KtVariableDeclaration.java @@ -16,12 +16,6 @@ package org.jetbrains.kotlin.psi; -import com.intellij.psi.PsiElement; -import org.jetbrains.annotations.Nullable; - -public interface KtVariableDeclaration extends KtCallableDeclaration, KtWithExpressionInitializer { +public interface KtVariableDeclaration extends KtCallableDeclaration, KtWithExpressionInitializer, KtValVarKeywordOwner { boolean isVar(); - - @Nullable - PsiElement getValOrVarKeyword(); } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/ModifiersChecker.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/ModifiersChecker.java index b69b3c5ccac..1e649e96b9f 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/ModifiersChecker.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/ModifiersChecker.java @@ -163,7 +163,7 @@ public class ModifiersChecker { } public void checkParameterHasNoValOrVar( - @NotNull KtParameter parameter, + @NotNull KtValVarKeywordOwner parameter, @NotNull DiagnosticFactory1 diagnosticFactory ) { PsiElement valOrVar = parameter.getValOrVarKeyword(); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ControlStructureTypingVisitor.java b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ControlStructureTypingVisitor.java index 5258e900e1f..09e6e7542e5 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ControlStructureTypingVisitor.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ControlStructureTypingVisitor.java @@ -407,6 +407,7 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor { loopScope, multiParameter, iteratorNextAsReceiver, loopRange, context ); components.modifiersChecker.withTrace(context.trace).checkModifiersForMultiDeclaration(multiParameter); + components.modifiersChecker.withTrace(context.trace).checkParameterHasNoValOrVar(multiParameter, VAL_OR_VAR_ON_LOOP_MULTI_PARAMETER); components.identifierChecker.checkDeclaration(multiParameter, context.trace); } } diff --git a/compiler/testData/diagnostics/tests/controlStructures/valVarLoopParameter.kt b/compiler/testData/diagnostics/tests/controlStructures/valVarLoopParameter.kt index c2cae7a215f..e4ec5dc1775 100644 --- a/compiler/testData/diagnostics/tests/controlStructures/valVarLoopParameter.kt +++ b/compiler/testData/diagnostics/tests/controlStructures/valVarLoopParameter.kt @@ -1,3 +1,18 @@ +class Pair { + operator fun component1(): Int = null!! + operator fun component2(): Int = null!! +} + +class Coll { + operator fun iterator(): It = It() +} + +class It { + operator fun next() = Pair() + operator fun hasNext() = false +} + + fun f() { for (val i in 1..4) { @@ -6,4 +21,12 @@ fun f() { for (var i in 1..4) { } + + for (val (i,j) in Coll()) { + + } + + for (var (i,j) in Coll()) { + + } } diff --git a/compiler/testData/diagnostics/tests/controlStructures/valVarLoopParameter.txt b/compiler/testData/diagnostics/tests/controlStructures/valVarLoopParameter.txt index c0c05c8eaba..dc73ab9eb53 100644 --- a/compiler/testData/diagnostics/tests/controlStructures/valVarLoopParameter.txt +++ b/compiler/testData/diagnostics/tests/controlStructures/valVarLoopParameter.txt @@ -1,3 +1,29 @@ package public fun f(): kotlin.Unit + +public final class Coll { + public constructor Coll() + 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 final operator fun iterator(): It + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public final class It { + public constructor It() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public final operator fun hasNext(): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public final operator fun next(): Pair + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public final class Pair { + public constructor Pair() + public final operator fun component1(): kotlin.Int + public final operator fun component2(): kotlin.Int + 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/testData/diagnostics/tests/declarationChecks/DataFlowInMultiDeclInFor.kt b/compiler/testData/diagnostics/tests/declarationChecks/DataFlowInMultiDeclInFor.kt index 2eecf2d6460..dd86f3308de 100644 --- a/compiler/testData/diagnostics/tests/declarationChecks/DataFlowInMultiDeclInFor.kt +++ b/compiler/testData/diagnostics/tests/declarationChecks/DataFlowInMultiDeclInFor.kt @@ -8,7 +8,7 @@ class A { } fun foo(list: List) { - for (var (c1, c2, c3) in list) { + for (var (c1, c2, c3) in list) { c1 = 1 c3 + 1 } diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt index 8a9dcba9a6c..7e0daf3c8e5 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt @@ -164,6 +164,7 @@ public class QuickFixRegistrar : QuickFixContributor { val removeValVarFromParameterFixFactory = RemoveValVarFromParameterFix.createFactory() VAL_OR_VAR_ON_FUN_PARAMETER.registerFactory(removeValVarFromParameterFixFactory) VAL_OR_VAR_ON_LOOP_PARAMETER.registerFactory(removeValVarFromParameterFixFactory) + VAL_OR_VAR_ON_LOOP_MULTI_PARAMETER.registerFactory(removeValVarFromParameterFixFactory) VAL_OR_VAR_ON_CATCH_PARAMETER.registerFactory(removeValVarFromParameterFixFactory) VAL_OR_VAR_ON_SECONDARY_CONSTRUCTOR_PARAMETER.registerFactory(removeValVarFromParameterFixFactory) diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/RemoveValVarFromParameterFix.java b/idea/src/org/jetbrains/kotlin/idea/quickfix/RemoveValVarFromParameterFix.java index bfb9f91a540..004c7deafe7 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/RemoveValVarFromParameterFix.java +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/RemoveValVarFromParameterFix.java @@ -26,13 +26,13 @@ import org.jetbrains.annotations.Nullable; import org.jetbrains.kotlin.diagnostics.Diagnostic; import org.jetbrains.kotlin.idea.KotlinBundle; import org.jetbrains.kotlin.psi.KtFile; -import org.jetbrains.kotlin.psi.KtParameter; +import org.jetbrains.kotlin.psi.KtValVarKeywordOwner; import org.jetbrains.kotlin.psi.psiUtil.PsiUtilsKt; -public class RemoveValVarFromParameterFix extends KotlinQuickFixAction { +public class RemoveValVarFromParameterFix extends KotlinQuickFixAction { private final String varOrVal; - public RemoveValVarFromParameterFix(@NotNull KtParameter element) { + public RemoveValVarFromParameterFix(@NotNull KtValVarKeywordOwner element) { super(element); PsiElement valOrVarNode = element.getValOrVarKeyword(); assert valOrVarNode != null : "Val or var node not found for " + PsiUtilsKt.getElementTextWithContext(element); @@ -64,7 +64,7 @@ public class RemoveValVarFromParameterFix extends KotlinQuickFixAction(), javaClass() - ) ?: continue + ) as KtCallableDeclaration? ?: continue if (parameterOrVariable.getTypeReference()?.isAncestor(usageElement) != true) continue diff --git a/idea/testData/quickfix/variables/removeValVarFromParameter/loopMultiParameter.kt b/idea/testData/quickfix/variables/removeValVarFromParameter/loopMultiParameter.kt new file mode 100644 index 00000000000..0c614bfd44e --- /dev/null +++ b/idea/testData/quickfix/variables/removeValVarFromParameter/loopMultiParameter.kt @@ -0,0 +1,12 @@ +// "Remove 'val' from parameter" "true" +class Pair +{ + operator fun component1(): A = null!! + operator fun component2(): B = null!! +} + +fun f(list: List>) { + for (val (x,y) in list) { + + } +} \ No newline at end of file diff --git a/idea/testData/quickfix/variables/removeValVarFromParameter/loopMultiParameter.kt.after b/idea/testData/quickfix/variables/removeValVarFromParameter/loopMultiParameter.kt.after new file mode 100644 index 00000000000..a51fc847aa3 --- /dev/null +++ b/idea/testData/quickfix/variables/removeValVarFromParameter/loopMultiParameter.kt.after @@ -0,0 +1,12 @@ +// "Remove 'val' from parameter" "true" +class Pair +{ + operator fun component1(): A = null!! + operator fun component2(): B = null!! +} + +fun f(list: List>) { + for ((x,y) in list) { + + } +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java index 76ae04d7b0d..6a7042a13ab 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java @@ -6851,6 +6851,12 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest { doTest(fileName); } + @TestMetadata("loopMultiParameter.kt") + public void testLoopMultiParameter() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/variables/removeValVarFromParameter/loopMultiParameter.kt"); + doTest(fileName); + } + @TestMetadata("loopParameter.kt") public void testLoopParameter() throws Exception { String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/variables/removeValVarFromParameter/loopParameter.kt");