/* * Copyright 2003-2014 Dave Griffith, Bas Leijdekkers * * 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.internationalization; import com.intellij.codeInsight.AnnotationUtil; import com.intellij.codeInsight.intention.AddAnnotationPsiFix; import com.intellij.codeInspection.ui.MultipleCheckboxOptionsPanel; import com.intellij.psi.*; import com.intellij.psi.tree.IElementType; import com.intellij.psi.util.PsiTreeUtil; import com.siyeh.InspectionGadgetsBundle; import com.siyeh.ig.BaseInspection; import com.siyeh.ig.BaseInspectionVisitor; import com.siyeh.ig.DelegatingFix; import com.siyeh.ig.InspectionGadgetsFix; import com.siyeh.ig.psiutils.*; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.util.ArrayList; import java.util.Collection; public class StringConcatenationInspectionBase extends BaseInspection { @SuppressWarnings({"PublicField"}) public boolean ignoreAsserts = false; @SuppressWarnings({"PublicField"}) public boolean ignoreSystemOuts = false; @SuppressWarnings({"PublicField"}) public boolean ignoreSystemErrs = false; @SuppressWarnings({"PublicField"}) public boolean ignoreThrowableArguments = false; @SuppressWarnings({"PublicField"}) public boolean ignoreConstantInitializers = false; @SuppressWarnings({"PublicField", "UnusedDeclaration"}) public boolean ignoreInTestCode = false; // keep for compatibility @SuppressWarnings("PublicField") public boolean ignoreInToString = false; @Override @NotNull public String getDisplayName() { return InspectionGadgetsBundle.message("string.concatenation.display.name"); } @Override @NotNull public String buildErrorString(Object... infos) { return InspectionGadgetsBundle.message("string.concatenation.problem.descriptor"); } @Override @NotNull protected InspectionGadgetsFix[] buildFixes(Object... infos) { final PsiPolyadicExpression polyadicExpression = (PsiPolyadicExpression)infos[0]; final Collection<InspectionGadgetsFix> result = new ArrayList<>(); final PsiElement parent = ParenthesesUtils.getParentSkipParentheses(polyadicExpression); if (parent instanceof PsiVariable) { final PsiVariable variable = (PsiVariable)parent; final InspectionGadgetsFix fix = createAddAnnotationFix(variable); result.add(fix); } else if (parent instanceof PsiAssignmentExpression) { final PsiAssignmentExpression assignmentExpression = (PsiAssignmentExpression)parent; final PsiExpression lhs = assignmentExpression.getLExpression(); if (lhs instanceof PsiReferenceExpression) { final PsiReferenceExpression referenceExpression = (PsiReferenceExpression)lhs; final PsiElement target = referenceExpression.resolve(); if (target instanceof PsiModifierListOwner) { final PsiModifierListOwner modifierListOwner = (PsiModifierListOwner)target; final InspectionGadgetsFix fix = createAddAnnotationFix(modifierListOwner); result.add(fix); } } } else if (parent instanceof PsiExpressionList) { final PsiElement grandParent = parent.getParent(); if (grandParent instanceof PsiMethodCallExpression) { final PsiMethodCallExpression methodCallExpression = (PsiMethodCallExpression)grandParent; final PsiReferenceExpression methodExpression = methodCallExpression.getMethodExpression(); final PsiExpression qualifierExpression = methodExpression.getQualifierExpression(); if (qualifierExpression instanceof PsiReferenceExpression) { final PsiReferenceExpression referenceExpression = (PsiReferenceExpression)qualifierExpression; final PsiElement target = referenceExpression.resolve(); if (target instanceof PsiModifierListOwner) { final PsiModifierListOwner modifierListOwner = (PsiModifierListOwner)target; result.add(createAddAnnotationFix(modifierListOwner)); } } } } final PsiExpression[] operands = polyadicExpression.getOperands(); for (PsiExpression operand : operands) { final PsiModifierListOwner element1 = getAnnotatableElement(operand); if (element1 != null) { final InspectionGadgetsFix fix = createAddAnnotationFix(element1); result.add(fix); } } final PsiElement expressionParent = PsiTreeUtil.getParentOfType(polyadicExpression, PsiReturnStatement.class, PsiExpressionList.class); if (!(expressionParent instanceof PsiExpressionList) && expressionParent != null) { final PsiMethod method = PsiTreeUtil.getParentOfType(expressionParent, PsiMethod.class); if (method != null) { final InspectionGadgetsFix fix = createAddAnnotationFix(method); result.add(fix); } } return result.toArray(new InspectionGadgetsFix[result.size()]); } private static DelegatingFix createAddAnnotationFix(PsiModifierListOwner variable) { return new DelegatingFix(new AddAnnotationPsiFix(AnnotationUtil.NON_NLS, variable,PsiNameValuePair.EMPTY_ARRAY)); } @Nullable public static PsiModifierListOwner getAnnotatableElement(PsiExpression expression) { if (!(expression instanceof PsiReferenceExpression)) { return null; } final PsiReferenceExpression referenceExpression = (PsiReferenceExpression)expression; final PsiElement element = referenceExpression.resolve(); if (!(element instanceof PsiModifierListOwner)) { return null; } return (PsiModifierListOwner)element; } @Override @Nullable public JComponent createOptionsPanel() { final MultipleCheckboxOptionsPanel optionsPanel = new MultipleCheckboxOptionsPanel(this); optionsPanel.addCheckbox(InspectionGadgetsBundle.message("inspection.option.ignore.assert"), "ignoreAsserts"); optionsPanel.addCheckbox(InspectionGadgetsBundle.message("inspection.option.ignore.system.out"), "ignoreSystemOuts"); optionsPanel.addCheckbox(InspectionGadgetsBundle.message("inspection.option.ignore.system.err"), "ignoreSystemErrs"); optionsPanel.addCheckbox(InspectionGadgetsBundle.message("inspection.option.ignore.exceptions"), "ignoreThrowableArguments"); optionsPanel.addCheckbox(InspectionGadgetsBundle.message("inspection.option.ignore.constant.initializers"), "ignoreConstantInitializers"); optionsPanel.addCheckbox(InspectionGadgetsBundle.message("inspection.option.ignore.in.tostring"), "ignoreInToString"); return optionsPanel; } @Override public BaseInspectionVisitor buildVisitor() { return new StringConcatenationVisitor(); } private class StringConcatenationVisitor extends BaseInspectionVisitor { @Override public void visitPolyadicExpression(@NotNull PsiPolyadicExpression expression) { super.visitPolyadicExpression(expression); final IElementType tokenType = expression.getOperationTokenType(); if (!JavaTokenType.PLUS.equals(tokenType)) { return; } final PsiType type = expression.getType(); if (!TypeUtils.isJavaLangString(type)) { return; } final PsiExpression[] operands = expression.getOperands(); for (PsiExpression operand : operands) { if (NonNlsUtils.isNonNlsAnnotated(operand)) { return; } } if (AnnotationUtil.isInsideAnnotation(expression)) { return; } if (ignoreAsserts) { final PsiAssertStatement assertStatement = PsiTreeUtil.getParentOfType(expression, PsiAssertStatement.class, true, PsiCodeBlock.class, PsiClass.class); if (assertStatement != null) { return; } } if (ignoreSystemErrs || ignoreSystemOuts) { final PsiMethodCallExpression methodCallExpression = PsiTreeUtil.getParentOfType(expression, PsiMethodCallExpression.class, true, PsiCodeBlock.class, PsiClass.class); if (methodCallExpression != null) { final PsiReferenceExpression methodExpression = methodCallExpression.getMethodExpression(); @NonNls final String canonicalText = methodExpression.getCanonicalText(); if (ignoreSystemOuts && "System.out.println".equals(canonicalText) || "System.out.print".equals(canonicalText)) { return; } if (ignoreSystemErrs && "System.err.println".equals(canonicalText) || "System.err.print".equals(canonicalText)) { return; } } } if (ignoreThrowableArguments) { if (ExceptionUtils.isExceptionArgument(expression)) return; } if (ignoreConstantInitializers) { PsiElement parent = expression.getParent(); while (parent instanceof PsiBinaryExpression) { parent = parent.getParent(); } if (parent instanceof PsiField) { final PsiField field = (PsiField)parent; if (field.hasModifierProperty(PsiModifier.STATIC) && field.hasModifierProperty(PsiModifier.FINAL)) { return; } final PsiClass containingClass = field.getContainingClass(); if (containingClass != null && containingClass.isInterface()) { return; } } } if (ignoreInToString) { final PsiMethod method = PsiTreeUtil.getParentOfType(expression, PsiMethod.class, true, PsiClass.class, PsiLambdaExpression.class); if (MethodUtils.isToString(method)) { return; } } if (NonNlsUtils.isNonNlsAnnotatedUse(expression)) { return; } for (int i = 1; i < operands.length; i++) { final PsiExpression operand = operands[i]; if (!ExpressionUtils.isStringConcatenationOperand(operand)) { continue; } final PsiJavaToken token = expression.getTokenBeforeOperand(operand); if (token == null) { continue; } registerError(token, expression); } } } }