diff --git a/java/java-analysis-impl/src/com/intellij/refactoring/util/duplicates/DuplicatesFinder.java b/java/java-analysis-impl/src/com/intellij/refactoring/util/duplicates/DuplicatesFinder.java index 1da4eb7fd6b8..2a7b7963145b 100644 --- a/java/java-analysis-impl/src/com/intellij/refactoring/util/duplicates/DuplicatesFinder.java +++ b/java/java-analysis-impl/src/com/intellij/refactoring/util/duplicates/DuplicatesFinder.java @@ -30,6 +30,7 @@ import com.intellij.refactoring.extractMethod.InputVariables; import com.intellij.refactoring.util.RefactoringChangeUtil; import com.intellij.util.ArrayUtil; import com.intellij.util.IncorrectOperationException; +import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.IntArrayList; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -518,6 +519,8 @@ public class DuplicatesFinder { final PsiJavaCodeReferenceElement candidateQualifier = ((PsiSuperExpression)candidate).getQualifier(); return contextClass == (candidateQualifier != null ? candidateQualifier.resolve() : PsiTreeUtil.getContextOfType(candidate, PsiClass.class)); } + } else if (pattern instanceof PsiModifierList) { + return candidate instanceof PsiModifierList && matchModifierList((PsiModifierList)pattern, (PsiModifierList)candidate); } PsiElement[] children1 = getFilteredChildren(pattern); @@ -541,6 +544,40 @@ public class DuplicatesFinder { return true; } + private static boolean matchModifierList(PsiModifierList modifierList1, PsiModifierList modifierList2) { + if (!(modifierList1.getParent() instanceof PsiLocalVariable)) { + // local variables can only have a final modifier, and are considered equivalent with or without it. + for (String modifier : PsiModifier.MODIFIERS) { + if (modifierList1.hasModifierProperty(modifier)) { + if (!modifierList2.hasModifierProperty(modifier)) { + return false; + } + } + else if (modifierList2.hasModifierProperty(modifier)) { + return false; + } + } + } + final List annotations1 = ContainerUtil.newArrayList(modifierList1.getAnnotations()); + final List annotations2 = ContainerUtil.newArrayList(modifierList2.getAnnotations()); + annotations1.removeIf(a -> CommonClassNames.JAVA_LANG_OVERRIDE.equals(a.getQualifiedName())); + annotations2.removeIf(a -> CommonClassNames.JAVA_LANG_OVERRIDE.equals(a.getQualifiedName())); + if (annotations1.size() != annotations2.size()) { + return false; + } + for (final Iterator iterator = annotations1.iterator(); iterator.hasNext(); ) { + final PsiAnnotation annotation1 = iterator.next(); + for (final Iterator iterator2 = annotations2.iterator(); iterator2.hasNext(); ) { + final PsiAnnotation annotation2 = iterator2.next(); + if (PsiEquivalenceUtil.areElementsEquivalent(annotation1, annotation2)) { + iterator.remove(); + iterator2.remove(); + } + } + } + return true; + } + private static boolean checkParameterModification(PsiExpression expression, final IElementType sign, PsiExpression candidate) { diff --git a/java/java-tests/testData/refactoring/extractMethod/LocalVariableAnnotationsOrder.java b/java/java-tests/testData/refactoring/extractMethod/LocalVariableAnnotationsOrder.java new file mode 100644 index 000000000000..7fa69c17de76 --- /dev/null +++ b/java/java-tests/testData/refactoring/extractMethod/LocalVariableAnnotationsOrder.java @@ -0,0 +1,13 @@ +class C { + { + @B @A int i = 0; + System.out.println(i); + } + + void f() { + @A @B int j = 0; + System.out.println(j); + } +} +@interface A {} +@interface B {} \ No newline at end of file diff --git a/java/java-tests/testData/refactoring/extractMethod/LocalVariableAnnotationsOrder_after.java b/java/java-tests/testData/refactoring/extractMethod/LocalVariableAnnotationsOrder_after.java new file mode 100644 index 000000000000..54630c858b1d --- /dev/null +++ b/java/java-tests/testData/refactoring/extractMethod/LocalVariableAnnotationsOrder_after.java @@ -0,0 +1,16 @@ +class C { + { + newMethod(); + } + + void f() { + newMethod(); + } + + private void newMethod() { + @A @B int j = 0; + System.out.println(j); + } +} +@interface A {} +@interface B {} \ No newline at end of file diff --git a/java/java-tests/testData/refactoring/extractMethod/LocalVariableModifierList.java b/java/java-tests/testData/refactoring/extractMethod/LocalVariableModifierList.java new file mode 100644 index 000000000000..d9d2f99e4e0b --- /dev/null +++ b/java/java-tests/testData/refactoring/extractMethod/LocalVariableModifierList.java @@ -0,0 +1,11 @@ +class C { + { + int i = 0; + System.out.println(i); + } + + void f() { + final int j = 0; + System.out.println(j); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/refactoring/extractMethod/LocalVariableModifierList_after.java b/java/java-tests/testData/refactoring/extractMethod/LocalVariableModifierList_after.java new file mode 100644 index 000000000000..e6792368059f --- /dev/null +++ b/java/java-tests/testData/refactoring/extractMethod/LocalVariableModifierList_after.java @@ -0,0 +1,14 @@ +class C { + { + newMethod(); + } + + void f() { + newMethod(); + } + + private void newMethod() { + final int j = 0; + System.out.println(j); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/refactoring/extractMethod/MethodAnnotations.java b/java/java-tests/testData/refactoring/extractMethod/MethodAnnotations.java new file mode 100644 index 000000000000..a21035126650 --- /dev/null +++ b/java/java-tests/testData/refactoring/extractMethod/MethodAnnotations.java @@ -0,0 +1,21 @@ +class Test10 { + void test() { + new Super() { + int get() { + return 0; + } + }; + + new Super() { + @Override + int get() { + return 0; + } + }; + } +} +class Super { + int get() { + return 1; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/refactoring/extractMethod/MethodAnnotations_after.java b/java/java-tests/testData/refactoring/extractMethod/MethodAnnotations_after.java new file mode 100644 index 000000000000..1c7baecdfe26 --- /dev/null +++ b/java/java-tests/testData/refactoring/extractMethod/MethodAnnotations_after.java @@ -0,0 +1,20 @@ +class Test10 { + void test() { + newMethod(); + + newMethod(); + } + + private void newMethod() { + new Super() { + int get() { + return 0; + } + }; + } +} +class Super { + int get() { + return 1; + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/refactoring/ExtractMethodTest.java b/java/java-tests/testSrc/com/intellij/refactoring/ExtractMethodTest.java index bfe820f7197f..a84f0d143dab 100644 --- a/java/java-tests/testSrc/com/intellij/refactoring/ExtractMethodTest.java +++ b/java/java-tests/testSrc/com/intellij/refactoring/ExtractMethodTest.java @@ -805,6 +805,18 @@ public class ExtractMethodTest extends LightCodeInsightTestCase { doTest(); } + public void testLocalVariableModifierList() throws Exception { + doTest(); + } + + public void testLocalVariableAnnotationsOrder() throws Exception { + doTest(); + } + + public void testMethodAnnotations() throws Exception { + doTest(); + } + public void testQualifyWhenConflictingNamePresent() throws Exception { final CodeStyleSettings settings = CodeStyleSettingsManager.getSettings(getProject()); settings.ELSE_ON_NEW_LINE = true;