diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/ExpectedInfos.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/ExpectedInfos.kt index 232fa73ee14..1d151cbd8f0 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/ExpectedInfos.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/ExpectedInfos.kt @@ -22,6 +22,7 @@ import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.idea.caches.resolve.ResolutionFacade import org.jetbrains.kotlin.idea.caches.resolve.getService import org.jetbrains.kotlin.idea.completion.smart.TypesWithContainsDetector +import org.jetbrains.kotlin.idea.core.IterableTypesDetection import org.jetbrains.kotlin.idea.core.IterableTypesDetector import org.jetbrains.kotlin.idea.core.mapArgumentsToParameters import org.jetbrains.kotlin.idea.util.FuzzyType @@ -506,7 +507,7 @@ class ExpectedInfos( null val scope = bindingContext.get(BindingContext.RESOLUTION_SCOPE, expressionWithType)!! - val iterableDetector = IterableTypesDetector(forExpression.project, moduleDescriptor, scope) + val iterableDetector = resolutionFacade.ideService(forExpression).createDetector(scope) val byTypeFilter = object : ByTypeFilter { override fun matchingSubstitutor(descriptorType: FuzzyType): TypeSubstitutor? { diff --git a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/IterableTypesDetection.kt b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/IterableTypesDetection.kt new file mode 100644 index 00000000000..bc3eb7d740f --- /dev/null +++ b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/IterableTypesDetection.kt @@ -0,0 +1,94 @@ +/* + * Copyright 2010-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.idea.core + +import com.intellij.openapi.project.Project +import org.jetbrains.kotlin.descriptors.ModuleDescriptor +import org.jetbrains.kotlin.idea.util.FuzzyType +import org.jetbrains.kotlin.incremental.components.NoLookupLocation +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.psi.JetPsiFactory +import org.jetbrains.kotlin.resolve.BindingTraceContext +import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo +import org.jetbrains.kotlin.resolve.scopes.JetScope +import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver +import org.jetbrains.kotlin.types.JetType +import org.jetbrains.kotlin.types.TypeUtils +import org.jetbrains.kotlin.types.expressions.ExpressionTypingContext +import org.jetbrains.kotlin.types.expressions.ForLoopConventionsChecker +import java.util.HashMap + +public class IterableTypesDetection( + private val project: Project, + private val moduleDescriptor: ModuleDescriptor, + private val forLoopConventionsChecker: ForLoopConventionsChecker +) { + companion object { + private val iteratorName = Name.identifier("iterator") + } + + public fun createDetector(scope: JetScope): IterableTypesDetector { + return Detector(scope) + } + private inner class Detector(private val scope: JetScope): IterableTypesDetector { + private val cache = HashMap() + + private val typesWithExtensionIterator: Collection = scope.getFunctions(iteratorName, NoLookupLocation.FROM_IDE) + .map { it.getExtensionReceiverParameter() } + .filterNotNull() + .map { it.getType() } + + override fun isIterable(type: FuzzyType, loopVarType: JetType?): Boolean { + val elementType = elementType(type) ?: return false + return loopVarType == null || elementType.checkIsSubtypeOf(loopVarType) != null + } + + override fun isIterable(type: JetType, loopVarType: JetType?): Boolean + = isIterable(FuzzyType(type, emptyList()), loopVarType) + + private fun elementType(type: FuzzyType): FuzzyType? { + return cache.getOrPut(type, { elementTypeNoCache(type) }) + } + + override fun elementType(type: JetType): FuzzyType? + = elementType(FuzzyType(type, emptyList())) + + private fun elementTypeNoCache(type: FuzzyType): FuzzyType? { + // optimization + if (!canBeIterable(type)) return null + + val expression = JetPsiFactory(project).createExpression("fake") + val expressionReceiver = ExpressionReceiver(expression, type.type) + val context = ExpressionTypingContext.newContext(BindingTraceContext(), scope, DataFlowInfo.EMPTY, TypeUtils.NO_EXPECTED_TYPE) + val elementType = forLoopConventionsChecker.checkIterableConvention(expressionReceiver, context) + return elementType?.let { FuzzyType(it, type.freeParameters) } + } + + private fun canBeIterable(type: FuzzyType): Boolean { + return type.type.memberScope.getFunctions(iteratorName, NoLookupLocation.FROM_IDE).isNotEmpty() || + typesWithExtensionIterator.any { type.checkIsSubtypeOf(it) != null } + } + } +} + +public interface IterableTypesDetector { + public fun isIterable(type: JetType, loopVarType: JetType? = null): Boolean + + public fun isIterable(type: FuzzyType, loopVarType: JetType? = null): Boolean + + public fun elementType(type: JetType): FuzzyType? +} \ No newline at end of file diff --git a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/IterableTypesDetector.kt b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/IterableTypesDetector.kt deleted file mode 100644 index ec290e8020f..00000000000 --- a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/IterableTypesDetector.kt +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright 2010-2015 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.jetbrains.kotlin.idea.core - -import com.intellij.openapi.project.Project -import org.jetbrains.kotlin.descriptors.ModuleDescriptor -import org.jetbrains.kotlin.frontend.di.createContainerForMacros -import org.jetbrains.kotlin.idea.util.FuzzyType -import org.jetbrains.kotlin.incremental.components.NoLookupLocation -import org.jetbrains.kotlin.name.Name -import org.jetbrains.kotlin.psi.JetPsiFactory -import org.jetbrains.kotlin.resolve.BindingTraceContext -import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo -import org.jetbrains.kotlin.resolve.scopes.JetScope -import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver -import org.jetbrains.kotlin.types.JetType -import org.jetbrains.kotlin.types.TypeUtils -import org.jetbrains.kotlin.types.expressions.ExpressionTypingContext -import java.util.HashMap - -public class IterableTypesDetector( - private val project: Project, - private val moduleDescriptor: ModuleDescriptor, - private val scope: JetScope -) { - - private val container = createContainerForMacros(project, moduleDescriptor) - private val cache = HashMap() - private val iteratorName = Name.identifier("iterator") - - private val typesWithExtensionIterator: Collection = scope.getFunctions(iteratorName, NoLookupLocation.FROM_IDE) - .map { it.getExtensionReceiverParameter() } - .filterNotNull() - .map { it.getType() } - - public fun isIterable(type: FuzzyType, loopVarType: JetType? = null): Boolean { - val elementType = elementType(type) ?: return false - return loopVarType == null || elementType.checkIsSubtypeOf(loopVarType) != null - } - - public fun isIterable(type: JetType, loopVarType: JetType? = null): Boolean - = isIterable(FuzzyType(type, emptyList()), loopVarType) - - public fun elementType(type: FuzzyType): FuzzyType? { - return cache.getOrPut(type, { elementTypeNoCache(type) }) - } - - public fun elementType(type: JetType): FuzzyType? - = elementType(FuzzyType(type, emptyList())) - - private fun elementTypeNoCache(type: FuzzyType): FuzzyType? { - // optimization - if (!canBeIterable(type)) return null - - val expression = JetPsiFactory(project).createExpression("fake") - val expressionReceiver = ExpressionReceiver(expression, type.type) - val expressionTypingComponents = container.expressionTypingComponents - val context = ExpressionTypingContext.newContext(BindingTraceContext(), scope, DataFlowInfo.EMPTY, TypeUtils.NO_EXPECTED_TYPE) - val elementType = expressionTypingComponents.getForLoopConventionsChecker().checkIterableConvention(expressionReceiver, context) - return elementType?.let { FuzzyType(it, type.freeParameters) } - } - - private fun canBeIterable(type: FuzzyType): Boolean { - return type.type.memberScope.getFunctions(iteratorName, NoLookupLocation.FROM_IDE).isNotEmpty() || - typesWithExtensionIterator.any { type.checkIsSubtypeOf(it) != null } - } -} diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/IterateExpressionIntention.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/IterateExpressionIntention.kt index 6c6b3d27f5a..1fe4ecac735 100644 --- a/idea/src/org/jetbrains/kotlin/idea/intentions/IterateExpressionIntention.kt +++ b/idea/src/org/jetbrains/kotlin/idea/intentions/IterateExpressionIntention.kt @@ -21,9 +21,9 @@ import com.intellij.codeInsight.template.TemplateBuilderImpl import com.intellij.codeInsight.template.impl.ConstantNode import com.intellij.openapi.editor.Editor import com.intellij.psi.PsiDocumentManager -import org.jetbrains.kotlin.idea.caches.resolve.analyze import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade -import org.jetbrains.kotlin.idea.core.IterableTypesDetector +import org.jetbrains.kotlin.idea.caches.resolve.ideService +import org.jetbrains.kotlin.idea.core.IterableTypesDetection import org.jetbrains.kotlin.idea.core.KotlinNameSuggester import org.jetbrains.kotlin.idea.core.NewDeclarationNameValidator import org.jetbrains.kotlin.idea.core.replaced @@ -47,11 +47,12 @@ public class IterateExpressionIntention : JetSelfTargetingIntention(expression).createDetector(scope) + val elementType = detector.elementType(type)?.type ?: return null return Data(type, elementType) } diff --git a/idea/src/org/jetbrains/kotlin/idea/liveTemplates/macro/BaseJetVariableMacro.java b/idea/src/org/jetbrains/kotlin/idea/liveTemplates/macro/BaseJetVariableMacro.java index a9d6a363b8d..de6490bf4e5 100644 --- a/idea/src/org/jetbrains/kotlin/idea/liveTemplates/macro/BaseJetVariableMacro.java +++ b/idea/src/org/jetbrains/kotlin/idea/liveTemplates/macro/BaseJetVariableMacro.java @@ -30,17 +30,19 @@ import com.intellij.psi.PsiNamedElement; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.kotlin.analyzer.AnalysisResult; import org.jetbrains.kotlin.descriptors.DeclarationDescriptor; import org.jetbrains.kotlin.descriptors.ReceiverParameterDescriptor; import org.jetbrains.kotlin.descriptors.VariableDescriptor; +import org.jetbrains.kotlin.idea.caches.resolve.ResolutionFacade; import org.jetbrains.kotlin.idea.caches.resolve.ResolvePackage; +import org.jetbrains.kotlin.idea.core.IterableTypesDetection; import org.jetbrains.kotlin.idea.core.IterableTypesDetector; import org.jetbrains.kotlin.idea.util.UtilPackage; import org.jetbrains.kotlin.psi.*; import org.jetbrains.kotlin.resolve.BindingContext; import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils; import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo; +import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode; import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter; import org.jetbrains.kotlin.resolve.scopes.JetScope; @@ -62,15 +64,16 @@ public abstract class BaseJetVariableMacro extends Macro { JetExpression contextExpression = findContextExpression(psiFile, context.getStartOffset()); if (contextExpression == null) return null; - AnalysisResult analysisResult = ResolvePackage.analyzeAndGetResult(contextExpression); + ResolutionFacade resolutionFacade = ResolvePackage.getResolutionFacade(contextExpression); - BindingContext bindingContext = analysisResult.getBindingContext(); + BindingContext bindingContext = resolutionFacade.analyze(contextExpression, BodyResolveMode.FULL); JetScope scope = bindingContext.get(BindingContext.RESOLUTION_SCOPE, contextExpression); if (scope == null) { return null; } - IterableTypesDetector iterableTypesDetector = new IterableTypesDetector(project, analysisResult.getModuleDescriptor(), scope); + IterableTypesDetector detector = + resolutionFacade.getIdeService(contextExpression, IterableTypesDetection.class).createDetector(scope); DataFlowInfo dataFlowInfo = getDataFlowInfo(bindingContext, contextExpression); @@ -85,7 +88,7 @@ public abstract class BaseJetVariableMacro extends Macro { continue; } - if (isSuitable(variableDescriptor, project, iterableTypesDetector)) { + if (isSuitable(variableDescriptor, project, detector)) { filteredDescriptors.add(variableDescriptor); } }