Improve JetNameValidatorImpl performance

This commit is contained in:
Alexey Sedunov
2014-04-29 14:26:30 +04:00
parent 5cfb8bbf99
commit 31a1ec0f05
8 changed files with 90 additions and 30 deletions
@@ -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<DeclarationDescriptor, Boolean> myFilter;
private final Target myTarget;
public JetNameValidatorImpl(PsiElement container, PsiElement anchor, Function1<DeclarationDescriptor, Boolean> 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<JetScope> visitedScopes = new HashSet<JetScope>();
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<JetScope> 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<Boolean> result = new Ref<Boolean>(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<DeclarationDescriptor> 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);
}
};
@@ -168,8 +168,8 @@ private fun List<Instruction>.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<JetExpression>()),
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(
@@ -195,13 +195,7 @@ public class KotlinIntroduceVariableHandler extends KotlinIntroduceHandlerBase {
JetNameValidatorImpl validator = new JetNameValidatorImpl(
commonContainer,
calculateAnchor(commonParent, commonContainer, allReplaces),
new Function1<DeclarationDescriptor, Boolean>() {
@Override
public Boolean invoke(DeclarationDescriptor descriptor) {
return descriptor instanceof VariableDescriptor;
}
}
JetNameValidatorImpl.Target.PROPERTIES
);
String[] suggestedNames = JetNameSuggester.suggestNames(expression, validator, "value");
final LinkedHashSet<String> suggestedNamesSet = new LinkedHashSet<String>();
@@ -0,0 +1,8 @@
fun foo() {
val a = 1
// NEXT_SIBLING
if (<selection>a > 0</selection>) {
fun b(): Int { return 0 }
println(b())
}
}
@@ -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())
}
}
@@ -0,0 +1,7 @@
fun foo() {
val a = 1
if (<selection>a > 0</selection>) {
val b = 0
println(b)
}
}
@@ -0,0 +1,8 @@
fun foo() {
val a = 1
val b1 = a > 0
if (b1) {
val b = 0
println(b)
}
}
@@ -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");