Smart completion for for loop range to use loop variable type if defined
This commit is contained in:
@@ -377,14 +377,23 @@ class SmartCompletion(
|
|||||||
val smartCastTypes: (VariableDescriptor) -> Collection<JetType> = TypesWithSmartCasts(bindingContext).calculate(expressionWithType, receiver)
|
val smartCastTypes: (VariableDescriptor) -> Collection<JetType> = TypesWithSmartCasts(bindingContext).calculate(expressionWithType, receiver)
|
||||||
|
|
||||||
val scope = bindingContext.get(BindingContext.RESOLUTION_SCOPE, expressionWithType)
|
val scope = bindingContext.get(BindingContext.RESOLUTION_SCOPE, expressionWithType)
|
||||||
val iterableDetector = IterableTypesDetector(project, moduleDescriptor, scope)
|
|
||||||
|
val loopVar = forExpression.getLoopParameter()
|
||||||
|
val loopVarType = if (loopVar != null && loopVar.getTypeReference() != null) {
|
||||||
|
val type = (resolutionFacade.resolveToDescriptor(loopVar) as VariableDescriptor).getType()
|
||||||
|
if (type.isError()) null else type
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
|
||||||
|
val iterableDetector = IterableTypesDetector(project, moduleDescriptor, scope, loopVarType)
|
||||||
|
|
||||||
fun filterDeclaration(descriptor: DeclarationDescriptor): Collection<LookupElement> {
|
fun filterDeclaration(descriptor: DeclarationDescriptor): Collection<LookupElement> {
|
||||||
val types = descriptor.fuzzyTypes(smartCastTypes)
|
val types = descriptor.fuzzyTypes(smartCastTypes)
|
||||||
|
|
||||||
fun createLookupElement() = lookupElementFactory.createLookupElement(descriptor, resolutionFacade, bindingContext, true)
|
fun createLookupElement() = lookupElementFactory.createLookupElement(descriptor, resolutionFacade, bindingContext, true)
|
||||||
|
|
||||||
//TODO: use type of variable if declared
|
|
||||||
if (types.any { iterableDetector.isIterable(it.type) }) {
|
if (types.any { iterableDetector.isIterable(it.type) }) {
|
||||||
return listOf(createLookupElement().addTail(Tail.RPARENTH))
|
return listOf(createLookupElement().addTail(Tail.RPARENTH))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ public abstract class BaseJetVariableMacro extends Macro {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
IterableTypesDetector iterableTypesDetector = new IterableTypesDetector(project, analysisResult.getModuleDescriptor(), scope);
|
IterableTypesDetector iterableTypesDetector = new IterableTypesDetector(project, analysisResult.getModuleDescriptor(), scope, null);
|
||||||
|
|
||||||
DataFlowInfo dataFlowInfo = getDataFlowInfo(bindingContext, contextExpression);
|
DataFlowInfo dataFlowInfo = getDataFlowInfo(bindingContext, contextExpression);
|
||||||
|
|
||||||
|
|||||||
@@ -34,7 +34,9 @@ import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
|
|||||||
public class IterableTypesDetector(
|
public class IterableTypesDetector(
|
||||||
private val project: Project,
|
private val project: Project,
|
||||||
private val moduleDescriptor: ModuleDescriptor,
|
private val moduleDescriptor: ModuleDescriptor,
|
||||||
private val scope: JetScope) {
|
private val scope: JetScope,
|
||||||
|
private val loopVarType: JetType? = null
|
||||||
|
) {
|
||||||
|
|
||||||
private val injector = InjectorForMacros(project, moduleDescriptor)
|
private val injector = InjectorForMacros(project, moduleDescriptor)
|
||||||
private val cache = HashMap<JetType, Boolean>()
|
private val cache = HashMap<JetType, Boolean>()
|
||||||
@@ -56,7 +58,9 @@ public class IterableTypesDetector(
|
|||||||
val expression = JetPsiFactory(project).createExpression("fake")
|
val expression = JetPsiFactory(project).createExpression("fake")
|
||||||
val expressionReceiver = ExpressionReceiver(expression, type)
|
val expressionReceiver = ExpressionReceiver(expression, type)
|
||||||
val context = ExpressionTypingContext.newContext(injector.getExpressionTypingServices(), BindingTraceContext(), scope, DataFlowInfo.EMPTY, TypeUtils.NO_EXPECTED_TYPE)
|
val context = ExpressionTypingContext.newContext(injector.getExpressionTypingServices(), BindingTraceContext(), scope, DataFlowInfo.EMPTY, TypeUtils.NO_EXPECTED_TYPE)
|
||||||
return injector.getExpressionTypingComponents().getForLoopConventionsChecker().checkIterableConvention(expressionReceiver, context) != null
|
val elementType = injector.getExpressionTypingComponents().getForLoopConventionsChecker().checkIterableConvention(expressionReceiver, context)
|
||||||
|
if (elementType == null) return false
|
||||||
|
return loopVarType == null || elementType.isSubtypeOf(loopVarType)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun canBeIterable(type: JetType): Boolean {
|
private fun canBeIterable(type: JetType): Boolean {
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
fun foo(p1: Collection<String>, p2: Collection<Int>, p3: Collection<String?>) {
|
||||||
|
for (i: Any in <caret>)
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXIST: p1
|
||||||
|
// EXIST: p2
|
||||||
|
// ABSENT: p3
|
||||||
|
// EXIST: { lookupString:"listOf", itemText: "listOf", tailText: "(vararg values: T) (kotlin)", typeText:"List<T>" }
|
||||||
@@ -980,5 +980,11 @@ public class JvmSmartCompletionTestGenerated extends AbstractJvmSmartCompletionT
|
|||||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/smart/forLoopRange/SmartCasts.kt");
|
String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/smart/forLoopRange/SmartCasts.kt");
|
||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("TypedLoopVar.kt")
|
||||||
|
public void testTypedLoopVar() throws Exception {
|
||||||
|
String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/smart/forLoopRange/TypedLoopVar.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user