diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/RecursionWeigher.java b/java/java-impl/src/com/intellij/codeInsight/completion/RecursionWeigher.java index 26b97a1422d3..58488fb7c383 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/RecursionWeigher.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/RecursionWeigher.java @@ -15,12 +15,15 @@ */ package com.intellij.codeInsight.completion; +import com.intellij.codeInsight.CodeInsightUtil; import com.intellij.codeInsight.ExpectedTypeInfo; import com.intellij.codeInsight.lookup.LookupElement; import com.intellij.codeInsight.lookup.LookupElementWeigher; import com.intellij.openapi.util.Comparing; import com.intellij.patterns.PsiJavaPatterns; import com.intellij.psi.*; +import com.intellij.psi.codeStyle.JavaCodeStyleManager; +import com.intellij.psi.codeStyle.VariableKind; import com.intellij.psi.filters.AndFilter; import com.intellij.psi.filters.ClassFilter; import com.intellij.psi.filters.ElementFilter; @@ -28,6 +31,7 @@ import com.intellij.psi.filters.element.ExcludeDeclaredFilter; import com.intellij.psi.filters.element.ExcludeSillyAssignment; import com.intellij.psi.scope.ElementClassFilter; import com.intellij.psi.search.searches.DeepestSuperMethodsSearch; +import com.intellij.psi.util.PropertyUtil; import com.intellij.psi.util.PsiTreeUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -42,7 +46,8 @@ class RecursionWeigher extends LookupElementWeigher { @Nullable private final PsiMethodCallExpression myExpression; private final PsiMethod myPositionMethod; private final ExpectedTypeInfo[] myExpectedInfos; - private final PsiExpression myQualifier; + private final PsiExpression myCallQualifier; + private final PsiExpression myPositionQualifier; private final boolean myDelegate; public RecursionWeigher(PsiElement position, @@ -56,8 +61,31 @@ class RecursionWeigher extends LookupElementWeigher { myExpression = expression; myPositionMethod = PsiTreeUtil.getParentOfType(position, PsiMethod.class, false); myExpectedInfos = expectedInfos; - myQualifier = myReference.getQualifierExpression(); - myDelegate = myQualifier != null && !(myQualifier instanceof PsiThisExpression); + myCallQualifier = normalizeQualifier(myReference.getQualifierExpression()); + myPositionQualifier = normalizeQualifier(position.getParent() instanceof PsiJavaCodeReferenceElement + ? ((PsiJavaCodeReferenceElement)position.getParent()).getQualifier() + : null); + myDelegate = isDelegatingCall(); + } + + @Nullable + private static PsiExpression normalizeQualifier(PsiElement qualifier) { + return qualifier instanceof PsiThisExpression || !(qualifier instanceof PsiExpression) ? null : (PsiExpression)qualifier; + } + + private boolean isDelegatingCall() { + if (myCallQualifier != null && + myPositionQualifier != null && + myCallQualifier != myPositionQualifier && + CodeInsightUtil.areExpressionsEquivalent(myCallQualifier, myPositionQualifier)) { + return false; + } + + if (myCallQualifier == null && myPositionQualifier == null) { + return false; + } + + return true; } @Nullable @@ -98,12 +126,16 @@ class RecursionWeigher extends LookupElementWeigher { return Result.passingObjectToItself; } - if (myExpression != null && myPositionMethod != null) { + if (myExpression != null) { if (myExpectedInfos != null) { final PsiType itemType = JavaCompletionUtil.getLookupElementType(element); - if (itemType != null) { - for (final ExpectedTypeInfo expectedInfo : myExpectedInfos) { - if (myPositionMethod.equals(expectedInfo.getCalledMethod()) && expectedInfo.getType().isAssignableFrom(itemType)) { + for (final ExpectedTypeInfo expectedInfo : myExpectedInfos) { + PsiMethod calledMethod = expectedInfo.getCalledMethod(); + if (calledMethod != null && itemType != null) { + if (calledMethod.equals(myPositionMethod) && expectedInfo.getType().isAssignableFrom(itemType)) { + return myDelegate ? Result.delegation : Result.recursive; + } + if (isGetterSetterAssignment(object, calledMethod)) { return myDelegate ? Result.delegation : Result.recursive; } } @@ -126,12 +158,32 @@ class RecursionWeigher extends LookupElementWeigher { return Result.normal; } + private static boolean isGetterSetterAssignment(Object lookupObject, PsiMethod calledMethod) { + if (!PropertyUtil.isSimplePropertySetter(calledMethod)) { + return false; + } + + String prop = PropertyUtil.getPropertyName(calledMethod); + assert prop != null; + if (lookupObject instanceof PsiField && + prop.equals(JavaCodeStyleManager.getInstance(calledMethod.getProject()) + .variableNameToPropertyName(((PsiField)lookupObject).getName(), VariableKind.FIELD))) { + return true; + } + if (lookupObject instanceof PsiMethod && + PropertyUtil.isSimplePropertyGetter((PsiMethod)lookupObject) && + prop.equals(PropertyUtil.getPropertyName((PsiMethod)lookupObject))) { + return true; + } + return false; + } + private boolean isPassingObjectToItself(Object object) { if (object instanceof PsiThisExpression) { - return !myDelegate || myQualifier instanceof PsiSuperExpression; + return !myDelegate || myCallQualifier instanceof PsiSuperExpression; } - return myQualifier instanceof PsiReferenceExpression && - object.equals(((PsiReferenceExpression)myQualifier).advancedResolve(true).getElement()); + return myCallQualifier instanceof PsiReferenceExpression && + object.equals(((PsiReferenceExpression)myCallQualifier).advancedResolve(true).getElement()); } @NotNull diff --git a/java/java-tests/testData/codeInsight/completion/smartTypeSorting/DispreferGetterInSetterCall.java b/java/java-tests/testData/codeInsight/completion/smartTypeSorting/DispreferGetterInSetterCall.java new file mode 100644 index 000000000000..80abddfe9250 --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/smartTypeSorting/DispreferGetterInSetterCall.java @@ -0,0 +1,13 @@ +public class Aaaaaaa { + int color; + + void setColor(int color) {} + int getColor() {} + int getZooColor() {} + + { + setColor(); + } + + +} diff --git a/java/java-tests/testData/codeInsight/completion/smartTypeSorting/PreferOtherGetterInSetterCall.java b/java/java-tests/testData/codeInsight/completion/smartTypeSorting/PreferOtherGetterInSetterCall.java new file mode 100644 index 000000000000..861cce30711a --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/smartTypeSorting/PreferOtherGetterInSetterCall.java @@ -0,0 +1,13 @@ +public class Aaaaaaa { + int color; + + void setColor(int color) {} + int getColor() {} + int getZooColor() {} + + void foo(Aaaaaaa a) { + setColor(a.); + } + + +} diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/completion/SmartTypeCompletionOrderingTest.groovy b/java/java-tests/testSrc/com/intellij/codeInsight/completion/SmartTypeCompletionOrderingTest.groovy index 348ceb177809..2480cc6bca78 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/completion/SmartTypeCompletionOrderingTest.groovy +++ b/java/java-tests/testSrc/com/intellij/codeInsight/completion/SmartTypeCompletionOrderingTest.groovy @@ -296,6 +296,13 @@ public class SmartTypeCompletionOrderingTest extends CompletionSortingTestCase { assert 'FList.emptyList' in lookup.items[5].allLookupStrings } + public void testDispreferGetterInSetterCall() { + checkPreferredItems 0, 'getZooColor', 'hashCode', 'color', 'getColor' + } + public void testPreferOtherGetterInSetterCall() { + checkPreferredItems 0, 'color', 'getColor', 'getZooColor', 'hashCode' + } + @Override protected String getBasePath() { return JavaTestUtil.getRelativeJavaTestDataPath() + BASE_PATH; diff --git a/java/openapi/src/com/intellij/psi/util/PropertyUtil.java b/java/openapi/src/com/intellij/psi/util/PropertyUtil.java index 4166a3b450f2..89b7159f9554 100644 --- a/java/openapi/src/com/intellij/psi/util/PropertyUtil.java +++ b/java/openapi/src/com/intellij/psi/util/PropertyUtil.java @@ -77,7 +77,7 @@ public class PropertyUtil { } @SuppressWarnings("HardCodedStringLiteral") - public static boolean isSimplePropertySetter(PsiMethod method) { + public static boolean isSimplePropertySetter(@Nullable PsiMethod method) { if (method == null) return false; if (method.isConstructor()) return false;