Fixed bug in partial body resolve

This commit is contained in:
Valentin Kipyatkov
2015-02-19 19:21:35 +03:00
parent db987eedee
commit 27f5d71e4f
6 changed files with 72 additions and 15 deletions
@@ -94,14 +94,14 @@ class PartialBodyResolveFilter(
if (statement is JetNamedDeclaration) { if (statement is JetNamedDeclaration) {
val name = statement.getName() val name = statement.getName()
if (name != null && nameFilter.accepts(name)) { if (name != null && nameFilter(name)) {
statementMarks.mark(statement, MarkLevel.NEED_REFERENCE_RESOLVE) statementMarks.mark(statement, MarkLevel.NEED_REFERENCE_RESOLVE)
} }
} }
else if (statement is JetMultiDeclaration) { else if (statement is JetMultiDeclaration) {
if (statement.getEntries().any { if (statement.getEntries().any {
val name = it.getName() val name = it.getName()
name != null && nameFilter.accepts(name) name != null && nameFilter(name)
}) { }) {
statementMarks.mark(statement, MarkLevel.NEED_REFERENCE_RESOLVE) statementMarks.mark(statement, MarkLevel.NEED_REFERENCE_RESOLVE)
} }
@@ -118,7 +118,7 @@ class PartialBodyResolveFilter(
updateNameFilter() updateNameFilter()
if (!nameFilter.isEmpty) { if (!nameFilter.isEmpty) {
val smartCastPlaces = potentialSmartCastPlaces(statement, { it.identifiers().all { nameFilter.accepts(it) } }) val smartCastPlaces = potentialSmartCastPlaces(statement, { it.affectsNames(nameFilter) })
if (!smartCastPlaces.isEmpty()) { if (!smartCastPlaces.isEmpty()) {
//TODO: do we really need correct resolve for ALL smart cast places? //TODO: do we really need correct resolve for ALL smart cast places?
smartCastPlaces.values() smartCastPlaces.values()
@@ -179,7 +179,7 @@ class PartialBodyResolveFilter(
override fun visitBinaryWithTypeRHSExpression(expression: JetBinaryExpressionWithTypeRHS) { override fun visitBinaryWithTypeRHSExpression(expression: JetBinaryExpressionWithTypeRHS) {
expression.acceptChildren(this) expression.acceptChildren(this)
if (expression.getOperationReference()?.getReferencedNameElementType() == JetTokens.AS_KEYWORD) { if (expression.getOperationReference().getReferencedNameElementType() == JetTokens.AS_KEYWORD) {
addIfCanBeSmartCast(expression.getLeft()) addIfCanBeSmartCast(expression.getLeft())
} }
} }
@@ -262,7 +262,7 @@ class PartialBodyResolveFilter(
override fun visitIsExpression(expression: JetIsExpression) { override fun visitIsExpression(expression: JetIsExpression) {
expression.acceptChildren(this) expression.acceptChildren(this)
result.addIfNotNull(expression.getLeftHandSide()?.smartCastExpressionName()) result.addIfNotNull(expression.getLeftHandSide().smartCastExpressionName())
} }
}) })
return result return result
@@ -379,18 +379,25 @@ class PartialBodyResolveFilter(
private fun JetElement.noControlFlowInside() = this is JetFunction || this is JetClass || this is JetClassBody private fun JetElement.noControlFlowInside() = this is JetFunction || this is JetClass || this is JetClassBody
} }
private data class SmartCastName(val receiverName: SmartCastName?, val selectorName: String) { private data class SmartCastName(
override fun toString(): String = if (receiverName != null) receiverName.toString() + "." + selectorName else selectorName private val receiverName: SmartCastName?,
private val selectorName: String? /* null means "this" (and receiverName should be null */
) {
{
if (selectorName == null) {
assert(receiverName == null, "selectorName is allowed to be null only when receiverName is also null (which means 'this')")
}
}
fun identifiers(): Collection<String> { override fun toString(): String = if (receiverName != null) receiverName.toString() + "." + selectorName else selectorName ?: "this"
return if (receiverName != null)
receiverName.identifiers() + listOf(selectorName) fun affectsNames(nameFilter: (String) -> Boolean): Boolean {
else if (selectorName == null) return true
listOf(selectorName) if (!nameFilter(selectorName)) return false
return receiverName == null || receiverName.affectsNames(nameFilter)
} }
} }
//TODO: this can be smart-cast too!
private fun JetExpression.smartCastExpressionName(): SmartCastName? { private fun JetExpression.smartCastExpressionName(): SmartCastName? {
return when (this) { return when (this) {
is JetSimpleNameExpression -> SmartCastName(null, this.getReferencedName()) is JetSimpleNameExpression -> SmartCastName(null, this.getReferencedName())
@@ -406,15 +413,17 @@ class PartialBodyResolveFilter(
return SmartCastName(receiverName, selectorName) return SmartCastName(receiverName, selectorName)
} }
is JetThisExpression -> SmartCastName(null, null)
else -> null else -> null
} }
} }
//TODO: declarations with special names (e.g. "get") //TODO: declarations with special names (e.g. "get")
private class NameFilter { private class NameFilter : (String) -> Boolean {
private var names: MutableSet<String>? = HashSet() private var names: MutableSet<String>? = HashSet()
fun accepts(name: String) = names == null || name in names!! override fun invoke(name: String) = names == null || name in names!!
val isEmpty: Boolean val isEmpty: Boolean
get() = names?.isEmpty() ?: false get() = names?.isEmpty() ?: false
@@ -0,0 +1,10 @@
Resolve target: fun foo(): kotlin.Unit
----------------------------------------------
class C {
fun foo(){}
}
fun Any.f() {
if (this !is C) return
<caret>foo()
}
@@ -0,0 +1,8 @@
class C {
fun foo(){}
}
fun Any.f() {
if (this !is C) return
<caret>foo()
}
@@ -0,0 +1,10 @@
Resolve target: fun foo(): kotlin.Unit
----------------------------------------------
class C {
fun foo(){}
}
fun Any.f() {
if (this !is C) return
this.<caret>foo()
}
@@ -0,0 +1,8 @@
class C {
fun foo(){}
}
fun Any.f() {
if (this !is C) return
this.<caret>foo()
}
@@ -360,6 +360,18 @@ public class PartialBodyResolveTestGenerated extends AbstractPartialBodyResolveT
doTest(fileName); doTest(fileName);
} }
@TestMetadata("SmartCastOfThis1.kt")
public void testSmartCastOfThis1() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/resolve/partialBodyResolve/SmartCastOfThis1.kt");
doTest(fileName);
}
@TestMetadata("SmartCastOfThis2.kt")
public void testSmartCastOfThis2() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/resolve/partialBodyResolve/SmartCastOfThis2.kt");
doTest(fileName);
}
@TestMetadata("SmartCastPointsResolveRequired1.kt") @TestMetadata("SmartCastPointsResolveRequired1.kt")
public void testSmartCastPointsResolveRequired1() throws Exception { public void testSmartCastPointsResolveRequired1() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/resolve/partialBodyResolve/SmartCastPointsResolveRequired1.kt"); String fileName = JetTestUtils.navigationMetadata("idea/testData/resolve/partialBodyResolve/SmartCastPointsResolveRequired1.kt");