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
|
@Override
|
||||||
public Name getNameAsName() {
|
public Name getNameAsName() {
|
||||||
String name = getName();
|
String name = getName();
|
||||||
return name != null ? Name.identifier(name) : null;
|
return name != null ? Name.guess(name) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ abstract class JetNamedDeclarationStub<T extends KotlinStubWithFqName> extends J
|
|||||||
@Override
|
@Override
|
||||||
public Name getNameAsName() {
|
public Name getNameAsName() {
|
||||||
String name = getName();
|
String name = getName();
|
||||||
return name != null ? Name.identifier(name) : null;
|
return name != null ? Name.guess(name) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -16,21 +16,20 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.idea.refactoring
|
package org.jetbrains.kotlin.idea.refactoring
|
||||||
|
|
||||||
import com.intellij.openapi.util.Ref
|
|
||||||
import com.intellij.psi.PsiElement
|
import com.intellij.psi.PsiElement
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.*
|
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||||
import org.jetbrains.kotlin.idea.core.*
|
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.name.Name
|
||||||
import org.jetbrains.kotlin.psi.JetElement
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.psi.JetExpression
|
import org.jetbrains.kotlin.psi.psiUtil.allChildren
|
||||||
import org.jetbrains.kotlin.psi.JetVisitorVoid
|
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.psi.psiUtil.siblings
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext
|
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension
|
import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension
|
||||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
||||||
import org.jetbrains.kotlin.resolve.scopes.JetScope
|
import org.jetbrains.kotlin.resolve.scopes.JetScope
|
||||||
|
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
|
||||||
import java.util.HashSet
|
|
||||||
|
|
||||||
public class NameValidatorImpl(
|
public class NameValidatorImpl(
|
||||||
private val container: PsiElement,
|
private val container: PsiElement,
|
||||||
@@ -43,54 +42,32 @@ public class NameValidatorImpl(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun invoke(name: String): Boolean {
|
override fun invoke(name: String): Boolean {
|
||||||
val visitedScopes = HashSet<JetScope>()
|
|
||||||
|
|
||||||
val identifier = Name.identifier(name)
|
val identifier = Name.identifier(name)
|
||||||
|
|
||||||
val sibling = if (anchor != null) {
|
val scopeContext = (anchor ?: container).parentsWithSelf.firstIsInstanceOrNull<JetElement>() ?: return true
|
||||||
anchor
|
val bindingContext = scopeContext.analyze(BodyResolveMode.PARTIAL_FOR_COMPLETION)
|
||||||
}
|
val resolutionScope = scopeContext.getResolutionScope(bindingContext, scopeContext.getResolutionFacade())
|
||||||
else {
|
if (resolutionScope.hasConflict(identifier)) return false
|
||||||
if (container is JetExpression) {
|
|
||||||
return !hasConflict(identifier, container, visitedScopes)
|
|
||||||
}
|
|
||||||
container.getFirstChild() ?: return true
|
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
private fun JetScope.hasConflict(name: Name): Boolean {
|
||||||
if (sibling !is JetElement) return false
|
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)
|
private fun JetNamedDeclaration.isConflicting(name: Name): Boolean {
|
||||||
|
if (getNameAsName() != name) return false
|
||||||
var conflictFound = false
|
if (this is JetCallableDeclaration && getReceiverTypeReference() != null) return false
|
||||||
sibling.accept(object : JetVisitorVoid() {
|
return when(target) {
|
||||||
override fun visitElement(element: PsiElement) {
|
Target.PROPERTIES -> this is JetVariableDeclaration
|
||||||
if (!conflictFound) {
|
Target.FUNCTIONS_AND_CLASSES -> this is JetNamedFunction || this is JetClassOrObject
|
||||||
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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-4
@@ -8,12 +8,12 @@ class A(val a: Int) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun test() {
|
fun test() {
|
||||||
val a = A(1)
|
val a2 = A(1)
|
||||||
val a1 = A(2)
|
val a3 = A(2)
|
||||||
a.foo(a.a + a1.a)
|
a2.foo(a2.a + a3.a)
|
||||||
with(A(1)) {
|
with(A(1)) {
|
||||||
val a1 = A(2)
|
val a1 = A(2)
|
||||||
foo(this.a + a1.a)
|
foo(a + a1.a)
|
||||||
val a = A(2)
|
val a = A(2)
|
||||||
this.foo(this.a + a.a)
|
this.foo(this.a + a.a)
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-4
@@ -8,12 +8,12 @@ class A(val a: Int) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun test() {
|
fun test() {
|
||||||
val a = A(1)
|
val a2 = A(1)
|
||||||
val a1 = A(2)
|
val a3 = A(2)
|
||||||
a.foo(a.a + a1.a)
|
a2.foo(a2.a + a3.a)
|
||||||
with(A(1)) {
|
with(A(1)) {
|
||||||
val a1 = A(2)
|
val a1 = A(2)
|
||||||
foo(this.a + a1.a)
|
foo(a + a1.a)
|
||||||
val a = A(2)
|
val a = A(2)
|
||||||
this.foo(this.a + a.a)
|
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);
|
doIntroduceVariableTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestMetadata("NameSuggestionBug.kt")
|
@TestMetadata("NameSuggestionBug1.kt")
|
||||||
public void testNameSuggestionBug() throws Exception {
|
public void testNameSuggestionBug1() throws Exception {
|
||||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/introduceVariable/NameSuggestionBug.kt");
|
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);
|
doIntroduceVariableTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user