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:
Denis Zharkov
2017-03-10 18:13:13 +03:00
parent 465a424af4
commit bd88919411
7 changed files with 44 additions and 11 deletions
@@ -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 }
}
}
@@ -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",
+3
View File
@@ -0,0 +1,3 @@
fun foo(map: Map<String, Int>) {
map.for<caret>
}
+5
View File
@@ -0,0 +1,5 @@
fun foo(map: Map<String, Int>) {
for (entry in map) {
<caret>
}
}
+3
View File
@@ -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>
}
}
@@ -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");