diff --git a/idea/src/org/jetbrains/jet/plugin/refactoring/JetNameValidatorImpl.java b/idea/src/org/jetbrains/jet/plugin/refactoring/JetNameValidatorImpl.java index 11f937e9459..626d4dece4d 100644 --- a/idea/src/org/jetbrains/jet/plugin/refactoring/JetNameValidatorImpl.java +++ b/idea/src/org/jetbrains/jet/plugin/refactoring/JetNameValidatorImpl.java @@ -18,60 +18,68 @@ package org.jetbrains.jet.plugin.refactoring; import com.intellij.openapi.util.Ref; import com.intellij.psi.PsiElement; -import kotlin.Function1; import org.jetbrains.annotations.NotNull; -import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor; import org.jetbrains.jet.lang.psi.JetElement; import org.jetbrains.jet.lang.psi.JetExpression; import org.jetbrains.jet.lang.psi.JetVisitorVoid; import org.jetbrains.jet.lang.resolve.BindingContext; -import org.jetbrains.jet.plugin.codeInsight.TipsManager; +import org.jetbrains.jet.lang.resolve.name.Name; +import org.jetbrains.jet.lang.resolve.scopes.JetScope; import org.jetbrains.jet.plugin.project.AnalyzerFacadeWithCache; -import java.util.Collection; +import java.util.HashSet; +import java.util.Set; public class JetNameValidatorImpl extends JetNameValidator { + public static enum Target { + FUNCTIONS_AND_CLASSES, + PROPERTIES + } + private final PsiElement myContainer; private final PsiElement myAnchor; - private final Function1 myFilter; + private final Target myTarget; - public JetNameValidatorImpl(PsiElement container, PsiElement anchor, Function1 filter) { + public JetNameValidatorImpl(PsiElement container, PsiElement anchor, Target target) { super(container.getProject()); myContainer = container; myAnchor = anchor; - myFilter = filter; + myTarget = target; } @Override protected boolean validateInner(String name) { + Set visitedScopes = new HashSet(); + PsiElement sibling; if (myAnchor != null) { sibling = myAnchor; } else { if (myContainer instanceof JetExpression) { - return checkElement(name, myContainer); + return checkElement(name, myContainer, visitedScopes); } sibling = myContainer.getFirstChild(); } while (sibling != null) { - if (!checkElement(name, sibling)) return false; + if (!checkElement(name, sibling, visitedScopes)) return false; sibling = sibling.getNextSibling(); } return true; } - private boolean checkElement(final String name, PsiElement sibling) { + private boolean checkElement(String name, PsiElement sibling, final Set visitedScopes) { if (!(sibling instanceof JetElement)) return true; - final BindingContext bindingContext = AnalyzerFacadeWithCache.getContextForElement((JetElement) sibling); + final BindingContext bindingContext = AnalyzerFacadeWithCache.getContextForElement((JetElement) sibling); + final Name identifier = Name.identifier(name); final Ref result = new Ref(true); JetVisitorVoid visitor = new JetVisitorVoid() { @Override - public void visitElement(PsiElement element) { + public void visitElement(@NotNull PsiElement element) { if (result.get()) { element.acceptChildren(this); } @@ -79,13 +87,26 @@ public class JetNameValidatorImpl extends JetNameValidator { @Override public void visitExpression(@NotNull JetExpression expression) { - Collection variants = TipsManager.getVariantsNoReceiver(expression, bindingContext); - for (DeclarationDescriptor variant : variants) { - if (variant.getName().asString().equals(name) && myFilter.invoke(variant)) { + JetScope resolutionScope = bindingContext.get(BindingContext.RESOLUTION_SCOPE, expression); + if (!visitedScopes.add(resolutionScope)) return; + + if (resolutionScope != null) { + boolean noConflict; + if (myTarget == Target.PROPERTIES) { + noConflict = resolutionScope.getProperties(identifier).isEmpty() + && resolutionScope.getLocalVariable(identifier) == null; + } + else { + noConflict = resolutionScope.getFunctions(identifier).isEmpty() + && resolutionScope.getClassifier(identifier) == null; + } + + if (!noConflict) { result.set(false); return; } } + super.visitExpression(expression); } }; diff --git a/idea/src/org/jetbrains/jet/plugin/refactoring/extractFunction/extractFunctionUtils.kt b/idea/src/org/jetbrains/jet/plugin/refactoring/extractFunction/extractFunctionUtils.kt index 333eb1065d0..17f7691f131 100644 --- a/idea/src/org/jetbrains/jet/plugin/refactoring/extractFunction/extractFunctionUtils.kt +++ b/idea/src/org/jetbrains/jet/plugin/refactoring/extractFunction/extractFunctionUtils.kt @@ -168,8 +168,8 @@ private fun List.analyzeControlFlow( is AbstractJumpInstruction -> { val element = it.getElement() if (element is JetReturnExpression - || element is JetBreakExpression - || element is JetContinueExpression) { + || element is JetBreakExpression + || element is JetContinueExpression) { jumpExits.add(it) } else if (element !is JetThrowExpression) { @@ -312,7 +312,7 @@ private fun ExtractionData.inferParametersInfo( val varNameValidator = JetNameValidatorImpl( commonParent.getParentByType(javaClass()), originalElements.first, - { it is VariableDescriptor } + JetNameValidatorImpl.Target.PROPERTIES ) val modifiedVarDescriptors = localInstructions.getModifiedVarDescriptors(bindingContext) @@ -536,7 +536,7 @@ fun ExtractionData.performAnalysis(): AnalysisResult { val functionNameValidator = JetNameValidatorImpl( nextSibling.getParent(), nextSibling, - {it is FunctionDescriptor || it is ClassDescriptor} + JetNameValidatorImpl.Target.FUNCTIONS_AND_CLASSES ) val functionName = JetNameSuggester.suggestNames(controlFlow.returnType, functionNameValidator, DEFAULT_FUNCTION_NAME).first() @@ -584,12 +584,12 @@ fun ExtractionDescriptor.validate(): ExtractionDescriptorWithConflicts { } as? PsiNamedElement if (currentTarget is JetParameter && currentTarget.getParent() == function.getValueParameterList()) continue if (currentDescriptor is LocalVariableDescriptor - && parameters.any { it.mirrorVarName == currentDescriptor.getName().asString() }) continue + && parameters.any { it.mirrorVarName == currentDescriptor.getName().asString() }) continue if (diagnostics.any { it.getFactory() == Errors.UNRESOLVED_REFERENCE } - || (currentDescriptor != null - && !ErrorUtils.isError(currentDescriptor) - && !compareDescriptors(currentDescriptor, resolveResult.descriptor))) { + || (currentDescriptor != null + && !ErrorUtils.isError(currentDescriptor) + && !compareDescriptors(currentDescriptor, resolveResult.descriptor))) { conflicts.putValue( currentRefExpr, JetRefactoringBundle.message( diff --git a/idea/src/org/jetbrains/jet/plugin/refactoring/introduce/introduceVariable/KotlinIntroduceVariableHandler.java b/idea/src/org/jetbrains/jet/plugin/refactoring/introduce/introduceVariable/KotlinIntroduceVariableHandler.java index 40abbf8cdab..4050baea7c2 100644 --- a/idea/src/org/jetbrains/jet/plugin/refactoring/introduce/introduceVariable/KotlinIntroduceVariableHandler.java +++ b/idea/src/org/jetbrains/jet/plugin/refactoring/introduce/introduceVariable/KotlinIntroduceVariableHandler.java @@ -195,13 +195,7 @@ public class KotlinIntroduceVariableHandler extends KotlinIntroduceHandlerBase { JetNameValidatorImpl validator = new JetNameValidatorImpl( commonContainer, calculateAnchor(commonParent, commonContainer, allReplaces), - new Function1() { - @Override - public Boolean invoke(DeclarationDescriptor descriptor) { - return descriptor instanceof VariableDescriptor; - } - } - + JetNameValidatorImpl.Target.PROPERTIES ); String[] suggestedNames = JetNameSuggester.suggestNames(expression, validator, "value"); final LinkedHashSet suggestedNamesSet = new LinkedHashSet(); diff --git a/idea/testData/refactoring/extractFunction/basic/noConflictWithInnerFunction.kt b/idea/testData/refactoring/extractFunction/basic/noConflictWithInnerFunction.kt new file mode 100644 index 00000000000..05b5406ebd3 --- /dev/null +++ b/idea/testData/refactoring/extractFunction/basic/noConflictWithInnerFunction.kt @@ -0,0 +1,8 @@ +fun foo() { + val a = 1 + // NEXT_SIBLING + if (a > 0) { + fun b(): Int { return 0 } + println(b()) + } +} \ No newline at end of file diff --git a/idea/testData/refactoring/extractFunction/basic/noConflictWithInnerFunction.kt.after b/idea/testData/refactoring/extractFunction/basic/noConflictWithInnerFunction.kt.after new file mode 100644 index 00000000000..b0a45c4cb1d --- /dev/null +++ b/idea/testData/refactoring/extractFunction/basic/noConflictWithInnerFunction.kt.after @@ -0,0 +1,12 @@ +fun foo() { + val a = 1 + // NEXT_SIBLING + fun b1(): Boolean { + return a > 0 + } + + if (b1()) { + fun b(): Int { return 0 } + println(b()) + } +} \ No newline at end of file diff --git a/idea/testData/refactoring/introduceVariable/noConflictWithInnerVariable.kt b/idea/testData/refactoring/introduceVariable/noConflictWithInnerVariable.kt new file mode 100644 index 00000000000..684c7f56abc --- /dev/null +++ b/idea/testData/refactoring/introduceVariable/noConflictWithInnerVariable.kt @@ -0,0 +1,7 @@ +fun foo() { + val a = 1 + if (a > 0) { + val b = 0 + println(b) + } +} \ No newline at end of file diff --git a/idea/testData/refactoring/introduceVariable/noConflictWithInnerVariable.kt.after b/idea/testData/refactoring/introduceVariable/noConflictWithInnerVariable.kt.after new file mode 100644 index 00000000000..93ca681b570 --- /dev/null +++ b/idea/testData/refactoring/introduceVariable/noConflictWithInnerVariable.kt.after @@ -0,0 +1,8 @@ +fun foo() { + val a = 1 + val b1 = a > 0 + if (b1) { + val b = 0 + println(b) + } +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/jet/plugin/refactoring/introduce/introduceVariable/JetExtractionTestGenerated.java b/idea/tests/org/jetbrains/jet/plugin/refactoring/introduce/introduceVariable/JetExtractionTestGenerated.java index 452e1f02fe2..374112ccfe5 100644 --- a/idea/tests/org/jetbrains/jet/plugin/refactoring/introduce/introduceVariable/JetExtractionTestGenerated.java +++ b/idea/tests/org/jetbrains/jet/plugin/refactoring/introduce/introduceVariable/JetExtractionTestGenerated.java @@ -128,6 +128,11 @@ public class JetExtractionTestGenerated extends AbstractJetExtractionTest { doIntroduceVariableTest("idea/testData/refactoring/introduceVariable/ManyOccurrences.kt"); } + @TestMetadata("noConflictWithInnerVariable.kt") + public void testNoConflictWithInnerVariable() throws Exception { + doIntroduceVariableTest("idea/testData/refactoring/introduceVariable/noConflictWithInnerVariable.kt"); + } + @TestMetadata("nonEquivalentReceivers.kt") public void testNonEquivalentReceivers() throws Exception { doIntroduceVariableTest("idea/testData/refactoring/introduceVariable/nonEquivalentReceivers.kt"); @@ -238,6 +243,11 @@ public class JetExtractionTestGenerated extends AbstractJetExtractionTest { doExtractFunctionTest("idea/testData/refactoring/extractFunction/basic/misdirectedRef.kt"); } + @TestMetadata("noConflictWithInnerFunction.kt") + public void testNoConflictWithInnerFunction() throws Exception { + doExtractFunctionTest("idea/testData/refactoring/extractFunction/basic/noConflictWithInnerFunction.kt"); + } + @TestMetadata("privateMemberRef.kt") public void testPrivateMemberRef() throws Exception { doExtractFunctionTest("idea/testData/refactoring/extractFunction/basic/privateMemberRef.kt");