Basic implementation of partial body resolve
This commit is contained in:
@@ -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()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user