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
@@ -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()
}