diff options
Diffstat (limited to 'plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/logging/StringConcatenationArgumentToLogCallInspection.java')
-rw-r--r-- | plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/logging/StringConcatenationArgumentToLogCallInspection.java | 303 |
1 files changed, 303 insertions, 0 deletions
diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/logging/StringConcatenationArgumentToLogCallInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/logging/StringConcatenationArgumentToLogCallInspection.java new file mode 100644 index 000000000000..40925af80232 --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/logging/StringConcatenationArgumentToLogCallInspection.java @@ -0,0 +1,303 @@ +/* + * 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. + */ +package com.siyeh.ig.logging; + +import com.intellij.codeInspection.ProblemDescriptor; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.psi.*; +import com.intellij.util.IncorrectOperationException; +import com.siyeh.InspectionGadgetsBundle; +import com.siyeh.ig.BaseInspection; +import com.siyeh.ig.BaseInspectionVisitor; +import com.siyeh.ig.InspectionGadgetsFix; +import com.siyeh.ig.psiutils.ExpressionUtils; +import com.siyeh.ig.psiutils.ParenthesesUtils; +import com.siyeh.ig.psiutils.TypeUtils; +import gnu.trove.THashSet; +import org.jetbrains.annotations.Nls; +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; + +/** + * @author Bas Leijdekkers + */ +public class StringConcatenationArgumentToLogCallInspection extends BaseInspection { + + @NonNls + private static final Set<String> logNames = new THashSet<String>(); + static { + logNames.add("trace"); + logNames.add("debug"); + logNames.add("info"); + logNames.add("warn"); + logNames.add("error"); + } + + @Nls + @NotNull + @Override + public String getDisplayName() { + return InspectionGadgetsBundle.message("string.concatenation.argument.to.log.call.display.name"); + } + + @NotNull + @Override + protected String buildErrorString(Object... infos) { + return InspectionGadgetsBundle.message("string.concatenation.argument.to.log.call.problem.descriptor"); + } + + @Nullable + @Override + protected InspectionGadgetsFix buildFix(Object... infos) { + if (!StringConcatenationArgumentToLogCallFix.isAvailable((PsiExpression)infos[0])) { + return null; + } + return new StringConcatenationArgumentToLogCallFix(); + } + + private static class StringConcatenationArgumentToLogCallFix extends InspectionGadgetsFix { + + public StringConcatenationArgumentToLogCallFix() {} + + @NotNull + @Override + public String getName() { + return InspectionGadgetsBundle.message("string.concatenation.argument.to.log.call.quickfix"); + } + @Override + @NotNull + public String getFamilyName() { + return getName(); + } + + @Override + protected void doFix(Project project, ProblemDescriptor descriptor) throws IncorrectOperationException { + final PsiElement element = descriptor.getPsiElement(); + final PsiElement grandParent = element.getParent().getParent(); + if (!(grandParent instanceof PsiMethodCallExpression)) { + return; + } + final PsiMethodCallExpression methodCallExpression = (PsiMethodCallExpression)grandParent; + final PsiExpressionList argumentList = methodCallExpression.getArgumentList(); + final PsiExpression[] arguments = argumentList.getExpressions(); + if (arguments.length == 0) { + return; + } + @NonNls final StringBuilder newMethodCall = new StringBuilder(methodCallExpression.getMethodExpression().getText()); + newMethodCall.append('('); + PsiExpression argument = arguments[0]; + int usedArguments; + if (!(argument instanceof PsiPolyadicExpression)) { + if (!TypeUtils.expressionHasTypeOrSubtype(argument, "org.slf4j.Marker") || arguments.length < 2) { + return; + } + newMethodCall.append(argument.getText()).append(','); + argument = arguments[1]; + usedArguments = 2; + if (!(argument instanceof PsiPolyadicExpression)) { + return; + } + } + else { + usedArguments = 1; + } + final PsiPolyadicExpression polyadicExpression = (PsiPolyadicExpression)argument; + final PsiMethod method = methodCallExpression.resolveMethod(); + if (method == null) { + return; + } + final String methodName = method.getName(); + final PsiClass containingClass = method.getContainingClass(); + if (containingClass == null) { + return; + } + final PsiMethod[] methods = containingClass.findMethodsByName(methodName, false); + boolean varArgs = false; + for (PsiMethod otherMethod : methods) { + if (otherMethod.isVarArgs()) { + varArgs = true; + break; + } + } + final List<PsiExpression> newArguments = new ArrayList(); + final PsiExpression[] operands = polyadicExpression.getOperands(); + boolean addPlus = false; + boolean inStringLiteral = false; + for (PsiExpression operand : operands) { + if (ExpressionUtils.isEvaluatedAtCompileTime(operand)) { + if (ExpressionUtils.hasStringType(operand) && operand instanceof PsiLiteralExpression) { + final String text = operand.getText(); + final int count = StringUtil.getOccurrenceCount(text, "{}"); + for (int i = 0; i < count && usedArguments + i < arguments.length; i++) { + newArguments.add(ParenthesesUtils.stripParentheses((PsiExpression)arguments[i + usedArguments].copy())); + } + usedArguments += count; + if (!inStringLiteral) { + if (addPlus) { + newMethodCall.append('+'); + } + newMethodCall.append('"'); + inStringLiteral = true; + } + newMethodCall.append(text.substring(1, text.length() - 1)); + } + else { + if (inStringLiteral) { + newMethodCall.append('"'); + inStringLiteral = false; + } + if (addPlus) { + newMethodCall.append('+'); + } + newMethodCall.append(operand.getText()); + } + } + else { + newArguments.add(ParenthesesUtils.stripParentheses((PsiExpression)operand.copy())); + if (!inStringLiteral) { + if (addPlus) { + newMethodCall.append('+'); + } + newMethodCall.append('"'); + inStringLiteral = true; + } + newMethodCall.append("{}"); + } + addPlus = true; + } + while (usedArguments < arguments.length) { + newArguments.add(arguments[usedArguments++]); + } + if (inStringLiteral) { + newMethodCall.append('"'); + } + if (!varArgs && newArguments.size() > 2) { + newMethodCall.append(", new Object[]{"); + boolean comma = false; + for (PsiExpression newArgument : newArguments) { + if (comma) { + newMethodCall.append(','); + } + else { + comma =true; + } + if (newArgument != null) { + newMethodCall.append(newArgument.getText()); + } + } + newMethodCall.append('}'); + } + else { + for (PsiExpression newArgument : newArguments) { + newMethodCall.append(','); + if (newArgument != null) { + newMethodCall.append(newArgument.getText()); + } + } + } + newMethodCall.append(')'); + replaceExpression(methodCallExpression, newMethodCall.toString()); + } + + public static boolean isAvailable(PsiExpression expression) { + if (!(expression instanceof PsiPolyadicExpression)) { + return false; + } + final PsiPolyadicExpression polyadicExpression = (PsiPolyadicExpression)expression; + final PsiExpression[] operands = polyadicExpression.getOperands(); + for (PsiExpression operand : operands) { + if (!ExpressionUtils.isEvaluatedAtCompileTime(operand)) { + return true; + } + } + return false; + } + } + + @Override + public BaseInspectionVisitor buildVisitor() { + return new StringConcatenationArgumentToLogCallVisitor(); + } + + private static class StringConcatenationArgumentToLogCallVisitor extends BaseInspectionVisitor { + + @Override + public void visitMethodCallExpression(PsiMethodCallExpression expression) { + super.visitMethodCallExpression(expression); + final PsiReferenceExpression methodExpression = expression.getMethodExpression(); + final String referenceName = methodExpression.getReferenceName(); + if (!logNames.contains(referenceName)) { + return; + } + final PsiMethod method = expression.resolveMethod(); + if (method == null) { + return; + } + final PsiClass containingClass = method.getContainingClass(); + if (containingClass == null || !"org.slf4j.Logger".equals(containingClass.getQualifiedName())) { + return; + } + final PsiExpressionList argumentList = expression.getArgumentList(); + final PsiExpression[] arguments = argumentList.getExpressions(); + if (arguments.length == 0) { + 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, argument); + } + + private static boolean containsNonConstantConcatenation(@Nullable PsiExpression expression) { + if (expression instanceof PsiParenthesizedExpression) { + final PsiParenthesizedExpression parenthesizedExpression = (PsiParenthesizedExpression)expression; + return containsNonConstantConcatenation(parenthesizedExpression.getExpression()); + } + else if (expression instanceof PsiPolyadicExpression) { + final PsiPolyadicExpression polyadicExpression = (PsiPolyadicExpression)expression; + 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; + } + } +} |