KT-4822 Wrong scope is used for local variable name completion

#KT-4822 Fixed
This commit is contained in:
Valentin Kipyatkov
2015-06-11 22:10:04 +03:00
parent 14ddc9d972
commit b301b22f47
14 changed files with 229 additions and 63 deletions
@@ -75,7 +75,7 @@ public class ReferenceVariantsHelper(
nameFilter: (Name) -> Boolean
): Collection<DeclarationDescriptor> {
val parent = expression.getParent()
val resolutionScope = context.correctedResolutionScope(expression) ?: return listOf()
val resolutionScope = context[BindingContext.RESOLUTION_SCOPE, expression] ?: return listOf()
val containingDeclaration = resolutionScope.getContainingDeclaration()
if (parent is JetImportDirective || parent is JetPackageDirective) {
@@ -156,7 +156,7 @@ public class ShadowedDeclarationsFilter(
}
val calleeExpression = call.getCalleeExpression() ?: return descriptors
var resolutionScope = bindingContext.correctedResolutionScope(calleeExpression) ?: return descriptors
var resolutionScope = bindingContext[BindingContext.RESOLUTION_SCOPE, calleeExpression] ?: return descriptors
if (descriptorsToImport.isNotEmpty()) {
resolutionScope = ChainedScope(resolutionScope.getContainingDeclaration(), "Scope with explicitly imported descriptors",
@@ -16,15 +16,8 @@
package org.jetbrains.kotlin.idea.util
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.VariableDescriptor
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
import org.jetbrains.kotlin.resolve.scopes.JetScope
import org.jetbrains.kotlin.utils.addIfNotNull
public fun JetFunctionLiteral.findLabelAndCall(): Pair<Name?, JetCallExpression?> {
val literalParent = (this.getParent() as JetFunctionLiteralExpression).getParent()
@@ -51,34 +44,3 @@ public fun JetFunctionLiteral.findLabelAndCall(): Pair<Name?, JetCallExpression?
}
}
}
// returns corrected resolution scope excluding variable inside its own initializer
// will not be needed after correcting JetScope stored BindingContext (see KT-4822 Wrong scope is used for local variable name completion)
public fun BindingContext.correctedResolutionScope(expression: JetExpression): JetScope? {
val scope = get(BindingContext.RESOLUTION_SCOPE, expression) ?: return null
val variablesToExclude = hashSetOf<VariableDescriptor>()
for (element in expression.parentsWithSelf) {
if (element is JetExpression) {
val declaration = element.getParent() as? JetVariableDeclaration ?: continue
if (element == declaration.getInitializer()) {
variablesToExclude.addIfNotNull(get(BindingContext.VARIABLE, declaration))
}
}
}
if (variablesToExclude.isEmpty()) return scope
return object : JetScope by scope {
override fun getDescriptors(kindFilter: DescriptorKindFilter, nameFilter: (Name) -> Boolean)
= scope.getDescriptors(kindFilter, nameFilter).filter { it !in variablesToExclude }
//TODO: it's not correct!
override fun getLocalVariable(name: Name): VariableDescriptor? {
val variable = scope.getLocalVariable(name) ?: return null
return if (variable in variablesToExclude) null else variable
}
override fun getProperties(name: Name) = scope.getProperties(name).filter { it !in variablesToExclude }
}
}
@@ -198,13 +198,28 @@ abstract class CompletionSessionBase(protected val configuration: CompletionSess
// set is used only for completion in code fragments
protected val referenceVariants: Collection<DeclarationDescriptor> by Delegates.lazy {
if (descriptorKindFilter != null) {
referenceVariantsHelper.getReferenceVariants(reference!!.expression, descriptorKindFilter!!, false, prefixMatcher.asNameFilter())
val expression = reference!!.expression
referenceVariantsHelper.getReferenceVariants(expression, descriptorKindFilter!!, false, prefixMatcher.asNameFilter())
.excludeNonInitializedVariable(expression)
}
else {
emptyList()
}
}
// filters out variable inside its initializer
private fun Collection<DeclarationDescriptor>.excludeNonInitializedVariable(expression: JetExpression): Collection<DeclarationDescriptor> {
for (element in expression.parentsWithSelf) {
val parent = element.getParent()
if (parent is JetVariableDeclaration && element == parent.getInitializer()) {
val descriptor = bindingContext[BindingContext.DECLARATION_TO_DESCRIPTOR, parent]
return this.filter { it != descriptor }
}
if (element is JetDeclaration) break // we can use variable inside lambda or anonymous object located in its initializer
}
return this
}
protected fun getRuntimeReceiverTypeReferenceVariants(): Collection<DeclarationDescriptor> {
val descriptors = referenceVariantsHelper.getReferenceVariants(reference!!.expression, descriptorKindFilter!!, true, prefixMatcher.asNameFilter())
return descriptors.filter { descriptor ->
@@ -25,7 +25,6 @@ import org.jetbrains.kotlin.descriptors.VariableDescriptor
import org.jetbrains.kotlin.idea.JetDescriptorIconProvider
import org.jetbrains.kotlin.idea.completion.*
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
import org.jetbrains.kotlin.idea.util.correctedResolutionScope
import org.jetbrains.kotlin.psi.JetExpression
import org.jetbrains.kotlin.renderer.render
import org.jetbrains.kotlin.resolve.BindingContext
@@ -41,7 +40,7 @@ class MultipleArgumentsItemProvider(val bindingContext: BindingContext,
public fun addToCollection(collection: MutableCollection<LookupElement>,
expectedInfos: Collection<ExpectedInfo>,
context: JetExpression) {
val resolutionScope = bindingContext.correctedResolutionScope(context) ?: return
val resolutionScope = bindingContext[BindingContext.RESOLUTION_SCOPE, context] ?: return
val added = HashSet<String>()
for (expectedInfo in expectedInfos) {
@@ -0,0 +1,8 @@
fun foo() {
val v = {
<caret>
val hello = 1
hello
}
}
// ABSENT: hello
@@ -0,0 +1,18 @@
val xxx = 3
fun foo(xxx: Int) {
val xxx = 1.0
if (true) {
val xxx = 'c'
if (true) {
val xxx: Any = run {
val xxx: String = xx<caret>
}
}
}
}
// EXIST: { lookupString: "xxx", itemText: "xxx", typeText: "Char" }
// NOTHING_ELSE
@@ -0,0 +1,3 @@
val xxx: String = <caret>
// ABSENT: xxx
@@ -0,0 +1,5 @@
fun foo(p: () -> Unit): String = ""
val xxx: String = foo { <caret> }
// EXIST: xxx
@@ -823,6 +823,12 @@ public class JSBasicCompletionTestGenerated extends AbstractJSBasicCompletionTes
doTest(fileName);
}
@TestMetadata("NoLocalsDeclaredAhead.kt")
public void testNoLocalsDeclaredAhead() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/NoLocalsDeclaredAhead.kt");
doTest(fileName);
}
@TestMetadata("NoNestedClassAfterReceiver.kt")
public void testNoNestedClassAfterReceiver() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/NoNestedClassAfterReceiver.kt");
@@ -1422,6 +1428,24 @@ public class JSBasicCompletionTestGenerated extends AbstractJSBasicCompletionTes
doTest(fileName);
}
@TestMetadata("InInitializer4.kt")
public void testInInitializer4() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/shadowing/InInitializer4.kt");
doTest(fileName);
}
@TestMetadata("InInitializer5.kt")
public void testInInitializer5() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/shadowing/InInitializer5.kt");
doTest(fileName);
}
@TestMetadata("InInitializer6.kt")
public void testInInitializer6() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/shadowing/InInitializer6.kt");
doTest(fileName);
}
@TestMetadata("Locals1.kt")
public void testLocals1() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/shadowing/Locals1.kt");
@@ -823,6 +823,12 @@ public class JvmBasicCompletionTestGenerated extends AbstractJvmBasicCompletionT
doTest(fileName);
}
@TestMetadata("NoLocalsDeclaredAhead.kt")
public void testNoLocalsDeclaredAhead() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/NoLocalsDeclaredAhead.kt");
doTest(fileName);
}
@TestMetadata("NoNestedClassAfterReceiver.kt")
public void testNoNestedClassAfterReceiver() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/NoNestedClassAfterReceiver.kt");
@@ -1422,6 +1428,24 @@ public class JvmBasicCompletionTestGenerated extends AbstractJvmBasicCompletionT
doTest(fileName);
}
@TestMetadata("InInitializer4.kt")
public void testInInitializer4() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/shadowing/InInitializer4.kt");
doTest(fileName);
}
@TestMetadata("InInitializer5.kt")
public void testInInitializer5() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/shadowing/InInitializer5.kt");
doTest(fileName);
}
@TestMetadata("InInitializer6.kt")
public void testInInitializer6() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/shadowing/InInitializer6.kt");
doTest(fileName);
}
@TestMetadata("Locals1.kt")
public void testLocals1() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/common/shadowing/Locals1.kt");