[java-inspections] IDEA-197280 Non-constant string concatenation as argument" could also be triggered for String.format

- Support simple cases for String.format
- Support simple cases for MessageFormat.format

GitOrigin-RevId: 50f52f526923950e30746691414bb89ce5fca5ab
This commit is contained in:
Mikhail Pyltsin
2024-03-29 18:34:16 +00:00
committed by intellij-monorepo-bot
parent 8a1cf90bc0
commit a750ec4eb0
20 changed files with 525 additions and 30 deletions
@@ -1918,8 +1918,10 @@ arrays.as.list.with.zero.or.one.argument.display.name=Call to 'Arrays.asList()'
arrays.as.list.with.one.argument.problem.descriptor=Call to <code>#ref()</code> with only one argument #loc
arrays.as.list.with.zero.arguments.problem.descriptor=Call to <code>#ref()</code> to create an empty List #loc
string.concatenation.argument.to.log.call.display.name=Non-constant string concatenation as argument to logging call
string.concatenation.argument.to.log.call.problem.descriptor=Non-constant string concatenation as argument to <code>#ref()</code> logging call #loc
string.concatenation.argument.to.log.call.problem.descriptor=Evaluated string as argument to <code>#ref()</code> logging call #loc
string.concatenation.argument.to.log.call.quickfix=Replace concatenation with parameterized log message
string.concatenation.argument.to.log.string.format.call.quickfix=Replace 'String.format()' with parameterized log message
string.concatenation.argument.to.log.message.format.call.quickfix=Replace 'Message.format()' with parameterized log message
assignment.to.superclass.field.display.name=Constructor assigns value to field defined in superclass
assignment.to.superclass.field.problem.descriptor=Assignment to field ''{0}'' defined in superclass ''{1}'' #loc
inner.class.referenced.via.subclass.display.name=Inner class referenced via subclass
@@ -2,6 +2,7 @@
package com.siyeh.ig.bugs.message;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.TextRange;
import com.intellij.util.containers.ContainerUtil;
import it.unimi.dsi.fastutil.ints.Int2IntFunction;
import org.jetbrains.annotations.ApiStatus;
@@ -67,7 +68,10 @@ public final class MessageFormatUtil {
if (part.getParsedType() == MessageFormatParsedType.FORMAT_ELEMENT &&
part.getMessageFormatElement() != null &&
part.getMessageFormatElement().getIndex() != null) {
placeholderIndexes.add(new MessageFormatPlaceholder(part.getMessageFormatElement().getIndex()));
placeholderIndexes.add(new MessageFormatPlaceholder(part.getMessageFormatElement().getIndex(),
new TextRange(part.start, part.start + part.text.length()),
part.getMessageFormatElement().formatType == null &&
part.getMessageFormatElement().currentPart == MessageFormatElementPart.ARGUMENT_INDEX));
}
}
return new MessageFormatResult(errors.isEmpty(), errors, placeholderIndexes);
@@ -445,7 +449,7 @@ public final class MessageFormatUtil {
@NotNull List<MessageFormatPlaceholder> placeholders) {
}
public record MessageFormatPlaceholder(int index) {
public record MessageFormatPlaceholder(int index, @NotNull TextRange range, boolean isString) {
}
static class MessageFormatPart {
@@ -203,7 +203,8 @@ public final class FormatDecode {
final Validator allowed;
if (isAllVerifier) {
allowed = new AllValidatorWithRange(TextRange.create(matcher.start(), matcher.end()));
allowed = new AllValidatorWithRange(TextRange.create(matcher.start(), matcher.end()),
new Spec(posSpec, flags, width, precision, dateSpec, conversion));
}
else if (dateSpec != null) { // a t or T
checkFlags(flagBits, LEFT_JUSTIFY | PREVIOUS, specifier);
@@ -414,9 +415,16 @@ public final class FormatDecode {
private static class AllValidatorWithRange extends AllValidator {
private final @NotNull TextRange myRange;
private final @NotNull Spec mySpec;
AllValidatorWithRange(@NotNull TextRange range) {
AllValidatorWithRange(@NotNull TextRange range, @NotNull Spec spec) {
myRange = range;
mySpec = spec;
}
@Override
public @NotNull Spec getSpec() {
return mySpec;
}
@Override
@@ -528,7 +536,7 @@ public final class FormatDecode {
}
}
static class MultiValidator extends Validator {
public static class MultiValidator extends Validator {
private final Set<Validator> validators = new HashSet<>(3);
@Nullable
@@ -556,7 +564,7 @@ public final class FormatDecode {
return true;
}
Set<Validator> getValidators() {
public Set<Validator> getValidators() {
return validators;
}
@@ -589,6 +597,19 @@ public final class FormatDecode {
public @Nullable TextRange getRange() {
return null;
}
public @Nullable Spec getSpec() {
return null;
}
}
public record Spec(@Nullable String posSpec ,
@Nullable String flags,
@Nullable String width,
@Nullable String precision,
@Nullable String dateSpec,
@Nullable String conversion){
}
public static class FormatArgument {
@@ -608,13 +629,15 @@ public final class FormatDecode {
return myExpression;
}
public static FormatArgument extract(@NotNull PsiCallExpression expression, List<String> methodNames, List<String> classNames) {
@Nullable
public static FormatArgument extract(@NotNull PsiCallExpression expression, @NotNull List<String> methodNames, @NotNull List<String> classNames) {
return extract(expression, methodNames, classNames, false);
}
@Nullable
public static FormatArgument extract(@NotNull PsiCallExpression expression,
List<String> methodNames,
List<String> classNames,
@NotNull List<String> methodNames,
@NotNull List<String> classNames,
boolean allowNotConstant) {
final PsiExpressionList argumentList = expression.getArgumentList();
if (argumentList == null) return null;
@@ -7,16 +7,22 @@ import com.intellij.codeInspection.options.OptPane;
import com.intellij.modcommand.ModPsiUpdater;
import com.intellij.modcommand.PsiUpdateModCommandQuickFix;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.WriteExternalException;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.psi.util.InheritanceUtil;
import com.intellij.psi.util.PsiLiteralUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.psi.util.TypeConversionUtil;
import com.siyeh.InspectionGadgetsBundle;
import com.siyeh.ig.BaseInspection;
import com.siyeh.ig.BaseInspectionVisitor;
import com.siyeh.ig.PsiReplacementUtil;
import com.siyeh.ig.bugs.message.MessageFormatUtil;
import com.siyeh.ig.callMatcher.CallMatcher;
import com.siyeh.ig.format.FormatDecode;
import com.siyeh.ig.psiutils.CommentTracker;
import com.siyeh.ig.psiutils.ExpressionUtils;
import com.siyeh.ig.psiutils.TypeUtils;
import one.util.streamex.EntryStream;
@@ -27,11 +33,11 @@ import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.*;
import static com.intellij.codeInspection.options.OptPane.*;
import static com.siyeh.ig.callMatcher.CallMatcher.anyOf;
import static com.siyeh.ig.callMatcher.CallMatcher.staticCall;
/**
* @author Bas Leijdekkers
@@ -51,6 +57,10 @@ public final class StringConcatenationArgumentToLogCallInspection extends BaseIn
private static final String LOG4J_LOGGER = "org.apache.logging.log4j.Logger";
private static final String LOG4J_BUILDER = "org.apache.logging.log4j.LogBuilder";
private static final String GET_LOGGER = "getLogger";
private static final CallMatcher MESSAGE_FORMAT_FORMAT = anyOf(
staticCall("java.text.MessageFormat", "format").parameterCount(2)
);
private static final String SLF4J_LOGGER = "org.slf4j.Logger";
@SuppressWarnings("PublicField") public int warnLevel = 0;
@@ -86,16 +96,26 @@ public final class StringConcatenationArgumentToLogCallInspection extends BaseIn
@Nullable
@Override
protected LocalQuickFix buildFix(Object... infos) {
if (!(infos[0] instanceof ProblemType problemType)) {
return null;
}
if (!(infos[1] instanceof PsiMethodCallExpression logCall)) {
return null;
}
if (!(infos[2] instanceof PsiExpression targetExpression)) {
return null;
}
if (isFormattedLog4J(logCall)) return null;
if (!StringConcatenationArgumentToLogCallFix.isAvailable((PsiExpression)infos[0])) {
return null;
}
return new StringConcatenationArgumentToLogCallFix();
return switch (problemType) {
case CONCATENATION ->
StringConcatenationArgumentToLogCallFix.isAvailable(targetExpression) ? new StringConcatenationArgumentToLogCallFix() : null;
case STRING_FORMAT -> StringFormatArgumentToLogCallFix.create(targetExpression);
case MESSAGE_FORMAT -> MessageFormatArgumentToLogCallFix.create(targetExpression);
};
}
private static boolean isFormattedLog4J(@NotNull PsiMethodCallExpression logCall) {
@@ -331,6 +351,218 @@ public final class StringConcatenationArgumentToLogCallInspection extends BaseIn
}
}
private static abstract class FormatArgumentToLogCallFix extends PsiUpdateModCommandQuickFix {
@NotNull
private final Map<TextRange, Integer> myTextMapping;
@NotNull
private final String format;
private FormatArgumentToLogCallFix(@NotNull Map<TextRange, Integer> textMapping,
@NotNull String format) {
myTextMapping = textMapping;
this.format = format;
}
@Override
protected void applyFix(@NotNull Project project, @NotNull PsiElement element, @NotNull ModPsiUpdater updater) {
if (!(element.getParent() instanceof PsiReferenceExpression referenceExpression &&
referenceExpression.getParent() instanceof PsiMethodCallExpression callExpression)) {
return;
}
PsiExpression[] expressions = callExpression.getArgumentList().getExpressions();
if (expressions.length != 1) {
return;
}
PsiExpression expression = expressions[0];
if (!(expression instanceof PsiMethodCallExpression formatCallExpression)) {
return;
}
StringBuilder builder = new StringBuilder();
CommentTracker tracker = new CommentTracker();
for (PsiElement child : callExpression.getChildren()) {
if (child instanceof PsiExpressionList) {
builder.append(createNewArgumentsFromCall(formatCallExpression, tracker));
}
else {
builder.append(tracker.text(child));
}
}
tracker.replace(callExpression, builder.toString());
}
@NotNull
private String createNewArgumentsFromCall(@NotNull PsiMethodCallExpression formatCallExpression,
@NotNull CommentTracker tracker) {
List<String> arguments = new ArrayList<>();
List<Map.Entry<TextRange, Integer>> placeholders =
myTextMapping.entrySet()
.stream()
.sorted(Comparator.<Map.Entry<TextRange, Integer>>comparingInt(t -> t.getKey().getStartOffset()).reversed())
.toList();
String formatWithPlaceholders = format;
PsiExpression[] expressions = formatCallExpression.getArgumentList().getExpressions();
for (Map.Entry<TextRange, Integer> placeholder : placeholders) {
formatWithPlaceholders = formatWithPlaceholders.substring(0, placeholder.getKey().getStartOffset()) + "{}" +
formatWithPlaceholders.substring(placeholder.getKey().getEndOffset());
arguments.add(tracker.text(expressions[placeholder.getValue()]));
}
arguments.add(formatWithPlaceholders);
Collections.reverse(arguments);
return "(" + String.join(", ", arguments) + ")";
}
}
private static class MessageFormatArgumentToLogCallFix extends FormatArgumentToLogCallFix {
private MessageFormatArgumentToLogCallFix(@NotNull Map<TextRange, Integer> result,
@NotNull String format) {
super(result, format);
}
@Override
public @NotNull String getFamilyName() {
return InspectionGadgetsBundle.message("string.concatenation.argument.to.log.message.format.call.quickfix");
}
@Nullable
static LocalQuickFix create(@NotNull PsiExpression expression) {
if (!(expression instanceof PsiMethodCallExpression callExpression)) {
return null;
}
PsiExpression[] arguments = callExpression.getArgumentList().getExpressions();
if (!(arguments[0] instanceof PsiLiteralExpression literalExpression && literalExpression.getValue() instanceof String pattern)) {
return null;
}
MessageFormatUtil.MessageFormatResult result = MessageFormatUtil.checkFormat(pattern);
if (!result.valid()) {
return null;
}
Map<TextRange, Integer> mapping = new HashMap<>();
List<MessageFormatUtil.MessageFormatPlaceholder> placeholders = result.placeholders();
for (MessageFormatUtil.MessageFormatPlaceholder placeholder : placeholders) {
if (!placeholder.isString()) {
return null;
}
if (placeholder.index() + 1 >= arguments.length) {
return null;
}
TextRange actualRange = ExpressionUtils.findStringLiteralRange(literalExpression, placeholder.range().getStartOffset(),
placeholder.range().getEndOffset());
if (actualRange == null) {
return null;
}
mapping.put(actualRange, placeholder.index() + 1);
}
Set<Integer> argumentIndexes = new HashSet<>(mapping.values());
if (argumentIndexes.size() != arguments.length - 1) {
return null;
}
return new MessageFormatArgumentToLogCallFix(mapping, literalExpression.getText());
}
}
private static class StringFormatArgumentToLogCallFix extends FormatArgumentToLogCallFix {
@Override
public @NotNull String getFamilyName() {
return InspectionGadgetsBundle.message("string.concatenation.argument.to.log.string.format.call.quickfix");
}
private StringFormatArgumentToLogCallFix(@NotNull Map<TextRange, Integer> result,
@NotNull String format) {
super(result, format);
}
@Nullable
static LocalQuickFix create(@NotNull PsiExpression originalExpression) {
if (!(originalExpression instanceof PsiMethodCallExpression callExpression)) {
return null;
}
FormatDecode.FormatArgument formatArgument =
FormatDecode.FormatArgument.extract(callExpression, List.of("format"), List.of("String"), true);
if (formatArgument == null || formatArgument.getIndex() != 1) {
return null;
}
PsiExpression expression = formatArgument.getExpression();
if (!(expression instanceof PsiLiteralExpression literalExpression && literalExpression.getText() != null)) {
return null;
}
PsiExpression[] arguments = Objects.requireNonNull(callExpression.getArgumentList()).getExpressions();
int argumentCount = arguments.length - formatArgument.getIndex();
if (!(literalExpression.getValue() instanceof String formatterString)) {
return null;
}
FormatDecode.Validator[] validators;
try {
validators = FormatDecode.decodeNoVerify(formatterString, argumentCount);
}
catch (FormatDecode.IllegalFormatException e) {
return null;
}
if (argumentCount != validators.length) return null;
Map<TextRange, Integer> result = new HashMap<>();
for (int i = 0; i < validators.length; i++) {
int index = formatArgument.getIndex() + i;
if (index >= arguments.length) return null;
FormatDecode.Validator metaValidator = validators[i];
if (metaValidator == null) continue;
Collection<FormatDecode.Validator> unpacked = metaValidator instanceof FormatDecode.MultiValidator multi ?
multi.getValidators() : List.of(metaValidator);
if (unpacked.size() != 1) return null;
FormatDecode.Validator validator = unpacked.iterator().next();
if (validator == null) return null;
PsiExpression argument = arguments[index];
if (!possibleToConvert(validator, argument)) return null;
TextRange stringRange = validator.getRange();
if (stringRange == null) return null;
TextRange range = ExpressionUtils.findStringLiteralRange(expression, stringRange.getStartOffset(),
stringRange.getEndOffset());
if (range == null) return null;
result.put(range, index);
}
return new StringFormatArgumentToLogCallFix(result, literalExpression.getText());
}
private static boolean possibleToConvert(@NotNull FormatDecode.Validator validator, PsiExpression argument) {
FormatDecode.Spec spec = validator.getSpec();
if (spec == null) return false;
if (spec.conversion() == null ||
!StringUtil.isEmpty(spec.width()) ||
!StringUtil.isEmpty(spec.dateSpec()) ||
!StringUtil.isEmpty(spec.flags()) ||
!StringUtil.isEmpty(spec.precision())) {
return false;
}
return switch (spec.conversion()) {
case "s" -> true;
case "b" -> argument.getType() != null && TypeConversionUtil.isBooleanType(argument.getType());
case "d" -> argument.getType() != null && TypeConversionUtil.isIntegralNumberType(argument.getType());
default -> false;
};
}
}
private enum ProblemType {
CONCATENATION, STRING_FORMAT, MESSAGE_FORMAT
}
private class StringConcatenationArgumentToLogCallVisitor extends BaseInspectionVisitor {
@Override
@@ -356,7 +588,7 @@ public final class StringConcatenationArgumentToLogCallInspection extends BaseIn
return;
}
final PsiClass containingClass = method.getContainingClass();
if (!InheritanceUtil.isInheritor(containingClass, "org.slf4j.Logger") &&
if (!InheritanceUtil.isInheritor(containingClass, SLF4J_LOGGER) &&
!InheritanceUtil.isInheritor(containingClass, LOG4J_LOGGER) &&
!InheritanceUtil.isInheritor(containingClass, LOG4J_BUILDER)) {
return;
@@ -366,6 +598,19 @@ public final class StringConcatenationArgumentToLogCallInspection extends BaseIn
if (arguments.length == 0) {
return;
}
if (arguments.length == 1 && arguments[0] instanceof PsiMethodCallExpression callExpression) {
FormatDecode.FormatArgument formatArgument =
FormatDecode.FormatArgument.extract(callExpression, List.of("format"), List.of("String"), true);
if (formatArgument != null) {
registerMethodCallError(expression, ProblemType.STRING_FORMAT, expression, callExpression);
return;
}
if (MESSAGE_FORMAT_FORMAT.test(callExpression)) {
registerMethodCallError(expression, ProblemType.MESSAGE_FORMAT, expression, callExpression);
return;
}
}
PsiExpression argument = arguments[0];
if (!ExpressionUtils.hasStringType(argument)) {
if (arguments.length < 2) {
@@ -379,7 +624,7 @@ public final class StringConcatenationArgumentToLogCallInspection extends BaseIn
if (!containsNonConstantConcatenation(argument)) {
return;
}
registerMethodCallError(expression, argument, expression);
registerMethodCallError(expression, ProblemType.CONCATENATION, expression, argument);
}
private static boolean containsNonConstantConcatenation(@Nullable PsiExpression expression) {
@@ -0,0 +1,12 @@
import org.slf4j.*;
import java.text.MessageFormat;
class LessArgumentsStringFormat {
Logger LOG = LoggerFactory.getLogger(LessArgumentsStringFormat.class);
void f() {
LOG.in<caret>fo(String.format("%s something %b %d", "text", true));
}
}
@@ -0,0 +1,12 @@
import org.slf4j.*;
import java.text.MessageFormat;
class MessageFormatFormatter {
Logger LOG = LoggerFactory.getLogger(MessageFormatFormatter.class);
void f() {
LOG.in<caret>fo(MessageFormat.format("{1}, {0, number, #.00}", 1, 2));
}
}
@@ -0,0 +1,12 @@
import org.slf4j.*;
import java.text.MessageFormat;
class MessageFormatMissingParameter {
Logger LOG = LoggerFactory.getLogger(MessageFormatMissingParameter.class);
void f() {
LOG.in<caret>fo(MessageFormat.format("{2}, 2 {1} {0} {126}", "1", 2, 3.0));
}
}
@@ -0,0 +1,12 @@
import org.slf4j.*;
import java.text.MessageFormat;
class SimpleMessMessageFormatMoreArguments {
Logger LOG = LoggerFactory.getLogger(SimpleMessMessageFormatMoreArguments.class);
void f() {
LOG.in<caret>fo(MessageFormat.format("{2}, 2 {1} {0}", "1", 2, 3.0, 125));
}
}
@@ -0,0 +1,12 @@
import org.slf4j.*;
import java.text.MessageFormat;
class MoreArgumentsStringFormat {
Logger LOG = LoggerFactory.getLogger(MoreArgumentsStringFormat.class);
void f() {
LOG.in<caret>fo(String.format("%s something %b %d", "text", true, 1, 125));
}
}
@@ -0,0 +1,12 @@
import org.slf4j.*;
import java.text.MessageFormat;
class NumberedStringFormat {
Logger LOG = LoggerFactory.getLogger(NumberedStringFormat.class);
void f() {
LOG.in<caret>fo("{} {} {} {}", "d", "c", "b", "a");
}
}
@@ -0,0 +1,12 @@
import org.slf4j.*;
import java.text.MessageFormat;
class NumberedStringFormat {
Logger LOG = LoggerFactory.getLogger(NumberedStringFormat.class);
void f() {
LOG.in<caret>fo(String.format("%4$s %3$s %2$s %1$s", "a", "b", "c", "d"));
}
}
@@ -0,0 +1,12 @@
import org.slf4j.*;
import java.text.MessageFormat;
class SimpleStringFormat {
Logger LOG = LoggerFactory.getLogger(SimpleStringFormat.class);
void f() {
LOG.in<caret>fo(String.format("%s test %<s %<s", "test"));
}
}
@@ -0,0 +1,12 @@
import org.slf4j.*;
import java.text.MessageFormat;
class SimpleMessageFormat {
Logger LOG = LoggerFactory.getLogger(SimpleMessageFormat.class);
void f() {
LOG.info("{}, 2 {} {}", 3.0, 2, "1");
}
}
@@ -0,0 +1,12 @@
import org.slf4j.*;
import java.text.MessageFormat;
class SimpleMessageFormat {
Logger LOG = LoggerFactory.getLogger(SimpleMessageFormat.class);
void f() {
LOG.in<caret>fo(MessageFormat.format("{2}, 2 {1} {0}", "1", 2, 3.0));
}
}
@@ -0,0 +1,12 @@
import org.slf4j.*;
import java.text.MessageFormat;
class SimpleStringFormat {
Logger LOG = LoggerFactory.getLogger(SimpleStringFormat.class);
void f() {
LOG.in<caret>fo("{} something {} {}", "text", true, 1);
}
}
@@ -0,0 +1,12 @@
import org.slf4j.*;
import java.text.MessageFormat;
class SimpleStringFormat {
Logger LOG = LoggerFactory.getLogger(SimpleStringFormat.class);
void f() {
LOG.in<caret>fo(String.format("%s something %b %d", "text", true, 1));
}
}
@@ -0,0 +1,12 @@
import org.slf4j.*;
import java.text.MessageFormat;
class StringFormatWithWidth {
Logger LOG = LoggerFactory.getLogger(SimpleMessageFormat.class);
void f() {
LOG.in<caret>fo(String.format("%4$2s %3$2s %2$2s %1$2s", "a", "b", "c", "d"));
}
}
@@ -0,0 +1,12 @@
import org.slf4j.*;
import java.text.MessageFormat;
class WrongStringFormat {
Logger LOG = LoggerFactory.getLogger(WrongStringFormat.class);
void f() {
LOG.in<caret>fo(String.format("%d something %D %d", "text", true, 1));
}
}
@@ -13,12 +13,12 @@ public class StringConcatenationArgumentToLogCallFixTest extends IGQuickFixesTes
super.setUp();
myDefaultHint = InspectionGadgetsBundle.message("string.concatenation.argument.to.log.call.quickfix");
myFixture.addClass("package org.slf4j; public interface Logger { void info(String format); }");
myFixture.addClass("package org.slf4j; public class LoggerFactory { public static Logger getLogger(Class clazz) { return null; }}");
myFixture.addClass("package org.slf4j; public final class LoggerFactory { public static Logger getLogger(Class clazz) { return null; }}");
myFixture.addClass("package org.apache.logging.log4j; public interface LogBuilder { void log(String format); LogBuilder withLocation(); }");
myFixture.addClass("package org.apache.logging.log4j; public interface Logger { LogBuilder atInfo(); void info(String format, Object... arguments); LogBuilder withLocation(); }");
myFixture.addClass("""
package org.apache.logging.log4j;
public class LogManager {
public final class LogManager {
public static Logger getLogger(Class clazz) {
return null;
}
@@ -26,6 +26,14 @@ public class StringConcatenationArgumentToLogCallFixTest extends IGQuickFixesTes
return null;
}
}""");
myFixture.addClass( """
package java.text;
public final class MessageFormat {
public static String format(String format, Object... params);
}"""
);
myFixture.enableInspections(new StringConcatenationArgumentToLogCallInspection());
}
@@ -42,6 +50,20 @@ public class StringConcatenationArgumentToLogCallFixTest extends IGQuickFixesTes
InspectionGadgetsBundle.message("string.concatenation.argument.to.log.call.display.name")));
}
public void testSimpleMessageFormat() { doTest(InspectionGadgetsBundle.message("string.concatenation.argument.to.log.message.format.call.quickfix")); }
public void testMessageFormatMissingParameter() { assertQuickfixNotAvailable(InspectionGadgetsBundle.message("string.concatenation.argument.to.log.message.format.call.quickfix")); }
public void testMessageFormatMoreArguments() { assertQuickfixNotAvailable(InspectionGadgetsBundle.message("string.concatenation.argument.to.log.message.format.call.quickfix")); }
public void testMessageFormatFormatter() { assertQuickfixNotAvailable(InspectionGadgetsBundle.message("string.concatenation.argument.to.log.message.format.call.quickfix")); }
public void testSimpleStringFormat() { doTest(InspectionGadgetsBundle.message("string.concatenation.argument.to.log.string.format.call.quickfix")); }
public void testStringFormatWithWidth() { assertQuickfixNotAvailable(InspectionGadgetsBundle.message("string.concatenation.argument.to.log.string.format.call.quickfix")); }
public void testNumberedStringFormat() { doTest(InspectionGadgetsBundle.message("string.concatenation.argument.to.log.string.format.call.quickfix")); }
public void testWrongStringFormat() { assertQuickfixNotAvailable(InspectionGadgetsBundle.message("string.concatenation.argument.to.log.string.format.call.quickfix")); }
public void testLessArgumentsStringFormat() { assertQuickfixNotAvailable(InspectionGadgetsBundle.message("string.concatenation.argument.to.log.string.format.call.quickfix")); }
public void testMoreArgumentsStringFormat() { assertQuickfixNotAvailable(InspectionGadgetsBundle.message("string.concatenation.argument.to.log.string.format.call.quickfix")); }
public void testPreviousArgumentStringFormat() { assertQuickfixNotAvailable(InspectionGadgetsBundle.message("string.concatenation.argument.to.log.string.format.call.quickfix")); }
@Override
protected String getRelativePath() {
return "logging/string_concatenation_argument_to_log_call";
@@ -38,7 +38,7 @@ public class StringConcatenationArgumentToLogCallInspectionTest extends LightJav
"""
package org.slf4j;\s
public class LoggerFactory {
public final class LoggerFactory {
public static Logger getLogger(Class clazz) {
return null;\s
}
@@ -55,7 +55,7 @@ public class StringConcatenationArgumentToLogCallInspectionTest extends LightJav
"""
package org.apache.logging.log4j;
public class LogManager {
public final class LogManager {
public static Logger getLogger() {
return null;
}
@@ -69,6 +69,11 @@ public class StringConcatenationArgumentToLogCallInspectionTest extends LightJav
public interface LogBuilder {
void log(String format, Object p0);
void log(String format, Object... params);
}""",
"""
package java.text;
public final class MessageFormat {
public static String format(String format, Object... params);
}"""
};
}
@@ -81,7 +86,7 @@ public class StringConcatenationArgumentToLogCallInspectionTest extends LightJav
Logger logger = LoggerFactory.getLogger(X.class);
final String CONST = "const";
String var = "var";
logger./*Non-constant string concatenation as argument to 'debug()' logging call*/debug/**/("string " + var + CONST);
logger./*Evaluated string as argument to 'debug()' logging call*/debug/**/("string " + var + CONST);
}
}"""
);
@@ -104,8 +109,8 @@ public class StringConcatenationArgumentToLogCallInspectionTest extends LightJav
class Logging {
private static final Logger LOG = LogManager.getLogger();
void m(int i) {
LOG./*Non-constant string concatenation as argument to 'info()' logging call*/info/**/("hello? " + i);
LOG./*Non-constant string concatenation as argument to 'fatal()' logging call*/fatal/**/("you got me " + i);
LOG./*Evaluated string as argument to 'info()' logging call*/info/**/("hello? " + i);
LOG./*Evaluated string as argument to 'fatal()' logging call*/fatal/**/("you got me " + i);
}
}""");
}
@@ -114,12 +119,12 @@ public class StringConcatenationArgumentToLogCallInspectionTest extends LightJav
doTest("""
import org.apache.logging.log4j.*;
class Log4JFormatted {
final class Log4JFormatted {
private static final Logger logger = LogManager.getFormattedLogger();
public static void m(String a) {
logger./*Non-constant string concatenation as argument to 'info()' logging call*/info/**/("1" + "2" + a);
logger./*Evaluated string as argument to 'info()' logging call*/info/**/("1" + "2" + a);
}
}""");
}
@@ -130,9 +135,35 @@ public class StringConcatenationArgumentToLogCallInspectionTest extends LightJav
class Logging {
private static final Logger LOG = LogManager.getLogger();
void m(int i) {
LOG.atDebug()./*Non-constant string concatenation as argument to 'log()' logging call*/log/**/("hello? " + i);
LOG.atInfo()./*Non-constant string concatenation as argument to 'log()' logging call*/log/**/("you got me " + i);
LOG.atDebug()./*Evaluated string as argument to 'log()' logging call*/log/**/("hello? " + i);
LOG.atInfo()./*Evaluated string as argument to 'log()' logging call*/log/**/("you got me " + i);
}
}""");
}
public void testSlfStringFormat() {
doTest("""
import org.slf4j.*;
class X {
void foo() {
Logger log = LoggerFactory.getLogger(X.class);
log./*Evaluated string as argument to 'info()' logging call*/info/**/(String.format("%s %d", "1", 1));
}
}"""
);
}
public void testSlfMessageFormat() {
doTest("""
import org.slf4j.*;
import java.text.MessageFormat;
class X {
void foo() {
Logger log = LoggerFactory.getLogger(X.class);
log./*Evaluated string as argument to 'info()' logging call*/info/**/(MessageFormat.format("{1}, {0}", "1", 2));
}
}"""
);
}
}