mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
FoldExpressionIntoStream: support String.join-able patterns (IDEA-190570)
This commit is contained in:
+92
-27
@@ -14,6 +14,7 @@ import com.intellij.psi.tree.IElementType;
|
||||
import com.intellij.psi.util.InheritanceUtil;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.siyeh.ig.psiutils.*;
|
||||
import one.util.streamex.IntStreamEx;
|
||||
import one.util.streamex.StreamEx;
|
||||
@@ -41,9 +42,15 @@ public class FoldExpressionIntoStreamInspection extends AbstractBaseJavaLocalIns
|
||||
public void visitPolyadicExpression(PsiPolyadicExpression expression) {
|
||||
TerminalGenerator generator = getGenerator(expression);
|
||||
if (generator == null) return;
|
||||
if (extractDiff(generator, expression).isEmpty()) return;
|
||||
List<PsiExpression> diff = extractDiff(generator, expression);
|
||||
if (diff.isEmpty()) return;
|
||||
if (!LambdaGenerationUtil.canBeUncheckedLambda(expression)) return;
|
||||
holder.registerProblem(expression, InspectionsBundle.message("inspection.fold.expression.into.stream.display.name"), new FoldExpressionIntoStreamFix());
|
||||
boolean stringJoin = generator.isStringJoin(expression, diff);
|
||||
String message = InspectionsBundle.message(stringJoin ?
|
||||
"inspection.fold.expression.into.string.display.name" :
|
||||
"inspection.fold.expression.into.stream.display.name");
|
||||
holder.registerProblem(expression, message,
|
||||
new FoldExpressionIntoStreamFix(stringJoin));
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -57,9 +64,16 @@ public class FoldExpressionIntoStreamInspection extends AbstractBaseJavaLocalIns
|
||||
for (int i = 1; i < operands.length; i++) {
|
||||
if (!Objects.equals(operands[0].getType(), operands[i].getType())) return Collections.emptyList();
|
||||
EquivalenceChecker.Match match = equivalence.expressionsMatch(operands[0], operands[i]);
|
||||
if (!match.isPartialMatch()) return Collections.emptyList();
|
||||
PsiExpression left = tryCast(match.getLeftDiff(), PsiExpression.class);
|
||||
PsiExpression right = tryCast(match.getRightDiff(), PsiExpression.class);
|
||||
PsiExpression left = null;
|
||||
PsiExpression right = null;
|
||||
if (match.isPartialMatch()) {
|
||||
left = tryCast(match.getLeftDiff(), PsiExpression.class);
|
||||
right = tryCast(match.getRightDiff(), PsiExpression.class);
|
||||
}
|
||||
else if (match.isExactMismatch() && generator.isDittoSupported()) {
|
||||
left = operands[0];
|
||||
right = operands[i];
|
||||
}
|
||||
if (left == null || right == null) return Collections.emptyList();
|
||||
if (elements.isEmpty()) {
|
||||
if (!StreamApiUtil.isSupportedStreamElement(left.getType()) || !ExpressionUtils.isSafelyRecomputableExpression(left)) {
|
||||
@@ -94,8 +108,16 @@ public class FoldExpressionIntoStreamInspection extends AbstractBaseJavaLocalIns
|
||||
return polyadicExpression.getOperands();
|
||||
}
|
||||
|
||||
default boolean isDittoSupported() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
String generateTerminal(PsiType elementType, String lambda, CommentTracker ct);
|
||||
|
||||
default boolean isStringJoin(PsiPolyadicExpression expression, List<? extends PsiExpression> diff) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -123,27 +145,17 @@ public class FoldExpressionIntoStreamInspection extends AbstractBaseJavaLocalIns
|
||||
} else {
|
||||
mapToString = "";
|
||||
}
|
||||
if (operands.length > 4 && operands.length % 2 == 1 && ExpressionUtils.isSafelyRecomputableExpression(operands[1]) &&
|
||||
PsiExpression delimiter = null;
|
||||
PsiExpression rest = null;
|
||||
if (operands.length > 4 && ExpressionUtils.isSafelyRecomputableExpression(operands[1]) &&
|
||||
IntStreamEx.range(1, operands.length, 2).elements(operands).pairMap(PsiEquivalenceUtil::areElementsEquivalent)
|
||||
.allMatch(Boolean.TRUE::equals)) {
|
||||
PsiExpression delimiter = operands[1];
|
||||
return new TerminalGenerator() {
|
||||
@Override
|
||||
public PsiExpression[] getOperands(PsiPolyadicExpression polyadicExpression) {
|
||||
PsiExpression[] ops = polyadicExpression.getOperands();
|
||||
return IntStreamEx.range(0, ops.length, 2).elements(ops).toArray(PsiExpression.EMPTY_ARRAY);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String generateTerminal(PsiType elementType, String lambda, CommentTracker ct) {
|
||||
return mapToString(elementType, operandType, lambda) + mapToString +
|
||||
".collect(" + CommonClassNames.JAVA_UTIL_STREAM_COLLECTORS + ".joining(" + ct.text(delimiter) + "))";
|
||||
}
|
||||
};
|
||||
delimiter = operands[1];
|
||||
if (operands.length % 2 == 0) {
|
||||
rest = ArrayUtil.getLastElement(operands);
|
||||
}
|
||||
}
|
||||
return (elementType, lambda, ct) -> mapToString(elementType, operandType, lambda) + mapToString +
|
||||
".collect(" + CommonClassNames.JAVA_UTIL_STREAM_COLLECTORS + ".joining())";
|
||||
return new JoiningTerminalGenerator(operandType, mapToString, delimiter, rest);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -154,11 +166,17 @@ public class FoldExpressionIntoStreamInspection extends AbstractBaseJavaLocalIns
|
||||
}
|
||||
|
||||
private static class FoldExpressionIntoStreamFix implements LocalQuickFix {
|
||||
private final boolean myStringJoin;
|
||||
|
||||
private FoldExpressionIntoStreamFix(boolean stringJoin) {myStringJoin = stringJoin;}
|
||||
|
||||
@Nls
|
||||
@NotNull
|
||||
@Override
|
||||
public String getFamilyName() {
|
||||
return InspectionsBundle.message("inspection.fold.expression.into.stream.fix.name");
|
||||
return InspectionsBundle.message(myStringJoin ?
|
||||
"inspection.fold.expression.into.string.fix.name" :
|
||||
"inspection.fold.expression.into.stream.fix.name");
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -172,7 +190,7 @@ public class FoldExpressionIntoStreamInspection extends AbstractBaseJavaLocalIns
|
||||
|
||||
PsiExpression[] operands = expression.getOperands();
|
||||
PsiExpression firstExpression = diffs.get(0);
|
||||
assert PsiTreeUtil.isAncestor(operands[0], firstExpression, true);
|
||||
assert PsiTreeUtil.isAncestor(operands[0], firstExpression, false);
|
||||
Object marker = new Object();
|
||||
PsiTreeUtil.mark(firstExpression, marker);
|
||||
CommentTracker ct = new CommentTracker();
|
||||
@@ -185,8 +203,12 @@ public class FoldExpressionIntoStreamInspection extends AbstractBaseJavaLocalIns
|
||||
String name = info.names.length > 0 ? info.names[0] : "v";
|
||||
name = codeStyleManager.suggestUniqueVariableName(name, expression, true);
|
||||
PsiElementFactory factory = JavaPsiFacade.getElementFactory(project);
|
||||
expressionCopy.replace(factory.createExpressionFromText(name, expressionCopy));
|
||||
String lambda = name + "->" + operandCopy.getText();
|
||||
PsiExpression expressionCopyReplaced = (PsiExpression)expressionCopy.replace(factory.createExpressionFromText(name, expressionCopy));
|
||||
if (operandCopy == expressionCopy) {
|
||||
operandCopy = expressionCopyReplaced;
|
||||
}
|
||||
String operandCopyText = operandCopy.getText();
|
||||
String lambda = operandCopyText.equals(name) ? null : name + "->" + operandCopyText;
|
||||
String streamClass = StreamApiUtil.getStreamClassForType(elementType);
|
||||
if (streamClass == null) return;
|
||||
String source = streamClass + "." + (elementType instanceof PsiClassType ? "<" + elementType.getCanonicalText() + ">" : "")
|
||||
@@ -204,4 +226,47 @@ public class FoldExpressionIntoStreamInspection extends AbstractBaseJavaLocalIns
|
||||
PsiDiamondTypeUtil.removeRedundantTypeArguments(result);
|
||||
}
|
||||
}
|
||||
|
||||
private static class JoiningTerminalGenerator implements TerminalGenerator {
|
||||
private final PsiType myOperandType;
|
||||
private final String myMapToString;
|
||||
private final PsiExpression myDelimiter;
|
||||
private final PsiExpression myRest;
|
||||
|
||||
public JoiningTerminalGenerator(PsiType operandType, String mapToString, PsiExpression delimiter, PsiExpression rest) {
|
||||
myOperandType = operandType;
|
||||
myMapToString = mapToString;
|
||||
myDelimiter = delimiter;
|
||||
myRest = rest;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PsiExpression[] getOperands(PsiPolyadicExpression polyadicExpression) {
|
||||
PsiExpression[] ops = polyadicExpression.getOperands();
|
||||
return myDelimiter == null ? ops :
|
||||
IntStreamEx.range(0, ops.length, 2).elements(ops).toArray(PsiExpression.EMPTY_ARRAY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDittoSupported() {
|
||||
return myDelimiter != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isStringJoin(PsiPolyadicExpression expression, List<? extends PsiExpression> diff) {
|
||||
if (!myMapToString.isEmpty()) return false;
|
||||
PsiExpression[] operands = getOperands(expression);
|
||||
return operands[0] == diff.get(0);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String generateTerminal(PsiType elementType, String lambda, CommentTracker ct) {
|
||||
String map = (lambda == null ? "" : mapToString(elementType, myOperandType, lambda)) + myMapToString;
|
||||
return map +
|
||||
".collect(" + CommonClassNames.JAVA_UTIL_STREAM_COLLECTORS +
|
||||
".joining(" + (myDelimiter == null ? "" : ct.text(myDelimiter)) + "))" +
|
||||
(myRest == null ? "" : "+" + ct.text(myRest));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
<html>
|
||||
<body>
|
||||
<p>Reports expressions with repeating pattern which could be replaced with Stream API.</p>
|
||||
<p>Reports expressions with repeating pattern which could be replaced with Stream API for String.join.</p>
|
||||
<!-- tooltip end -->
|
||||
<p>This inspection only reports if the project or module is configured to use a language level of 8 or higher.</p>
|
||||
<p><small>New in 2018.2</small></p>
|
||||
</body>
|
||||
</html>
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
// "Fold expression into Stream chain" "true"
|
||||
class Test {
|
||||
void test2(Integer a, Integer b, Integer c, Integer d) {
|
||||
String result = Stream.of(a, b, c, d).map(String::valueOf).collect(Collectors.joining(",")) + ",";
|
||||
}
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
// "Fold expression into 'String.join'" "true"
|
||||
class Test {
|
||||
void test(String a, String b, String c, String d) {
|
||||
String result = String.join(",", a, b, c, d) + ",";
|
||||
}
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
// "Fold expression into Stream chain" "true"
|
||||
class Test {
|
||||
void test2(Integer a, Integer b, Integer c, Integer d) {
|
||||
String result = a + "," + b + "," + c + "," <caret>+ d + ",";
|
||||
}
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
// "Fold expression into 'String.join'" "true"
|
||||
class Test {
|
||||
void test(String a, String b, String c, String d) {
|
||||
String result = a + "," + b + "," + c + ","<caret> + d + ",";
|
||||
}
|
||||
}
|
||||
@@ -962,6 +962,8 @@ inspection.redundant.explicit.close=Redundant close
|
||||
inspection.redundant.explicit.close.fix.name=Remove redundant close
|
||||
inspection.fold.expression.into.stream.display.name=Expression can be folded into Stream chain
|
||||
inspection.fold.expression.into.stream.fix.name=Fold expression into Stream chain
|
||||
inspection.fold.expression.into.string.display.name=Expression can be folded into 'String.join'
|
||||
inspection.fold.expression.into.string.fix.name=Fold expression into 'String.join'
|
||||
|
||||
inspection.charset.object.can.be.used.display.name=Standard Charset object can be used
|
||||
inspection.charset.object.can.be.used.message={0} can be used instead
|
||||
|
||||
Reference in New Issue
Block a user