Rewritten NameValidatorImpl to be both more efficient and more correct
This commit is contained in:
@@ -48,7 +48,7 @@ abstract class JetNamedDeclarationNotStubbed extends JetDeclarationImpl implemen
|
||||
@Override
|
||||
public Name getNameAsName() {
|
||||
String name = getName();
|
||||
return name != null ? Name.identifier(name) : null;
|
||||
return name != null ? Name.guess(name) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -62,7 +62,7 @@ abstract class JetNamedDeclarationStub<T extends KotlinStubWithFqName> extends J
|
||||
@Override
|
||||
public Name getNameAsName() {
|
||||
String name = getName();
|
||||
return name != null ? Name.identifier(name) : null;
|
||||
return name != null ? Name.guess(name) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -16,21 +16,20 @@
|
||||
|
||||
package org.jetbrains.kotlin.idea.refactoring
|
||||
|
||||
import com.intellij.openapi.util.Ref
|
||||
import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.*
|
||||
import org.jetbrains.kotlin.idea.core.*
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
|
||||
import org.jetbrains.kotlin.idea.core.getResolutionScope
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.psi.JetElement
|
||||
import org.jetbrains.kotlin.psi.JetExpression
|
||||
import org.jetbrains.kotlin.psi.JetVisitorVoid
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.allChildren
|
||||
import org.jetbrains.kotlin.psi.psiUtil.findDescendantOfType
|
||||
import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
|
||||
import org.jetbrains.kotlin.psi.psiUtil.siblings
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension
|
||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
||||
import org.jetbrains.kotlin.resolve.scopes.JetScope
|
||||
|
||||
import java.util.HashSet
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
|
||||
|
||||
public class NameValidatorImpl(
|
||||
private val container: PsiElement,
|
||||
@@ -43,54 +42,32 @@ public class NameValidatorImpl(
|
||||
}
|
||||
|
||||
override fun invoke(name: String): Boolean {
|
||||
val visitedScopes = HashSet<JetScope>()
|
||||
|
||||
val identifier = Name.identifier(name)
|
||||
|
||||
val sibling = if (anchor != null) {
|
||||
anchor
|
||||
}
|
||||
else {
|
||||
if (container is JetExpression) {
|
||||
return !hasConflict(identifier, container, visitedScopes)
|
||||
}
|
||||
container.getFirstChild() ?: return true
|
||||
}
|
||||
val scopeContext = (anchor ?: container).parentsWithSelf.firstIsInstanceOrNull<JetElement>() ?: return true
|
||||
val bindingContext = scopeContext.analyze(BodyResolveMode.PARTIAL_FOR_COMPLETION)
|
||||
val resolutionScope = scopeContext.getResolutionScope(bindingContext, scopeContext.getResolutionFacade())
|
||||
if (resolutionScope.hasConflict(identifier)) return false
|
||||
|
||||
return sibling.siblings().none { hasConflict(identifier, it, visitedScopes) }
|
||||
val elementsToCheck = anchor?.siblings() ?: container.allChildren
|
||||
return elementsToCheck.none {
|
||||
it.findDescendantOfType<JetNamedDeclaration> { it.isConflicting(identifier) } != null
|
||||
}
|
||||
}
|
||||
|
||||
private fun hasConflict(name: Name, sibling: PsiElement, visitedScopes: MutableSet<JetScope>): Boolean {
|
||||
if (sibling !is JetElement) return false
|
||||
private fun JetScope.hasConflict(name: Name): Boolean {
|
||||
return when(target) {
|
||||
Target.PROPERTIES -> getProperties(name).any { !it.isExtension } || getLocalVariable(name) != null
|
||||
Target.FUNCTIONS_AND_CLASSES -> getFunctions(name).any { !it.isExtension } || getClassifier(name) != null
|
||||
}
|
||||
}
|
||||
|
||||
val context = sibling.analyze(BodyResolveMode.FULL)
|
||||
|
||||
var conflictFound = false
|
||||
sibling.accept(object : JetVisitorVoid() {
|
||||
override fun visitElement(element: PsiElement) {
|
||||
if (!conflictFound) {
|
||||
element.acceptChildren(this)
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitExpression(expression: JetExpression) {
|
||||
val resolutionScope = expression.getResolutionScope(context, expression.getResolutionFacade())
|
||||
|
||||
if (!visitedScopes.add(resolutionScope)) return
|
||||
|
||||
val conflict = if (target === Target.PROPERTIES)
|
||||
resolutionScope.getProperties(name).any { !it.isExtension } || resolutionScope.getLocalVariable(name) != null
|
||||
else
|
||||
resolutionScope.getFunctions(name).any { !it.isExtension } || resolutionScope.getClassifier(name) != null
|
||||
|
||||
if (conflict) {
|
||||
conflictFound = true
|
||||
return
|
||||
}
|
||||
|
||||
super.visitExpression(expression)
|
||||
}
|
||||
})
|
||||
return conflictFound
|
||||
private fun JetNamedDeclaration.isConflicting(name: Name): Boolean {
|
||||
if (getNameAsName() != name) return false
|
||||
if (this is JetCallableDeclaration && getReceiverTypeReference() != null) return false
|
||||
return when(target) {
|
||||
Target.PROPERTIES -> this is JetVariableDeclaration
|
||||
Target.FUNCTIONS_AND_CLASSES -> this is JetNamedFunction || this is JetClassOrObject
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+4
-4
@@ -8,12 +8,12 @@ class A(val a: Int) {
|
||||
}
|
||||
|
||||
fun test() {
|
||||
val a = A(1)
|
||||
val a1 = A(2)
|
||||
a.foo(a.a + a1.a)
|
||||
val a2 = A(1)
|
||||
val a3 = A(2)
|
||||
a2.foo(a2.a + a3.a)
|
||||
with(A(1)) {
|
||||
val a1 = A(2)
|
||||
foo(this.a + a1.a)
|
||||
foo(a + a1.a)
|
||||
val a = A(2)
|
||||
this.foo(this.a + a.a)
|
||||
}
|
||||
|
||||
+4
-4
@@ -8,12 +8,12 @@ class A(val a: Int) {
|
||||
}
|
||||
|
||||
fun test() {
|
||||
val a = A(1)
|
||||
val a1 = A(2)
|
||||
a.foo(a.a + a1.a)
|
||||
val a2 = A(1)
|
||||
val a3 = A(2)
|
||||
a2.foo(a2.a + a3.a)
|
||||
with(A(1)) {
|
||||
val a1 = A(2)
|
||||
foo(this.a + a1.a)
|
||||
foo(a + a1.a)
|
||||
val a = A(2)
|
||||
this.foo(this.a + a.a)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
class For {
|
||||
var xxx: For? = null
|
||||
}
|
||||
|
||||
|
||||
fun foo(f: For) {
|
||||
<selection>f.xxx</selection>
|
||||
|
||||
if (true) {
|
||||
val xxx = 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
class For {
|
||||
var xxx: For? = null
|
||||
}
|
||||
|
||||
|
||||
fun foo(f: For) {
|
||||
val xxx1 = f.xxx
|
||||
|
||||
if (true) {
|
||||
val xxx = 1
|
||||
}
|
||||
}
|
||||
+9
-3
@@ -163,9 +163,15 @@ public class JetExtractionTestGenerated extends AbstractJetExtractionTest {
|
||||
doIntroduceVariableTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("NameSuggestionBug.kt")
|
||||
public void testNameSuggestionBug() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/introduceVariable/NameSuggestionBug.kt");
|
||||
@TestMetadata("NameSuggestionBug1.kt")
|
||||
public void testNameSuggestionBug1() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/introduceVariable/NameSuggestionBug1.kt");
|
||||
doIntroduceVariableTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("NameSuggestionBug2.kt")
|
||||
public void testNameSuggestionBug2() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/introduceVariable/NameSuggestionBug2.kt");
|
||||
doIntroduceVariableTest(fileName);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user