SSR: fix generics matching & replacing (IDEA-190128)

This commit is contained in:
Bas Leijdekkers
2018-04-14 19:53:05 +02:00
parent 8bbcaaf745
commit d0d64c83d4
4 changed files with 84 additions and 13 deletions
@@ -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
@@ -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);
@@ -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<String> list = new java.util.LinkedList<String>();" +
" List<Integer> list2 = new java.util.ArrayList<Integer>();" +
" List<Double> list3 = new ArrayList<>();" +
"}";
assertEquals("should properly replace with diamond",
"import java.util.ArrayList;" +
"import java.util.List;" +
"class X {" +
" List<String> list = new java.util.LinkedList<>();" +
" List<Integer> list2 = new ArrayList<>();" +
" List<Double> 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<String> list = new /*1*/java.util.LinkedList<String>();" +
" List<Integer> list2 = new /*1*/ArrayList<Integer>();" +
" List<Double> 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<String> list = new java.util.LinkedList</*0*/String>();" +
" List<Integer> list2 = new ArrayList</*0*/Integer>();" +
" List<Double> list3 = new ArrayList<>();" +
"}",
Replacer.testReplace(in, "new '_X<'_p+>()", "new $X$</*0*/$p$>()", options, getProject(), true));
}
}
@@ -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<Integer, String>()"));
assertEquals("find non-diamond", 1, findMatchesCount(source, "new A<'_p{1,100}>()"));
}
public void testFindSuperCall() {