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