IterateExpressionIntention: checking for name conflicts

This commit is contained in:
Valentin Kipyatkov
2015-06-26 21:44:33 +03:00
parent abb729b2ed
commit 5c0195ff68
6 changed files with 46 additions and 11 deletions
@@ -51,10 +51,17 @@ public class CollectingNameValidator @jvmOverloads constructor(
} }
public class NewDeclarationNameValidator( public class NewDeclarationNameValidator(
private val container: PsiElement, private val visibleDeclarationsContext: JetElement?,
private val anchor: PsiElement?, private val checkDeclarationsIn: Sequence<PsiElement>,
private val target: NewDeclarationNameValidator.Target private val target: NewDeclarationNameValidator.Target
) : (String) -> Boolean { ) : (String) -> Boolean {
public constructor(container: PsiElement, anchor: PsiElement?, target: NewDeclarationNameValidator.Target)
: this(
(anchor ?: container).parentsWithSelf.firstIsInstanceOrNull<JetElement>(),
anchor?.siblings() ?: container.allChildren,
target)
public enum class Target { public enum class Target {
VARIABLES, VARIABLES,
FUNCTIONS_AND_CLASSES FUNCTIONS_AND_CLASSES
@@ -63,13 +70,13 @@ public class NewDeclarationNameValidator(
override fun invoke(name: String): Boolean { override fun invoke(name: String): Boolean {
val identifier = Name.identifier(name) val identifier = Name.identifier(name)
val scopeContext = (anchor ?: container).parentsWithSelf.firstIsInstanceOrNull<JetElement>() ?: return true if (visibleDeclarationsContext != null) {
val bindingContext = scopeContext.analyze(BodyResolveMode.PARTIAL_FOR_COMPLETION) val bindingContext = visibleDeclarationsContext.analyze(BodyResolveMode.PARTIAL_FOR_COMPLETION)
val resolutionScope = scopeContext.getResolutionScope(bindingContext, scopeContext.getResolutionFacade()) val resolutionScope = visibleDeclarationsContext.getResolutionScope(bindingContext, visibleDeclarationsContext.getResolutionFacade())
if (resolutionScope.hasConflict(identifier)) return false if (resolutionScope.hasConflict(identifier)) return false
}
val elementsToCheck = anchor?.siblings() ?: container.allChildren return checkDeclarationsIn.none {
return elementsToCheck.none {
it.findDescendantOfType<JetNamedDeclaration> { it.isConflicting(identifier) } != null it.findDescendantOfType<JetNamedDeclaration> { it.isConflicting(identifier) } != null
} }
} }
@@ -25,9 +25,11 @@ import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
import org.jetbrains.kotlin.idea.core.IterableTypesDetector import org.jetbrains.kotlin.idea.core.IterableTypesDetector
import org.jetbrains.kotlin.idea.core.KotlinNameSuggester import org.jetbrains.kotlin.idea.core.KotlinNameSuggester
import org.jetbrains.kotlin.idea.core.NewDeclarationNameValidator
import org.jetbrains.kotlin.idea.core.replaced import org.jetbrains.kotlin.idea.core.replaced
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.siblings
import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.types.JetType import org.jetbrains.kotlin.types.JetType
@@ -57,8 +59,8 @@ public class IterateExpressionIntention : JetSelfTargetingIntention<JetExpressio
//TODO: multi-declaration (when?) //TODO: multi-declaration (when?)
val elementType = data(element)!!.elementType val elementType = data(element)!!.elementType
//TODO: name validation val nameValidator = NewDeclarationNameValidator(element, element.siblings(), NewDeclarationNameValidator.Target.VARIABLES)
val names = KotlinNameSuggester.suggestIterationVariableNames(element, elementType, { true }, "e") val names = KotlinNameSuggester.suggestIterationVariableNames(element, elementType, nameValidator, "e")
var forExpression = JetPsiFactory(element).createExpressionByPattern("for($0 in $1) {\nx\n}", names.first(), element) as JetForExpression var forExpression = JetPsiFactory(element).createExpressionByPattern("for($0 in $1) {\nx\n}", names.first(), element) as JetForExpression
forExpression = element.replaced(forExpression) forExpression = element.replaced(forExpression)
@@ -234,7 +234,7 @@ public open class KotlinIntroduceParameterHandler(
is JetClass -> targetParent.getBody() is JetClass -> targetParent.getBody()
else -> null else -> null
} ?: throw AssertionError("Body element is not found: ${targetParent.getElementTextWithContext()}") } ?: throw AssertionError("Body element is not found: ${targetParent.getElementTextWithContext()}")
val nameValidator = NewDeclarationNameValidator(body, null, NewDeclarationNameValidator.Target.VARIABLES) val nameValidator = NewDeclarationNameValidator(body, sequenceOf(body), NewDeclarationNameValidator.Target.VARIABLES)
val suggestedNames = KotlinNameSuggester.suggestNamesByType(replacementType, nameValidator, "p") val suggestedNames = KotlinNameSuggester.suggestNamesByType(replacementType, nameValidator, "p")
val parametersUsages = findInternalUsagesOfParametersAndReceiver(targetParent, functionDescriptor) val parametersUsages = findInternalUsagesOfParametersAndReceiver(targetParent, functionDescriptor)
@@ -0,0 +1,9 @@
fun foo(names: List<String>, name: String) {
if (name == "") {
val name2 = ""
}
names<caret>
val name1 = ""
}
@@ -0,0 +1,11 @@
fun foo(names: List<String>, name: String) {
if (name == "") {
val name2 = ""
}
for (name2 in names) {
<caret>
}
val name1 = ""
}
@@ -5241,6 +5241,12 @@ public class IntentionTestGenerated extends AbstractIntentionTest {
doTest(fileName); doTest(fileName);
} }
@TestMetadata("noNameConflict.kt")
public void testNoNameConflict() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/intentions/iterateExpression/noNameConflict.kt");
doTest(fileName);
}
@TestMetadata("simple.kt") @TestMetadata("simple.kt")
public void testSimple() throws Exception { public void testSimple() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/intentions/iterateExpression/simple.kt"); String fileName = JetTestUtils.navigationMetadata("idea/testData/intentions/iterateExpression/simple.kt");