From f057445f6e0672841688fcdcb0ad1de1f85a386a Mon Sep 17 00:00:00 2001 From: Valentin Kipyatkov Date: Fri, 16 Jan 2015 19:36:41 +0300 Subject: [PATCH] KT-5035 Smart completion should work after "in" keyword in for loop #KT-5035 Fixed --- .../ForLoopConventionsChecker.java | 21 +----- .../idea/completion/smart/SmartCompletion.kt | 36 ++++++++++ .../kotlin/idea/completion/smart/Utils.kt | 14 +++- .../macro/BaseJetVariableMacro.java | 11 ++-- .../macro/JetAnyVariableMacro.java | 9 ++- .../macro/JetIterableVariableMacro.java | 9 ++- .../macro/JetSuggestVariableNameMacro.java | 6 +- .../kotlin/idea/util/IterableTypesDetector.kt | 65 +++++++++++++++++++ .../completion/handlers/smart/ForLoopRange.kt | 13 ++++ .../handlers/smart/ForLoopRange.kt.after | 13 ++++ .../handlers/smart/ForLoopRange2.kt | 13 ++++ .../handlers/smart/ForLoopRange2.kt.after | 13 ++++ .../completion/smart/forLoopRange/AfterDot.kt | 9 +++ .../forLoopRange/ExtensionIteratorMethod.kt | 14 ++++ .../smart/forLoopRange/NoGenericT.kt | 9 +++ .../completion/smart/forLoopRange/Nullable.kt | 12 ++++ .../completion/smart/forLoopRange/Simple.kt | 16 +++++ .../smart/forLoopRange/SmartCasts.kt | 8 +++ .../JvmSmartCompletionTestGenerated.java | 46 +++++++++++++ .../SmartCompletionHandlerTestGenerated.java | 12 ++++ 20 files changed, 308 insertions(+), 41 deletions(-) create mode 100644 idea/src/org/jetbrains/kotlin/idea/util/IterableTypesDetector.kt create mode 100644 idea/testData/completion/handlers/smart/ForLoopRange.kt create mode 100644 idea/testData/completion/handlers/smart/ForLoopRange.kt.after create mode 100644 idea/testData/completion/handlers/smart/ForLoopRange2.kt create mode 100644 idea/testData/completion/handlers/smart/ForLoopRange2.kt.after create mode 100644 idea/testData/completion/smart/forLoopRange/AfterDot.kt create mode 100644 idea/testData/completion/smart/forLoopRange/ExtensionIteratorMethod.kt create mode 100644 idea/testData/completion/smart/forLoopRange/NoGenericT.kt create mode 100644 idea/testData/completion/smart/forLoopRange/Nullable.kt create mode 100644 idea/testData/completion/smart/forLoopRange/Simple.kt create mode 100644 idea/testData/completion/smart/forLoopRange/SmartCasts.kt diff --git a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ForLoopConventionsChecker.java b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ForLoopConventionsChecker.java index 3c31855f45a..fa8af280abb 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ForLoopConventionsChecker.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ForLoopConventionsChecker.java @@ -22,27 +22,21 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.kotlin.builtins.KotlinBuiltIns; import org.jetbrains.kotlin.descriptors.FunctionDescriptor; -import org.jetbrains.kotlin.descriptors.VariableDescriptor; import org.jetbrains.kotlin.diagnostics.DiagnosticFactory1; import org.jetbrains.kotlin.name.Name; import org.jetbrains.kotlin.psi.Call; import org.jetbrains.kotlin.psi.JetExpression; -import org.jetbrains.kotlin.resolve.BindingTraceContext; import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall; import org.jetbrains.kotlin.resolve.calls.results.OverloadResolutionResults; -import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo; -import org.jetbrains.kotlin.resolve.scopes.JetScope; import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver; import org.jetbrains.kotlin.resolve.scopes.receivers.TransientReceiver; import org.jetbrains.kotlin.types.JetType; -import org.jetbrains.kotlin.types.TypeUtils; import org.jetbrains.kotlin.util.slicedMap.WritableSlice; import javax.inject.Inject; import java.util.Collections; import static org.jetbrains.kotlin.diagnostics.Errors.*; -import static org.jetbrains.kotlin.psi.PsiPackage.JetPsiFactory; import static org.jetbrains.kotlin.resolve.BindingContext.*; public class ForLoopConventionsChecker { @@ -72,21 +66,8 @@ public class ForLoopConventionsChecker { this.builtIns = builtIns; } - public boolean isVariableIterable(@NotNull VariableDescriptor variableDescriptor, @NotNull JetScope scope) { - JetExpression expression = JetPsiFactory(project).createExpression("fake"); - ExpressionReceiver expressionReceiver = new ExpressionReceiver(expression, variableDescriptor.getType()); - ExpressionTypingContext context = ExpressionTypingContext.newContext( - expressionTypingServices, - new BindingTraceContext(), - scope, - DataFlowInfo.EMPTY, - TypeUtils.NO_EXPECTED_TYPE - ); - return checkIterableConvention(expressionReceiver, context) != null; - } - @Nullable - /*package*/ JetType checkIterableConvention(@NotNull ExpressionReceiver loopRange, ExpressionTypingContext context) { + public JetType checkIterableConvention(@NotNull ExpressionReceiver loopRange, ExpressionTypingContext context) { JetExpression loopRangeExpression = loopRange.getExpression(); // Make a fake call loopRange.iterator(), and try to resolve it diff --git a/idea/src/org/jetbrains/kotlin/idea/completion/smart/SmartCompletion.kt b/idea/src/org/jetbrains/kotlin/idea/completion/smart/SmartCompletion.kt index a907d6d22d8..de5ee19072c 100644 --- a/idea/src/org/jetbrains/kotlin/idea/completion/smart/SmartCompletion.kt +++ b/idea/src/org/jetbrains/kotlin/idea/completion/smart/SmartCompletion.kt @@ -37,6 +37,9 @@ import org.jetbrains.kotlin.idea.util.FuzzyType import org.jetbrains.kotlin.idea.util.fuzzyReturnType import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor import org.jetbrains.kotlin.psi.psiUtil.getReceiverExpression +import org.jetbrains.kotlin.idea.util.IterableTypesDetector +import org.jetbrains.kotlin.idea.util.nullability +import org.jetbrains.kotlin.idea.util.TypeNullability trait InheritanceItemsSearcher { fun search(nameFilter: (String) -> Boolean, consumer: (LookupElement) -> Unit) @@ -110,6 +113,9 @@ class SmartCompletion( expression } + val loopRangePositionResult = buildForLoopRangePosition(expressionWithType, receiver) + if (loopRangePositionResult != null) return loopRangePositionResult + val allExpectedInfos = calcExpectedInfos(expressionWithType) ?: return null val filteredExpectedInfos = allExpectedInfos.filter { !it.type.isError() } if (filteredExpectedInfos.isEmpty()) return null @@ -363,6 +369,36 @@ class SmartCompletion( return Result(null, items, null) } + private fun buildForLoopRangePosition(expressionWithType: JetExpression, receiver: JetExpression?): Result? { + val forExpression = (expressionWithType.getParent() as? JetContainerNode) + ?.getParent() as? JetForExpression ?: return null + if (expressionWithType != forExpression.getLoopRange()) return null + + val smartCastTypes: (VariableDescriptor) -> Collection = TypesWithSmartCasts(bindingContext).calculate(expressionWithType, receiver) + + val scope = bindingContext.get(BindingContext.RESOLUTION_SCOPE, expressionWithType) + val iterableDetector = IterableTypesDetector(project, moduleDescriptor, scope) + + fun filterDeclaration(descriptor: DeclarationDescriptor): Collection { + val types = descriptor.fuzzyTypes(smartCastTypes) + + fun createLookupElement() = lookupElementFactory.createLookupElement(descriptor, resolutionFacade, bindingContext, true) + + //TODO: use type of variable if declared + if (types.any { iterableDetector.isIterable(it.type) }) { + return listOf(createLookupElement().addTail(Tail.RPARENTH)) + } + + if (types.any { it.nullability() == TypeNullability.NULLABLE && iterableDetector.isIterable(it.type.makeNotNullable()) }) { + return lookupElementsForNullable(::createLookupElement).map { it.addTail(Tail.RPARENTH) } + } + + return listOf() + } + + return Result(::filterDeclaration, listOf(), null) + } + private fun lookupElementForType(jetType: JetType): LookupElement? { if (jetType.isError()) return null val classifier = jetType.getConstructor().getDeclarationDescriptor() ?: return null diff --git a/idea/src/org/jetbrains/kotlin/idea/completion/smart/Utils.kt b/idea/src/org/jetbrains/kotlin/idea/completion/smart/Utils.kt index fb8d23b97f8..226055a739a 100644 --- a/idea/src/org/jetbrains/kotlin/idea/completion/smart/Utils.kt +++ b/idea/src/org/jetbrains/kotlin/idea/completion/smart/Utils.kt @@ -171,6 +171,14 @@ fun MutableCollection.addLoo } private fun MutableCollection.addLookupElementsForNullable(factory: () -> LookupElement?, matchedInfos: Collection) { + for (element in lookupElementsForNullable(factory)) { + add(element.addTailAndNameSimilarity(matchedInfos)) + } +} + +private fun lookupElementsForNullable(factory: () -> LookupElement?): Collection { + val result = ArrayList(2) + var lookupElement = factory() if (lookupElement != null) { lookupElement = object: LookupElementDecorator(lookupElement!!) { @@ -184,7 +192,7 @@ private fun MutableCollection.addLookupElementsForNullable(factor } lookupElement = lookupElement!!.suppressAutoInsertion() lookupElement = lookupElement!!.assignSmartCompletionPriority(SmartCompletionItemPriority.NULLABLE) - add(lookupElement!!.addTailAndNameSimilarity(matchedInfos)) + result.add(lookupElement) } lookupElement = factory() @@ -200,8 +208,10 @@ private fun MutableCollection.addLookupElementsForNullable(factor } lookupElement = lookupElement!!.suppressAutoInsertion() lookupElement = lookupElement!!.assignSmartCompletionPriority(SmartCompletionItemPriority.NULLABLE) - add(lookupElement!!.addTailAndNameSimilarity(matchedInfos)) + result.add(lookupElement) } + + return result } fun functionType(function: FunctionDescriptor): JetType? { diff --git a/idea/src/org/jetbrains/kotlin/idea/liveTemplates/macro/BaseJetVariableMacro.java b/idea/src/org/jetbrains/kotlin/idea/liveTemplates/macro/BaseJetVariableMacro.java index 25d6941d3b4..4f309d84592 100644 --- a/idea/src/org/jetbrains/kotlin/idea/liveTemplates/macro/BaseJetVariableMacro.java +++ b/idea/src/org/jetbrains/kotlin/idea/liveTemplates/macro/BaseJetVariableMacro.java @@ -32,8 +32,8 @@ import org.jetbrains.annotations.Nullable; import org.jetbrains.kotlin.analyzer.AnalysisResult; import org.jetbrains.kotlin.descriptors.DeclarationDescriptor; import org.jetbrains.kotlin.descriptors.VariableDescriptor; -import org.jetbrains.kotlin.di.InjectorForMacros; import org.jetbrains.kotlin.idea.caches.resolve.ResolvePackage; +import org.jetbrains.kotlin.idea.util.IterableTypesDetector; import org.jetbrains.kotlin.idea.util.UtilPackage; import org.jetbrains.kotlin.psi.*; import org.jetbrains.kotlin.resolve.BindingContext; @@ -41,7 +41,6 @@ import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils; import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo; import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter; import org.jetbrains.kotlin.resolve.scopes.JetScope; -import org.jetbrains.kotlin.types.expressions.ExpressionTypingComponents; import java.util.ArrayList; import java.util.LinkedHashSet; @@ -72,8 +71,7 @@ public abstract class BaseJetVariableMacro extends Macro { return null; } - ExpressionTypingComponents components = - new InjectorForMacros(project, analysisResult.getModuleDescriptor()).getExpressionTypingComponents(); + IterableTypesDetector iterableTypesDetector = new IterableTypesDetector(project, analysisResult.getModuleDescriptor(), scope); DataFlowInfo dataFlowInfo = getDataFlowInfo(bindingContext, contextExpression); @@ -88,7 +86,7 @@ public abstract class BaseJetVariableMacro extends Macro { continue; } - if (isSuitable(variableDescriptor, scope, project, components)) { + if (isSuitable(variableDescriptor, project, iterableTypesDetector)) { filteredDescriptors.add(variableDescriptor); } } @@ -110,9 +108,8 @@ public abstract class BaseJetVariableMacro extends Macro { protected abstract boolean isSuitable( @NotNull VariableDescriptor variableDescriptor, - @NotNull JetScope scope, @NotNull Project project, - @NotNull ExpressionTypingComponents components + @NotNull IterableTypesDetector iterableTypesDetector ); @Nullable diff --git a/idea/src/org/jetbrains/kotlin/idea/liveTemplates/macro/JetAnyVariableMacro.java b/idea/src/org/jetbrains/kotlin/idea/liveTemplates/macro/JetAnyVariableMacro.java index 6ec4b7ce783..d243dcc07e2 100644 --- a/idea/src/org/jetbrains/kotlin/idea/liveTemplates/macro/JetAnyVariableMacro.java +++ b/idea/src/org/jetbrains/kotlin/idea/liveTemplates/macro/JetAnyVariableMacro.java @@ -20,8 +20,7 @@ import com.intellij.openapi.project.Project; import org.jetbrains.annotations.NotNull; import org.jetbrains.kotlin.descriptors.VariableDescriptor; import org.jetbrains.kotlin.idea.JetBundle; -import org.jetbrains.kotlin.resolve.scopes.JetScope; -import org.jetbrains.kotlin.types.expressions.ExpressionTypingComponents; +import org.jetbrains.kotlin.idea.util.IterableTypesDetector; public class JetAnyVariableMacro extends BaseJetVariableMacro { @Override @@ -35,7 +34,11 @@ public class JetAnyVariableMacro extends BaseJetVariableMacro { } @Override - protected boolean isSuitable(@NotNull VariableDescriptor variableDescriptor, @NotNull JetScope scope, @NotNull Project project, @NotNull ExpressionTypingComponents components) { + protected boolean isSuitable( + @NotNull VariableDescriptor variableDescriptor, + @NotNull Project project, + @NotNull IterableTypesDetector iterableTypesDetector + ) { return true; } } diff --git a/idea/src/org/jetbrains/kotlin/idea/liveTemplates/macro/JetIterableVariableMacro.java b/idea/src/org/jetbrains/kotlin/idea/liveTemplates/macro/JetIterableVariableMacro.java index 00fe1f3f6cc..001493d5992 100644 --- a/idea/src/org/jetbrains/kotlin/idea/liveTemplates/macro/JetIterableVariableMacro.java +++ b/idea/src/org/jetbrains/kotlin/idea/liveTemplates/macro/JetIterableVariableMacro.java @@ -20,8 +20,7 @@ import com.intellij.openapi.project.Project; import org.jetbrains.annotations.NotNull; import org.jetbrains.kotlin.descriptors.VariableDescriptor; import org.jetbrains.kotlin.idea.JetBundle; -import org.jetbrains.kotlin.resolve.scopes.JetScope; -import org.jetbrains.kotlin.types.expressions.ExpressionTypingComponents; +import org.jetbrains.kotlin.idea.util.IterableTypesDetector; public class JetIterableVariableMacro extends BaseJetVariableMacro { @@ -38,10 +37,10 @@ public class JetIterableVariableMacro extends BaseJetVariableMacro { @Override protected boolean isSuitable( @NotNull VariableDescriptor variableDescriptor, - @NotNull JetScope scope, @NotNull Project project, - @NotNull ExpressionTypingComponents components + @NotNull IterableTypesDetector iterableTypesDetector ) { - return components.getForLoopConventionsChecker().isVariableIterable(variableDescriptor, scope); + //TODO: smart-casts + return iterableTypesDetector.isIterable(variableDescriptor.getType()); } } diff --git a/idea/src/org/jetbrains/kotlin/idea/liveTemplates/macro/JetSuggestVariableNameMacro.java b/idea/src/org/jetbrains/kotlin/idea/liveTemplates/macro/JetSuggestVariableNameMacro.java index 4fb5d9ba0b7..29b964ce9b5 100644 --- a/idea/src/org/jetbrains/kotlin/idea/liveTemplates/macro/JetSuggestVariableNameMacro.java +++ b/idea/src/org/jetbrains/kotlin/idea/liveTemplates/macro/JetSuggestVariableNameMacro.java @@ -20,8 +20,7 @@ import com.intellij.openapi.project.Project; import org.jetbrains.annotations.NotNull; import org.jetbrains.kotlin.descriptors.VariableDescriptor; import org.jetbrains.kotlin.idea.JetBundle; -import org.jetbrains.kotlin.resolve.scopes.JetScope; -import org.jetbrains.kotlin.types.expressions.ExpressionTypingComponents; +import org.jetbrains.kotlin.idea.util.IterableTypesDetector; public class JetSuggestVariableNameMacro extends BaseJetVariableMacro { @Override @@ -37,9 +36,8 @@ public class JetSuggestVariableNameMacro extends BaseJetVariableMacro { @Override protected boolean isSuitable( @NotNull VariableDescriptor variableDescriptor, - @NotNull JetScope scope, @NotNull Project project, - @NotNull ExpressionTypingComponents components + @NotNull IterableTypesDetector iterableTypesDetector ) { return variableDescriptor.getType().isMarkedNullable(); } diff --git a/idea/src/org/jetbrains/kotlin/idea/util/IterableTypesDetector.kt b/idea/src/org/jetbrains/kotlin/idea/util/IterableTypesDetector.kt new file mode 100644 index 00000000000..68206ed6823 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/util/IterableTypesDetector.kt @@ -0,0 +1,65 @@ +/* + * 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.util + +import org.jetbrains.kotlin.types.JetType +import org.jetbrains.kotlin.psi.JetPsiFactory +import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver +import org.jetbrains.kotlin.types.expressions.ExpressionTypingContext +import org.jetbrains.kotlin.resolve.BindingTraceContext +import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo +import org.jetbrains.kotlin.types.TypeUtils +import com.intellij.openapi.project.Project +import org.jetbrains.kotlin.di.InjectorForMacros +import org.jetbrains.kotlin.descriptors.ModuleDescriptor +import org.jetbrains.kotlin.resolve.scopes.JetScope +import java.util.HashMap +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf + +public class IterableTypesDetector( + private val project: Project, + private val moduleDescriptor: ModuleDescriptor, + private val scope: JetScope) { + + private val injector = InjectorForMacros(project, moduleDescriptor) + private val cache = HashMap() + private val iteratorName = Name.identifier("iterator") + + private val typesWithIteratorExtensions: Collection = scope.getFunctions(iteratorName) + .map { it.getExtensionReceiverParameter() } + .filterNotNull() + .map { it.getType() } + + public fun isIterable(type: JetType): Boolean { + return cache.getOrPut(type, { isIterableNoCache(type) }) + } + + private fun isIterableNoCache(type: JetType): Boolean { + // optimization + if (!canBeIterable(type)) return false + + val expression = JetPsiFactory(project).createExpression("fake") + val expressionReceiver = ExpressionReceiver(expression, type) + val context = ExpressionTypingContext.newContext(injector.getExpressionTypingServices(), BindingTraceContext(), scope, DataFlowInfo.EMPTY, TypeUtils.NO_EXPECTED_TYPE) + return injector.getExpressionTypingComponents().getForLoopConventionsChecker().checkIterableConvention(expressionReceiver, context) != null + } + + private fun canBeIterable(type: JetType): Boolean { + return type.getMemberScope().getFunctions(iteratorName).isEmpty() || typesWithIteratorExtensions.any { type.isSubtypeOf(type) } + } +} \ No newline at end of file diff --git a/idea/testData/completion/handlers/smart/ForLoopRange.kt b/idea/testData/completion/handlers/smart/ForLoopRange.kt new file mode 100644 index 00000000000..71b7739234d --- /dev/null +++ b/idea/testData/completion/handlers/smart/ForLoopRange.kt @@ -0,0 +1,13 @@ +trait X +trait Y + +fun X.iterator(): Y +fun Y.next(): Int +fun Y.hasNext(): Boolean + + +fun foo(x: X) { + for (i in ) +} + +// ELEMENT: x diff --git a/idea/testData/completion/handlers/smart/ForLoopRange.kt.after b/idea/testData/completion/handlers/smart/ForLoopRange.kt.after new file mode 100644 index 00000000000..722d4ff5044 --- /dev/null +++ b/idea/testData/completion/handlers/smart/ForLoopRange.kt.after @@ -0,0 +1,13 @@ +trait X +trait Y + +fun X.iterator(): Y +fun Y.next(): Int +fun Y.hasNext(): Boolean + + +fun foo(x: X) { + for (i in x) +} + +// ELEMENT: x diff --git a/idea/testData/completion/handlers/smart/ForLoopRange2.kt b/idea/testData/completion/handlers/smart/ForLoopRange2.kt new file mode 100644 index 00000000000..c4c619f829c --- /dev/null +++ b/idea/testData/completion/handlers/smart/ForLoopRange2.kt @@ -0,0 +1,13 @@ +trait X +trait Y + +fun X.iterator(): Y +fun Y.next(): Int +fun Y.hasNext(): Boolean + + +fun foo(x: X?) { + for (i in ) +} + +// ELEMENT_TEXT: "!! x" diff --git a/idea/testData/completion/handlers/smart/ForLoopRange2.kt.after b/idea/testData/completion/handlers/smart/ForLoopRange2.kt.after new file mode 100644 index 00000000000..f0448511a2e --- /dev/null +++ b/idea/testData/completion/handlers/smart/ForLoopRange2.kt.after @@ -0,0 +1,13 @@ +trait X +trait Y + +fun X.iterator(): Y +fun Y.next(): Int +fun Y.hasNext(): Boolean + + +fun foo(x: X?) { + for (i in x!!) +} + +// ELEMENT_TEXT: "!! x" diff --git a/idea/testData/completion/smart/forLoopRange/AfterDot.kt b/idea/testData/completion/smart/forLoopRange/AfterDot.kt new file mode 100644 index 00000000000..f84c43d3a66 --- /dev/null +++ b/idea/testData/completion/smart/forLoopRange/AfterDot.kt @@ -0,0 +1,9 @@ +fun foo(p: String) { + for (i in p.) +} + +fun String.extFun(): Collection{} + +// EXIST: toString +// EXIST: substring +// EXIST: extFun diff --git a/idea/testData/completion/smart/forLoopRange/ExtensionIteratorMethod.kt b/idea/testData/completion/smart/forLoopRange/ExtensionIteratorMethod.kt new file mode 100644 index 00000000000..253fa123c65 --- /dev/null +++ b/idea/testData/completion/smart/forLoopRange/ExtensionIteratorMethod.kt @@ -0,0 +1,14 @@ +trait X +trait Y + +fun X.iterator(): Y +fun Y.next(): Int +fun Y.hasNext(): Boolean + + +fun foo(x: X, y: Y) { + for (i in ) +} + +// EXIST: x +// ABSENT: y diff --git a/idea/testData/completion/smart/forLoopRange/NoGenericT.kt b/idea/testData/completion/smart/forLoopRange/NoGenericT.kt new file mode 100644 index 00000000000..19fbbc324f0 --- /dev/null +++ b/idea/testData/completion/smart/forLoopRange/NoGenericT.kt @@ -0,0 +1,9 @@ +fun xxx1(): T {} +fun> xxx2(): T {} + +fun foo() { + for (i in ) +} + +// ABSENT: xxx1 +// EXIST: xxx2 diff --git a/idea/testData/completion/smart/forLoopRange/Nullable.kt b/idea/testData/completion/smart/forLoopRange/Nullable.kt new file mode 100644 index 00000000000..144d90c8248 --- /dev/null +++ b/idea/testData/completion/smart/forLoopRange/Nullable.kt @@ -0,0 +1,12 @@ +fun foo(p: String?) { + for (i in ) +} + +fun f(): Collection? {} + +// ABSENT: { lookupString:"p", itemText: "p" } +// EXIST: { lookupString:"p", itemText: "!! p", typeText:"String?" } +// EXIST: { lookupString:"p", itemText: "?: p", typeText:"String?" } +// ABSENT: { lookupString:"f", itemText: "f" } +// EXIST: { lookupString:"f", itemText: "!! f", typeText:"Collection?" } +// EXIST: { lookupString:"f", itemText: "?: f", typeText:"Collection?" } diff --git a/idea/testData/completion/smart/forLoopRange/Simple.kt b/idea/testData/completion/smart/forLoopRange/Simple.kt new file mode 100644 index 00000000000..0019b37a808 --- /dev/null +++ b/idea/testData/completion/smart/forLoopRange/Simple.kt @@ -0,0 +1,16 @@ +fun foo(p1: String, p2: Int, p3: Collection) { + for (i in ) +} + +fun f1(): String{} +fun f2(): Int{} +fun f3(): Iterable{} + +// EXIST: p1 +// ABSENT: p2 +// EXIST: p3 +// EXIST: f1 +// ABSENT: f2 +// EXIST: f3 +// EXIST: { lookupString:"listOf", itemText: "listOf", tailText: "(vararg values: T) (kotlin)", typeText:"List" } + diff --git a/idea/testData/completion/smart/forLoopRange/SmartCasts.kt b/idea/testData/completion/smart/forLoopRange/SmartCasts.kt new file mode 100644 index 00000000000..ad645eb8326 --- /dev/null +++ b/idea/testData/completion/smart/forLoopRange/SmartCasts.kt @@ -0,0 +1,8 @@ +fun foo(p1: Any, p2: Any) { + if (p1 is String) { + for (i in ) + } +} + +// EXIST: p1 +// ABSENT: p2 diff --git a/idea/tests/org/jetbrains/kotlin/completion/JvmSmartCompletionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/completion/JvmSmartCompletionTestGenerated.java index 71584be8a13..86ff7361efd 100644 --- a/idea/tests/org/jetbrains/kotlin/completion/JvmSmartCompletionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/completion/JvmSmartCompletionTestGenerated.java @@ -30,6 +30,7 @@ import java.util.regex.Pattern; @SuppressWarnings("all") @TestMetadata("idea/testData/completion/smart") @TestDataPath("$PROJECT_ROOT") +@InnerTestClasses({JvmSmartCompletionTestGenerated.ForLoopRange.class}) @RunWith(JUnit3RunnerWithInners.class) public class JvmSmartCompletionTestGenerated extends AbstractJvmSmartCompletionTest { @TestMetadata("AfterAs.kt") @@ -935,4 +936,49 @@ public class JvmSmartCompletionTestGenerated extends AbstractJvmSmartCompletionT String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/smart/WrongReceiver.kt"); doTest(fileName); } + + @TestMetadata("idea/testData/completion/smart/forLoopRange") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class ForLoopRange extends AbstractJvmSmartCompletionTest { + @TestMetadata("AfterDot.kt") + public void testAfterDot() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/smart/forLoopRange/AfterDot.kt"); + doTest(fileName); + } + + public void testAllFilesPresentInForLoopRange() throws Exception { + JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/completion/smart/forLoopRange"), Pattern.compile("^(.+)\\.kt$"), true); + } + + @TestMetadata("ExtensionIteratorMethod.kt") + public void testExtensionIteratorMethod() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/smart/forLoopRange/ExtensionIteratorMethod.kt"); + doTest(fileName); + } + + @TestMetadata("NoGenericT.kt") + public void testNoGenericT() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/smart/forLoopRange/NoGenericT.kt"); + doTest(fileName); + } + + @TestMetadata("Nullable.kt") + public void testNullable() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/smart/forLoopRange/Nullable.kt"); + doTest(fileName); + } + + @TestMetadata("Simple.kt") + public void testSimple() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/smart/forLoopRange/Simple.kt"); + doTest(fileName); + } + + @TestMetadata("SmartCasts.kt") + public void testSmartCasts() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/smart/forLoopRange/SmartCasts.kt"); + doTest(fileName); + } + } } diff --git a/idea/tests/org/jetbrains/kotlin/completion/handlers/SmartCompletionHandlerTestGenerated.java b/idea/tests/org/jetbrains/kotlin/completion/handlers/SmartCompletionHandlerTestGenerated.java index 922416f609a..be4ffefbab1 100644 --- a/idea/tests/org/jetbrains/kotlin/completion/handlers/SmartCompletionHandlerTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/completion/handlers/SmartCompletionHandlerTestGenerated.java @@ -312,6 +312,18 @@ public class SmartCompletionHandlerTestGenerated extends AbstractSmartCompletion doTest(fileName); } + @TestMetadata("ForLoopRange.kt") + public void testForLoopRange() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/handlers/smart/ForLoopRange.kt"); + doTest(fileName); + } + + @TestMetadata("ForLoopRange2.kt") + public void testForLoopRange2() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/handlers/smart/ForLoopRange2.kt"); + doTest(fileName); + } + @TestMetadata("FunctionReference1.kt") public void testFunctionReference1() throws Exception { String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/handlers/smart/FunctionReference1.kt");