mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-04-19 13:02:30 +07:00
[codeInsight] IDEA-113640 Provide intention to combine System.out.println(String.format(...)) into System.out.printf
This patch adds processing of Java 14's text blocks to RedundantStringFormatCallInspection and fixes the problems from the code review Signed-off-by: Nikita Eshkeev <nikita.eshkeev@jetbrains.com> GitOrigin-RevId: 126cfc001e7b201b62060333de7f71480403fb93
This commit is contained in:
committed by
intellij-monorepo-bot
parent
c8bc41207e
commit
8db5967f7b
@@ -12,6 +12,7 @@ class Main {
|
||||
/* first arg start */ "Hello"/* first arg end */,
|
||||
/* second arg start */ "World" /* second arg end */);
|
||||
System.out.printf(/* one */ Locale.US, /* two */ "%s, %s!" /* three */, /* four */ "Hello" /* five */, /* six */ "World" /* seven */);
|
||||
System.out.print("hello" + String.format("%n"))
|
||||
}
|
||||
|
||||
Main() {
|
||||
|
||||
@@ -10,6 +10,7 @@ class Main {
|
||||
: /* second leg start */ "%s: %s") + "%n" /* second leg end */,
|
||||
/* first arg start */ "Hello"/* first arg end */,
|
||||
/* second arg start */ "World" /* second arg end */);
|
||||
System.out.println("hello" + String.format("%n"))
|
||||
}
|
||||
|
||||
Main() {
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
// "Fix all 'Redundant call to 'String.format()'' problems in file" "true"
|
||||
package java.lang;
|
||||
|
||||
class String {
|
||||
String(Object original) {}
|
||||
public native String formatted(Object... args);
|
||||
}
|
||||
|
||||
class Main {
|
||||
static {
|
||||
/* 6 */
|
||||
String s = (/* 1 */ new /* 2 */ String(/* 3 */"""
|
||||
Hello, World!
|
||||
"""/* 4 */)/* 5 */);
|
||||
String s1 = new String("""
|
||||
%s, %s!
|
||||
""").formatted("Hello", "World");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
// "Fix all 'Redundant call to 'String.format()'' problems in file" "true"
|
||||
|
||||
import java.io.PrintStream;
|
||||
|
||||
class Main {
|
||||
static {
|
||||
System.out.print(/* one */"""
|
||||
Hello
|
||||
""" /* two */);
|
||||
System.out.printf(/* three */"""
|
||||
Hello%n
|
||||
""" /* four */);
|
||||
}
|
||||
|
||||
Main() {
|
||||
System.out.print(/* one */"""
|
||||
Hello
|
||||
""" /* two */);
|
||||
System.out.printf(/* three */"""
|
||||
Hello%n
|
||||
""" /* four */);
|
||||
}
|
||||
|
||||
void f() {
|
||||
System.out.print(/* one */"""
|
||||
Hello
|
||||
""" /* two */);
|
||||
System.out.printf(/* three */"""
|
||||
Hello%n
|
||||
""" /* four */);
|
||||
}
|
||||
|
||||
void out(PrintStream printer) {
|
||||
printer.print(/* one */"""
|
||||
Hello
|
||||
""" /* two */);
|
||||
printer.printf(/* three */"""
|
||||
Hello%n
|
||||
""" /* four */);
|
||||
}
|
||||
|
||||
void caller() {
|
||||
printf(/* one */"""
|
||||
Hello
|
||||
""" /* two */);
|
||||
printf(/* three */"""
|
||||
Hello%n
|
||||
""" /* four */);
|
||||
}
|
||||
|
||||
static void printf(String value) {}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
// "Fix all 'Redundant call to 'String.format()'' problems in file" "true"
|
||||
package java.lang;
|
||||
|
||||
import java.io.PrintStream;
|
||||
|
||||
class String {
|
||||
String(Object original) {}
|
||||
public native String formatted(Object... args);
|
||||
}
|
||||
|
||||
class Main {
|
||||
static {
|
||||
/* one */
|
||||
System.out.printf(/* three */(new String("""
|
||||
%s, %s!
|
||||
"""/* two */)) + "%n", "Hello"/* four */, /* six */ "World" /* seven */);
|
||||
/* one */
|
||||
System.out.printf(/* six */( /* two */ ( /* three */ new String("""
|
||||
%s, %s!
|
||||
"""/* four */) /* five */)), "Hello"/* seven */, /* eight */ "World" /* nine */);
|
||||
|
||||
/* one */
|
||||
System.out.printf(/* six */new String("""
|
||||
%s, %s!
|
||||
"""/* two */), "Hello"/* seven */, /* eight */ "World" /* nine */);
|
||||
|
||||
System.out.printf(/* five */( /* one */ new String("""
|
||||
%s,
|
||||
""" /* two */) + /* three */
|
||||
new String("""
|
||||
%s!
|
||||
""") + "%n"/* four */), "Hello"/* six */, /* seven */ "World" /* eight */);
|
||||
|
||||
System.out.printf(/* five */( /* one */ new String("""
|
||||
%s,
|
||||
""" /* two */) + new String(/* three */
|
||||
"""
|
||||
%s!
|
||||
""")/* four */), "Hello"/* six */, /* seven */ "World" /* eight */);
|
||||
}
|
||||
|
||||
Main() {
|
||||
|
||||
}
|
||||
|
||||
void f() {
|
||||
|
||||
}
|
||||
|
||||
void out(PrintStream printer) {
|
||||
|
||||
}
|
||||
|
||||
void caller() {
|
||||
println(( /* one */ new String("""
|
||||
%s,
|
||||
""" /* two */ + /* three */
|
||||
"""
|
||||
%s!
|
||||
"""/* four */)).formatted(/* five */"Hello"/* six */, /* seven */ "World" /* eight */));
|
||||
}
|
||||
|
||||
static void println(String value) {}
|
||||
}
|
||||
@@ -12,6 +12,7 @@ class Main {
|
||||
/* first arg start */ "Hello"/* first arg end */,
|
||||
/* second arg start */ "World" /* second arg end */));
|
||||
System.out.print(String.format(/* one */ Locale.US, /* two */ "%s, %s!" /* three */, /* four */ "Hello" /* five */, /* six */ "World" /* seven */));
|
||||
System.out.print("hello" + String.format("%n"))
|
||||
}
|
||||
|
||||
Main() {
|
||||
|
||||
@@ -10,6 +10,7 @@ class Main {
|
||||
: /* second leg start */ "%s: %s" /* second leg end */,
|
||||
/* first arg start */ "Hello"/* first arg end */,
|
||||
/* second arg start */ "World" /* second arg end */));
|
||||
System.out.println("hello" + String.format("%n"))
|
||||
}
|
||||
|
||||
Main() {
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
// "Fix all 'Redundant call to 'String.format()'' problems in file" "true"
|
||||
package java.lang;
|
||||
|
||||
class String {
|
||||
String(Object original) {}
|
||||
public native String formatted(Object... args);
|
||||
}
|
||||
|
||||
class Main {
|
||||
static {
|
||||
String s = (/* 1 */ new /* 2 */ String(/* 3 */"""
|
||||
Hello, World!
|
||||
"""/* 4 */)/* 5 */)./* 6 */<caret>formatted();
|
||||
String s1 = new String("""
|
||||
%s, %s!
|
||||
""").formatted("Hello", "World");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
// "Fix all 'Redundant call to 'String.format()'' problems in file" "true"
|
||||
|
||||
import java.io.PrintStream;
|
||||
|
||||
class Main {
|
||||
static {
|
||||
System.out.<caret>printf(/* one */"""
|
||||
Hello
|
||||
""" /* two */);
|
||||
System.out.printf(/* three */"""
|
||||
Hello%n
|
||||
""" /* four */);
|
||||
}
|
||||
|
||||
Main() {
|
||||
System.out.printf(/* one */"""
|
||||
Hello
|
||||
""" /* two */);
|
||||
System.out.printf(/* three */"""
|
||||
Hello%n
|
||||
""" /* four */);
|
||||
}
|
||||
|
||||
void f() {
|
||||
System.out.printf(/* one */"""
|
||||
Hello
|
||||
""" /* two */);
|
||||
System.out.printf(/* three */"""
|
||||
Hello%n
|
||||
""" /* four */);
|
||||
}
|
||||
|
||||
void out(PrintStream printer) {
|
||||
printer.printf(/* one */"""
|
||||
Hello
|
||||
""" /* two */);
|
||||
printer.printf(/* three */"""
|
||||
Hello%n
|
||||
""" /* four */);
|
||||
}
|
||||
|
||||
void caller() {
|
||||
printf(/* one */"""
|
||||
Hello
|
||||
""" /* two */);
|
||||
printf(/* three */"""
|
||||
Hello%n
|
||||
""" /* four */);
|
||||
}
|
||||
|
||||
static void printf(String value) {}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
// "Fix all 'Redundant call to 'String.format()'' problems in file" "true"
|
||||
package java.lang;
|
||||
|
||||
import java.io.PrintStream;
|
||||
|
||||
class String {
|
||||
String(Object original) {}
|
||||
public native String formatted(Object... args);
|
||||
}
|
||||
|
||||
class Main {
|
||||
static {
|
||||
System.out.println(/* one */ new String("""
|
||||
%s, %s!
|
||||
"""/* two */).<caret>formatted(/* three */"Hello"/* four */, /* six */ "World" /* seven */));
|
||||
System.out.print(/* one */ ( /* two */ ( /* three */ new String("""
|
||||
%s, %s!
|
||||
"""/* four */) /* five */))
|
||||
.formatted(/* six */"Hello"/* seven */, /* eight */ "World" /* nine */));
|
||||
|
||||
System.out.print(/* one */ new String("""
|
||||
%s, %s!
|
||||
"""/* two */).formatted(/* six */"Hello"/* seven */, /* eight */ "World" /* nine */));
|
||||
|
||||
System.out.println(( /* one */ new String("""
|
||||
%s,
|
||||
""" /* two */) + /* three */
|
||||
new String("""
|
||||
%s!
|
||||
""")/* four */).formatted(/* five */"Hello"/* six */, /* seven */ "World" /* eight */));
|
||||
|
||||
System.out.print(( /* one */ new String("""
|
||||
%s,
|
||||
""" /* two */) + new String(/* three */
|
||||
"""
|
||||
%s!
|
||||
""")/* four */).formatted(/* five */"Hello"/* six */, /* seven */ "World" /* eight */));
|
||||
}
|
||||
|
||||
Main() {
|
||||
|
||||
}
|
||||
|
||||
void f() {
|
||||
|
||||
}
|
||||
|
||||
void out(PrintStream printer) {
|
||||
|
||||
}
|
||||
|
||||
void caller() {
|
||||
println(( /* one */ new String("""
|
||||
%s,
|
||||
""" /* two */ + /* three */
|
||||
"""
|
||||
%s!
|
||||
"""/* four */)).formatted(/* five */"Hello"/* six */, /* seven */ "World" /* eight */));
|
||||
}
|
||||
|
||||
static void println(String value) {}
|
||||
}
|
||||
@@ -3,10 +3,17 @@ package com.intellij.java.codeInsight.intention;
|
||||
|
||||
import com.intellij.codeInsight.daemon.quickFix.LightQuickFixParameterizedTestCase;
|
||||
import com.intellij.codeInspection.LocalInspectionTool;
|
||||
import com.intellij.pom.java.LanguageLevel;
|
||||
import com.siyeh.ig.performance.RedundantStringFormatCallInspection;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class ReplaceStringFormatWithPrintfIntentionTest extends LightQuickFixParameterizedTestCase {
|
||||
|
||||
@Override
|
||||
protected LanguageLevel getLanguageLevel() {
|
||||
return LanguageLevel.JDK_14_PREVIEW;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getBasePath() {
|
||||
return "/codeInsight/replaceStringFormat";
|
||||
|
||||
@@ -1680,6 +1680,7 @@ if.may.be.conditional.quickfix=Replace with conditional expression
|
||||
redundant.string.format.call.display.name=Redundant call to 'String.format()'
|
||||
redundant.call.problem.descriptor=Redundant call to <code>#ref()</code> #loc
|
||||
redundant.string.format.call.quickfix=Remove redundant call to 'String.format()'
|
||||
redundant.string.formatted.call.quickfix=Remove redundant call to 'String.formatted()'
|
||||
junit4.test.method.in.class.extending.junit3.testcase.display.name=JUnit 4 test method in class extending JUnit 3 TestCase
|
||||
junit4.test.method.in.class.extending.junit3.testcase.problem.descriptor=Method <code>#ref()</code> annotated with '@Test' inside class extending JUnit 3 TestCase #loc
|
||||
ignore.test.method.in.class.extending.junit3.testcase.problem.descriptor=JUnit 3 test method <code>#ref()</code> annotated with '@Ignore' won't be ignored #loc
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.siyeh.ig.performance;
|
||||
|
||||
import com.intellij.codeInsight.daemon.impl.analysis.HighlightingFeature;
|
||||
import com.intellij.codeInspection.*;
|
||||
import com.intellij.codeInspection.util.IntentionFamilyName;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.psi.*;
|
||||
@@ -29,7 +31,7 @@ public final class RedundantStringFormatCallInspection extends LocalInspectionTo
|
||||
|
||||
@Override
|
||||
public @NotNull PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, final boolean isOnTheFly) {
|
||||
return new RemoveRedundantStringFormatVisitor(holder, isOnTheFly);
|
||||
return new RemoveRedundantStringFormatVisitor(holder, isOnTheFly, HighlightingFeature.TEXT_BLOCKS.isAvailable(holder.getFile()));
|
||||
}
|
||||
|
||||
private static final class RemoveRedundantStringFormatVisitor extends JavaElementVisitor {
|
||||
@@ -42,19 +44,26 @@ public final class RedundantStringFormatCallInspection extends LocalInspectionTo
|
||||
.parameterTypes(String.class.getName());
|
||||
|
||||
private static final CallMatcher STRING_FORMAT = staticCall(String.class.getName(), "format");
|
||||
private static final CallMatcher STRING_FORMATTED = instanceCall(String.class.getName(), "formatted")
|
||||
.parameterTypes("java.lang.Object...");
|
||||
|
||||
private final CallMapper<ProblemDescriptor> myProcessors = new CallMapper<ProblemDescriptor>()
|
||||
.register(PRINTSTREAM_PRINTF, this::getRedundantPrintfProblem)
|
||||
.register(STRING_FORMAT, this::getRedundantStringFormatProblem);
|
||||
@NotNull
|
||||
private final CallMapper<ProblemDescriptor> myProcessors;
|
||||
|
||||
@NotNull private final ProblemsHolder myHolder;
|
||||
private final boolean myIsOnTheFly;
|
||||
@NotNull private final InspectionManager myManager;
|
||||
|
||||
private RemoveRedundantStringFormatVisitor(@NotNull final ProblemsHolder holder, final boolean isOnTheFly) {
|
||||
private RemoveRedundantStringFormatVisitor(@NotNull final ProblemsHolder holder, final boolean isOnTheFly, boolean withTextBlocks) {
|
||||
myHolder = holder;
|
||||
myManager = myHolder.getManager();
|
||||
myIsOnTheFly = isOnTheFly;
|
||||
myProcessors = new CallMapper<ProblemDescriptor>()
|
||||
.register(PRINTSTREAM_PRINTF, this::getRedundantPrintfProblem)
|
||||
.register(STRING_FORMAT, this::getRedundantStringFormatProblem);
|
||||
if (withTextBlocks) {
|
||||
myProcessors.register(STRING_FORMATTED, this::getRedundantFormattedProblem);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -93,16 +102,39 @@ public final class RedundantStringFormatCallInspection extends LocalInspectionTo
|
||||
new RemoveRedundantStringFormatFix(),
|
||||
ProblemHighlightType.GENERIC_ERROR_OR_WARNING, myIsOnTheFly);
|
||||
}
|
||||
final PsiMethodCallExpression printlnCall = PsiTreeUtil.getParentOfType(call, PsiMethodCallExpression.class);
|
||||
final PsiMethodCallExpression printlnCall = getDirectParentMethod(call);
|
||||
final boolean isPrintlnCall = PRINTSTREAM_PRINTLN.test(printlnCall);
|
||||
if (!isPrintlnCall) {
|
||||
if (!PRINTSTREAM_PRINT.test(printlnCall)) return null;
|
||||
|
||||
}
|
||||
|
||||
return myManager.createProblemDescriptor(methodNameReference,
|
||||
InspectionGadgetsBundle.message("redundant.call.problem.descriptor"),
|
||||
new StringFormatToPrintfQuickFix(isPrintlnCall),
|
||||
new ReplaceStringFormatWithPrintfFix(isPrintlnCall),
|
||||
ProblemHighlightType.GENERIC_ERROR_OR_WARNING, myIsOnTheFly);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private ProblemDescriptor getRedundantFormattedProblem(@NotNull final PsiMethodCallExpression call) {
|
||||
|
||||
final PsiElement methodNameReference = call.getMethodExpression().getReferenceNameElement();
|
||||
if (methodNameReference == null) return null;
|
||||
|
||||
if (call.getArgumentList().getExpressionCount() == 0) {
|
||||
return myManager.createProblemDescriptor(methodNameReference,
|
||||
InspectionGadgetsBundle.message("redundant.call.problem.descriptor"),
|
||||
new RemoveRedundantStringFormattedFix(),
|
||||
ProblemHighlightType.GENERIC_ERROR_OR_WARNING, myIsOnTheFly);
|
||||
}
|
||||
final PsiMethodCallExpression printlnCall = getDirectParentMethod(call);
|
||||
final boolean isPrintlnCall = PRINTSTREAM_PRINTLN.test(printlnCall);
|
||||
if (!isPrintlnCall) {
|
||||
if (!PRINTSTREAM_PRINT.test(printlnCall)) return null;
|
||||
}
|
||||
|
||||
return myManager.createProblemDescriptor(methodNameReference,
|
||||
InspectionGadgetsBundle.message("redundant.call.problem.descriptor"),
|
||||
new ReplaceStringFormattedWithPrintfFix(isPrintlnCall),
|
||||
ProblemHighlightType.GENERIC_ERROR_OR_WARNING, myIsOnTheFly);
|
||||
}
|
||||
|
||||
@@ -168,7 +200,7 @@ public final class RedundantStringFormatCallInspection extends LocalInspectionTo
|
||||
|
||||
final PsiExpressionList argumentList = printStreamPrintfCall.getArgumentList();
|
||||
final PsiExpression arg = PsiUtil.skipParenthesizedExprDown(argumentList.getExpressions()[0]);
|
||||
if (arg instanceof PsiMethodCallExpression && STRING_FORMAT.test((PsiMethodCallExpression)arg) && isStringFormatCallRedundant((PsiMethodCallExpression)arg)) {
|
||||
if (arg instanceof PsiMethodCallExpression && STRING_FORMAT.matches(arg) && isStringFormatCallRedundant((PsiMethodCallExpression)arg)) {
|
||||
removeRedundantStringFormatCall((PsiMethodCallExpression)arg);
|
||||
}
|
||||
}
|
||||
@@ -195,10 +227,10 @@ public final class RedundantStringFormatCallInspection extends LocalInspectionTo
|
||||
}
|
||||
}
|
||||
|
||||
private static final class StringFormatToPrintfQuickFix implements LocalQuickFix {
|
||||
private static final class ReplaceStringFormatWithPrintfFix implements LocalQuickFix {
|
||||
private final boolean myIsPrintlnCall;
|
||||
|
||||
private StringFormatToPrintfQuickFix(boolean isPrintlnCall) {
|
||||
private ReplaceStringFormatWithPrintfFix(boolean isPrintlnCall) {
|
||||
myIsPrintlnCall = isPrintlnCall;
|
||||
}
|
||||
|
||||
@@ -209,15 +241,10 @@ public final class RedundantStringFormatCallInspection extends LocalInspectionTo
|
||||
|
||||
@Override
|
||||
public void applyFix(@NotNull final Project project, @NotNull final ProblemDescriptor descriptor) {
|
||||
final PsiElement methodName = descriptor.getPsiElement();
|
||||
if ((methodName == null) || !(methodName.getParent() instanceof PsiReferenceExpression) ||
|
||||
!(methodName.getParent().getParent() instanceof PsiMethodCallExpression)) {
|
||||
return;
|
||||
}
|
||||
final PsiMethodCallExpression stringFormatCall = getDirectParentMethod(descriptor.getPsiElement());
|
||||
if (stringFormatCall == null) return;
|
||||
|
||||
final PsiMethodCallExpression stringFormatCall = (PsiMethodCallExpression)methodName.getParent().getParent();
|
||||
|
||||
final PsiMethodCallExpression printlnCall = PsiTreeUtil.getParentOfType(stringFormatCall, PsiMethodCallExpression.class);
|
||||
final PsiMethodCallExpression printlnCall = getDirectParentMethod(stringFormatCall);
|
||||
if (printlnCall == null) return;
|
||||
|
||||
final PsiExpressionList stringFormatArgs = stringFormatCall.getArgumentList();
|
||||
@@ -229,105 +256,180 @@ public final class RedundantStringFormatCallInspection extends LocalInspectionTo
|
||||
final PsiExpressionList printlnArgs = printlnCall.getArgumentList();
|
||||
new CommentTracker().replaceAndRestoreComments(printlnArgs, stringFormatArgs);
|
||||
}
|
||||
}
|
||||
|
||||
private static void addNewlineToFormatValue(@NotNull final PsiExpressionList stringFormatArgs) {
|
||||
if (stringFormatArgs.getExpressionCount() == 0) return;
|
||||
|
||||
final PsiExpression formatValueArg = getArgWithFormatValue(stringFormatArgs);
|
||||
if (formatValueArg != null) {
|
||||
appendWithNewlineToken(formatValueArg);
|
||||
}
|
||||
private static final class RemoveRedundantStringFormattedFix implements LocalQuickFix {
|
||||
@Override
|
||||
public @IntentionFamilyName @NotNull String getFamilyName() {
|
||||
return InspectionGadgetsBundle.message("redundant.string.formatted.call.quickfix");
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Contract(pure = true)
|
||||
private static PsiExpression getArgWithFormatValue(@NotNull final PsiExpressionList stringFormatArgs) {
|
||||
final PsiExpression firstFormatArg = PsiUtil.skipParenthesizedExprDown(stringFormatArgs.getExpressions()[0]);
|
||||
if (firstFormatArg == null) return null;
|
||||
final PsiType firstType = firstFormatArg.getType();
|
||||
|
||||
if (firstType == null) return null;
|
||||
|
||||
if (firstType.equalsToText(Locale.class.getName())) {
|
||||
if (stringFormatArgs.getExpressionCount() <= 1) return null;
|
||||
|
||||
final PsiExpression secondFormatArg = PsiUtil.skipParenthesizedExprDown(stringFormatArgs.getExpressions()[1]);
|
||||
if (secondFormatArg == null) return null;
|
||||
|
||||
final PsiType secondType = secondFormatArg.getType();
|
||||
if (secondType == null || !secondType.equalsToText(String.class.getName())) return null;
|
||||
|
||||
return secondFormatArg;
|
||||
@Override
|
||||
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
|
||||
final PsiElement methodName = descriptor.getPsiElement();
|
||||
if ((methodName == null) || !(methodName.getParent() instanceof PsiReferenceExpression) ||
|
||||
!(methodName.getParent().getParent() instanceof PsiMethodCallExpression)) {
|
||||
return;
|
||||
}
|
||||
else if (firstType.equalsToText(String.class.getName())) {
|
||||
return firstFormatArg;
|
||||
final PsiMethodCallExpression stringFormattedCall = (PsiMethodCallExpression)methodName.getParent().getParent();
|
||||
final PsiExpression expression = stringFormattedCall.getMethodExpression().getQualifierExpression();
|
||||
if (expression != null) {
|
||||
new CommentTracker().replaceAndRestoreComments(stringFormattedCall, expression);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static final class ReplaceStringFormattedWithPrintfFix implements LocalQuickFix {
|
||||
private static final Logger LOGGER = Logger.getInstance(ReplaceStringFormattedWithPrintfFix.class);
|
||||
|
||||
private final boolean myIsPrintlnCall;
|
||||
|
||||
private ReplaceStringFormattedWithPrintfFix(boolean isPrintlnCall) {myIsPrintlnCall = isPrintlnCall;}
|
||||
|
||||
@Override
|
||||
public @IntentionFamilyName @NotNull String getFamilyName() {
|
||||
return InspectionGadgetsBundle.message("redundant.string.formatted.call.quickfix");
|
||||
}
|
||||
|
||||
private static void appendWithNewlineToken(@NotNull final PsiExpression expr) {
|
||||
final PsiElement formatArg = PsiUtil.skipParenthesizedExprDown(expr);
|
||||
if (formatArg == null) return;
|
||||
@Override
|
||||
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
|
||||
final PsiMethodCallExpression stringFormattedCall = getDirectParentMethod(descriptor.getPsiElement());
|
||||
if (stringFormattedCall == null) return;
|
||||
|
||||
final String newLineToken = "%n";
|
||||
final PsiMethodCallExpression printlnCall = PsiTreeUtil.getParentOfType(stringFormattedCall, PsiMethodCallExpression.class);
|
||||
if (printlnCall == null) return;
|
||||
|
||||
if (formatArg instanceof PsiLiteralExpression) {
|
||||
final PsiLiteralExpression replacement = joinWithNewlineToken((PsiLiteralExpression)formatArg);
|
||||
formatArg.replace(replacement);
|
||||
final PsiExpression textBlock = stringFormattedCall.getMethodExpression().getQualifierExpression();
|
||||
if (textBlock == null) return;
|
||||
|
||||
final PsiExpressionList formattedArgs = stringFormattedCall.getArgumentList();
|
||||
|
||||
final PsiExpression firstArg = formattedArgs.getExpressions()[0];
|
||||
|
||||
final CommentTracker ct = new CommentTracker();
|
||||
final PsiElement element = formattedArgs.addBefore(ct.markUnchanged(textBlock), firstArg);
|
||||
|
||||
if (!(element instanceof PsiExpression)) {
|
||||
// unlikely
|
||||
LOGGER.error(String.format("The '%s' element after insertion is not instance of PsiExpression", element.getText()));
|
||||
return;
|
||||
}
|
||||
else if (formatArg instanceof PsiPolyadicExpression){
|
||||
final PsiElement lastChild = skipParenIfPossible(formatArg.getLastChild());
|
||||
if (lastChild instanceof PsiLiteralExpression) {
|
||||
final PsiLiteralExpression replacement = joinWithNewlineToken((PsiLiteralExpression)lastChild);
|
||||
lastChild.replace(replacement);
|
||||
}
|
||||
else {
|
||||
final CommentTracker ct = new CommentTracker();
|
||||
final String text = String.format("%s + \"%s\"", ct.text(formatArg), newLineToken);
|
||||
ct.replaceAndRestoreComments(formatArg, text);
|
||||
}
|
||||
|
||||
if (myIsPrintlnCall ) {
|
||||
appendWithNewlineToken((PsiExpression)element);
|
||||
}
|
||||
|
||||
ct.replaceAndRestoreComments(printlnCall.getArgumentList(), formattedArgs);
|
||||
ExpressionUtils.bindCallTo(printlnCall, "printf");
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PsiMethodCallExpression getDirectParentMethod(@Nullable final PsiElement methodName) {
|
||||
if (methodName == null || methodName.getParent() == null) return null;
|
||||
|
||||
if (!(methodName.getParent().getParent() instanceof PsiMethodCallExpression)) return null;
|
||||
|
||||
return (PsiMethodCallExpression)methodName.getParent().getParent();
|
||||
}
|
||||
|
||||
private static void addNewlineToFormatValue(@NotNull final PsiExpressionList stringFormatArgs) {
|
||||
if (stringFormatArgs.getExpressionCount() == 0) return;
|
||||
|
||||
final PsiExpression formatValueArg = getArgWithFormatValue(stringFormatArgs);
|
||||
if (formatValueArg != null) {
|
||||
appendWithNewlineToken(formatValueArg);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Contract(pure = true)
|
||||
private static PsiExpression getArgWithFormatValue(@NotNull final PsiExpressionList stringFormatArgs) {
|
||||
final PsiExpression firstFormatArg = stringFormatArgs.getExpressions()[0];
|
||||
if (firstFormatArg == null) return null;
|
||||
final PsiType firstType = firstFormatArg.getType();
|
||||
|
||||
if (firstType == null) return null;
|
||||
|
||||
if (firstType.equalsToText(Locale.class.getName())) {
|
||||
if (stringFormatArgs.getExpressionCount() <= 1) return null;
|
||||
|
||||
final PsiExpression secondFormatArg = stringFormatArgs.getExpressions()[1];
|
||||
if (secondFormatArg == null) return null;
|
||||
|
||||
final PsiType secondType = secondFormatArg.getType();
|
||||
if (secondType == null || !secondType.equalsToText(String.class.getName())) return null;
|
||||
|
||||
return secondFormatArg;
|
||||
}
|
||||
else if (firstType.equalsToText(String.class.getName())) {
|
||||
return firstFormatArg;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void appendWithNewlineToken(@NotNull final PsiExpression expr) {
|
||||
final PsiElement formatArg = PsiUtil.skipParenthesizedExprDown(expr);
|
||||
if (formatArg == null) return;
|
||||
|
||||
final String newLineToken = "%n";
|
||||
|
||||
if (formatArg instanceof PsiLiteralExpression) {
|
||||
final PsiLiteralExpression replacement = joinWithNewlineToken((PsiLiteralExpression)formatArg);
|
||||
formatArg.replace(replacement);
|
||||
}
|
||||
else if (formatArg instanceof PsiPolyadicExpression){
|
||||
final PsiElement lastChild = skipParenIfPossible(formatArg.getLastChild());
|
||||
if (lastChild instanceof PsiLiteralExpression) {
|
||||
final PsiLiteralExpression replacement = joinWithNewlineToken((PsiLiteralExpression)lastChild);
|
||||
lastChild.replace(replacement);
|
||||
}
|
||||
else {
|
||||
final CommentTracker ct = new CommentTracker();
|
||||
final String text = String.format("(%s) + \"%s\"", ct.text(formatArg), newLineToken);
|
||||
final String text = String.format("%s + \"%s\"", ct.text(formatArg), newLineToken);
|
||||
ct.replaceAndRestoreComments(formatArg, text);
|
||||
}
|
||||
}
|
||||
else {
|
||||
final CommentTracker ct = new CommentTracker();
|
||||
final String text = String.format("(%s) + \"%s\"", ct.text(formatArg), newLineToken);
|
||||
ct.replaceAndRestoreComments(formatArg, text);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PsiElement skipParenIfPossible(@NotNull PsiElement element) {
|
||||
if (!(element instanceof PsiExpression)) return element;
|
||||
@Nullable
|
||||
private static PsiElement skipParenIfPossible(@NotNull PsiElement element) {
|
||||
if (!(element instanceof PsiExpression)) return element;
|
||||
|
||||
return PsiUtil.skipParenthesizedExprDown((PsiExpression)element);
|
||||
return PsiUtil.skipParenthesizedExprDown((PsiExpression)element);
|
||||
}
|
||||
|
||||
@Contract(value = "null -> null; !null -> !null", pure = true)
|
||||
private static PsiLiteralExpression joinWithNewlineToken(@Nullable final PsiLiteralExpression expression) {
|
||||
if (expression == null) return null;
|
||||
|
||||
final Object value = expression.getValue();
|
||||
if (value == null) return expression;
|
||||
|
||||
final StringBuilder newExpression = new StringBuilder();
|
||||
|
||||
final String leftText = value.toString();
|
||||
if (expression.isTextBlock()) {
|
||||
final String indent = StringUtil.repeat(" ", PsiLiteralUtil.getTextBlockIndent(expression));
|
||||
newExpression.append("\"\"\"").append('\n').append(indent);
|
||||
newExpression.append(leftText.replaceAll("\n", "\n" + indent));
|
||||
newExpression.append("%n");
|
||||
newExpression.append("\"\"\"");
|
||||
}
|
||||
else {
|
||||
newExpression.append('"');
|
||||
newExpression.append(StringUtil.escapeStringCharacters(leftText));
|
||||
newExpression.append("%n");
|
||||
newExpression.append('"');
|
||||
}
|
||||
|
||||
@Contract(value = "null -> null; !null -> !null", pure = true)
|
||||
private static PsiLiteralExpression joinWithNewlineToken(@Nullable final PsiLiteralExpression expression) {
|
||||
if (expression == null) return null;
|
||||
|
||||
final Object value = expression.getValue();
|
||||
if (value == null) return expression;
|
||||
|
||||
final StringBuilder newExpression = new StringBuilder();
|
||||
|
||||
final String leftText = value.toString();
|
||||
if (expression.isTextBlock()) {
|
||||
final String indent = StringUtil.repeat(" ", PsiLiteralUtil.getTextBlockIndent(expression));
|
||||
newExpression.append("\"\"\"").append('\n').append(indent);
|
||||
newExpression.append(leftText.replaceAll("\n", "\n" + indent));
|
||||
newExpression.append("%n");
|
||||
newExpression.append("\"\"\"");
|
||||
}
|
||||
else {
|
||||
newExpression.append('"');
|
||||
newExpression.append(StringUtil.escapeStringCharacters(leftText));
|
||||
newExpression.append("%n");
|
||||
newExpression.append('"');
|
||||
}
|
||||
|
||||
final PsiElementFactory factory = JavaPsiFacade.getElementFactory(expression.getProject());
|
||||
return (PsiLiteralExpression)factory.createExpressionFromText(newExpression.toString(), null);
|
||||
}
|
||||
final PsiElementFactory factory = JavaPsiFacade.getElementFactory(expression.getProject());
|
||||
return (PsiLiteralExpression)factory.createExpressionFromText(newExpression.toString(), null);
|
||||
}
|
||||
|
||||
private static void removeRedundantStringFormatCall(@NotNull PsiMethodCallExpression stringFormat) {
|
||||
@@ -350,5 +452,4 @@ public final class RedundantStringFormatCallInspection extends LocalInspectionTo
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user