mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[java-inspections] IDEA-337706 Support conversion System.out to log calls
- quickfix for SystemOutErrInspection and ThrowablePrintedToSystemOutInspection - StringConcatenationArgumentToLogCallInspection now uses String.valueOf for one argument (exception type) - SystemOutErrInspection has info level - ConvertSystemOutToLogCallFix doesn't highlight ThrowablePrintedToSystemOutInspection's problems GitOrigin-RevId: f611e6ccb0e15e5b806ed776b88b12b097d3bb5d
This commit is contained in:
committed by
intellij-monorepo-bot
parent
206d995dcd
commit
77cac2bb83
@@ -245,6 +245,8 @@ use.obsolete.collection.type.problem.descriptor=Obsolete collection type <code>#
|
||||
use.obsolete.collection.type.ignore.library.arguments.option=Ignore obsolete collection types where they are required
|
||||
use.system.out.err.display.name=Use of 'System.out' or 'System.err'
|
||||
use.system.out.err.problem.descriptor=Uses of <code>#ref</code> should probably be replaced with more robust logging #loc
|
||||
use.system.out.err.problem.fix.err.option=Log method for 'System.err':
|
||||
use.system.out.err.problem.fix.out.option=Log method for 'System.out':
|
||||
dumpstack.call.display.name=Call to 'Thread.dumpStack()'
|
||||
dumpstack.call.problem.descriptor=Call to <code>Thread.#ref()</code> should probably be replaced with more robust logging #loc
|
||||
printstacktrace.call.display.name=Call to 'printStackTrace()'
|
||||
@@ -1956,6 +1958,9 @@ invert.method.quickfix=Invert method
|
||||
invert.quickfix=Invert ''{0}''
|
||||
throwable.printed.to.system.out.display.name='Throwable' printed to 'System.out'
|
||||
throwable.printed.to.system.out.problem.descriptor=''Throwable'' argument <code>#ref</code> to ''System.{0}.{1}()'' call
|
||||
throwable.printed.to.system.out.problem.fix.level.option=Log method for fix:
|
||||
convert.system.out.to.log.call.family.name=Convert ''System.out'' call to log call
|
||||
convert.system.out.to.log.call.name=Convert ''System.out'' call to call of ''{0}''
|
||||
suppress.for.tests.scope.quickfix=Suppress for 'Tests' scope
|
||||
implicit.default.charset.usage.display.name=Implicit platform default charset
|
||||
implicit.default.charset.usage.problem.descriptor=Call to <code>#ref()</code> uses the platform's default charset
|
||||
|
||||
-91
@@ -1,91 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.siyeh.ig.maturity;
|
||||
|
||||
import com.intellij.psi.*;
|
||||
import com.siyeh.InspectionGadgetsBundle;
|
||||
import com.siyeh.ig.BaseInspection;
|
||||
import com.siyeh.ig.BaseInspectionVisitor;
|
||||
import com.siyeh.ig.psiutils.TypeUtils;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* @author Bas Leijdekkers
|
||||
*/
|
||||
public final class ThrowablePrintedToSystemOutInspection extends BaseInspection {
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected String buildErrorString(Object... infos) {
|
||||
final String fieldName = (String)infos[0];
|
||||
final String methodName = (String)infos[1];
|
||||
return InspectionGadgetsBundle.message("throwable.printed.to.system.out.problem.descriptor", fieldName, methodName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabledByDefault() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaseInspectionVisitor buildVisitor() {
|
||||
return new ThrowablePrintedToSystemOutVisitor();
|
||||
}
|
||||
|
||||
private static class ThrowablePrintedToSystemOutVisitor extends BaseInspectionVisitor {
|
||||
|
||||
@Override
|
||||
public void visitMethodCallExpression(@NotNull PsiMethodCallExpression expression) {
|
||||
super.visitMethodCallExpression(expression);
|
||||
final PsiReferenceExpression methodExpression = expression.getMethodExpression();
|
||||
@NonNls final String methodName = methodExpression.getReferenceName();
|
||||
if (!"print".equals(methodName) && !"println".equals(methodName)) {
|
||||
return;
|
||||
}
|
||||
final PsiExpressionList argumentList = expression.getArgumentList();
|
||||
final PsiExpression[] arguments = argumentList.getExpressions();
|
||||
if (arguments.length != 1) {
|
||||
return;
|
||||
}
|
||||
final PsiExpression argument = arguments[0];
|
||||
if (!TypeUtils.expressionHasTypeOrSubtype(argument, CommonClassNames.JAVA_LANG_THROWABLE)) {
|
||||
return;
|
||||
}
|
||||
final PsiMethod method = expression.resolveMethod();
|
||||
if (method == null) {
|
||||
return;
|
||||
}
|
||||
final PsiExpression qualifier = methodExpression.getQualifierExpression();
|
||||
if (!(qualifier instanceof PsiReferenceExpression qualifierReference)) {
|
||||
return;
|
||||
}
|
||||
final PsiElement target = qualifierReference.resolve();
|
||||
if (!(target instanceof PsiField field)) {
|
||||
return;
|
||||
}
|
||||
@NonNls final String fieldName = field.getName();
|
||||
if (!"out".equals(fieldName) && !"err".equals(fieldName)) {
|
||||
return;
|
||||
}
|
||||
final PsiClass aClass = field.getContainingClass();
|
||||
if (aClass == null || !"java.lang.System".equals(aClass.getQualifiedName())) {
|
||||
return;
|
||||
}
|
||||
registerError(argument, fieldName, methodName);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1412,7 +1412,7 @@
|
||||
<!--group.names.code.maturity.issues-->
|
||||
<localInspection groupPath="Java" language="JAVA" suppressId="UseOfSystemOutOrSystemErr" shortName="SystemOutErr" bundle="messages.InspectionGadgetsBundle"
|
||||
key="use.system.out.err.display.name" groupBundle="messages.InspectionsBundle"
|
||||
groupKey="group.names.code.maturity.issues" enabledByDefault="false" level="WARNING"
|
||||
groupKey="group.names.code.maturity.issues" enabledByDefault="true" level="INFORMATION"
|
||||
implementationClass="com.siyeh.ig.maturity.SystemOutErrInspection"/>
|
||||
<localInspection groupPath="Java" language="JAVA" suppressId="CallToPrintStackTrace" shortName="ThrowablePrintStackTrace" bundle="messages.InspectionGadgetsBundle"
|
||||
key="printstacktrace.call.display.name" groupBundle="messages.InspectionsBundle"
|
||||
|
||||
+103
-60
@@ -110,6 +110,10 @@ public final class StringConcatenationArgumentToLogCallInspection extends BaseIn
|
||||
|
||||
if (isFormattedLog4J(logCall)) return null;
|
||||
|
||||
return getQuickFix(problemType, targetExpression);
|
||||
}
|
||||
|
||||
public static @Nullable PsiUpdateModCommandQuickFix getQuickFix(@NotNull ProblemType problemType, @NotNull PsiExpression targetExpression) {
|
||||
return switch (problemType) {
|
||||
case CONCATENATION ->
|
||||
StringConcatenationArgumentToLogCallFix.isAvailable(targetExpression) ? new StringConcatenationArgumentToLogCallFix() : null;
|
||||
@@ -168,7 +172,11 @@ public final class StringConcatenationArgumentToLogCallInspection extends BaseIn
|
||||
return new StringConcatenationArgumentToLogCallVisitor();
|
||||
}
|
||||
|
||||
private static class StringConcatenationArgumentToLogCallFix extends PsiUpdateModCommandQuickFix {
|
||||
public interface EvaluatedStringFix {
|
||||
void fix(@NotNull Project project, @NotNull PsiMethodCallExpression logCall);
|
||||
}
|
||||
|
||||
private static class StringConcatenationArgumentToLogCallFix extends PsiUpdateModCommandQuickFix implements EvaluatedStringFix {
|
||||
|
||||
StringConcatenationArgumentToLogCallFix() { }
|
||||
|
||||
@@ -184,6 +192,11 @@ public final class StringConcatenationArgumentToLogCallInspection extends BaseIn
|
||||
if (!(grandParent instanceof PsiMethodCallExpression methodCallExpression)) {
|
||||
return;
|
||||
}
|
||||
fix(project, methodCallExpression);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fix(@NotNull Project project, @NotNull PsiMethodCallExpression methodCallExpression) {
|
||||
final PsiExpressionList argumentList = methodCallExpression.getArgumentList();
|
||||
final PsiExpression[] arguments = argumentList.getExpressions();
|
||||
if (arguments.length == 0) {
|
||||
@@ -306,10 +319,16 @@ public final class StringConcatenationArgumentToLogCallInspection extends BaseIn
|
||||
newMethodCall.append('}');
|
||||
}
|
||||
else {
|
||||
for (PsiExpression newArgument : newArguments) {
|
||||
newMethodCall.append(',');
|
||||
if (newArgument != null) {
|
||||
newMethodCall.append(newArgument.getText());
|
||||
if (newArguments.size() == 1 && newArguments.get(0) != null &&
|
||||
InheritanceUtil.isInheritor(newArguments.get(0).getType(), CommonClassNames.JAVA_LANG_THROWABLE)) {
|
||||
newMethodCall.append(", String.valueOf(").append(newArguments.get(0).getText()).append(")");
|
||||
}
|
||||
else {
|
||||
for (PsiExpression newArgument : newArguments) {
|
||||
newMethodCall.append(',');
|
||||
if (newArgument != null) {
|
||||
newMethodCall.append(newArgument.getText());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -351,7 +370,7 @@ public final class StringConcatenationArgumentToLogCallInspection extends BaseIn
|
||||
}
|
||||
}
|
||||
|
||||
private static abstract class FormatArgumentToLogCallFix extends PsiUpdateModCommandQuickFix {
|
||||
private static abstract class FormatArgumentToLogCallFix extends PsiUpdateModCommandQuickFix implements EvaluatedStringFix {
|
||||
|
||||
@NotNull
|
||||
private final Map<TextRange, Integer> myTextMapping;
|
||||
@@ -366,11 +385,7 @@ public final class StringConcatenationArgumentToLogCallInspection extends BaseIn
|
||||
}
|
||||
|
||||
@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;
|
||||
}
|
||||
public void fix(@NotNull Project project, @NotNull PsiMethodCallExpression callExpression) {
|
||||
PsiExpression[] expressions = callExpression.getArgumentList().getExpressions();
|
||||
if (expressions.length != 1) {
|
||||
return;
|
||||
@@ -395,6 +410,15 @@ public final class StringConcatenationArgumentToLogCallInspection extends BaseIn
|
||||
tracker.replace(callExpression, builder.toString());
|
||||
}
|
||||
|
||||
@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;
|
||||
}
|
||||
fix(project, callExpression);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private String createNewArgumentsFromCall(@NotNull PsiMethodCallExpression formatCallExpression,
|
||||
@NotNull CommentTracker tracker) {
|
||||
@@ -432,7 +456,7 @@ public final class StringConcatenationArgumentToLogCallInspection extends BaseIn
|
||||
|
||||
|
||||
@Nullable
|
||||
static LocalQuickFix create(@NotNull PsiExpression expression) {
|
||||
static PsiUpdateModCommandQuickFix create(@NotNull PsiExpression expression) {
|
||||
if (!(expression instanceof PsiMethodCallExpression callExpression)) {
|
||||
return null;
|
||||
}
|
||||
@@ -487,7 +511,7 @@ public final class StringConcatenationArgumentToLogCallInspection extends BaseIn
|
||||
}
|
||||
|
||||
@Nullable
|
||||
static LocalQuickFix create(@NotNull PsiExpression originalExpression) {
|
||||
static PsiUpdateModCommandQuickFix create(@NotNull PsiExpression originalExpression) {
|
||||
if (!(originalExpression instanceof PsiMethodCallExpression callExpression)) {
|
||||
return null;
|
||||
}
|
||||
@@ -559,7 +583,7 @@ public final class StringConcatenationArgumentToLogCallInspection extends BaseIn
|
||||
}
|
||||
}
|
||||
|
||||
private enum ProblemType {
|
||||
public enum ProblemType {
|
||||
CONCATENATION, STRING_FORMAT, MESSAGE_FORMAT
|
||||
}
|
||||
|
||||
@@ -598,54 +622,73 @@ 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) {
|
||||
return;
|
||||
}
|
||||
argument = arguments[1];
|
||||
if (!ExpressionUtils.hasStringType(argument)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (!containsNonConstantConcatenation(argument)) {
|
||||
return;
|
||||
}
|
||||
registerMethodCallError(expression, ProblemType.CONCATENATION, expression, argument);
|
||||
}
|
||||
LogConcatenationContext result = getLogConcatenationContext(arguments);
|
||||
if (result == null) return;
|
||||
|
||||
private static boolean containsNonConstantConcatenation(@Nullable PsiExpression expression) {
|
||||
if (expression instanceof PsiParenthesizedExpression parenthesizedExpression) {
|
||||
return containsNonConstantConcatenation(parenthesizedExpression.getExpression());
|
||||
}
|
||||
else if (expression instanceof PsiPolyadicExpression polyadicExpression) {
|
||||
if (!ExpressionUtils.hasStringType(polyadicExpression)) {
|
||||
return false;
|
||||
}
|
||||
if (!JavaTokenType.PLUS.equals(polyadicExpression.getOperationTokenType())) {
|
||||
return false;
|
||||
}
|
||||
final PsiExpression[] operands = polyadicExpression.getOperands();
|
||||
for (PsiExpression operand : operands) {
|
||||
if (!ExpressionUtils.isEvaluatedAtCompileTime(operand)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
registerMethodCallError(expression, result.problemType(), expression, result.argument());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public record LogConcatenationContext(@NotNull PsiExpression argument, @NotNull ProblemType problemType) {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static LogConcatenationContext getLogConcatenationContext(PsiExpression @NotNull [] arguments) {
|
||||
PsiExpression argument = arguments[0];
|
||||
|
||||
ProblemType problemType = null;
|
||||
|
||||
if (arguments.length == 1 && argument instanceof PsiMethodCallExpression callExpression) {
|
||||
FormatDecode.FormatArgument formatArgument =
|
||||
FormatDecode.FormatArgument.extract(callExpression, List.of("format"), List.of("String"), true);
|
||||
if (formatArgument != null) {
|
||||
problemType = ProblemType.STRING_FORMAT;
|
||||
}
|
||||
else if (MESSAGE_FORMAT_FORMAT.test(callExpression)) {
|
||||
problemType = ProblemType.MESSAGE_FORMAT;
|
||||
}
|
||||
}
|
||||
|
||||
if (problemType == null) {
|
||||
if (!ExpressionUtils.hasStringType(argument)) {
|
||||
if (arguments.length < 2) {
|
||||
return null;
|
||||
}
|
||||
argument = arguments[1];
|
||||
}
|
||||
if (!ExpressionUtils.hasStringType(argument)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!containsNonConstantConcatenation(argument)) {
|
||||
return null;
|
||||
}
|
||||
problemType = ProblemType.CONCATENATION;
|
||||
}
|
||||
|
||||
return new LogConcatenationContext(argument, problemType);
|
||||
}
|
||||
|
||||
private static boolean containsNonConstantConcatenation(@Nullable PsiExpression expression) {
|
||||
if (expression instanceof PsiParenthesizedExpression parenthesizedExpression) {
|
||||
return containsNonConstantConcatenation(parenthesizedExpression.getExpression());
|
||||
}
|
||||
else if (expression instanceof PsiPolyadicExpression polyadicExpression) {
|
||||
if (!ExpressionUtils.hasStringType(polyadicExpression)) {
|
||||
return false;
|
||||
}
|
||||
if (!JavaTokenType.PLUS.equals(polyadicExpression.getOperationTokenType())) {
|
||||
return false;
|
||||
}
|
||||
final PsiExpression[] operands = polyadicExpression.getOperands();
|
||||
for (PsiExpression operand : operands) {
|
||||
if (!ExpressionUtils.isEvaluatedAtCompileTime(operand)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,301 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.siyeh.ig.maturity;
|
||||
|
||||
import com.intellij.java.JavaBundle;
|
||||
import com.intellij.lang.logging.JvmLogger;
|
||||
import com.intellij.modcommand.*;
|
||||
import com.intellij.openapi.module.Module;
|
||||
import com.intellij.openapi.module.ModuleUtilCore;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.NlsSafe;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.codeStyle.VariableKind;
|
||||
import com.intellij.psi.search.GlobalSearchScope;
|
||||
import com.intellij.psi.util.InheritanceUtil;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.ui.logging.JvmLoggingSettingsStorage;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.siyeh.HardcodedMethodConstants;
|
||||
import com.siyeh.InspectionGadgetsBundle;
|
||||
import com.siyeh.ig.callMatcher.CallMatcher;
|
||||
import com.siyeh.ig.logging.StringConcatenationArgumentToLogCallInspection;
|
||||
import com.siyeh.ig.psiutils.CommentTracker;
|
||||
import com.siyeh.ig.psiutils.VariableNameGenerator;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static com.intellij.lang.logging.UnspecifiedLogger.UNSPECIFIED_LOGGER_ID;
|
||||
import static com.intellij.psi.CommonClassNames.JAVA_IO_PRINT_STREAM;
|
||||
import static com.intellij.psi.CommonClassNames.JAVA_LANG_SYSTEM;
|
||||
|
||||
public class ConvertSystemOutToLogCallFix extends PsiBasedModCommandAction<PsiMethodCallExpression> {
|
||||
|
||||
private static final CallMatcher myCallMatcher = CallMatcher.instanceCall(JAVA_IO_PRINT_STREAM, "println", "print");
|
||||
private static final Set<String> SUPPORTED_LOGGING_SYSTEMS = Set.of("Log4j2", "Slf4j", "Lombok Slf4j", "Lombok Log4j2");
|
||||
|
||||
@NotNull
|
||||
private final List<JvmLogger> myLoggers;
|
||||
|
||||
@NotNull
|
||||
private final String myMethodName;
|
||||
|
||||
@Nullable
|
||||
private final String myLogName;
|
||||
|
||||
private ConvertSystemOutToLogCallFix(@NotNull List<JvmLogger> loggers,
|
||||
@Nullable String logName,
|
||||
@NotNull String method,
|
||||
@NotNull PsiMethodCallExpression logCall) {
|
||||
super(logCall);
|
||||
myLoggers = loggers;
|
||||
myMethodName = method;
|
||||
myLogName = logName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String getFamilyName() {
|
||||
if (myLoggers.size() != 1) {
|
||||
return InspectionGadgetsBundle.message("convert.system.out.to.log.call.family.name");
|
||||
}
|
||||
return InspectionGadgetsBundle.message("convert.system.out.to.log.call.name", myLoggers.get(0).toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @NotNull ModCommand perform(@NotNull ActionContext context, @NotNull PsiMethodCallExpression originalCall) {
|
||||
if (myLoggers.isEmpty()) {
|
||||
return ModCommand.nop();
|
||||
}
|
||||
|
||||
if (myLogName != null) {
|
||||
return ModCommand.psiUpdate(originalCall, logCall -> replaceWithLogCall(logCall, myLogName, myMethodName));
|
||||
}
|
||||
|
||||
if (myLoggers.size() > 1) {
|
||||
return ModCommand.chooseAction(JavaBundle.message("dialog.title.choose.logger"),
|
||||
ContainerUtil.map(myLoggers,
|
||||
log -> new ConvertSystemOutToLogCallFix(List.of(log), null, myMethodName,
|
||||
originalCall)));
|
||||
}
|
||||
|
||||
return ModCommand.psiUpdate(originalCall, callExpression -> {
|
||||
JvmLogger logger = myLoggers.get(0);
|
||||
|
||||
PsiClass upperClass = getUpperClass(callExpression); //not always a good idea for all cases, but asking is too annoying
|
||||
|
||||
if (upperClass == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
String logFieldName = logger.getLogFieldName(upperClass);
|
||||
if (logFieldName == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Project project = context.project();
|
||||
PsiElement loggerElement = logger.createLogger(project, upperClass);
|
||||
if (loggerElement == null) {
|
||||
return;
|
||||
}
|
||||
logger.insertLoggerAtClass(project, upperClass, loggerElement);
|
||||
|
||||
replaceWithLogCall(callExpression, logFieldName, myMethodName);
|
||||
});
|
||||
}
|
||||
|
||||
private static void replaceWithLogCall(@NotNull PsiMethodCallExpression callExpression,
|
||||
@NotNull String logName,
|
||||
@NotNull String logMethodName) {
|
||||
PsiExpression[] expressions = callExpression.getArgumentList().getExpressions();
|
||||
if(expressions.length != 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
PsiExpression expression = expressions[0];
|
||||
Project project = callExpression.getProject();
|
||||
|
||||
String arguments = getArguments(expression);
|
||||
|
||||
String text = logName + "." + logMethodName + "(" + arguments + ")";
|
||||
|
||||
CommentTracker tracker = new CommentTracker();
|
||||
PsiElement newElement = tracker.replace(callExpression, text);
|
||||
|
||||
if (!(newElement instanceof PsiMethodCallExpression newCall)) {
|
||||
return;
|
||||
}
|
||||
StringConcatenationArgumentToLogCallInspection.LogConcatenationContext context =
|
||||
StringConcatenationArgumentToLogCallInspection.getLogConcatenationContext(newCall.getArgumentList().getExpressions());
|
||||
|
||||
if (context == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
PsiUpdateModCommandQuickFix fix = StringConcatenationArgumentToLogCallInspection.getQuickFix(context.problemType(), context.argument());
|
||||
if (fix instanceof StringConcatenationArgumentToLogCallInspection.EvaluatedStringFix evaluatedStringFix) {
|
||||
evaluatedStringFix.fix(project, newCall);
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static String getArguments(@NotNull PsiExpression expression) {
|
||||
PsiType expressionType = expression.getType();
|
||||
|
||||
if (expressionType==null ||expressionType.equalsToText(CommonClassNames.JAVA_LANG_STRING)) {
|
||||
return expression.getText();
|
||||
}
|
||||
|
||||
if (InheritanceUtil.isInheritor(expressionType, CommonClassNames.JAVA_LANG_EXCEPTION)) {
|
||||
return "\"e: \" , " + expression.getText();
|
||||
}
|
||||
|
||||
return "String.valueOf(" + expression.getText() + ")";
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static ModCommandAction createFix(@NotNull PsiMethodCallExpression callExpression,
|
||||
@NotNull String method) {
|
||||
if (!myCallMatcher.matches(callExpression)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
PsiReferenceExpression expressionPrint = callExpression.getMethodExpression();
|
||||
if (!(expressionPrint.getQualifierExpression() instanceof PsiReferenceExpression expression)) {
|
||||
return null;
|
||||
}
|
||||
String name = expression.getReferenceName();
|
||||
if (!HardcodedMethodConstants.OUT.equals(name) &&
|
||||
!HardcodedMethodConstants.ERR.equals(name)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
PsiElement referent = expression.resolve();
|
||||
if (!(referent instanceof PsiField systemField)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final PsiClass containingClass = systemField.getContainingClass();
|
||||
if (containingClass == null) {
|
||||
return null;
|
||||
}
|
||||
final String className = containingClass.getQualifiedName();
|
||||
if (!JAVA_LANG_SYSTEM.equals(className)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (callExpression.getArgumentList().getExpressions().length != 1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Module module = ModuleUtilCore.findModuleForFile(callExpression.getContainingFile());
|
||||
if (module == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
List<JvmLogger> loggers = JvmLogger.Companion.findSuitableLoggers(module, true);
|
||||
|
||||
if (loggers.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
PsiClass psiClass = getUpperClass(callExpression);
|
||||
if (psiClass == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Project project = callExpression.getProject();
|
||||
GlobalSearchScope globalSearchScope = GlobalSearchScope.everythingScope(project);
|
||||
List<JvmLogger> loggersWithTargetMethod = new ArrayList<>();
|
||||
for (JvmLogger logger : loggers) {
|
||||
PsiClass loggerClass = JavaPsiFacade.getInstance(project).findClass(logger.getLoggerTypeName(), globalSearchScope);
|
||||
if (loggerClass != null && loggerClass.findMethodsByName(method, true).length > 0 &&
|
||||
SUPPORTED_LOGGING_SYSTEMS.contains(logger.getId())) {
|
||||
loggersWithTargetMethod.add(logger);
|
||||
}
|
||||
}
|
||||
|
||||
if (loggersWithTargetMethod.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
List<PsiField> existedLoggers = new ArrayList<>();
|
||||
PsiField[] fields = psiClass.getFields();
|
||||
Set<String> allSupportedLoggers = loggersWithTargetMethod.stream().map(log -> log.getLoggerTypeName()).collect(Collectors.toSet());
|
||||
for (PsiField field : fields) {
|
||||
if (allSupportedLoggers.contains(field.getType().getCanonicalText())) {
|
||||
existedLoggers.add(field);
|
||||
}
|
||||
}
|
||||
|
||||
if (!existedLoggers.isEmpty()) {
|
||||
String possibleLogName = existedLoggers.get(0).getName();
|
||||
PsiResolveHelper helper = JavaPsiFacade.getInstance(project).getResolveHelper();
|
||||
PsiVariable variable = helper.resolveAccessibleReferencedVariable(possibleLogName, callExpression);
|
||||
if (variable instanceof PsiField field &&
|
||||
allSupportedLoggers.contains(field.getType().getCanonicalText())) {
|
||||
return new ConvertSystemOutToLogCallFix(loggersWithTargetMethod, possibleLogName, method, callExpression);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
List<JvmLogger> availableLoggers = new ArrayList<>();
|
||||
for (JvmLogger logger: loggersWithTargetMethod) {
|
||||
String logFieldName = logger.getLogFieldName(psiClass);
|
||||
if (logFieldName == null) {
|
||||
continue;
|
||||
}
|
||||
String proposedName = new VariableNameGenerator(callExpression, VariableKind.LOCAL_VARIABLE).byName(logFieldName)
|
||||
.generate(true);
|
||||
if (!proposedName.equals(logFieldName)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (logger.isPossibleToPlaceLoggerAtClass(psiClass)) {
|
||||
availableLoggers.add(logger);
|
||||
}
|
||||
}
|
||||
|
||||
if (availableLoggers.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
JvmLoggingSettingsStorage loggingSettingsStorage = project.getService(JvmLoggingSettingsStorage.class);
|
||||
JvmLoggingSettingsStorage.State state = loggingSettingsStorage.getState();
|
||||
String id = state.getLoggerId();
|
||||
|
||||
if (UNSPECIFIED_LOGGER_ID.equals(id)) {
|
||||
return new ConvertSystemOutToLogCallFix(availableLoggers, null, method, callExpression);
|
||||
}
|
||||
|
||||
Optional<JvmLogger> chosenLogger = availableLoggers.stream().filter(log -> log.getId().equals(id)).findAny();
|
||||
if (chosenLogger.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new ConvertSystemOutToLogCallFix(chosenLogger.stream().toList(), null, method, callExpression);
|
||||
}
|
||||
|
||||
private static @Nullable PsiClass getUpperClass(@NotNull PsiElement source) {
|
||||
PsiElement target = source;
|
||||
PsiElement next = source;
|
||||
while (next != null) {
|
||||
target = next;
|
||||
next = PsiTreeUtil.findFirstParent(target, true,
|
||||
parent -> parent instanceof PsiClass &&
|
||||
!(parent instanceof PsiImplicitClass) &&
|
||||
!(parent instanceof PsiAnonymousClass));
|
||||
}
|
||||
return target instanceof PsiClass ? (PsiClass)target : null;
|
||||
}
|
||||
|
||||
public enum PopularLogLevel {
|
||||
TRACE, DEBUG, INFO, WARN, ERROR;
|
||||
|
||||
@NlsSafe
|
||||
public String toMethodName() {
|
||||
return name().toLowerCase(Locale.ROOT);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,28 +2,83 @@
|
||||
package com.siyeh.ig.maturity;
|
||||
|
||||
import com.intellij.codeInspection.LocalQuickFix;
|
||||
import com.intellij.psi.PsiClass;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiField;
|
||||
import com.intellij.psi.PsiReferenceExpression;
|
||||
import com.intellij.codeInspection.options.OptPane;
|
||||
import com.intellij.modcommand.ModCommandAction;
|
||||
import com.intellij.modcommand.ModCommandService;
|
||||
import com.intellij.profile.codeInspection.InspectionProjectProfileManager;
|
||||
import com.intellij.psi.*;
|
||||
import com.siyeh.HardcodedMethodConstants;
|
||||
import com.siyeh.InspectionGadgetsBundle;
|
||||
import com.siyeh.ig.BaseInspection;
|
||||
import com.siyeh.ig.BaseInspectionVisitor;
|
||||
import com.siyeh.ig.fixes.SuppressForTestsScopeFix;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import static com.intellij.psi.CommonClassNames.JAVA_LANG_SYSTEM;
|
||||
|
||||
/**
|
||||
* @author Bas Leijdekkers
|
||||
*/
|
||||
public final class SystemOutErrInspection extends BaseInspection {
|
||||
|
||||
@Nullable
|
||||
public ConvertSystemOutToLogCallFix.PopularLogLevel myErrLogLevel = ConvertSystemOutToLogCallFix.PopularLogLevel.ERROR;
|
||||
public ConvertSystemOutToLogCallFix.PopularLogLevel myOutLogLevel = ConvertSystemOutToLogCallFix.PopularLogLevel.INFO;
|
||||
|
||||
|
||||
@Override
|
||||
protected LocalQuickFix buildFix(Object... infos) {
|
||||
public @NotNull OptPane getOptionsPane() {
|
||||
return OptPane.pane(
|
||||
OptPane.dropdown(
|
||||
"myErrLogLevel",
|
||||
InspectionGadgetsBundle.message("use.system.out.err.problem.fix.err.option"),
|
||||
ConvertSystemOutToLogCallFix.PopularLogLevel.class,
|
||||
level -> level.toMethodName()),
|
||||
OptPane.dropdown(
|
||||
"myOutLogLevel",
|
||||
InspectionGadgetsBundle.message("use.system.out.err.problem.fix.out.option"),
|
||||
ConvertSystemOutToLogCallFix.PopularLogLevel.class,
|
||||
level -> level.toMethodName())
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected LocalQuickFix @NotNull [] buildFixes(Object... infos) {
|
||||
List<LocalQuickFix> fixes = new ArrayList<>();
|
||||
|
||||
final PsiElement context = (PsiElement)infos[0];
|
||||
return SuppressForTestsScopeFix.build(this, context);
|
||||
|
||||
SuppressForTestsScopeFix testsScopeFix = SuppressForTestsScopeFix.build(this, context);
|
||||
if(testsScopeFix != null) {
|
||||
fixes.add(testsScopeFix);
|
||||
}
|
||||
|
||||
if (context instanceof PsiReferenceExpression referenceExpression &&
|
||||
referenceExpression.getParent() instanceof PsiReferenceExpression probablyReferenceToCall &&
|
||||
probablyReferenceToCall.getParent() instanceof PsiMethodCallExpression callExpression) {
|
||||
|
||||
String name = referenceExpression.getReferenceName();
|
||||
String methodName = null;
|
||||
if (HardcodedMethodConstants.OUT.equals(name)) {
|
||||
methodName = myOutLogLevel.name().toLowerCase(Locale.ROOT);
|
||||
}
|
||||
else if (HardcodedMethodConstants.ERR.equals(name)) {
|
||||
methodName = myErrLogLevel.name().toLowerCase(Locale.ROOT);
|
||||
}
|
||||
|
||||
if (methodName != null) {
|
||||
ModCommandAction fix = ConvertSystemOutToLogCallFix.createFix(callExpression, methodName);
|
||||
if (fix != null) {
|
||||
LocalQuickFix localQuickFix = ModCommandService.getInstance().wrapToQuickFix(fix);
|
||||
fixes.add(localQuickFix);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return fixes.toArray(LocalQuickFix.EMPTY_ARRAY);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -44,11 +99,28 @@ public final class SystemOutErrInspection extends BaseInspection {
|
||||
return new SystemOutErrVisitor();
|
||||
}
|
||||
|
||||
private static class SystemOutErrVisitor extends BaseInspectionVisitor {
|
||||
private class SystemOutErrVisitor extends BaseInspectionVisitor {
|
||||
|
||||
@Override
|
||||
public void visitMethodCallExpression(@NotNull PsiMethodCallExpression expression) {
|
||||
super.visitMethodCallExpression(expression);
|
||||
PsiReferenceExpression methodExpression = expression.getMethodExpression();
|
||||
if (methodExpression.getQualifierExpression() instanceof PsiReferenceExpression referenceExpression) {
|
||||
inspectReferenceIfItIsSystemOutErr(referenceExpression);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitReferenceExpression(@NotNull PsiReferenceExpression expression) {
|
||||
super.visitReferenceExpression(expression);
|
||||
if (expression.getParent() instanceof PsiReferenceExpression parentReferenceExpression &&
|
||||
parentReferenceExpression.getParent() instanceof PsiMethodCallExpression) {
|
||||
return;
|
||||
}
|
||||
inspectReferenceIfItIsSystemOutErr(expression);
|
||||
}
|
||||
|
||||
private void inspectReferenceIfItIsSystemOutErr(@NotNull PsiReferenceExpression expression) {
|
||||
final String name = expression.getReferenceName();
|
||||
if (!HardcodedMethodConstants.OUT.equals(name) &&
|
||||
!HardcodedMethodConstants.ERR.equals(name)) {
|
||||
@@ -63,9 +135,23 @@ public final class SystemOutErrInspection extends BaseInspection {
|
||||
return;
|
||||
}
|
||||
final String className = containingClass.getQualifiedName();
|
||||
if (!"java.lang.System".equals(className)) {
|
||||
if (!JAVA_LANG_SYSTEM.equals(className)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (expression.getParent() instanceof PsiReferenceExpression probablyReferenceToCall &&
|
||||
probablyReferenceToCall.getParent() instanceof PsiMethodCallExpression callExpression) {
|
||||
|
||||
if (ThrowablePrintedToSystemOutInspection.getExceptionIsPrintedToSystemOutResult(callExpression) != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
boolean informationLevel = InspectionProjectProfileManager.isInformationLevel(getShortName(), expression);
|
||||
if (informationLevel) {
|
||||
registerError(callExpression, expression);
|
||||
}
|
||||
}
|
||||
|
||||
registerError(expression, expression);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,131 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.siyeh.ig.maturity;
|
||||
|
||||
import com.intellij.codeInspection.LocalQuickFix;
|
||||
import com.intellij.codeInspection.options.OptPane;
|
||||
import com.intellij.modcommand.ModCommandAction;
|
||||
import com.intellij.modcommand.ModCommandService;
|
||||
import com.intellij.psi.*;
|
||||
import com.siyeh.HardcodedMethodConstants;
|
||||
import com.siyeh.InspectionGadgetsBundle;
|
||||
import com.siyeh.ig.BaseInspection;
|
||||
import com.siyeh.ig.BaseInspectionVisitor;
|
||||
import com.siyeh.ig.psiutils.TypeUtils;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import static com.intellij.psi.CommonClassNames.JAVA_LANG_SYSTEM;
|
||||
|
||||
/**
|
||||
* @author Bas Leijdekkers
|
||||
*/
|
||||
public final class ThrowablePrintedToSystemOutInspection extends BaseInspection {
|
||||
|
||||
public ConvertSystemOutToLogCallFix.PopularLogLevel myLogLevel = ConvertSystemOutToLogCallFix.PopularLogLevel.ERROR;
|
||||
|
||||
@Override
|
||||
public @NotNull OptPane getOptionsPane() {
|
||||
return OptPane.pane(
|
||||
OptPane.dropdown(
|
||||
"myLogLevel",
|
||||
InspectionGadgetsBundle.message("throwable.printed.to.system.out.problem.fix.level.option"),
|
||||
ConvertSystemOutToLogCallFix.PopularLogLevel.class,
|
||||
level -> level.toMethodName())
|
||||
);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected String buildErrorString(Object... infos) {
|
||||
final String fieldName = (String)infos[0];
|
||||
final String methodName = (String)infos[1];
|
||||
return InspectionGadgetsBundle.message("throwable.printed.to.system.out.problem.descriptor", fieldName, methodName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabledByDefault() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaseInspectionVisitor buildVisitor() {
|
||||
return new ThrowablePrintedToSystemOutVisitor();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @Nullable LocalQuickFix buildFix(Object... infos) {
|
||||
if (infos.length != 3) {
|
||||
return null;
|
||||
}
|
||||
if (!(infos[2] instanceof PsiExpression expression &&
|
||||
expression.getParent() instanceof PsiExpressionList expressionList &&
|
||||
expressionList.getParent() instanceof PsiMethodCallExpression call)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
ModCommandAction fix = ConvertSystemOutToLogCallFix.createFix(call, myLogLevel.name().toLowerCase(Locale.ROOT));
|
||||
if (fix != null) {
|
||||
return ModCommandService.getInstance().wrapToQuickFix(fix);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static class ThrowablePrintedToSystemOutVisitor extends BaseInspectionVisitor {
|
||||
|
||||
@Override
|
||||
public void visitMethodCallExpression(@NotNull PsiMethodCallExpression expression) {
|
||||
super.visitMethodCallExpression(expression);
|
||||
ExceptionIsPrintedToSystemOutResult result = getExceptionIsPrintedToSystemOutResult(expression);
|
||||
if (result == null) return;
|
||||
registerError(result.argument(), result.fieldName(), result.methodName(), result.argument());
|
||||
}
|
||||
}
|
||||
|
||||
public static @Nullable ExceptionIsPrintedToSystemOutResult getExceptionIsPrintedToSystemOutResult(@NotNull PsiMethodCallExpression expression) {
|
||||
final PsiReferenceExpression methodExpression = expression.getMethodExpression();
|
||||
@NonNls final String methodName = methodExpression.getReferenceName();
|
||||
if (!"print".equals(methodName) && !"println".equals(methodName)) {
|
||||
return null;
|
||||
}
|
||||
final PsiExpressionList argumentList = expression.getArgumentList();
|
||||
final PsiExpression[] arguments = argumentList.getExpressions();
|
||||
if (arguments.length != 1) {
|
||||
return null;
|
||||
}
|
||||
final PsiExpression argument = arguments[0];
|
||||
|
||||
if (!TypeUtils.expressionHasTypeOrSubtype(argument, CommonClassNames.JAVA_LANG_THROWABLE)) {
|
||||
return null;
|
||||
}
|
||||
final PsiMethod method = expression.resolveMethod();
|
||||
if (method == null) {
|
||||
return null;
|
||||
}
|
||||
final PsiExpression qualifier = methodExpression.getQualifierExpression();
|
||||
if (!(qualifier instanceof PsiReferenceExpression qualifierReference)) {
|
||||
return null;
|
||||
}
|
||||
final PsiElement target = qualifierReference.resolve();
|
||||
if (!(target instanceof PsiField field)) {
|
||||
return null;
|
||||
}
|
||||
@NonNls final String fieldName = field.getName();
|
||||
if (!HardcodedMethodConstants.OUT.equals(fieldName) && !HardcodedMethodConstants.ERR.equals(fieldName)) {
|
||||
return null;
|
||||
}
|
||||
final PsiClass aClass = field.getContainingClass();
|
||||
if (aClass == null || !JAVA_LANG_SYSTEM.equals(aClass.getQualifiedName())) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new ExceptionIsPrintedToSystemOutResult(methodName, argument, fieldName);
|
||||
}
|
||||
|
||||
public record ExceptionIsPrintedToSystemOutResult(@NotNull String methodName,
|
||||
@NotNull PsiExpression argument,
|
||||
@NotNull String fieldName) {
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,28 @@
|
||||
Reports usages of <code>System.out</code> or <code>System.err</code>.
|
||||
<p>Such statements are often used for temporary debugging and should be either removed from the production code, or replaced by a more robust
|
||||
logging facility.</p>
|
||||
|
||||
<p>The provided quick-fix supports <b>SLF4J</b> and <b>Log4j 2</b>.
|
||||
It replaces <code>System.out</code> and <code>System.err</code> calls with log calls</p>
|
||||
|
||||
<p><b>Example:</b></p>
|
||||
<pre><code>
|
||||
public static void test(Object o) {
|
||||
System.out.println("Test: "+ o);
|
||||
}
|
||||
</code></pre>
|
||||
<p>After the quick-fix is applied:</p>
|
||||
<pre><code>
|
||||
private static final Logger log = LoggerFactory.getLogger(Test.class);
|
||||
|
||||
public static void test(Object o) {
|
||||
log.info("Test: {}", o);;
|
||||
}
|
||||
</code></pre>
|
||||
<!-- tooltip end -->
|
||||
<p>
|
||||
Use the <b>Log method for 'System.err'</b> option to specify a method which it is used to log a message for 'System.err' calls.
|
||||
Use the <b>Log method for 'System.out'</b> option to specify a method which it is used to log a message for 'System.out' calls.
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
@@ -5,8 +5,9 @@ Reports calls to <code>System.out.println()</code> with an exception as an argum
|
||||
It is recommended that you use logger instead.</p>
|
||||
<p>Calls to <code>System.out.print()</code>, <code>System.err.println()</code>, and <code>System.err.print()</code> with an exception argument are also
|
||||
reported. It is better to use a logger to log exceptions instead.</p>
|
||||
<!-- tooltip end -->
|
||||
<p>For example, instead of:</p>
|
||||
<p>The provided quick-fix supports <b>SLF4J</b> and <b>Log4j 2</b>.
|
||||
It replaces <code>System.out.println()</code> call with log calls</p>
|
||||
<p><b>Example:</b></p>
|
||||
<pre><code>
|
||||
try {
|
||||
foo();
|
||||
@@ -14,14 +15,17 @@ reported. It is better to use a logger to log exceptions instead.</p>
|
||||
System.out.println(e);
|
||||
}
|
||||
</code></pre>
|
||||
<p>use the following code:</p>
|
||||
<p>After the quick-fix is applied:</p>
|
||||
<pre><code>
|
||||
try {
|
||||
foo();
|
||||
} catch (Exception e) {
|
||||
logger.warn(e); // logger call may be different
|
||||
log.error("e: ", e);
|
||||
}
|
||||
</code></pre>
|
||||
|
||||
<!-- tooltip end -->
|
||||
<p>
|
||||
Use the <b>Log method for fix</b> option to specify a method which it is used to log a message.
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
@@ -36,6 +36,190 @@ public class SystemOutErrInspectionTest extends LightJavaInspectionTestCase {
|
||||
"}");
|
||||
}
|
||||
|
||||
private void addSlf4j() {
|
||||
addEnvironmentClass("""
|
||||
package org.slf4j;
|
||||
@SuppressWarnings("ALL") public class LoggerFactory {
|
||||
public static Logger getLogger(Class clazz) { return null; }
|
||||
}
|
||||
public interface Logger {
|
||||
void error(String format, Object... arguments);
|
||||
void info(String format, Object... arguments);
|
||||
}
|
||||
""");
|
||||
}
|
||||
|
||||
private void addMessageFormat() {
|
||||
addEnvironmentClass("""
|
||||
package java.text;
|
||||
public final class MessageFormat{
|
||||
public static String format(String format, Object... arguments) {return format;}
|
||||
}
|
||||
""");
|
||||
}
|
||||
|
||||
public void testSimpleLogFix() {
|
||||
addSlf4j();
|
||||
|
||||
doTest(
|
||||
"""
|
||||
class Test{
|
||||
void foo(Object o) {
|
||||
/*Uses of 'System.out' should probably be replaced with more robust logging*/System.out<caret>/**/.println("Something " + o);
|
||||
}
|
||||
}
|
||||
""");
|
||||
|
||||
checkQuickFix("Convert 'System.out' call to call of 'Slf4j'",
|
||||
"""
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
class Test{
|
||||
private static final Logger log = LoggerFactory.getLogger(Test.class);
|
||||
|
||||
void foo(Object o) {
|
||||
log.info("Something {}", o);
|
||||
}
|
||||
}
|
||||
""");
|
||||
}
|
||||
|
||||
public void testSimpleExistedLogFix() {
|
||||
addSlf4j();
|
||||
|
||||
doTest(
|
||||
"""
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
class Test{
|
||||
private static final Logger log = LoggerFactory.getLogger(Test.class);
|
||||
|
||||
void foo(Exception o) {
|
||||
/*Uses of 'System.out' should probably be replaced with more robust logging*/System.out<caret>/**/.println("Something " + o);
|
||||
}
|
||||
}
|
||||
""");
|
||||
|
||||
checkQuickFix("Convert 'System.out' call to call of 'Slf4j'",
|
||||
"""
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
class Test{
|
||||
private static final Logger log = LoggerFactory.getLogger(Test.class);
|
||||
|
||||
void foo(Exception o) {
|
||||
log.info("Something {}", String.valueOf(o));
|
||||
}
|
||||
}
|
||||
""");
|
||||
}
|
||||
|
||||
public void testSimpleExistedLogWithRecordFix() {
|
||||
addSlf4j();
|
||||
|
||||
doTest(
|
||||
"""
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
class Test{
|
||||
private static final Logger log = LoggerFactory.getLogger(Test.class);
|
||||
|
||||
record R(){}
|
||||
|
||||
void foo(R r) {
|
||||
/*Uses of 'System.out' should probably be replaced with more robust logging*/System.out<caret>/**/.println(r);
|
||||
}
|
||||
}
|
||||
""");
|
||||
|
||||
checkQuickFix("Convert 'System.out' call to call of 'Slf4j'",
|
||||
"""
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
class Test{
|
||||
private static final Logger log = LoggerFactory.getLogger(Test.class);
|
||||
|
||||
record R(){}
|
||||
|
||||
void foo(R r) {
|
||||
log.info(String.valueOf(r));
|
||||
}
|
||||
}
|
||||
""");
|
||||
}
|
||||
|
||||
public void testSimpleExistedLogWithStringFormatFix() {
|
||||
addSlf4j();
|
||||
|
||||
doTest(
|
||||
"""
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
class Test{
|
||||
private static final Logger log = LoggerFactory.getLogger(Test.class);
|
||||
|
||||
void foo(Object r) {
|
||||
/*Uses of 'System.out' should probably be replaced with more robust logging*/System.out<caret>/**/.println(String.format("test %s test", r));
|
||||
}
|
||||
}
|
||||
""");
|
||||
|
||||
checkQuickFix("Convert 'System.out' call to call of 'Slf4j'",
|
||||
"""
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
class Test{
|
||||
private static final Logger log = LoggerFactory.getLogger(Test.class);
|
||||
|
||||
void foo(Object r) {
|
||||
log.info("test {} test", r);
|
||||
}
|
||||
}
|
||||
""");
|
||||
}
|
||||
|
||||
public void testSimpleExistedLogWithMessageFormatFix() {
|
||||
addSlf4j();
|
||||
addMessageFormat();
|
||||
|
||||
doTest(
|
||||
"""
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import java.text.MessageFormat;
|
||||
|
||||
class Test{
|
||||
private static final Logger log = LoggerFactory.getLogger(Test.class);
|
||||
|
||||
void foo(Object r) {
|
||||
/*Uses of 'System.out' should probably be replaced with more robust logging*/System.out<caret>/**/.println(MessageFormat.format("test {0} test", r));
|
||||
}
|
||||
}
|
||||
""");
|
||||
|
||||
checkQuickFix("Convert 'System.out' call to call of 'Slf4j'",
|
||||
"""
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import java.text.MessageFormat;
|
||||
|
||||
class Test{
|
||||
private static final Logger log = LoggerFactory.getLogger(Test.class);
|
||||
|
||||
void foo(Object r) {
|
||||
log.info("test {} test", r);
|
||||
}
|
||||
}
|
||||
""");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected InspectionProfileEntry getInspection() {
|
||||
return new SystemOutErrInspection();
|
||||
|
||||
+78
-15
@@ -1,18 +1,4 @@
|
||||
/*
|
||||
* Copyright 2000-2013 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.siyeh.ig.maturity;
|
||||
|
||||
import com.intellij.codeInspection.InspectionProfileEntry;
|
||||
@@ -36,6 +22,83 @@ public class ThrowablePrintedToSystemOutInspectionTest extends LightJavaInspecti
|
||||
doStatementTest("System.out.println(/*'Throwable' argument 'new RuntimeException()' to 'System.out.println()' call*/new RuntimeException()/**/);");
|
||||
}
|
||||
|
||||
@SuppressWarnings("ThrowableNotThrown")
|
||||
public void testSimpleLogFix() {
|
||||
addSlf4j();
|
||||
|
||||
doTest(
|
||||
"""
|
||||
class Test{
|
||||
void foo() {
|
||||
final RuntimeException x = new RuntimeException();
|
||||
System.out.println(/*'Throwable' argument 'x' to 'System.out.println()' call*/x<caret>/**/);
|
||||
}
|
||||
}
|
||||
""");
|
||||
|
||||
checkQuickFix("Convert 'System.out' call to call of 'Slf4j'",
|
||||
"""
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
class Test{
|
||||
private static final Logger log = LoggerFactory.getLogger(Test.class);
|
||||
|
||||
void foo() {
|
||||
final RuntimeException x = new RuntimeException();
|
||||
log.error("e: ", x);
|
||||
}
|
||||
}
|
||||
""");
|
||||
}
|
||||
|
||||
private void addSlf4j() {
|
||||
addEnvironmentClass("""
|
||||
package org.slf4j;
|
||||
@SuppressWarnings("ALL") public class LoggerFactory {
|
||||
public static Logger getLogger(Class clazz) { return null; }
|
||||
}
|
||||
public interface Logger {
|
||||
void error(String format, Object... arguments);
|
||||
}
|
||||
""");
|
||||
}
|
||||
|
||||
@SuppressWarnings("ThrowableNotThrown")
|
||||
public void testSimpleExistedLogFix() {
|
||||
addSlf4j();
|
||||
|
||||
doTest(
|
||||
"""
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
class Test{
|
||||
private static final Logger logger = LoggerFactory.getLogger(Test.class);
|
||||
|
||||
void foo() {
|
||||
final RuntimeException x = new RuntimeException();
|
||||
System.out.println(/*'Throwable' argument 'x' to 'System.out.println()' call*/x<caret>/**/);
|
||||
}
|
||||
}
|
||||
""");
|
||||
|
||||
checkQuickFix("Convert 'System.out' call to call of 'Slf4j'",
|
||||
"""
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
class Test{
|
||||
private static final Logger logger = LoggerFactory.getLogger(Test.class);
|
||||
|
||||
void foo() {
|
||||
final RuntimeException x = new RuntimeException();
|
||||
logger.error("e: ", x);
|
||||
}
|
||||
}
|
||||
""");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected InspectionProfileEntry getInspection() {
|
||||
return new ThrowablePrintedToSystemOutInspection();
|
||||
|
||||
Reference in New Issue
Block a user