Basic implementation of partial body resolve

This commit is contained in:
Valentin Kipyatkov
2014-11-17 20:20:16 +03:00
parent 9809e4fd0a
commit a675b5ba38
56 changed files with 852 additions and 32 deletions
@@ -75,6 +75,11 @@ public class BodyResolver {
this.expressionTypingServices = expressionTypingServices;
}
@NotNull
public ExpressionTypingServices getExpressionTypingServices() {
return expressionTypingServices;
}
@Inject
public void setCallResolver(@NotNull CallResolver callResolver) {
this.callResolver = callResolver;
@@ -19,6 +19,8 @@ package org.jetbrains.jet.lang.types.expressions;
import com.google.common.base.Function;
import com.intellij.openapi.project.Project;
import com.intellij.psi.tree.IElementType;
import kotlin.Function1;
import kotlin.KotlinPackage;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.analyzer.AnalyzerPackage;
@@ -73,6 +75,8 @@ public class ExpressionTypingServices {
private AnnotationResolver annotationResolver;
@NotNull
private CallResolverExtensionProvider extensionProvider;
@Nullable
private Function1<JetElement, Boolean> filter;
@NotNull
public Project getProject() {
@@ -139,6 +143,15 @@ public class ExpressionTypingServices {
this.extensionProvider = extensionProvider;
}
@Nullable
public Function1<JetElement, Boolean> getFilter() {
return filter;
}
public void setFilter(@Nullable Function1<JetElement, Boolean> filter) {
this.filter = filter;
}
public ExpressionTypingServices(@NotNull ExpressionTypingComponents components) {
this.expressionTypingComponents = components;
this.expressionTypingFacade = ExpressionTypingVisitorDispatcher.create(components);
@@ -206,6 +219,10 @@ public class ExpressionTypingServices {
) {
List<JetElement> block = expression.getStatements();
if (filter != null && !(expression instanceof JetPsiUtil.JetExpressionWrapper)) {
block = KotlinPackage.filter(block, filter);
}
// SCRIPT: get code descriptor for script declaration
DeclarationDescriptor containingDescriptor = context.scope.getContainingDeclaration();
if (containingDescriptor instanceof ScriptDescriptor) {
@@ -131,6 +131,7 @@ import org.jetbrains.jet.j2k.test.AbstractJavaToKotlinConverterForWebDemoTest
import org.jetbrains.jet.plugin.decompiler.textBuilder.AbstractDecompiledTextTest
import org.jetbrains.jet.completion.AbstractMultiFileSmartCompletionTest
import org.jetbrains.jet.completion.handlers.AbstractCompletionCharFilterTest
import org.jetbrains.jet.resolve.AbstractPartialBodyResolveTest
fun main(args: Array<String>) {
System.setProperty("java.awt.headless", "true")
@@ -306,6 +307,10 @@ fun main(args: Array<String>) {
model("resolve/additionalLazyResolve")
}
testClass(javaClass<AbstractPartialBodyResolveTest>()) {
model("resolve/partialBodyResolve")
}
testClass(javaClass<AbstractJetPsiCheckerTest>()) {
model("checker", recursive = false)
model("checker/regression")
@@ -22,7 +22,9 @@ import com.google.common.base.Predicates;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.util.PsiTreeUtil;
import kotlin.Function1;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.analyzer.AnalyzerPackage;
import org.jetbrains.jet.di.InjectorForBodyResolve;
import org.jetbrains.jet.lang.cfg.JetFlowInformationProvider;
@@ -59,11 +61,16 @@ public abstract class ElementResolver {
@NotNull
public BindingContext getElementAdditionalResolve(@NotNull JetElement jetElement) {
return elementAdditionalResolve(jetElement);
return elementAdditionalResolve(jetElement, null);
}
@NotNull
public BindingContext resolveToElement(@NotNull JetElement jetElement) {
return resolveToElement(jetElement, false);
}
@NotNull
public BindingContext resolveToElement(@NotNull JetElement jetElement, boolean partialBodyResolve) {
@SuppressWarnings("unchecked") JetElement elementOfAdditionalResolve = (JetElement) JetPsiUtil.getTopmostParentOfTypes(
jetElement,
JetNamedFunction.class,
@@ -83,6 +90,13 @@ public abstract class ElementResolver {
elementOfAdditionalResolve = jetElement;
}
if (partialBodyResolve && elementOfAdditionalResolve instanceof JetDeclarationWithBody) {
//TODO: do not resolve again if whole body resolve cached already
JetExpression body = ((JetDeclarationWithBody) elementOfAdditionalResolve).getBodyExpression();
Function1<JetElement, Boolean> filter = body != null ? new PartialBodyResolveFilter(jetElement, body) : null;
return elementAdditionalResolve(elementOfAdditionalResolve, filter);
}
return getElementAdditionalResolve(elementOfAdditionalResolve);
}
@@ -107,7 +121,7 @@ public abstract class ElementResolver {
}
@NotNull
protected BindingContext elementAdditionalResolve(@NotNull JetElement resolveElement) {
protected BindingContext elementAdditionalResolve(@NotNull JetElement resolveElement, @Nullable Function1<JetElement, Boolean> filter) {
// All additional resolve should be done to separate trace
BindingTrace trace = resolveSession.getStorageManager().createSafeTrace(
new DelegatingBindingTrace(resolveSession.getBindingContext(), "trace to resolve element", resolveElement));
@@ -115,10 +129,10 @@ public abstract class ElementResolver {
JetFile file = resolveElement.getContainingJetFile();
if (resolveElement instanceof JetNamedFunction) {
functionAdditionalResolve(resolveSession, (JetNamedFunction) resolveElement, trace, file);
functionAdditionalResolve(resolveSession, (JetNamedFunction) resolveElement, trace, file, filter);
}
else if (resolveElement instanceof JetClassInitializer) {
initializerAdditionalResolve(resolveSession, (JetClassInitializer) resolveElement, trace, file);
initializerAdditionalResolve(resolveSession, (JetClassInitializer) resolveElement, trace, file, filter);
}
else if (resolveElement instanceof JetProperty) {
propertyAdditionalResolve(resolveSession, (JetProperty) resolveElement, trace, file);
@@ -135,7 +149,7 @@ public abstract class ElementResolver {
annotationAdditionalResolve(resolveSession, (JetAnnotationEntry) resolveElement);
}
else if (resolveElement instanceof JetClass) {
constructorAdditionalResolve(resolveSession, (JetClass) resolveElement, trace, file);
constructorAdditionalResolve(resolveSession, (JetClass) resolveElement, trace, file, filter);
}
else if (resolveElement instanceof JetTypeParameter) {
typeParameterAdditionalResolve(resolveSession, (JetTypeParameter) resolveElement);
@@ -293,7 +307,7 @@ public abstract class ElementResolver {
// Activate resolving of supertypes
ForceResolveUtil.forceResolveAllContents(descriptor.getTypeConstructor().getSupertypes());
BodyResolver bodyResolver = createBodyResolver(resolveSession, trace, file);
BodyResolver bodyResolver = createBodyResolver(resolveSession, trace, file, null);
bodyResolver.resolveDelegationSpecifierList(createEmptyContext(resolveSession), classOrObject, descriptor,
descriptor.getUnsubstitutedPrimaryConstructor(),
descriptor.getScopeForClassHeaderResolution(),
@@ -313,7 +327,7 @@ public abstract class ElementResolver {
return resolveSession.getScopeProvider().getResolutionScopeForDeclaration(declaration);
}
});
BodyResolver bodyResolver = createBodyResolver(resolveSession, trace, file);
BodyResolver bodyResolver = createBodyResolver(resolveSession, trace, file, null);
PropertyDescriptor descriptor = (PropertyDescriptor) resolveSession.resolveToDescriptor(jetProperty);
ForceResolveUtil.forceResolveAllContents(descriptor);
@@ -338,13 +352,14 @@ public abstract class ElementResolver {
ResolveSession resolveSession,
JetNamedFunction namedFunction,
BindingTrace trace,
JetFile file
JetFile file,
@Nullable Function1<JetElement, Boolean> filter
) {
JetScope scope = resolveSession.getScopeProvider().getResolutionScopeForDeclaration(namedFunction);
FunctionDescriptor functionDescriptor = (FunctionDescriptor) resolveSession.resolveToDescriptor(namedFunction);
ForceResolveUtil.forceResolveAllContents(functionDescriptor);
BodyResolver bodyResolver = createBodyResolver(resolveSession, trace, file);
BodyResolver bodyResolver = createBodyResolver(resolveSession, trace, file, filter);
bodyResolver.resolveFunctionBody(createEmptyContext(resolveSession), trace, namedFunction, functionDescriptor, scope);
}
@@ -352,7 +367,8 @@ public abstract class ElementResolver {
ResolveSession resolveSession,
JetClass klass,
BindingTrace trace,
JetFile file
JetFile file,
@Nullable Function1<JetElement, Boolean> filter
) {
JetScope scope = resolveSession.getScopeProvider().getResolutionScopeForDeclaration(klass);
@@ -363,7 +379,7 @@ public abstract class ElementResolver {
classDescriptor,
JetPsiUtil.getElementTextWithContext(klass));
BodyResolver bodyResolver = createBodyResolver(resolveSession, trace, file);
BodyResolver bodyResolver = createBodyResolver(resolveSession, trace, file, filter);
bodyResolver.resolveConstructorParameterDefaultValuesAndAnnotations(createEmptyContext(resolveSession), trace, klass,
constructorDescriptor, scope);
}
@@ -372,18 +388,19 @@ public abstract class ElementResolver {
ResolveSession resolveSession,
JetClassInitializer classInitializer,
BindingTrace trace,
JetFile file
JetFile file,
@Nullable Function1<JetElement, Boolean> filter
) {
JetClassOrObject classOrObject = PsiTreeUtil.getParentOfType(classInitializer, JetClassOrObject.class);
LazyClassDescriptor classOrObjectDescriptor = (LazyClassDescriptor) resolveSession.resolveToDescriptor(classOrObject);
BodyResolver bodyResolver = createBodyResolver(resolveSession, trace, file);
BodyResolver bodyResolver = createBodyResolver(resolveSession, trace, file, filter);
bodyResolver.resolveAnonymousInitializer(createEmptyContext(resolveSession), classInitializer, classOrObjectDescriptor);
return true;
}
private BodyResolver createBodyResolver(ResolveSession resolveSession, BindingTrace trace, JetFile file) {
private BodyResolver createBodyResolver(ResolveSession resolveSession, BindingTrace trace, JetFile file, @Nullable Function1<JetElement, Boolean> filter) {
InjectorForBodyResolve bodyResolve = new InjectorForBodyResolve(
file.getProject(),
createParameters(resolveSession),
@@ -391,7 +408,9 @@ public abstract class ElementResolver {
resolveSession.getModuleDescriptor(),
getAdditionalCheckerProvider(file)
);
return bodyResolve.getBodyResolver();
BodyResolver resolver = bodyResolve.getBodyResolver();
resolver.getExpressionTypingServices().setFilter(filter);
return resolver;
}
private static TopDownAnalysisParameters createParameters(@NotNull ResolveSession resolveSession) {
@@ -0,0 +1,260 @@
/*
* Copyright 2010-2014 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.jet.lang.resolve.lazy
import java.util.HashSet
import org.jetbrains.jet.lang.psi.*
import org.jetbrains.jet.lang.psi.psiUtil.siblings
import org.jetbrains.jet.lexer.JetTokens
import org.jetbrains.jet.utils.addIfNotNull
import java.util.ArrayList
import java.util.HashMap
import com.intellij.psi.PsiElement
class PartialBodyResolveFilter(elementToResolve: JetElement, private val body: JetExpression) : (JetElement) -> Boolean {
private val statementsToResolve = HashSet<JetExpression>()
private val processedBlocks = HashSet<JetBlockExpression>()
;{
addStatementsToResolve(elementToResolve)
}
override fun invoke(statement: JetElement): Boolean {
val block = statement.getParent() as JetBlockExpression
if (block !in processedBlocks) {
processBlock(block)
}
return statement in statementsToResolve
}
private fun addStatementsToResolve(element: JetElement) {
if (element == body) return
val parent = element.getParent() as JetElement
if (parent is JetBlockExpression) {
processBlock(parent)
if (element in statementsToResolve) return // already processed
if (element is JetExpression) {
statementsToResolve.add(element)
}
for (statement in element.siblings(forward = false, withItself = false)) {
if (statement !is JetExpression) continue
val smartCastPlaces = potentialSmartCastPlaces(statement)
if (!smartCastPlaces.isEmpty()) {
statementsToResolve.add(statement)
statementsToResolve.addStatementsForPlaces(statement, smartCastPlaces.values().flatMap { it })
}
else if (statement is JetDeclaration) {
statementsToResolve.add(statement)
}
}
}
addStatementsToResolve(parent)
}
private fun processBlock(block: JetBlockExpression) {
if (processedBlocks.add(block)) {
val lastStatement = block.lastStatement()
if (lastStatement != null && lastStatement !in statementsToResolve && isValueNeeded(block)) {
addStatementsToResolve(lastStatement)
}
}
}
private fun potentialSmartCastPlaces(expression: JetExpression, filter: (String) -> Boolean = { true }): Map<String, List<JetExpression>> {
val map = HashMap<String, ArrayList<JetExpression>>(0)
fun addIfCanBeSmartCasted(expression: JetExpression) {
val name = expression.smartCastedExpressionName() ?: return
if (!filter(name)) return
var list = map[name]
if (list == null) {
list = ArrayList(1)
map[name] = list
}
list!!.add(expression)
}
expression.accept(object : ControlFlowVisitor(){
override fun visitPostfixExpression(expression: JetPostfixExpression) {
expression.acceptChildren(this)
if (expression.getOperationToken() == JetTokens.EXCLEXCL) {
addIfCanBeSmartCasted(expression.getBaseExpression())
}
}
override fun visitBinaryWithTypeRHSExpression(expression: JetBinaryExpressionWithTypeRHS) {
expression.acceptChildren(this)
if (expression.getOperationReference()?.getReferencedNameElementType() == JetTokens.AS_KEYWORD) {
addIfCanBeSmartCasted(expression.getLeft())
}
}
override fun visitIfExpression(expression: JetIfExpression) {
val condition = expression.getCondition()
val thenBranch = expression.getThen()
val elseBranch = expression.getElse()
val smartCastedNames = collectPossiblySmartCastedInCondition(condition).filter(filter)
if (smartCastedNames.isNotEmpty()) {
val exits = collectAlwaysExitPoints(thenBranch) + collectAlwaysExitPoints(elseBranch)
if (exits.isNotEmpty()) {
for (name in smartCastedNames) {
var list = map[name]
if (list == null) {
list = ArrayList(exits.size)
map[name] = list
}
list!!.addAll(exits)
}
}
}
condition.acceptChildren(this)
if (thenBranch != null && elseBranch != null) {
//TODO: merge casts!
thenBranch.acceptChildren(this)
elseBranch.acceptChildren(this)
}
}
//TODO: when
})
return map
}
private fun collectPossiblySmartCastedInCondition(condition: JetExpression?): Set<String> {
val result = HashSet<String>()
condition?.accept(object : ControlFlowVisitor() {
override fun visitBinaryExpression(expression: JetBinaryExpression) {
expression.acceptChildren(this)
val operation = expression.getOperationToken()
if (operation == JetTokens.EQEQ || operation == JetTokens.EXCLEQ) {
result.addIfNotNull(expression.getLeft()?.smartCastedExpressionName())
result.addIfNotNull(expression.getRight()?.smartCastedExpressionName())
}
}
override fun visitIsExpression(expression: JetIsExpression) {
expression.acceptChildren(this)
result.addIfNotNull(expression.getLeftHandSide()?.smartCastedExpressionName())
}
})
return result
}
//TODO: more precise analysis
private fun collectAlwaysExitPoints(expression: JetExpression?): Collection<JetExpression> {
val result = ArrayList<JetExpression>()
expression?.accept(object : ControlFlowVisitor() {
override fun visitReturnExpression(expression: JetReturnExpression) {
result.add(expression)
}
override fun visitThrowExpression(expression: JetThrowExpression) {
result.add(expression)
}
//TODO: check loop entrance
override fun visitBreakExpression(expression: JetBreakExpression) {
result.add(expression)
}
override fun visitContinueExpression(expression: JetContinueExpression) {
result.add(expression)
}
})
return result
}
private abstract class ControlFlowVisitor : JetVisitorVoid() {
override fun visitJetElement(element: JetElement) {
if (element.noControlFlowInside()) return
element.acceptChildren(this)
}
private fun JetElement.noControlFlowInside() = this is JetFunction || this is JetClass || this is JetClassBody
}
private fun MutableSet<JetExpression>.addStatementsForPlaces(thisStatement: JetExpression, places: Collection<JetExpression>) {
@PlacesLoop
for (place in places) {
var parent: PsiElement = place
while (parent != thisStatement) {
if (parent.isStatement()) {
if (!add(parent as JetExpression)) continue@PlacesLoop
}
parent = parent.getParent()
}
}
}
private fun PsiElement.isStatement() = this is JetExpression && getParent() is JetBlockExpression
//TODO: this.a
private fun JetExpression.smartCastedExpressionName(): String? {
return when (this) {
is JetSimpleNameExpression -> this.getReferencedName()
is JetQualifiedExpression -> {
val selectorName = getSelectorExpression().smartCastedExpressionName() ?: return null
val receiverName = getReceiverExpression().smartCastedExpressionName() ?: return null
return selectorName + "." + receiverName
}
else -> null
}
}
// private fun JetExpression?.isNullLiteral() = this?.getNode()?.getElementType() == JetNodeTypes.NULL
//TODO: review logic
private fun isValueNeeded(expression: JetExpression): Boolean {
val parent = expression.getParent()
return when (parent) {
is JetBlockExpression -> expression == parent.lastStatement() && isValueNeeded(parent)
is JetContainerNode -> { //TODO - not quite correct
val pparent = parent.getParent() as? JetExpression
pparent != null && isValueNeeded(pparent)
}
is JetDeclarationWithBody -> {
if (expression == parent.getBodyExpression())
!parent.hasBlockBody() && !parent.hasDeclaredReturnType()
else
true
}
else -> true
}
}
private fun JetBlockExpression.lastStatement(): JetExpression?
= getLastChild().siblings(forward = false).filterIsInstance<JetExpression>().firstOrNull()
}
@@ -53,6 +53,10 @@ public class KotlinCacheService(val project: Project) {
return cache.getLazyResolveSession(element).resolveToElement(element)
}
override fun analyzeWithPartialBodyResolve(element: JetElement): BindingContext {
return cache.getLazyResolveSession(element).resolveToElementWithPartialBodyResolve(element)
}
override fun findModuleDescriptor(element: JetElement): ModuleDescriptor {
return cache.getLazyResolveSession(element).getModuleDescriptor()
}
@@ -27,6 +27,8 @@ public trait ResolutionFacade {
public fun analyze(element: JetElement): BindingContext
public fun analyzeWithPartialBodyResolve(element: JetElement): BindingContext
public fun analyzeFullyAndGetResult(elements: Collection<JetElement>): AnalysisResult
public fun resolveToDescriptor(declaration: JetDeclaration): DeclarationDescriptor
@@ -52,7 +52,7 @@ public class ResolveElementCache extends ElementResolver {
manager.createWeaklyRetainedMemoizedFunction(new Function1<JetElement, BindingContext>() {
@Override
public BindingContext invoke(JetElement jetElement) {
return elementAdditionalResolve(jetElement);
return elementAdditionalResolve(jetElement, null);
}
});
@@ -54,7 +54,11 @@ public class ResolveSessionForBodies implements KotlinCodeAnalyzer {
@NotNull
public BindingContext resolveToElement(JetElement element) {
return resolveElementCache.resolveToElement(element);
return resolveElementCache.resolveToElement(element, false);
}
public BindingContext resolveToElementWithPartialBodyResolve(JetElement element) {
return resolveElementCache.resolveToElement(element, true);
}
@NotNull
@@ -40,6 +40,7 @@ import org.jetbrains.jet.plugin.util.extensionsUtils.isExtensionCallable
public class KotlinIndicesHelper(
private val project: Project,
private val resolutionFacade: ResolutionFacade,
private val bindingContext: BindingContext,
private val scope: GlobalSearchScope,
private val moduleDescriptor: ModuleDescriptor,
private val visibilityFilter: (DeclarationDescriptor) -> Boolean
@@ -73,7 +74,7 @@ public class KotlinIndicesHelper(
}
public fun getTopLevelCallablesByName(name: String, context: JetExpression /*TODO: to be dropped*/): Collection<CallableDescriptor> {
val jetScope = resolutionFacade.analyze(context).get(BindingContext.RESOLUTION_SCOPE, context) ?: return listOf()
val jetScope = bindingContext[BindingContext.RESOLUTION_SCOPE, context] ?: return listOf()
val result = HashSet<CallableDescriptor>()
@@ -127,7 +128,7 @@ public class KotlinIndicesHelper(
val sourceNames = JetTopLevelFunctionsFqnNameIndex.getInstance().getAllKeys(project).stream() + JetTopLevelPropertiesFqnNameIndex.getInstance().getAllKeys(project).stream()
val allFqNames = sourceNames.map { FqName(it) } + JetFromJavaDescriptorHelper.getTopLevelCallableFqNames(project, scope, false).stream()
val jetScope = resolutionFacade.analyze(context).get(BindingContext.RESOLUTION_SCOPE, context) ?: return listOf()
val jetScope = bindingContext[BindingContext.RESOLUTION_SCOPE, context] ?: return listOf()
return allFqNames.filter { nameFilter(it.shortName().asString()) }
.toSet()
@@ -135,7 +136,6 @@ public class KotlinIndicesHelper(
}
public fun getCallableExtensions(nameFilter: (String) -> Boolean, expression: JetSimpleNameExpression): Collection<CallableDescriptor> {
val bindingContext = resolutionFacade.analyze(expression)
val dataFlowInfo = bindingContext.getDataFlowInfo(expression)
val functionsIndex = JetTopLevelFunctionsFqnNameIndex.getInstance()
@@ -30,11 +30,16 @@ import org.jetbrains.jet.plugin.project.ProjectStructureUtil
import org.jetbrains.jet.plugin.caches.KotlinIndicesHelper
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor
import com.intellij.psi.search.GlobalSearchScope
import org.jetbrains.jet.lang.resolve.BindingContext
import org.jetbrains.jet.lang.descriptors.ModuleDescriptor
import org.jetbrains.jet.plugin.caches.resolve.ResolutionFacade
import org.jetbrains.jet.lang.resolve.BindingContext
import org.jetbrains.jet.plugin.caches.resolve.ResolutionFacade
import org.jetbrains.jet.lang.descriptors.ModuleDescriptor
class AllClassesCompletion(val parameters: CompletionParameters,
val resolutionFacade: ResolutionFacade,
val bindingContext: BindingContext,
val moduleDescriptor: ModuleDescriptor,
val scope: GlobalSearchScope,
val prefixMatcher: PrefixMatcher,
@@ -44,7 +49,7 @@ class AllClassesCompletion(val parameters: CompletionParameters,
val builtIns = KotlinBuiltIns.getInstance().getNonPhysicalClasses().filter { kindFilter(it.getKind()) && prefixMatcher.prefixMatches(it.getName().asString()) }
result.addDescriptorElements(builtIns, suppressAutoInsertion = true)
val helper = KotlinIndicesHelper(scope.getProject(), resolutionFacade, scope, moduleDescriptor, visibilityFilter)
val helper = KotlinIndicesHelper(scope.getProject(), resolutionFacade, bindingContext, scope, moduleDescriptor, visibilityFilter)
result.addDescriptorElements(helper.getClassDescriptors({ prefixMatcher.prefixMatches(it) }, kindFilter),
suppressAutoInsertion = true)
@@ -53,7 +53,7 @@ abstract class CompletionSessionBase(protected val configuration: CompletionSess
private val file = position.getContainingFile() as JetFile
protected val resolutionFacade: ResolutionFacade = file.getResolutionFacade()
protected val moduleDescriptor: ModuleDescriptor = resolutionFacade.findModuleDescriptor(file)
protected val bindingContext: BindingContext? = jetReference?.let { resolutionFacade.analyze(it.expression) }
protected val bindingContext: BindingContext? = jetReference?.let { resolutionFacade.analyzeWithPartialBodyResolve(it.expression) }
protected val inDescriptor: DeclarationDescriptor? = jetReference?.let { bindingContext!!.get(BindingContext.RESOLUTION_SCOPE, it.expression)?.getContainingDeclaration() }
// set prefix matcher here to override default one which relies on CompletionUtil.findReferencePrefix()
@@ -92,7 +92,7 @@ abstract class CompletionSessionBase(protected val configuration: CompletionSess
}
protected val indicesHelper: KotlinIndicesHelper
= KotlinIndicesHelper(project, resolutionFacade, searchScope, moduleDescriptor) { isVisibleDescriptor(it) }
get() = KotlinIndicesHelper(project, resolutionFacade, bindingContext!!, searchScope, moduleDescriptor) { isVisibleDescriptor(it) }
protected fun isVisibleDescriptor(descriptor: DeclarationDescriptor): Boolean {
if (configuration.completeNonAccessibleDeclarations) return true
@@ -141,7 +141,7 @@ abstract class CompletionSessionBase(protected val configuration: CompletionSess
protected fun addAllClasses(kindFilter: (ClassKind) -> Boolean) {
AllClassesCompletion(
parameters, resolutionFacade, moduleDescriptor,
parameters, resolutionFacade, bindingContext!!, moduleDescriptor,
searchScope, prefixMatcher, kindFilter, { isVisibleDescriptor(it) }
).collect(collector)
}
@@ -228,7 +228,7 @@ class SmartCompletionSession(configuration: CompletionSessionConfiguration, para
override fun doComplete() {
if (jetReference != null) {
val completion = SmartCompletion(jetReference.expression, resolutionFacade, { isVisibleDescriptor(it) }, parameters.getOriginalFile() as JetFile, boldImmediateLookupElementFactory)
val completion = SmartCompletion(jetReference.expression, resolutionFacade, bindingContext!!, { isVisibleDescriptor(it) }, parameters.getOriginalFile() as JetFile, boldImmediateLookupElementFactory)
val result = completion.execute()
if (result != null) {
collector.addElements(result.additionalItems)
@@ -295,14 +295,14 @@ class ExpectedInfos(val bindingContext: BindingContext, val resolutionFacade: Re
private fun calculateForInitializer(expressionWithType: JetExpression): Collection<ExpectedInfo>? {
val property = expressionWithType.getParent() as? JetProperty ?: return null
if (expressionWithType != property.getInitializer()) return null
val propertyDescriptor = resolutionFacade.resolveToDescriptor(property) as? VariableDescriptor ?: return null
val propertyDescriptor = bindingContext[BindingContext.DECLARATION_TO_DESCRIPTOR, property] as? VariableDescriptor ?: return null
return listOf(ExpectedInfo(propertyDescriptor.getType(), propertyDescriptor.getName().asString(), null))
}
private fun calculateForExpressionBody(expressionWithType: JetExpression): Collection<ExpectedInfo>? {
val declaration = expressionWithType.getParent() as? JetDeclarationWithBody ?: return null
if (expressionWithType != declaration.getBodyExpression() || declaration.hasBlockBody()) return null
val descriptor = resolutionFacade.resolveToDescriptor(declaration) as? FunctionDescriptor ?: return null
val descriptor = bindingContext[BindingContext.DECLARATION_TO_DESCRIPTOR, declaration] as? FunctionDescriptor ?: return null
return functionReturnValueExpectedInfo(descriptor).toList()
}
@@ -84,7 +84,7 @@ private fun needExplicitParameterTypes(context: InsertionContext, placeholderRan
if (expression == null) return false
val resolutionFacade = file.getResolutionFacade()
val bindingContext = resolutionFacade.analyze(expression)
val bindingContext = resolutionFacade.analyzeWithPartialBodyResolve(expression)
val expectedInfos = ExpectedInfos(bindingContext, resolutionFacade).calculate(expression) ?: return false
val functionTypes = expectedInfos.map { it.type }.filter { KotlinBuiltIns.getInstance().isExactFunctionOrExtensionFunctionType(it) }.toSet()
if (functionTypes.size <= 1) return false
@@ -37,10 +37,10 @@ import org.jetbrains.jet.plugin.caches.resolve.resolveToDescriptor
class SmartCompletion(val expression: JetSimpleNameExpression,
val resolutionFacade: ResolutionFacade,
val bindingContext: BindingContext,
val visibilityFilter: (DeclarationDescriptor) -> Boolean,
val originalFile: JetFile,
val boldImmediateLookupElementFactory: LookupElementFactory) {
private val bindingContext = resolutionFacade.analyze(expression)
private val project = expression.getProject()
public data class Result(val declarationFilter: ((DeclarationDescriptor) -> Collection<LookupElement>)?,
@@ -175,7 +175,7 @@ class SmartCompletion(val expression: JetSimpleNameExpression,
is JetProperty -> {
//TODO: this can be filtered out by ordinary completion
if (expression == parent.getInitializer()) {
return resolutionFacade.resolveToDescriptor(parent).toSet()
return bindingContext[BindingContext.DECLARATION_TO_DESCRIPTOR, parent].toSet()
}
}
@@ -185,7 +185,7 @@ class SmartCompletion(val expression: JetSimpleNameExpression,
if (operationToken == JetTokens.EQ || operationToken == JetTokens.EQEQ || operationToken == JetTokens.EXCLEQ) {
val left = parent.getLeft()
if (left is JetReferenceExpression) {
return resolutionFacade.analyze(left)[BindingContext.REFERENCE_TARGET, left].toSet()
return bindingContext[BindingContext.REFERENCE_TARGET, left].toSet()
}
}
}
@@ -139,7 +139,7 @@ public class AutoImportFix(element: JetSimpleNameExpression) : JetHintAction<Jet
val result = ArrayList<PrioritizedFqName>()
val moduleDescriptor = resolutionFacade.findModuleDescriptor(element)
val indicesHelper = KotlinIndicesHelper(file.getProject(), resolutionFacade, searchScope, moduleDescriptor, ::isVisible)
val indicesHelper = KotlinIndicesHelper(file.getProject(), resolutionFacade, bindingContext, searchScope, moduleDescriptor, ::isVisible)
if (!element.isImportDirectiveExpression() && !JetPsiUtil.isSelectorInQualified(element)) {
result.addAll(getClassNames(referenceName, file, searchScope))
@@ -0,0 +1,6 @@
Resolve target: value-parameter val p: kotlin.Any smart-casted to kotlin.String
Skipped statements:
y(p as? Int)
z(f() as String)
p1 as String
z(p1 as String)
@@ -0,0 +1,19 @@
fun foo(p: Any, p1: Any?) {
x(e.f as String)
y(p as? Int)
z(f() as String)
if (a) {
print((p as String).size)
}
else {
print((p as String).length)
}
if (y()) {
print(<caret>p.size)
p1 as String
}
z(p1 as String)
}
@@ -0,0 +1,5 @@
Resolve target: value-parameter val p: kotlin.String? smart-casted to kotlin.String
Skipped statements:
y(f()!!)
p1!!
z(p1!!)
@@ -0,0 +1,18 @@
fun foo(p: String?, p1: Any?) {
x(e.f!!)
y(f()!!)
if (a) {
print(p!!.size)
}
else {
print(p!!.length)
}
if (y()) {
print(<caret>p.size)
p1!!
}
z(p1!!)
}
@@ -0,0 +1,6 @@
Resolve target: val v2: kotlin.Int
Skipped statements:
x()
run { val v2 = 1 println(v2) }
run { val v2 = 2 println(v2) }
z()
@@ -0,0 +1,24 @@
fun foo(p: Int) {
x()
val v1 = p * p
if (y()) {
val v2 = v1 * v1
val v3 = v1 * v2
run {
val v2 = 1
println(v2)
}
print(<caret>v2)
run {
val v2 = 2
println(v2)
}
}
z()
}
@@ -0,0 +1,2 @@
Resolve target: value-parameter val o: kotlin.Any? smart-casted to kotlin.Any
Skipped statements:
@@ -0,0 +1,9 @@
fun foo(o: Any?, p: Int) {
if (p > 0) {
if (o == null) return
}
else {
if (o !is String) return
}
<caret>o.javaClass
}
@@ -0,0 +1,2 @@
Resolve target: value-parameter val o: kotlin.String? smart-casted to kotlin.String
Skipped statements:
@@ -0,0 +1,4 @@
fun foo(o: String?, o1: String) {
if (o != o1) return
<caret>o.hashCode()
}
@@ -0,0 +1,2 @@
Resolve target: val kotlin.String.size: kotlin.Int
Skipped statements:
@@ -0,0 +1,6 @@
fun foo(p: Any) {
if (p !is String) {
error("Not String")
}
println(<caret>p.size)
}
@@ -0,0 +1,3 @@
Resolve target: value-parameter val p: kotlin.Any? smart-casted to kotlin.String
Skipped statements:
print("null")
@@ -0,0 +1,12 @@
fun foo(p: Any?) {
if (p !is String) {
if (p == null) {
print("null")
return
}
else {
return
}
}
println(<caret>p.size)
}
@@ -0,0 +1,2 @@
Resolve target: val v: kotlin.Any smart-casted to kotlin.String
Skipped statements:
@@ -0,0 +1,6 @@
class C(public val v: Any)
fun foo(c: C) {
if (c.v !is String) return
println(c.<caret>v.size)
}
@@ -0,0 +1,2 @@
Resolve target: value-parameter val p: kotlin.Any smart-casted to kotlin.String
Skipped statements:
@@ -0,0 +1,6 @@
fun foo(p: Any) {
if (p !is String) {
throw IllegalArgumentException()
}
println(<caret>p.size)
}
@@ -0,0 +1,3 @@
Resolve target: value-parameter val p: kotlin.Any? smart-casted to kotlin.Any
Skipped statements:
print("not null")
@@ -0,0 +1,9 @@
fun foo(p: Any?) {
if (p != null) {
print("not null")
}
else {
return
}
<caret>p.hashCode()
}
@@ -0,0 +1,3 @@
Resolve target: value-parameter val p: kotlin.Any?
Skipped statements:
if (p != null) return
@@ -0,0 +1,4 @@
fun foo(p: Any?) {
if (p != null) return
<caret>p.hashCode()
}
@@ -0,0 +1,2 @@
Resolve target: val x: kotlin.Any? smart-casted to kotlin.Any
Skipped statements:
@@ -0,0 +1,9 @@
fun foo() {
for (i in 1..10) {
val x = take()
if (x == null) break
<caret>x.hashCode()
}
}
fun take(): Any? = null
@@ -0,0 +1,2 @@
Resolve target: val x: kotlin.Any? smart-casted to kotlin.Any
Skipped statements:
@@ -0,0 +1,9 @@
fun foo() {
for (i in 1..10) {
val x = take()
if (x == null) continue
<caret>x.hashCode()
}
}
fun take(): Any? = null
@@ -0,0 +1,3 @@
Resolve target: value-parameter val p: kotlin.Any?
Skipped statements:
if (p == null) { print("null") }
@@ -0,0 +1,6 @@
fun foo(p: Any?) {
if (p == null) {
print("null")
}
<caret>p.hashCode()
}
@@ -0,0 +1,3 @@
Resolve target: value-parameter val p: kotlin.Any? smart-casted to kotlin.Any
Skipped statements:
print("null")
@@ -0,0 +1,7 @@
fun foo(p: Any?) {
if (p == null) {
print("null")
return
}
<caret>p.hashCode()
}
@@ -0,0 +1,3 @@
Resolve target: val kotlin.String.size: kotlin.Int
Skipped statements:
if (x()) return
@@ -0,0 +1,4 @@
fun foo(p: String) {
if (x()) return
println(p.<caret>size)
}
@@ -0,0 +1,2 @@
Resolve target: null
Skipped statements:
@@ -0,0 +1,6 @@
fun foo(s: String){}
fun foo(c: Char){}
fun bar(b: Boolean, s: String, c: Char){
foo(if (b) "abc" else <caret>xxx)
}
@@ -0,0 +1,3 @@
Resolve target: fun f(): kotlin.Unit
Skipped statements:
x()
@@ -0,0 +1,8 @@
class C {
fun f(){}
}
fun foo() {
val lambda = {() -> x(); C() }
lambda().<caret>f()
}
@@ -0,0 +1,4 @@
Resolve target: value-parameter val p: kotlin.Int
Skipped statements:
x()
z()
@@ -0,0 +1,7 @@
fun foo(p: Int) {
x()
if (y()) {
print(<caret>p)
}
z()
}
@@ -0,0 +1,128 @@
/*
* Copyright 2010-2014 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.jet.resolve
import org.jetbrains.jet.plugin.JetLightCodeInsightFixtureTestCase
import org.jetbrains.jet.lang.psi.JetFile
import org.jetbrains.jet.lang.psi.psiUtil.getParentByType
import org.jetbrains.jet.lang.resolve.BindingContext
import org.jetbrains.jet.JetTestCaseBuilder
import org.jetbrains.jet.plugin.JetWithJdkAndRuntimeLightProjectDescriptor
import org.jetbrains.jet.lang.psi.JetExpression
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor
import java.util.HashSet
import org.jetbrains.jet.JetTestUtils
import java.io.File
import org.jetbrains.jet.lang.psi.JetBlockExpression
import org.junit.Assert
import org.jetbrains.jet.lang.types.JetType
import org.jetbrains.jet.lang.psi.psiUtil.parents
import org.jetbrains.jet.renderer.DescriptorRenderer
import org.jetbrains.jet.lang.descriptors.VariableDescriptor
import org.jetbrains.jet.lang.psi.JetSimpleNameExpression
import org.jetbrains.jet.lang.psi.psiUtil.getReceiverExpression
import org.jetbrains.jet.plugin.caches.resolve.getResolutionFacade
public abstract class AbstractPartialBodyResolveTest : JetLightCodeInsightFixtureTestCase() {
override fun getTestDataPath() = JetTestCaseBuilder.getHomeDirectory()
override fun getProjectDescriptor() = JetWithJdkAndRuntimeLightProjectDescriptor.INSTANCE
public fun doTest(testPath: String) {
myFixture.configureByFile(testPath)
val file = myFixture.getFile() as JetFile
val offset = myFixture.getEditor().getCaretModel().getOffset()
val element = file.findElementAt(offset)
val refExpression = element.getParentByType(javaClass<JetSimpleNameExpression>()) ?: error("No JetSimpleNameExpression at caret")
val resolutionFacade = file.getResolutionFacade()
// optimized resolve
val (target1, type1, processedStatements1) = doResolve(refExpression, resolutionFacade.analyzeWithPartialBodyResolve(refExpression))
// full body resolve
val (target2, type2, processedStatements2) = doResolve(refExpression, resolutionFacade.analyze(refExpression))
val set = HashSet(processedStatements2)
assert (set.containsAll(processedStatements1))
set.removeAll(processedStatements1)
val builder = StringBuilder()
builder.append("Resolve target: ${target2.presentation(type2)}\n")
builder.append("Skipped statements:\n")
set.sortBy { it.getTextOffset() }.forEach {
if (!it.parents(withItself = false).any { it in set }) { // do not dump skipped statements which are inside other skipped statement
builder append it.presentation() append "\n"
}
}
JetTestUtils.assertEqualsToFile(File(testPath.substringBeforeLast('.') + ".dump"), builder.toString())
//TODO: discuss that descriptors are different
Assert.assertEquals(target2.presentation(type2), target1.presentation(type1))
}
private fun doResolve(refExpression: JetSimpleNameExpression, bindingContext: BindingContext): Triple<DeclarationDescriptor?, JetType?, Collection<JetExpression>> {
val target = bindingContext[BindingContext.REFERENCE_TARGET, refExpression]
val processedStatements = bindingContext.getSliceContents(BindingContext.PROCESSED)
.filter { it.value }
.map { it.key }
.filter { it.getParent() is JetBlockExpression }
val receiver = refExpression.getReceiverExpression()
val expressionWithType = if (receiver != null) {
refExpression.getParent() as? JetExpression ?: refExpression
}
else {
refExpression
}
val type = bindingContext[BindingContext.EXPRESSION_TYPE, expressionWithType]
return Triple(target, type, processedStatements)
}
private fun DeclarationDescriptor?.presentation(type: JetType?): String {
if (this == null) return "null"
val s = DescriptorRenderer.COMPACT.render(this)
val renderType = this is VariableDescriptor && type != this.getReturnType()
if (!renderType) return s
return s + " smart-casted to " + if (type != null) DescriptorRenderer.COMPACT.renderType(type) else "unknown type"
}
private fun JetExpression.presentation(): String {
val text = getText()
val builder = StringBuilder()
var dropSpace = false
for (c in text) {
when (c) {
' ', '\n', '\r' -> {
if (!dropSpace) builder.append(' ')
dropSpace = true
}
else -> {
builder.append(c)
dropSpace = false
}
}
}
return builder.toString()
}
}
@@ -0,0 +1,140 @@
/*
* Copyright 2010-2014 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.jet.resolve;
import com.intellij.testFramework.TestDataPath;
import org.jetbrains.jet.JUnit3RunnerWithInners;
import org.jetbrains.jet.JetTestUtils;
import org.jetbrains.jet.test.InnerTestClasses;
import org.jetbrains.jet.test.TestMetadata;
import org.junit.runner.RunWith;
import java.io.File;
import java.util.regex.Pattern;
/** This class is generated by {@link org.jetbrains.jet.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */
@SuppressWarnings("all")
@TestMetadata("idea/testData/resolve/partialBodyResolve")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public class PartialBodyResolveTestGenerated extends AbstractPartialBodyResolveTest {
public void testAllFilesPresentInPartialBodyResolve() throws Exception {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/resolve/partialBodyResolve"), Pattern.compile("^(.+)\\.kt$"), true);
}
@TestMetadata("As.kt")
public void testAs() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/resolve/partialBodyResolve/As.kt");
doTest(fileName);
}
@TestMetadata("BangBang.kt")
public void testBangBang() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/resolve/partialBodyResolve/BangBang.kt");
doTest(fileName);
}
@TestMetadata("DeclarationsBefore.kt")
public void testDeclarationsBefore() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/resolve/partialBodyResolve/DeclarationsBefore.kt");
doTest(fileName);
}
@TestMetadata("IfBranchesSmartCast.kt")
public void testIfBranchesSmartCast() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/resolve/partialBodyResolve/IfBranchesSmartCast.kt");
doTest(fileName);
}
@TestMetadata("IfEqAutoCast.kt")
public void testIfEqAutoCast() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/resolve/partialBodyResolve/IfEqAutoCast.kt");
doTest(fileName);
}
@TestMetadata("IfNotIsReturn.kt")
public void testIfNotIsReturn() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/resolve/partialBodyResolve/IfNotIsReturn.kt");
doTest(fileName);
}
@TestMetadata("IfNotIsReturn2.kt")
public void testIfNotIsReturn2() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/resolve/partialBodyResolve/IfNotIsReturn2.kt");
doTest(fileName);
}
@TestMetadata("IfNotIsThrow.kt")
public void testIfNotIsThrow() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/resolve/partialBodyResolve/IfNotIsThrow.kt");
doTest(fileName);
}
@TestMetadata("IfNotNullElseReturn.kt")
public void testIfNotNullElseReturn() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/resolve/partialBodyResolve/IfNotNullElseReturn.kt");
doTest(fileName);
}
@TestMetadata("IfNullBreak.kt")
public void testIfNullBreak() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/resolve/partialBodyResolve/IfNullBreak.kt");
doTest(fileName);
}
@TestMetadata("IfNullContinue.kt")
public void testIfNullContinue() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/resolve/partialBodyResolve/IfNullContinue.kt");
doTest(fileName);
}
@TestMetadata("IfNullPrint.kt")
public void testIfNullPrint() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/resolve/partialBodyResolve/IfNullPrint.kt");
doTest(fileName);
}
@TestMetadata("IfNullReturn.kt")
public void testIfNullReturn() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/resolve/partialBodyResolve/IfNullReturn.kt");
doTest(fileName);
}
@TestMetadata("IfReturn.kt")
public void testIfReturn() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/resolve/partialBodyResolve/IfReturn.kt");
doTest(fileName);
}
@TestMetadata("InIfExpressionElse.kt")
public void testInIfExpressionElse() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/resolve/partialBodyResolve/InIfExpressionElse.kt");
doTest(fileName);
}
@TestMetadata("Lambda.kt")
public void testLambda() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/resolve/partialBodyResolve/Lambda.kt");
doTest(fileName);
}
@TestMetadata("Simple.kt")
public void testSimple() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/resolve/partialBodyResolve/Simple.kt");
doTest(fileName);
}
}