| /* |
| * Copyright 2001-2013 the original author or authors. |
| * |
| * 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 org.jetbrains.generate.tostring.inspection; |
| |
| import com.intellij.codeInspection.ProblemHighlightType; |
| import com.intellij.codeInspection.ProblemsHolder; |
| import com.intellij.psi.*; |
| import com.intellij.psi.util.PropertyUtil; |
| import gnu.trove.THashSet; |
| import org.jetbrains.annotations.NonNls; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.generate.tostring.GenerateToStringContext; |
| import org.jetbrains.generate.tostring.GenerateToStringUtils; |
| |
| import java.util.Collections; |
| |
| /** |
| * Inspection to check if the current class toString() method is out of |
| * sync with the fields defined. It uses filter information from the settings |
| * to exclude certain fields (eg. constants etc.). Will only warn if the |
| * class has a toString() method. |
| */ |
| public class FieldNotUsedInToStringInspection extends AbstractToStringInspection { |
| |
| @Override |
| @NotNull |
| public String getDisplayName() { |
| return "Field not used in 'toString()' method"; |
| } |
| |
| @Override |
| @NotNull |
| public String getShortName() { |
| return "FieldNotUsedInToString"; |
| } |
| |
| @Override |
| public boolean runForWholeFile() { |
| return true; |
| } |
| |
| @NotNull |
| @Override |
| public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, boolean isOnTheFly) { |
| return new FieldNotUsedInToStringVisitor(holder); |
| } |
| |
| private static class FieldNotUsedInToStringVisitor extends JavaElementVisitor{ |
| |
| private final ProblemsHolder myHolder; |
| |
| public FieldNotUsedInToStringVisitor(ProblemsHolder holder) { |
| myHolder = holder; |
| } |
| |
| @Override |
| public void visitField(PsiField field) { |
| super.visitField(field); |
| } |
| |
| @Override |
| public void visitMethod(PsiMethod method) { |
| super.visitMethod(method); |
| @NonNls final String methodName = method.getName(); |
| if (!"toString".equals(methodName)) { |
| return; |
| } |
| final PsiParameterList parameterList = method.getParameterList(); |
| if (parameterList.getParametersCount() != 0) { |
| return; |
| } |
| final PsiType returnType = method.getReturnType(); |
| final PsiClassType javaLangString = PsiType.getJavaLangString(method.getManager(), method.getResolveScope()); |
| if (!javaLangString.equals(returnType)) { |
| return; |
| } |
| final PsiClass aClass = method.getContainingClass(); |
| if (aClass == null) { |
| return; |
| } |
| final PsiField[] fields = |
| GenerateToStringUtils.filterAvailableFields(aClass, GenerateToStringContext.getConfig().getFilterPattern()); |
| final PsiMethod[] methods; |
| if (GenerateToStringContext.getConfig().isEnableMethods()) { |
| methods = GenerateToStringUtils.filterAvailableMethods(aClass, GenerateToStringContext.getConfig().getFilterPattern()); |
| } |
| else { |
| methods = PsiMethod.EMPTY_ARRAY; |
| } |
| final FieldUsedVisitor visitor = new FieldUsedVisitor(fields, methods); |
| method.accept(visitor); |
| for (PsiField field : visitor.getUnusedFields()) { |
| final String fieldName = field.getName(); |
| myHolder.registerProblem(field.getNameIdentifier(), "Field '" + fieldName + "' is not used in 'toString()' method", |
| ProblemHighlightType.GENERIC_ERROR_OR_WARNING, GenerateToStringQuickFix.getInstance()); |
| } |
| for (PsiMethod unusedMethod : visitor.getUnusedMethods()) { |
| final PsiIdentifier identifier = unusedMethod.getNameIdentifier(); |
| final PsiElement target = identifier == null ? unusedMethod : identifier; |
| myHolder.registerProblem(target, "Method '" + unusedMethod.getName() + "' is not used in 'toString()' method", |
| ProblemHighlightType.GENERIC_ERROR_OR_WARNING, GenerateToStringQuickFix.getInstance()); |
| } |
| } |
| } |
| |
| private static class FieldUsedVisitor extends JavaRecursiveElementVisitor { |
| |
| private final THashSet<PsiField> myUnusedFields = new THashSet<PsiField>(); |
| private final THashSet<PsiMethod> myUnusedMethods = new THashSet<PsiMethod>(); |
| |
| public FieldUsedVisitor(PsiField[] fields, PsiMethod[] methods) { |
| Collections.addAll(myUnusedFields, fields); |
| Collections.addAll(myUnusedMethods, methods); |
| } |
| |
| @Override |
| public void visitReferenceExpression(PsiReferenceExpression expression) { |
| if (myUnusedFields.isEmpty() && myUnusedMethods.isEmpty()) { |
| return; |
| } |
| super.visitReferenceExpression(expression); |
| final PsiElement target = expression.resolve(); |
| if (target instanceof PsiField) { |
| final PsiField field = (PsiField)target; |
| myUnusedFields.remove(field); |
| } |
| else if (target instanceof PsiMethod) { |
| final PsiMethod method = (PsiMethod)target; |
| if (usesReflection(method)) { |
| myUnusedFields.clear(); |
| myUnusedMethods.clear(); |
| } |
| else { |
| myUnusedMethods.remove(method); |
| final PsiField field = PropertyUtil.findPropertyFieldByMember(method); |
| myUnusedFields.remove(field); |
| } |
| } |
| } |
| |
| private static boolean usesReflection(PsiMethod method) { |
| @NonNls final String name = method.getName(); |
| final PsiClass containingClass = method.getContainingClass(); |
| if (containingClass == null) { |
| return false; |
| } |
| @NonNls final String qualifiedName = containingClass.getQualifiedName(); |
| if ("getDeclaredFields".equals(name)) { |
| return "java.lang.Class".equals(qualifiedName); |
| } |
| if ("toString".equals(name)) { |
| return "org.apache.commons.lang.builder.ReflectionToStringBuilder".equals(qualifiedName) || |
| "java.util.Objects".equals(qualifiedName); |
| } |
| return false; |
| } |
| |
| public THashSet<PsiField> getUnusedFields() { |
| return myUnusedFields; |
| } |
| |
| public THashSet<PsiMethod> getUnusedMethods() { |
| return myUnusedMethods; |
| } |
| } |
| } |