From a675b5ba38350213392e6ab09f361ed65fd98701 Mon Sep 17 00:00:00 2001 From: Valentin Kipyatkov Date: Mon, 17 Nov 2014 20:20:16 +0300 Subject: [PATCH] Basic implementation of partial body resolve --- .../jet/lang/resolve/BodyResolver.java | 5 + .../expressions/ExpressionTypingServices.java | 17 ++ .../jet/generators/tests/GenerateTests.kt | 5 + .../lang/resolve/lazy/ElementResolver.java | 49 +++- .../resolve/lazy/PartialBodyResolveFilter.kt | 260 ++++++++++++++++++ .../caches/resolve/KotlinCacheService.kt | 4 + .../plugin/caches/resolve/ResolutionFacade.kt | 2 + .../plugin/project/ResolveElementCache.java | 2 +- .../project/ResolveSessionForBodies.java | 6 +- .../jet/plugin/caches/KotlinIndicesHelper.kt | 6 +- .../plugin/completion/AllClassesCompletion.kt | 7 +- .../plugin/completion/CompletionSession.kt | 8 +- .../jet/plugin/completion/ExpectedInfos.kt | 4 +- .../completion/handlers/GenerateLambda.kt | 2 +- .../completion/smart/SmartCompletion.kt | 6 +- .../jet/plugin/quickfix/AutoImportFix.kt | 2 +- .../resolve/partialBodyResolve/As.dump | 6 + .../testData/resolve/partialBodyResolve/As.kt | 19 ++ .../resolve/partialBodyResolve/BangBang.dump | 5 + .../resolve/partialBodyResolve/BangBang.kt | 18 ++ .../DeclarationsBefore.dump | 6 + .../partialBodyResolve/DeclarationsBefore.kt | 24 ++ .../IfBranchesSmartCast.dump | 2 + .../partialBodyResolve/IfBranchesSmartCast.kt | 9 + .../partialBodyResolve/IfEqAutoCast.dump | 2 + .../partialBodyResolve/IfEqAutoCast.kt | 4 + .../partialBodyResolve/IfNotIsError.dump | 2 + .../partialBodyResolve/IfNotIsError.kt.todo | 6 + .../partialBodyResolve/IfNotIsReturn.dump | 3 + .../partialBodyResolve/IfNotIsReturn.kt | 12 + .../partialBodyResolve/IfNotIsReturn2.dump | 2 + .../partialBodyResolve/IfNotIsReturn2.kt | 6 + .../partialBodyResolve/IfNotIsThrow.dump | 2 + .../partialBodyResolve/IfNotIsThrow.kt | 6 + .../IfNotNullElseReturn.dump | 3 + .../partialBodyResolve/IfNotNullElseReturn.kt | 9 + .../partialBodyResolve/IfNotNullReturn.dump | 3 + .../IfNotNullReturn.kt.todo | 4 + .../partialBodyResolve/IfNullBreak.dump | 2 + .../resolve/partialBodyResolve/IfNullBreak.kt | 9 + .../partialBodyResolve/IfNullContinue.dump | 2 + .../partialBodyResolve/IfNullContinue.kt | 9 + .../partialBodyResolve/IfNullPrint.dump | 3 + .../resolve/partialBodyResolve/IfNullPrint.kt | 6 + .../partialBodyResolve/IfNullReturn.dump | 3 + .../partialBodyResolve/IfNullReturn.kt | 7 + .../resolve/partialBodyResolve/IfReturn.dump | 3 + .../resolve/partialBodyResolve/IfReturn.kt | 4 + .../InIfExpressionElse.dump | 2 + .../partialBodyResolve/InIfExpressionElse.kt | 6 + .../resolve/partialBodyResolve/Lambda.dump | 3 + .../resolve/partialBodyResolve/Lambda.kt | 8 + .../resolve/partialBodyResolve/Simple.dump | 4 + .../resolve/partialBodyResolve/Simple.kt | 7 + .../resolve/AbstractPartialBodyResolveTest.kt | 128 +++++++++ .../PartialBodyResolveTestGenerated.java | 140 ++++++++++ 56 files changed, 852 insertions(+), 32 deletions(-) create mode 100644 idea/ide-common/src/org/jetbrains/jet/lang/resolve/lazy/PartialBodyResolveFilter.kt create mode 100644 idea/testData/resolve/partialBodyResolve/As.dump create mode 100644 idea/testData/resolve/partialBodyResolve/As.kt create mode 100644 idea/testData/resolve/partialBodyResolve/BangBang.dump create mode 100644 idea/testData/resolve/partialBodyResolve/BangBang.kt create mode 100644 idea/testData/resolve/partialBodyResolve/DeclarationsBefore.dump create mode 100644 idea/testData/resolve/partialBodyResolve/DeclarationsBefore.kt create mode 100644 idea/testData/resolve/partialBodyResolve/IfBranchesSmartCast.dump create mode 100644 idea/testData/resolve/partialBodyResolve/IfBranchesSmartCast.kt create mode 100644 idea/testData/resolve/partialBodyResolve/IfEqAutoCast.dump create mode 100644 idea/testData/resolve/partialBodyResolve/IfEqAutoCast.kt create mode 100644 idea/testData/resolve/partialBodyResolve/IfNotIsError.dump create mode 100644 idea/testData/resolve/partialBodyResolve/IfNotIsError.kt.todo create mode 100644 idea/testData/resolve/partialBodyResolve/IfNotIsReturn.dump create mode 100644 idea/testData/resolve/partialBodyResolve/IfNotIsReturn.kt create mode 100644 idea/testData/resolve/partialBodyResolve/IfNotIsReturn2.dump create mode 100644 idea/testData/resolve/partialBodyResolve/IfNotIsReturn2.kt create mode 100644 idea/testData/resolve/partialBodyResolve/IfNotIsThrow.dump create mode 100644 idea/testData/resolve/partialBodyResolve/IfNotIsThrow.kt create mode 100644 idea/testData/resolve/partialBodyResolve/IfNotNullElseReturn.dump create mode 100644 idea/testData/resolve/partialBodyResolve/IfNotNullElseReturn.kt create mode 100644 idea/testData/resolve/partialBodyResolve/IfNotNullReturn.dump create mode 100644 idea/testData/resolve/partialBodyResolve/IfNotNullReturn.kt.todo create mode 100644 idea/testData/resolve/partialBodyResolve/IfNullBreak.dump create mode 100644 idea/testData/resolve/partialBodyResolve/IfNullBreak.kt create mode 100644 idea/testData/resolve/partialBodyResolve/IfNullContinue.dump create mode 100644 idea/testData/resolve/partialBodyResolve/IfNullContinue.kt create mode 100644 idea/testData/resolve/partialBodyResolve/IfNullPrint.dump create mode 100644 idea/testData/resolve/partialBodyResolve/IfNullPrint.kt create mode 100644 idea/testData/resolve/partialBodyResolve/IfNullReturn.dump create mode 100644 idea/testData/resolve/partialBodyResolve/IfNullReturn.kt create mode 100644 idea/testData/resolve/partialBodyResolve/IfReturn.dump create mode 100644 idea/testData/resolve/partialBodyResolve/IfReturn.kt create mode 100644 idea/testData/resolve/partialBodyResolve/InIfExpressionElse.dump create mode 100644 idea/testData/resolve/partialBodyResolve/InIfExpressionElse.kt create mode 100644 idea/testData/resolve/partialBodyResolve/Lambda.dump create mode 100644 idea/testData/resolve/partialBodyResolve/Lambda.kt create mode 100644 idea/testData/resolve/partialBodyResolve/Simple.dump create mode 100644 idea/testData/resolve/partialBodyResolve/Simple.kt create mode 100644 idea/tests/org/jetbrains/jet/resolve/AbstractPartialBodyResolveTest.kt create mode 100644 idea/tests/org/jetbrains/jet/resolve/PartialBodyResolveTestGenerated.java diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/BodyResolver.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/BodyResolver.java index 28fc49ece72..b91da1df0f4 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/BodyResolver.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/BodyResolver.java @@ -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; diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ExpressionTypingServices.java b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ExpressionTypingServices.java index 742b19d2809..e2f7d3591cb 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ExpressionTypingServices.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ExpressionTypingServices.java @@ -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 filter; @NotNull public Project getProject() { @@ -139,6 +143,15 @@ public class ExpressionTypingServices { this.extensionProvider = extensionProvider; } + @Nullable + public Function1 getFilter() { + return filter; + } + + public void setFilter(@Nullable Function1 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 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) { diff --git a/generators/src/org/jetbrains/jet/generators/tests/GenerateTests.kt b/generators/src/org/jetbrains/jet/generators/tests/GenerateTests.kt index a368b846291..088631f1cee 100644 --- a/generators/src/org/jetbrains/jet/generators/tests/GenerateTests.kt +++ b/generators/src/org/jetbrains/jet/generators/tests/GenerateTests.kt @@ -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) { System.setProperty("java.awt.headless", "true") @@ -306,6 +307,10 @@ fun main(args: Array) { model("resolve/additionalLazyResolve") } + testClass(javaClass()) { + model("resolve/partialBodyResolve") + } + testClass(javaClass()) { model("checker", recursive = false) model("checker/regression") diff --git a/idea/ide-common/src/org/jetbrains/jet/lang/resolve/lazy/ElementResolver.java b/idea/ide-common/src/org/jetbrains/jet/lang/resolve/lazy/ElementResolver.java index 408235f90b9..a934214e6b4 100644 --- a/idea/ide-common/src/org/jetbrains/jet/lang/resolve/lazy/ElementResolver.java +++ b/idea/ide-common/src/org/jetbrains/jet/lang/resolve/lazy/ElementResolver.java @@ -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 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 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 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 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 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 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) { diff --git a/idea/ide-common/src/org/jetbrains/jet/lang/resolve/lazy/PartialBodyResolveFilter.kt b/idea/ide-common/src/org/jetbrains/jet/lang/resolve/lazy/PartialBodyResolveFilter.kt new file mode 100644 index 00000000000..524e698bc92 --- /dev/null +++ b/idea/ide-common/src/org/jetbrains/jet/lang/resolve/lazy/PartialBodyResolveFilter.kt @@ -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() + private val processedBlocks = HashSet() + + ;{ + 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> { + val map = HashMap>(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 { + val result = HashSet() + 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 { + val result = ArrayList() + 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.addStatementsForPlaces(thisStatement: JetExpression, places: Collection) { + @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().firstOrNull() +} + diff --git a/idea/idea-analysis/src/org/jetbrains/jet/plugin/caches/resolve/KotlinCacheService.kt b/idea/idea-analysis/src/org/jetbrains/jet/plugin/caches/resolve/KotlinCacheService.kt index 2c8e9de64a4..35c15ad6aab 100644 --- a/idea/idea-analysis/src/org/jetbrains/jet/plugin/caches/resolve/KotlinCacheService.kt +++ b/idea/idea-analysis/src/org/jetbrains/jet/plugin/caches/resolve/KotlinCacheService.kt @@ -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() } diff --git a/idea/idea-analysis/src/org/jetbrains/jet/plugin/caches/resolve/ResolutionFacade.kt b/idea/idea-analysis/src/org/jetbrains/jet/plugin/caches/resolve/ResolutionFacade.kt index 47320c89a79..1352c63c51f 100644 --- a/idea/idea-analysis/src/org/jetbrains/jet/plugin/caches/resolve/ResolutionFacade.kt +++ b/idea/idea-analysis/src/org/jetbrains/jet/plugin/caches/resolve/ResolutionFacade.kt @@ -27,6 +27,8 @@ public trait ResolutionFacade { public fun analyze(element: JetElement): BindingContext + public fun analyzeWithPartialBodyResolve(element: JetElement): BindingContext + public fun analyzeFullyAndGetResult(elements: Collection): AnalysisResult public fun resolveToDescriptor(declaration: JetDeclaration): DeclarationDescriptor diff --git a/idea/idea-analysis/src/org/jetbrains/jet/plugin/project/ResolveElementCache.java b/idea/idea-analysis/src/org/jetbrains/jet/plugin/project/ResolveElementCache.java index da204bf1ae6..cc7e2d55b75 100644 --- a/idea/idea-analysis/src/org/jetbrains/jet/plugin/project/ResolveElementCache.java +++ b/idea/idea-analysis/src/org/jetbrains/jet/plugin/project/ResolveElementCache.java @@ -52,7 +52,7 @@ public class ResolveElementCache extends ElementResolver { manager.createWeaklyRetainedMemoizedFunction(new Function1() { @Override public BindingContext invoke(JetElement jetElement) { - return elementAdditionalResolve(jetElement); + return elementAdditionalResolve(jetElement, null); } }); diff --git a/idea/idea-analysis/src/org/jetbrains/jet/plugin/project/ResolveSessionForBodies.java b/idea/idea-analysis/src/org/jetbrains/jet/plugin/project/ResolveSessionForBodies.java index bebe6dae9b4..b48f46c4f70 100644 --- a/idea/idea-analysis/src/org/jetbrains/jet/plugin/project/ResolveSessionForBodies.java +++ b/idea/idea-analysis/src/org/jetbrains/jet/plugin/project/ResolveSessionForBodies.java @@ -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 diff --git a/idea/src/org/jetbrains/jet/plugin/caches/KotlinIndicesHelper.kt b/idea/src/org/jetbrains/jet/plugin/caches/KotlinIndicesHelper.kt index c868eaebf2f..3129b1e41dc 100644 --- a/idea/src/org/jetbrains/jet/plugin/caches/KotlinIndicesHelper.kt +++ b/idea/src/org/jetbrains/jet/plugin/caches/KotlinIndicesHelper.kt @@ -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 { - val jetScope = resolutionFacade.analyze(context).get(BindingContext.RESOLUTION_SCOPE, context) ?: return listOf() + val jetScope = bindingContext[BindingContext.RESOLUTION_SCOPE, context] ?: return listOf() val result = HashSet() @@ -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 { - val bindingContext = resolutionFacade.analyze(expression) val dataFlowInfo = bindingContext.getDataFlowInfo(expression) val functionsIndex = JetTopLevelFunctionsFqnNameIndex.getInstance() diff --git a/idea/src/org/jetbrains/jet/plugin/completion/AllClassesCompletion.kt b/idea/src/org/jetbrains/jet/plugin/completion/AllClassesCompletion.kt index 691cc65881c..4a1790fd06e 100644 --- a/idea/src/org/jetbrains/jet/plugin/completion/AllClassesCompletion.kt +++ b/idea/src/org/jetbrains/jet/plugin/completion/AllClassesCompletion.kt @@ -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) diff --git a/idea/src/org/jetbrains/jet/plugin/completion/CompletionSession.kt b/idea/src/org/jetbrains/jet/plugin/completion/CompletionSession.kt index 2a67d2aaf94..fe020c46516 100644 --- a/idea/src/org/jetbrains/jet/plugin/completion/CompletionSession.kt +++ b/idea/src/org/jetbrains/jet/plugin/completion/CompletionSession.kt @@ -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) diff --git a/idea/src/org/jetbrains/jet/plugin/completion/ExpectedInfos.kt b/idea/src/org/jetbrains/jet/plugin/completion/ExpectedInfos.kt index 0d2bb470739..5b320469dc6 100644 --- a/idea/src/org/jetbrains/jet/plugin/completion/ExpectedInfos.kt +++ b/idea/src/org/jetbrains/jet/plugin/completion/ExpectedInfos.kt @@ -295,14 +295,14 @@ class ExpectedInfos(val bindingContext: BindingContext, val resolutionFacade: Re private fun calculateForInitializer(expressionWithType: JetExpression): Collection? { 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? { 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() } diff --git a/idea/src/org/jetbrains/jet/plugin/completion/handlers/GenerateLambda.kt b/idea/src/org/jetbrains/jet/plugin/completion/handlers/GenerateLambda.kt index 36562d7f517..599e3983eb4 100644 --- a/idea/src/org/jetbrains/jet/plugin/completion/handlers/GenerateLambda.kt +++ b/idea/src/org/jetbrains/jet/plugin/completion/handlers/GenerateLambda.kt @@ -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 diff --git a/idea/src/org/jetbrains/jet/plugin/completion/smart/SmartCompletion.kt b/idea/src/org/jetbrains/jet/plugin/completion/smart/SmartCompletion.kt index 33a0757b88f..a44573fcacd 100644 --- a/idea/src/org/jetbrains/jet/plugin/completion/smart/SmartCompletion.kt +++ b/idea/src/org/jetbrains/jet/plugin/completion/smart/SmartCompletion.kt @@ -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)?, @@ -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() } } } diff --git a/idea/src/org/jetbrains/jet/plugin/quickfix/AutoImportFix.kt b/idea/src/org/jetbrains/jet/plugin/quickfix/AutoImportFix.kt index 826b53d0140..d93eec6db15 100644 --- a/idea/src/org/jetbrains/jet/plugin/quickfix/AutoImportFix.kt +++ b/idea/src/org/jetbrains/jet/plugin/quickfix/AutoImportFix.kt @@ -139,7 +139,7 @@ public class AutoImportFix(element: JetSimpleNameExpression) : JetHintAction() 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)) diff --git a/idea/testData/resolve/partialBodyResolve/As.dump b/idea/testData/resolve/partialBodyResolve/As.dump new file mode 100644 index 00000000000..73ef7de224b --- /dev/null +++ b/idea/testData/resolve/partialBodyResolve/As.dump @@ -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) diff --git a/idea/testData/resolve/partialBodyResolve/As.kt b/idea/testData/resolve/partialBodyResolve/As.kt new file mode 100644 index 00000000000..ae648332743 --- /dev/null +++ b/idea/testData/resolve/partialBodyResolve/As.kt @@ -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(p.size) + p1 as String + } + + z(p1 as String) +} diff --git a/idea/testData/resolve/partialBodyResolve/BangBang.dump b/idea/testData/resolve/partialBodyResolve/BangBang.dump new file mode 100644 index 00000000000..162a996d387 --- /dev/null +++ b/idea/testData/resolve/partialBodyResolve/BangBang.dump @@ -0,0 +1,5 @@ +Resolve target: value-parameter val p: kotlin.String? smart-casted to kotlin.String +Skipped statements: +y(f()!!) +p1!! +z(p1!!) diff --git a/idea/testData/resolve/partialBodyResolve/BangBang.kt b/idea/testData/resolve/partialBodyResolve/BangBang.kt new file mode 100644 index 00000000000..3f0fa2007f1 --- /dev/null +++ b/idea/testData/resolve/partialBodyResolve/BangBang.kt @@ -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(p.size) + p1!! + } + + z(p1!!) +} diff --git a/idea/testData/resolve/partialBodyResolve/DeclarationsBefore.dump b/idea/testData/resolve/partialBodyResolve/DeclarationsBefore.dump new file mode 100644 index 00000000000..22d45c28ccd --- /dev/null +++ b/idea/testData/resolve/partialBodyResolve/DeclarationsBefore.dump @@ -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() diff --git a/idea/testData/resolve/partialBodyResolve/DeclarationsBefore.kt b/idea/testData/resolve/partialBodyResolve/DeclarationsBefore.kt new file mode 100644 index 00000000000..89ba1cb3b64 --- /dev/null +++ b/idea/testData/resolve/partialBodyResolve/DeclarationsBefore.kt @@ -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(v2) + + run { + val v2 = 2 + println(v2) + } + } + + z() +} diff --git a/idea/testData/resolve/partialBodyResolve/IfBranchesSmartCast.dump b/idea/testData/resolve/partialBodyResolve/IfBranchesSmartCast.dump new file mode 100644 index 00000000000..1c569ab1dab --- /dev/null +++ b/idea/testData/resolve/partialBodyResolve/IfBranchesSmartCast.dump @@ -0,0 +1,2 @@ +Resolve target: value-parameter val o: kotlin.Any? smart-casted to kotlin.Any +Skipped statements: diff --git a/idea/testData/resolve/partialBodyResolve/IfBranchesSmartCast.kt b/idea/testData/resolve/partialBodyResolve/IfBranchesSmartCast.kt new file mode 100644 index 00000000000..b0c34d5a6b2 --- /dev/null +++ b/idea/testData/resolve/partialBodyResolve/IfBranchesSmartCast.kt @@ -0,0 +1,9 @@ +fun foo(o: Any?, p: Int) { + if (p > 0) { + if (o == null) return + } + else { + if (o !is String) return + } + o.javaClass +} \ No newline at end of file diff --git a/idea/testData/resolve/partialBodyResolve/IfEqAutoCast.dump b/idea/testData/resolve/partialBodyResolve/IfEqAutoCast.dump new file mode 100644 index 00000000000..55d4dd145f7 --- /dev/null +++ b/idea/testData/resolve/partialBodyResolve/IfEqAutoCast.dump @@ -0,0 +1,2 @@ +Resolve target: value-parameter val o: kotlin.String? smart-casted to kotlin.String +Skipped statements: diff --git a/idea/testData/resolve/partialBodyResolve/IfEqAutoCast.kt b/idea/testData/resolve/partialBodyResolve/IfEqAutoCast.kt new file mode 100644 index 00000000000..6b25734df00 --- /dev/null +++ b/idea/testData/resolve/partialBodyResolve/IfEqAutoCast.kt @@ -0,0 +1,4 @@ +fun foo(o: String?, o1: String) { + if (o != o1) return + o.hashCode() +} \ No newline at end of file diff --git a/idea/testData/resolve/partialBodyResolve/IfNotIsError.dump b/idea/testData/resolve/partialBodyResolve/IfNotIsError.dump new file mode 100644 index 00000000000..18ac239473c --- /dev/null +++ b/idea/testData/resolve/partialBodyResolve/IfNotIsError.dump @@ -0,0 +1,2 @@ +Resolve target: val kotlin.String.size: kotlin.Int +Skipped statements: diff --git a/idea/testData/resolve/partialBodyResolve/IfNotIsError.kt.todo b/idea/testData/resolve/partialBodyResolve/IfNotIsError.kt.todo new file mode 100644 index 00000000000..b80a6440ae9 --- /dev/null +++ b/idea/testData/resolve/partialBodyResolve/IfNotIsError.kt.todo @@ -0,0 +1,6 @@ +fun foo(p: Any) { + if (p !is String) { + error("Not String") + } + println(p.size) +} \ No newline at end of file diff --git a/idea/testData/resolve/partialBodyResolve/IfNotIsReturn.dump b/idea/testData/resolve/partialBodyResolve/IfNotIsReturn.dump new file mode 100644 index 00000000000..6b805adef18 --- /dev/null +++ b/idea/testData/resolve/partialBodyResolve/IfNotIsReturn.dump @@ -0,0 +1,3 @@ +Resolve target: value-parameter val p: kotlin.Any? smart-casted to kotlin.String +Skipped statements: +print("null") diff --git a/idea/testData/resolve/partialBodyResolve/IfNotIsReturn.kt b/idea/testData/resolve/partialBodyResolve/IfNotIsReturn.kt new file mode 100644 index 00000000000..ff1f54345c7 --- /dev/null +++ b/idea/testData/resolve/partialBodyResolve/IfNotIsReturn.kt @@ -0,0 +1,12 @@ +fun foo(p: Any?) { + if (p !is String) { + if (p == null) { + print("null") + return + } + else { + return + } + } + println(p.size) +} \ No newline at end of file diff --git a/idea/testData/resolve/partialBodyResolve/IfNotIsReturn2.dump b/idea/testData/resolve/partialBodyResolve/IfNotIsReturn2.dump new file mode 100644 index 00000000000..cb36d9b5d79 --- /dev/null +++ b/idea/testData/resolve/partialBodyResolve/IfNotIsReturn2.dump @@ -0,0 +1,2 @@ +Resolve target: val v: kotlin.Any smart-casted to kotlin.String +Skipped statements: diff --git a/idea/testData/resolve/partialBodyResolve/IfNotIsReturn2.kt b/idea/testData/resolve/partialBodyResolve/IfNotIsReturn2.kt new file mode 100644 index 00000000000..471d73f7d8e --- /dev/null +++ b/idea/testData/resolve/partialBodyResolve/IfNotIsReturn2.kt @@ -0,0 +1,6 @@ +class C(public val v: Any) + +fun foo(c: C) { + if (c.v !is String) return + println(c.v.size) +} \ No newline at end of file diff --git a/idea/testData/resolve/partialBodyResolve/IfNotIsThrow.dump b/idea/testData/resolve/partialBodyResolve/IfNotIsThrow.dump new file mode 100644 index 00000000000..7578f928cce --- /dev/null +++ b/idea/testData/resolve/partialBodyResolve/IfNotIsThrow.dump @@ -0,0 +1,2 @@ +Resolve target: value-parameter val p: kotlin.Any smart-casted to kotlin.String +Skipped statements: diff --git a/idea/testData/resolve/partialBodyResolve/IfNotIsThrow.kt b/idea/testData/resolve/partialBodyResolve/IfNotIsThrow.kt new file mode 100644 index 00000000000..aa10a20db1f --- /dev/null +++ b/idea/testData/resolve/partialBodyResolve/IfNotIsThrow.kt @@ -0,0 +1,6 @@ +fun foo(p: Any) { + if (p !is String) { + throw IllegalArgumentException() + } + println(p.size) +} \ No newline at end of file diff --git a/idea/testData/resolve/partialBodyResolve/IfNotNullElseReturn.dump b/idea/testData/resolve/partialBodyResolve/IfNotNullElseReturn.dump new file mode 100644 index 00000000000..7034d64f6e5 --- /dev/null +++ b/idea/testData/resolve/partialBodyResolve/IfNotNullElseReturn.dump @@ -0,0 +1,3 @@ +Resolve target: value-parameter val p: kotlin.Any? smart-casted to kotlin.Any +Skipped statements: +print("not null") diff --git a/idea/testData/resolve/partialBodyResolve/IfNotNullElseReturn.kt b/idea/testData/resolve/partialBodyResolve/IfNotNullElseReturn.kt new file mode 100644 index 00000000000..3fb6bbe85a3 --- /dev/null +++ b/idea/testData/resolve/partialBodyResolve/IfNotNullElseReturn.kt @@ -0,0 +1,9 @@ +fun foo(p: Any?) { + if (p != null) { + print("not null") + } + else { + return + } + p.hashCode() +} \ No newline at end of file diff --git a/idea/testData/resolve/partialBodyResolve/IfNotNullReturn.dump b/idea/testData/resolve/partialBodyResolve/IfNotNullReturn.dump new file mode 100644 index 00000000000..e643a6f675e --- /dev/null +++ b/idea/testData/resolve/partialBodyResolve/IfNotNullReturn.dump @@ -0,0 +1,3 @@ +Resolve target: value-parameter val p: kotlin.Any? +Skipped statements: +if (p != null) return diff --git a/idea/testData/resolve/partialBodyResolve/IfNotNullReturn.kt.todo b/idea/testData/resolve/partialBodyResolve/IfNotNullReturn.kt.todo new file mode 100644 index 00000000000..05a66211d7a --- /dev/null +++ b/idea/testData/resolve/partialBodyResolve/IfNotNullReturn.kt.todo @@ -0,0 +1,4 @@ +fun foo(p: Any?) { + if (p != null) return + p.hashCode() +} \ No newline at end of file diff --git a/idea/testData/resolve/partialBodyResolve/IfNullBreak.dump b/idea/testData/resolve/partialBodyResolve/IfNullBreak.dump new file mode 100644 index 00000000000..a7ff535776f --- /dev/null +++ b/idea/testData/resolve/partialBodyResolve/IfNullBreak.dump @@ -0,0 +1,2 @@ +Resolve target: val x: kotlin.Any? smart-casted to kotlin.Any +Skipped statements: diff --git a/idea/testData/resolve/partialBodyResolve/IfNullBreak.kt b/idea/testData/resolve/partialBodyResolve/IfNullBreak.kt new file mode 100644 index 00000000000..7577b909d7b --- /dev/null +++ b/idea/testData/resolve/partialBodyResolve/IfNullBreak.kt @@ -0,0 +1,9 @@ +fun foo() { + for (i in 1..10) { + val x = take() + if (x == null) break + x.hashCode() + } +} + +fun take(): Any? = null \ No newline at end of file diff --git a/idea/testData/resolve/partialBodyResolve/IfNullContinue.dump b/idea/testData/resolve/partialBodyResolve/IfNullContinue.dump new file mode 100644 index 00000000000..a7ff535776f --- /dev/null +++ b/idea/testData/resolve/partialBodyResolve/IfNullContinue.dump @@ -0,0 +1,2 @@ +Resolve target: val x: kotlin.Any? smart-casted to kotlin.Any +Skipped statements: diff --git a/idea/testData/resolve/partialBodyResolve/IfNullContinue.kt b/idea/testData/resolve/partialBodyResolve/IfNullContinue.kt new file mode 100644 index 00000000000..13fa539f5d9 --- /dev/null +++ b/idea/testData/resolve/partialBodyResolve/IfNullContinue.kt @@ -0,0 +1,9 @@ +fun foo() { + for (i in 1..10) { + val x = take() + if (x == null) continue + x.hashCode() + } +} + +fun take(): Any? = null \ No newline at end of file diff --git a/idea/testData/resolve/partialBodyResolve/IfNullPrint.dump b/idea/testData/resolve/partialBodyResolve/IfNullPrint.dump new file mode 100644 index 00000000000..1598d70e5ce --- /dev/null +++ b/idea/testData/resolve/partialBodyResolve/IfNullPrint.dump @@ -0,0 +1,3 @@ +Resolve target: value-parameter val p: kotlin.Any? +Skipped statements: +if (p == null) { print("null") } diff --git a/idea/testData/resolve/partialBodyResolve/IfNullPrint.kt b/idea/testData/resolve/partialBodyResolve/IfNullPrint.kt new file mode 100644 index 00000000000..b2498dc36b4 --- /dev/null +++ b/idea/testData/resolve/partialBodyResolve/IfNullPrint.kt @@ -0,0 +1,6 @@ +fun foo(p: Any?) { + if (p == null) { + print("null") + } + p.hashCode() +} \ No newline at end of file diff --git a/idea/testData/resolve/partialBodyResolve/IfNullReturn.dump b/idea/testData/resolve/partialBodyResolve/IfNullReturn.dump new file mode 100644 index 00000000000..730615033a5 --- /dev/null +++ b/idea/testData/resolve/partialBodyResolve/IfNullReturn.dump @@ -0,0 +1,3 @@ +Resolve target: value-parameter val p: kotlin.Any? smart-casted to kotlin.Any +Skipped statements: +print("null") diff --git a/idea/testData/resolve/partialBodyResolve/IfNullReturn.kt b/idea/testData/resolve/partialBodyResolve/IfNullReturn.kt new file mode 100644 index 00000000000..c62a8c73704 --- /dev/null +++ b/idea/testData/resolve/partialBodyResolve/IfNullReturn.kt @@ -0,0 +1,7 @@ +fun foo(p: Any?) { + if (p == null) { + print("null") + return + } + p.hashCode() +} \ No newline at end of file diff --git a/idea/testData/resolve/partialBodyResolve/IfReturn.dump b/idea/testData/resolve/partialBodyResolve/IfReturn.dump new file mode 100644 index 00000000000..b146f1d642a --- /dev/null +++ b/idea/testData/resolve/partialBodyResolve/IfReturn.dump @@ -0,0 +1,3 @@ +Resolve target: val kotlin.String.size: kotlin.Int +Skipped statements: +if (x()) return diff --git a/idea/testData/resolve/partialBodyResolve/IfReturn.kt b/idea/testData/resolve/partialBodyResolve/IfReturn.kt new file mode 100644 index 00000000000..545076e38e6 --- /dev/null +++ b/idea/testData/resolve/partialBodyResolve/IfReturn.kt @@ -0,0 +1,4 @@ +fun foo(p: String) { + if (x()) return + println(p.size) +} diff --git a/idea/testData/resolve/partialBodyResolve/InIfExpressionElse.dump b/idea/testData/resolve/partialBodyResolve/InIfExpressionElse.dump new file mode 100644 index 00000000000..dda141ad7a4 --- /dev/null +++ b/idea/testData/resolve/partialBodyResolve/InIfExpressionElse.dump @@ -0,0 +1,2 @@ +Resolve target: null +Skipped statements: diff --git a/idea/testData/resolve/partialBodyResolve/InIfExpressionElse.kt b/idea/testData/resolve/partialBodyResolve/InIfExpressionElse.kt new file mode 100644 index 00000000000..8035c9c4d2f --- /dev/null +++ b/idea/testData/resolve/partialBodyResolve/InIfExpressionElse.kt @@ -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 xxx) +} diff --git a/idea/testData/resolve/partialBodyResolve/Lambda.dump b/idea/testData/resolve/partialBodyResolve/Lambda.dump new file mode 100644 index 00000000000..9fd7087886f --- /dev/null +++ b/idea/testData/resolve/partialBodyResolve/Lambda.dump @@ -0,0 +1,3 @@ +Resolve target: fun f(): kotlin.Unit +Skipped statements: +x() diff --git a/idea/testData/resolve/partialBodyResolve/Lambda.kt b/idea/testData/resolve/partialBodyResolve/Lambda.kt new file mode 100644 index 00000000000..b7914432530 --- /dev/null +++ b/idea/testData/resolve/partialBodyResolve/Lambda.kt @@ -0,0 +1,8 @@ +class C { + fun f(){} +} + +fun foo() { + val lambda = {() -> x(); C() } + lambda().f() +} diff --git a/idea/testData/resolve/partialBodyResolve/Simple.dump b/idea/testData/resolve/partialBodyResolve/Simple.dump new file mode 100644 index 00000000000..345025d0377 --- /dev/null +++ b/idea/testData/resolve/partialBodyResolve/Simple.dump @@ -0,0 +1,4 @@ +Resolve target: value-parameter val p: kotlin.Int +Skipped statements: +x() +z() diff --git a/idea/testData/resolve/partialBodyResolve/Simple.kt b/idea/testData/resolve/partialBodyResolve/Simple.kt new file mode 100644 index 00000000000..16992c20fd6 --- /dev/null +++ b/idea/testData/resolve/partialBodyResolve/Simple.kt @@ -0,0 +1,7 @@ +fun foo(p: Int) { + x() + if (y()) { + print(p) + } + z() +} diff --git a/idea/tests/org/jetbrains/jet/resolve/AbstractPartialBodyResolveTest.kt b/idea/tests/org/jetbrains/jet/resolve/AbstractPartialBodyResolveTest.kt new file mode 100644 index 00000000000..d96b53a0218 --- /dev/null +++ b/idea/tests/org/jetbrains/jet/resolve/AbstractPartialBodyResolveTest.kt @@ -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()) ?: 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> { + 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() + } +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/jet/resolve/PartialBodyResolveTestGenerated.java b/idea/tests/org/jetbrains/jet/resolve/PartialBodyResolveTestGenerated.java new file mode 100644 index 00000000000..5ab2c77aa27 --- /dev/null +++ b/idea/tests/org/jetbrains/jet/resolve/PartialBodyResolveTestGenerated.java @@ -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); + } +}