Refine predicate for 'iter' postfix template
Use IterableTypesDetection to determine if the given expression may be iterated #KT-14134 Fixed #KT-14129 Fixed
This commit is contained in:
@@ -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 }
|
||||
}
|
||||
}
|
||||
|
||||
+15
-8
@@ -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<IterableTypesDetection>().createDetector(scope)
|
||||
return detector.isIterable(type)
|
||||
}
|
||||
|
||||
internal object KtAssertPostfixTemplate : ConstantStringBasedPostfixTemplate(
|
||||
"assert",
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
fun foo(map: Map<String, Int>) {
|
||||
map.for<caret>
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
fun foo(map: Map<String, Int>) {
|
||||
for (entry in map) {
|
||||
<caret>
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fun foo(list: List<String>) {
|
||||
list[0].for<caret>
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
fun foo(list: List<String>) {
|
||||
for (c in list[0]) {
|
||||
<caret>
|
||||
}
|
||||
}
|
||||
+12
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user