diff --git a/core/descriptors/src/org/jetbrains/kotlin/types/TypeUtils.kt b/core/descriptors/src/org/jetbrains/kotlin/types/TypeUtils.kt index c84d423bdb4..0f32b37c9a9 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/types/TypeUtils.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/types/TypeUtils.kt @@ -22,7 +22,6 @@ import org.jetbrains.kotlin.descriptors.annotations.Annotations import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.calls.inference.isCaptured -import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe import org.jetbrains.kotlin.types.* import org.jetbrains.kotlin.types.checker.KotlinTypeChecker @@ -60,7 +59,6 @@ fun KotlinType.isBoolean(): Boolean = KotlinBuiltIns.isBoolean(this) fun KotlinType.isPrimitiveNumberType(): Boolean = KotlinBuiltIns.isPrimitiveType(this) && !isBoolean() fun KotlinType.isBooleanOrNullableBoolean(): Boolean = KotlinBuiltIns.isBooleanOrNullableBoolean(this) fun KotlinType.isThrowable(): Boolean = isConstructedFromClassWithGivenFqName(KotlinBuiltIns.FQ_NAMES.throwable) && !isMarkedNullable -fun KotlinType.isIterator(): Boolean = isConstructedFromClassWithGivenFqName(KotlinBuiltIns.FQ_NAMES.iterator) && !isMarkedNullable fun KotlinType.isConstructedFromClassWithGivenFqName(fqName: FqName) = (constructor.declarationDescriptor as? ClassDescriptor)?.fqNameUnsafe == fqName.toUnsafe() @@ -207,4 +205,4 @@ fun KotlinType.containsTypeProjectionsInTopLevelArguments(): Boolean { if (isError) return false val possiblyInnerType = buildPossiblyInnerType() ?: return false return possiblyInnerType.arguments.any { it.isStarProjection || it.projectionKind != Variance.INVARIANT } -} \ No newline at end of file +} diff --git a/idea/src/org/jetbrains/kotlin/idea/codeInsight/postfix/stringBasedPostfixTemplates.kt b/idea/src/org/jetbrains/kotlin/idea/codeInsight/postfix/stringBasedPostfixTemplates.kt index fc733a10957..b9b3fd9c9ee 100644 --- a/idea/src/org/jetbrains/kotlin/idea/codeInsight/postfix/stringBasedPostfixTemplates.kt +++ b/idea/src/org/jetbrains/kotlin/idea/codeInsight/postfix/stringBasedPostfixTemplates.kt @@ -22,12 +22,16 @@ import com.intellij.codeInsight.template.impl.MacroCallNode import com.intellij.codeInsight.template.postfix.templates.PostfixTemplateExpressionSelector import com.intellij.codeInsight.template.postfix.templates.StringBasedPostfixTemplate import com.intellij.psi.PsiElement +import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade +import org.jetbrains.kotlin.idea.core.IterableTypesDetection import org.jetbrains.kotlin.idea.liveTemplates.macro.SuggestVariableNameMacro -import org.jetbrains.kotlin.incremental.components.NoLookupLocation +import org.jetbrains.kotlin.idea.resolve.ideService +import org.jetbrains.kotlin.idea.util.getResolutionScope +import org.jetbrains.kotlin.psi.KtExpression +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.calls.callUtil.getType import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.typeUtil.isBoolean -import org.jetbrains.kotlin.types.typeUtil.isIterator -import org.jetbrains.kotlin.util.OperatorNameConventions internal abstract class ConstantStringBasedPostfixTemplate( name: String, @@ -46,7 +50,7 @@ internal class KtForEachPostfixTemplate( name, "for (item in expr)", "for (\$name$ in \$expr$) {\n \$END$\n}", - createExpressionSelector(statementsOnly = true, typePredicate = KotlinType::containsIteratorMethod) + createExpressionSelectorWithComplexFilter(statementsOnly = true, predicate = KtExpression::hasIterableType) ) { override fun setVariables(template: Template, element: PsiElement) { val name = MacroCallNode(SuggestVariableNameMacro()) @@ -54,10 +58,13 @@ internal class KtForEachPostfixTemplate( } } -private fun KotlinType.containsIteratorMethod() = - memberScope.getContributedFunctions(OperatorNameConventions.ITERATOR, NoLookupLocation.FROM_IDE).any { - it.returnType?.isIterator() ?: false && it.valueParameters.isEmpty() - } +private fun KtExpression.hasIterableType(bindingContext: BindingContext): Boolean { + val resolutionFacade = getResolutionFacade() + val type = getType(bindingContext) ?: return false + val scope = getResolutionScope(bindingContext, resolutionFacade) + val detector = resolutionFacade.ideService().createDetector(scope) + return detector.isIterable(type) +} internal object KtAssertPostfixTemplate : ConstantStringBasedPostfixTemplate( "assert", diff --git a/idea/testData/codeInsight/postfix/forOnMap.kt b/idea/testData/codeInsight/postfix/forOnMap.kt new file mode 100644 index 00000000000..cfc940e3d01 --- /dev/null +++ b/idea/testData/codeInsight/postfix/forOnMap.kt @@ -0,0 +1,3 @@ +fun foo(map: Map) { + map.for +} diff --git a/idea/testData/codeInsight/postfix/forOnMap.kt.after b/idea/testData/codeInsight/postfix/forOnMap.kt.after new file mode 100644 index 00000000000..e43b9823f11 --- /dev/null +++ b/idea/testData/codeInsight/postfix/forOnMap.kt.after @@ -0,0 +1,5 @@ +fun foo(map: Map) { + for (entry in map) { + + } +} diff --git a/idea/testData/codeInsight/postfix/forOnString.kt b/idea/testData/codeInsight/postfix/forOnString.kt new file mode 100644 index 00000000000..d6a70d96e6e --- /dev/null +++ b/idea/testData/codeInsight/postfix/forOnString.kt @@ -0,0 +1,3 @@ +fun foo(list: List) { + list[0].for +} diff --git a/idea/testData/codeInsight/postfix/forOnString.kt.after b/idea/testData/codeInsight/postfix/forOnString.kt.after new file mode 100644 index 00000000000..55c5494fa86 --- /dev/null +++ b/idea/testData/codeInsight/postfix/forOnString.kt.after @@ -0,0 +1,5 @@ +fun foo(list: List) { + for (c in list[0]) { + + } +} diff --git a/idea/tests/org/jetbrains/kotlin/idea/codeInsight/postfix/PostfixTemplateProviderTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/codeInsight/postfix/PostfixTemplateProviderTestGenerated.java index 12738fcbd99..6240d791536 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/codeInsight/postfix/PostfixTemplateProviderTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/codeInsight/postfix/PostfixTemplateProviderTestGenerated.java @@ -66,6 +66,18 @@ public class PostfixTemplateProviderTestGenerated extends AbstractPostfixTemplat doTest(fileName); } + @TestMetadata("forOnMap.kt") + public void testForOnMap() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/codeInsight/postfix/forOnMap.kt"); + doTest(fileName); + } + + @TestMetadata("forOnString.kt") + public void testForOnString() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/codeInsight/postfix/forOnString.kt"); + doTest(fileName); + } + @TestMetadata("if.kt") public void testIf() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/codeInsight/postfix/if.kt");