IterableVariableMacro to honor smart casts

This commit is contained in:
Valentin Kipyatkov
2015-11-17 16:33:18 +03:00
parent 51647853f3
commit 0e740dc0a6
8 changed files with 29 additions and 20 deletions
@@ -25,7 +25,7 @@ import org.jetbrains.kotlin.descriptors.VariableDescriptor
import org.jetbrains.kotlin.idea.KotlinDescriptorIconProvider import org.jetbrains.kotlin.idea.KotlinDescriptorIconProvider
import org.jetbrains.kotlin.idea.completion.ArgumentPositionData import org.jetbrains.kotlin.idea.completion.ArgumentPositionData
import org.jetbrains.kotlin.idea.completion.ExpectedInfo import org.jetbrains.kotlin.idea.completion.ExpectedInfo
import org.jetbrains.kotlin.idea.completion.SmartCastCalculator import org.jetbrains.kotlin.idea.core.SmartCastCalculator
import org.jetbrains.kotlin.idea.completion.Tail import org.jetbrains.kotlin.idea.completion.Tail
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
import org.jetbrains.kotlin.idea.util.getResolutionScope import org.jetbrains.kotlin.idea.util.getResolutionScope
@@ -27,6 +27,7 @@ import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor
import org.jetbrains.kotlin.idea.completion.* import org.jetbrains.kotlin.idea.completion.*
import org.jetbrains.kotlin.idea.completion.handlers.WithTailInsertHandler import org.jetbrains.kotlin.idea.completion.handlers.WithTailInsertHandler
import org.jetbrains.kotlin.idea.core.SmartCastCalculator
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
import org.jetbrains.kotlin.idea.util.CallTypeAndReceiver import org.jetbrains.kotlin.idea.util.CallTypeAndReceiver
import org.jetbrains.kotlin.idea.util.FuzzyType import org.jetbrains.kotlin.idea.util.FuzzyType
@@ -81,7 +82,7 @@ class SmartCompletion(
private val callableTypeExpectedInfo = expectedInfos.filterCallableExpected() private val callableTypeExpectedInfo = expectedInfos.filterCallableExpected()
public val smartCastCalculator: SmartCastCalculator by lazy(LazyThreadSafetyMode.NONE) { public val smartCastCalculator: SmartCastCalculator by lazy(LazyThreadSafetyMode.NONE) {
SmartCastCalculator(bindingContext, resolutionFacade.moduleDescriptor, expression, resolutionFacade) SmartCastCalculator(bindingContext, resolutionFacade.moduleDescriptor, expression, callTypeAndReceiver.receiver as? KtExpression, resolutionFacade)
} }
public val descriptorFilter: ((DeclarationDescriptor, AbstractLookupElementFactory) -> Collection<LookupElement>)? = public val descriptorFilter: ((DeclarationDescriptor, AbstractLookupElementFactory) -> Collection<LookupElement>)? =
@@ -29,6 +29,7 @@ import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor
import org.jetbrains.kotlin.idea.completion.* import org.jetbrains.kotlin.idea.completion.*
import org.jetbrains.kotlin.idea.completion.handlers.WithExpressionPrefixInsertHandler import org.jetbrains.kotlin.idea.completion.handlers.WithExpressionPrefixInsertHandler
import org.jetbrains.kotlin.idea.completion.handlers.WithTailInsertHandler import org.jetbrains.kotlin.idea.completion.handlers.WithTailInsertHandler
import org.jetbrains.kotlin.idea.core.SmartCastCalculator
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
import org.jetbrains.kotlin.idea.util.* import org.jetbrains.kotlin.idea.util.*
import org.jetbrains.kotlin.resolve.callableReferences.getReflectionTypeForCandidateDescriptor import org.jetbrains.kotlin.resolve.callableReferences.getReflectionTypeForCandidateDescriptor
@@ -14,19 +14,18 @@
* limitations under the License. * limitations under the License.
*/ */
package org.jetbrains.kotlin.idea.completion package org.jetbrains.kotlin.idea.core
import com.intellij.openapi.util.Pair import com.intellij.openapi.util.Pair
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.descriptors.ClassifierDescriptor import org.jetbrains.kotlin.descriptors.ClassifierDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.ReceiverParameterDescriptor import org.jetbrains.kotlin.descriptors.ReceiverParameterDescriptor
import org.jetbrains.kotlin.descriptors.VariableDescriptor import org.jetbrains.kotlin.descriptors.VariableDescriptor
import org.jetbrains.kotlin.idea.util.getResolutionScope
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
import org.jetbrains.kotlin.idea.util.getImplicitReceiversWithInstance import org.jetbrains.kotlin.idea.util.getImplicitReceiversWithInstance
import org.jetbrains.kotlin.idea.util.getResolutionScope
import org.jetbrains.kotlin.psi.KtExpression import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.psi.KtSimpleNameExpression
import org.jetbrains.kotlin.psi.psiUtil.getReceiverExpression
import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.bindingContextUtil.getDataFlowInfo import org.jetbrains.kotlin.resolve.bindingContextUtil.getDataFlowInfo
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo
@@ -43,15 +42,14 @@ import java.util.*
class SmartCastCalculator( class SmartCastCalculator(
val bindingContext: BindingContext, val bindingContext: BindingContext,
val containingDeclarationOrModule: DeclarationDescriptor, val containingDeclarationOrModule: DeclarationDescriptor,
expression: KtExpression, contextElement: PsiElement,
receiver: KtExpression?,
resolutionFacade: ResolutionFacade resolutionFacade: ResolutionFacade
) { ) {
private val receiver = if (expression is KtSimpleNameExpression) expression.getReceiverExpression() else null
// keys are VariableDescriptor's and ThisReceiver's // keys are VariableDescriptor's and ThisReceiver's
private val entityToSmartCastInfo: Map<Any, SmartCastInfo> = processDataFlowInfo( private val entityToSmartCastInfo: Map<Any, SmartCastInfo> = processDataFlowInfo(
bindingContext.getDataFlowInfo(expression), bindingContext.getDataFlowInfo(contextElement),
expression.getResolutionScope(bindingContext, resolutionFacade), contextElement.getResolutionScope(bindingContext, resolutionFacade),
receiver) receiver)
fun types(descriptor: VariableDescriptor): Collection<KotlinType> { fun types(descriptor: VariableDescriptor): Collection<KotlinType> {
@@ -114,7 +112,7 @@ class SmartCastCalculator(
val entityToInfo = HashMap<Any, SmartCastInfo>() val entityToInfo = HashMap<Any, SmartCastInfo>()
for ((dataFlowValue, types) in dataFlowInfo.completeTypeInfo.asMap().entrySet()) { for ((dataFlowValue, types) in dataFlowInfo.completeTypeInfo.asMap().entries) {
val entity = dataFlowValueToEntity.invoke(dataFlowValue) val entity = dataFlowValueToEntity.invoke(dataFlowValue)
if (entity != null) { if (entity != null) {
entityToInfo[entity] = SmartCastInfo(types, false) entityToInfo[entity] = SmartCastInfo(types, false)
@@ -23,6 +23,7 @@ import org.jetbrains.kotlin.descriptors.VariableDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
import org.jetbrains.kotlin.idea.core.IterableTypesDetection import org.jetbrains.kotlin.idea.core.IterableTypesDetection
import org.jetbrains.kotlin.idea.core.IterableTypesDetector import org.jetbrains.kotlin.idea.core.IterableTypesDetector
import org.jetbrains.kotlin.idea.core.SmartCastCalculator
import org.jetbrains.kotlin.idea.util.getResolutionScope import org.jetbrains.kotlin.idea.util.getResolutionScope
import org.jetbrains.kotlin.psi.KtElement import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingContext
@@ -30,6 +31,7 @@ import org.jetbrains.kotlin.resolve.BindingContext
class IterableVariableMacro : BaseKotlinVariableMacro() { class IterableVariableMacro : BaseKotlinVariableMacro() {
private companion object { private companion object {
val ITERABLE_TYPES_DETECTOR_KEY = Key<IterableTypesDetector>("ITERABLE_TYPES_DETECTOR_KEY") val ITERABLE_TYPES_DETECTOR_KEY = Key<IterableTypesDetector>("ITERABLE_TYPES_DETECTOR_KEY")
val SMART_CAST_CALCULATOR_KEY = Key<SmartCastCalculator>("SMART_CAST_CALCULATOR_KEY")
} }
override fun getName() = "kotlinIterableVariable" override fun getName() = "kotlinIterableVariable"
@@ -39,12 +41,15 @@ class IterableVariableMacro : BaseKotlinVariableMacro() {
val resolutionFacade = contextElement.getResolutionFacade() val resolutionFacade = contextElement.getResolutionFacade()
val scope = contextElement.getResolutionScope(bindingContext, resolutionFacade) val scope = contextElement.getResolutionScope(bindingContext, resolutionFacade)
val detector = resolutionFacade.getIdeService(IterableTypesDetection::class.java).createDetector(scope) val detector = resolutionFacade.getIdeService(IterableTypesDetection::class.java).createDetector(scope)
val smartCastCalculator = SmartCastCalculator(bindingContext, scope.ownerDescriptor, contextElement, null, resolutionFacade)
userData.putUserData(ITERABLE_TYPES_DETECTOR_KEY, detector) userData.putUserData(ITERABLE_TYPES_DETECTOR_KEY, detector)
userData.putUserData(SMART_CAST_CALCULATOR_KEY, smartCastCalculator)
} }
override fun isSuitable(variableDescriptor: VariableDescriptor, project: Project, userData: UserDataHolder): Boolean { override fun isSuitable(variableDescriptor: VariableDescriptor, project: Project, userData: UserDataHolder): Boolean {
val detector = userData.getUserData(ITERABLE_TYPES_DETECTOR_KEY)!! val detector = userData.getUserData(ITERABLE_TYPES_DETECTOR_KEY)!!
//TODO: smart-casts val smartCastCalculator = userData.getUserData(SMART_CAST_CALCULATOR_KEY)!!
return detector.isIterable(variableDescriptor.type, null) val types = smartCastCalculator.types(variableDescriptor)
return types.any { detector.isIterable(it, null) }
} }
} }
+5 -3
View File
@@ -7,12 +7,14 @@ class MyClass {
public var collection : HashSet<Int>? = null public var collection : HashSet<Int>? = null
private var isAlive : Boolean = false private var isAlive : Boolean = false
fun main(args : Array<String>, v : Int) { fun main(args : Array<String>, v : Int, o: Any) {
var str = "" var str = ""
val myList = ArrayList<String>() val myList = ArrayList<String>()
val stream = FileInputStream(".") val stream = FileInputStream(".")
for (collection in args) { if (o is String) {
<caret> for (collection in args) {
<caret>
}
} }
} }
} }
+4 -2
View File
@@ -7,10 +7,12 @@ class MyClass {
public var collection : HashSet<Int>? = null public var collection : HashSet<Int>? = null
private var isAlive : Boolean = false private var isAlive : Boolean = false
fun main(args : Array<String>, v : Int) { fun main(args : Array<String>, v : Int, o: Any) {
var str = "" var str = ""
val myList = ArrayList<String>() val myList = ArrayList<String>()
val stream = FileInputStream(".") val stream = FileInputStream(".")
<caret> if (o is String) {
<caret>
}
} }
} }
@@ -198,7 +198,7 @@ public class LiveTemplatesTest extends KotlinLightCodeInsightFixtureTestCase {
public void testIter() { public void testIter() {
start(); start();
assertStringItems("args", "myList", "str", "stream"); assertStringItems("args", "myList", "o", "str", "stream");
type("args"); type("args");
nextTab(2); nextTab(2);