diff --git a/java/structuralsearch-java/src/com/intellij/structuralsearch/JavaStructuralSearchProfile.java b/java/structuralsearch-java/src/com/intellij/structuralsearch/JavaStructuralSearchProfile.java index 63f9dea6cc87..1f5c5e704087 100644 --- a/java/structuralsearch-java/src/com/intellij/structuralsearch/JavaStructuralSearchProfile.java +++ b/java/structuralsearch-java/src/com/intellij/structuralsearch/JavaStructuralSearchProfile.java @@ -58,14 +58,24 @@ public class JavaStructuralSearchProfile extends StructuralSearchProfile { @Override public String getText(PsiElement match, int start, int end) { if (match instanceof PsiIdentifier) { - PsiElement parent = match.getParent(); + final PsiElement parent = match.getParent(); if (parent instanceof PsiJavaCodeReferenceElement && !(parent instanceof PsiExpression)) { - match = parent; // care about generic + final PsiJavaCodeReferenceElement referenceElement = (PsiJavaCodeReferenceElement)parent; + final String text = referenceElement.getText(); + if (end != -1) { + return text.substring(start, end); + } + final PsiReferenceParameterList parameterList = referenceElement.getParameterList(); + if (parameterList != null) { + // get text without type parameters + return text.substring(start, parameterList.getStartOffsetInParent()); + } + return text; } } final String matchText = match.getText(); - if (start==0 && end==-1) return matchText; - return matchText.substring(start,end == -1? matchText.length():end); + if (start == 0 && end == -1) return matchText; + return matchText.substring(start, (end == -1) ? matchText.length() : end); } @Override diff --git a/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/JavaMatchingVisitor.java b/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/JavaMatchingVisitor.java index 470ffb3537db..764d641b4286 100644 --- a/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/JavaMatchingVisitor.java +++ b/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/JavaMatchingVisitor.java @@ -670,19 +670,22 @@ public class JavaMatchingVisitor extends JavaElementVisitor { return (referenceElement != null) ? referenceElement : getInnermostComponentTypeElement(typeElement); } - private static PsiTypeElement[] getTypeParameters(PsiJavaCodeReferenceElement referenceElement, boolean replaceDiamondWithExplicitTypes) { + private static PsiTypeElement[] getTypeParameters(PsiJavaCodeReferenceElement referenceElement, Boolean replaceDiamondWithExplicitTypes) { final PsiReferenceParameterList referenceElementParameterList = referenceElement.getParameterList(); if (referenceElementParameterList == null) { return null; } final PsiTypeElement[] typeParameterElements = referenceElementParameterList.getTypeParameterElements(); - if (typeParameterElements.length != 1 || !replaceDiamondWithExplicitTypes) { + if (typeParameterElements.length != 1 || replaceDiamondWithExplicitTypes == Boolean.FALSE) { return typeParameterElements; } final PsiType type = typeParameterElements[0].getType(); if (!(type instanceof PsiDiamondType)) { return typeParameterElements; } + if (replaceDiamondWithExplicitTypes == null) { + return null; + } final PsiDiamondType diamondType = (PsiDiamondType)type; final PsiDiamondType.DiamondInferenceResult inferenceResult = diamondType.resolveInferredTypes(); final StringBuilder text = new StringBuilder(referenceElement.getQualifiedName()); @@ -704,17 +707,28 @@ public class JavaMatchingVisitor extends JavaElementVisitor { return newParameterList == null ? null : newParameterList.getTypeParameterElements(); } - private static boolean hasDiamondTypeParameter(PsiElement element) { + private Boolean shouldReplaceDiamondWithExplicitTypes(PsiElement element) { if (!(element instanceof PsiJavaCodeReferenceElement)) { - return false; + return Boolean.TRUE; } final PsiJavaCodeReferenceElement javaCodeReferenceElement = (PsiJavaCodeReferenceElement)element; final PsiReferenceParameterList parameterList = javaCodeReferenceElement.getParameterList(); if (parameterList == null) { - return false; + return Boolean.TRUE; } final PsiTypeElement[] elements = parameterList.getTypeParameterElements(); - return elements.length == 1 && elements[0].getType() instanceof PsiDiamondType; + if (elements.length != 1) { + return Boolean.TRUE; + } + final PsiTypeElement typeElement = elements[0]; + final MatchingHandler handler = myMatchingVisitor.getMatchContext().getPattern().getHandler(typeElement); + if (handler instanceof SubstitutionHandler) { + final SubstitutionHandler substitutionHandler = (SubstitutionHandler)handler; + if (substitutionHandler.getMinOccurs() > 0) { + return null; + } + } + return Boolean.valueOf(!(typeElement.getType() instanceof PsiDiamondType)); } private boolean matchType(final PsiElement patternType, final PsiElement matchedType) { @@ -726,7 +740,7 @@ public class JavaMatchingVisitor extends JavaElementVisitor { PsiElement[] typeParameters = null; if (matchedElement instanceof PsiJavaCodeReferenceElement) { final PsiJavaCodeReferenceElement referenceElement = (PsiJavaCodeReferenceElement)matchedElement; - typeParameters = getTypeParameters(referenceElement, !hasDiamondTypeParameter(patternElement)); + typeParameters = getTypeParameters(referenceElement, shouldReplaceDiamondWithExplicitTypes(patternElement)); } else if (matchedElement instanceof PsiTypeParameter) { matchedElement = ((PsiTypeParameter)matchedElement).getNameIdentifier(); @@ -743,13 +757,20 @@ public class JavaMatchingVisitor extends JavaElementVisitor { if (patternElement instanceof PsiJavaCodeReferenceElement) { final PsiJavaCodeReferenceElement referenceElement = (PsiJavaCodeReferenceElement)patternElement; final PsiReferenceParameterList list = referenceElement.getParameterList(); + boolean typeParametersMatched = false; if (list != null) { final PsiTypeElement[] elements = list.getTypeParameterElements(); - if (elements.length > 0 && (typeParameters == null || !myMatchingVisitor.matchSequentially(elements, typeParameters))) { - return false; + if (elements.length > 0) { + typeParametersMatched = true; + if (!myMatchingVisitor.matchSequentially(elements, (typeParameters == null) ? PsiElement.EMPTY_ARRAY : typeParameters)) { + return false; + } } } patternElement = referenceElement.getReferenceNameElement(); + if (typeParametersMatched && matchedElement instanceof PsiJavaCodeReferenceElement) { + matchedElement = ((PsiJavaCodeReferenceElement)matchedElement).getReferenceNameElement(); + } } final int matchedArrayDimensions = getArrayDimensions(matchedType); diff --git a/platform/structuralsearch/testSource/com/intellij/structuralsearch/StructuralReplaceTest.java b/platform/structuralsearch/testSource/com/intellij/structuralsearch/StructuralReplaceTest.java index ecfbbf6e8062..19b6350f5561 100644 --- a/platform/structuralsearch/testSource/com/intellij/structuralsearch/StructuralReplaceTest.java +++ b/platform/structuralsearch/testSource/com/intellij/structuralsearch/StructuralReplaceTest.java @@ -2460,4 +2460,43 @@ public class StructuralReplaceTest extends StructuralReplaceTestCase { " }" + "}", Replacer.testReplace(in, what, by, options, getProject(), true)); } + + public void testReplaceGenerics() { + options.setToShortenFQN(false); + String in = "import java.util.ArrayList;" + + "import java.util.List;" + + "class X {" + + " List list = new java.util.LinkedList();" + + " List list2 = new java.util.ArrayList();" + + " List list3 = new ArrayList<>();" + + "}"; + + assertEquals("should properly replace with diamond", + "import java.util.ArrayList;" + + "import java.util.List;" + + "class X {" + + " List list = new java.util.LinkedList<>();" + + " List list2 = new ArrayList<>();" + + " List list3 = new ArrayList<>();" + + "}", + Replacer.testReplace(in, "new '_X<'_p+>()", "new $X$<>()", options, getProject(), true)); + assertEquals("should keep generics when matching without", + "import java.util.ArrayList;" + + "import java.util.List;" + + "class X {" + + " List list = new /*1*/java.util.LinkedList();" + + " List list2 = new /*1*/ArrayList();" + + " List list3 = new /*1*/ArrayList<>();" + + "}", + Replacer.testReplace(in, "new '_X()", "new /*1*/$X$()", options, getProject(), true)); + assertEquals("should not duplicate generic parameters", + "import java.util.ArrayList;" + + "import java.util.List;" + + "class X {" + + " List list = new java.util.LinkedList();" + + " List list2 = new ArrayList();" + + " List list3 = new ArrayList<>();" + + "}", + Replacer.testReplace(in, "new '_X<'_p+>()", "new $X$()", options, getProject(), true)); + } } \ No newline at end of file diff --git a/platform/structuralsearch/testSource/com/intellij/structuralsearch/StructuralSearchTest.java b/platform/structuralsearch/testSource/com/intellij/structuralsearch/StructuralSearchTest.java index 46dc3ad7a99e..2deaad201c6f 100644 --- a/platform/structuralsearch/testSource/com/intellij/structuralsearch/StructuralSearchTest.java +++ b/platform/structuralsearch/testSource/com/intellij/structuralsearch/StructuralSearchTest.java @@ -2682,6 +2682,7 @@ public class StructuralSearchTest extends StructuralSearchTestCase { "}}"; assertEquals("find diamond new expressions", 3, findMatchesCount(source, "new A<>()")); assertEquals("find parameterized new expressions", 2, findMatchesCount(source, "new A()")); + assertEquals("find non-diamond", 1, findMatchesCount(source, "new A<'_p{1,100}>()")); } public void testFindSuperCall() {