| /* |
| * Copyright 2003-2012 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.style; |
| |
| import com.intellij.codeInspection.CleanupLocalInspectionTool; |
| import com.intellij.codeInspection.ProblemDescriptor; |
| import com.intellij.codeInspection.ProblemHighlightType; |
| import com.intellij.codeInspection.ui.MultipleCheckboxOptionsPanel; |
| import com.intellij.openapi.project.Project; |
| import com.intellij.openapi.util.TextRange; |
| import com.intellij.psi.*; |
| import com.intellij.psi.util.PsiTreeUtil; |
| 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.ClassUtils; |
| import org.jetbrains.annotations.NotNull; |
| |
| import javax.swing.*; |
| |
| public class UnnecessarilyQualifiedStaticUsageInspection extends BaseInspection implements CleanupLocalInspectionTool{ |
| |
| /** |
| * @noinspection PublicField |
| */ |
| public boolean m_ignoreStaticFieldAccesses = false; |
| |
| /** |
| * @noinspection PublicField |
| */ |
| public boolean m_ignoreStaticMethodCalls = false; |
| |
| /** |
| * @noinspection PublicField |
| */ |
| public boolean m_ignoreStaticAccessFromStaticContext = false; |
| |
| @Override |
| @NotNull |
| public String getDisplayName() { |
| return InspectionGadgetsBundle.message("unnecessarily.qualified.static.usage.display.name"); |
| } |
| |
| @Override |
| @NotNull |
| public String buildErrorString(Object... infos) { |
| final PsiJavaCodeReferenceElement element = (PsiJavaCodeReferenceElement)infos[0]; |
| final PsiElement parent = element.getParent(); |
| if (parent instanceof PsiMethodCallExpression) { |
| return InspectionGadgetsBundle.message("unnecessarily.qualified.static.usage.problem.descriptor", element.getText()); |
| } |
| else { |
| return InspectionGadgetsBundle.message("unnecessarily.qualified.static.usage.problem.descriptor1", element.getText()); |
| } |
| } |
| |
| @Override |
| public JComponent createOptionsPanel() { |
| final MultipleCheckboxOptionsPanel optionsPanel = new MultipleCheckboxOptionsPanel(this); |
| optionsPanel.addCheckbox(InspectionGadgetsBundle.message("unnecessarily.qualified.static.usage.ignore.field.option"), |
| "m_ignoreStaticFieldAccesses"); |
| optionsPanel.addCheckbox(InspectionGadgetsBundle.message("unnecessarily.qualified.static.usage.ignore.method.option"), |
| "m_ignoreStaticMethodCalls"); |
| optionsPanel.addCheckbox(InspectionGadgetsBundle.message("only.report.qualified.static.usages.option"), |
| "m_ignoreStaticAccessFromStaticContext"); |
| return optionsPanel; |
| } |
| |
| @Override |
| public InspectionGadgetsFix buildFix(Object... infos) { |
| return new UnnecessarilyQualifiedStaticUsageFix(); |
| } |
| |
| private static class UnnecessarilyQualifiedStaticUsageFix extends InspectionGadgetsFix { |
| |
| @Override |
| @NotNull |
| public String getName() { |
| return InspectionGadgetsBundle.message("unnecessary.qualifier.for.this.remove.quickfix"); |
| } |
| |
| @NotNull |
| @Override |
| public String getFamilyName() { |
| return getName(); |
| } |
| |
| @Override |
| public void doFix(Project project, ProblemDescriptor descriptor) throws IncorrectOperationException { |
| final PsiElement element = descriptor.getPsiElement(); |
| element.delete(); |
| } |
| } |
| |
| @Override |
| public BaseInspectionVisitor buildVisitor() { |
| return new UnnecessarilyQualifiedStaticUsageVisitor(); |
| } |
| |
| private class UnnecessarilyQualifiedStaticUsageVisitor extends BaseInspectionVisitor { |
| |
| @Override |
| public void visitReferenceElement(PsiJavaCodeReferenceElement reference) { |
| super.visitReferenceElement(reference); |
| final PsiElement qualifier = reference.getQualifier(); |
| if (qualifier == null) { |
| return; |
| } |
| if (!isUnnecessarilyQualifiedAccess(reference)) { |
| return; |
| } |
| registerError(qualifier, ProblemHighlightType.LIKE_UNUSED_SYMBOL, reference); |
| } |
| |
| @Override |
| public void visitReferenceExpression(PsiReferenceExpression expression) { |
| visitReferenceElement(expression); |
| } |
| |
| private boolean isUnnecessarilyQualifiedAccess(@NotNull PsiJavaCodeReferenceElement referenceElement) { |
| if (referenceElement instanceof PsiMethodReferenceExpression) { |
| return false; |
| } |
| final PsiElement parent = referenceElement.getParent(); |
| if (parent instanceof PsiImportStatementBase) { |
| return false; |
| } |
| final PsiElement qualifierElement = referenceElement.getQualifier(); |
| if (!(qualifierElement instanceof PsiJavaCodeReferenceElement)) { |
| return false; |
| } |
| final PsiJavaCodeReferenceElement qualifier = (PsiJavaCodeReferenceElement)qualifierElement; |
| if (isGenericReference(referenceElement, qualifier)) { |
| return false; |
| } |
| final PsiElement target = referenceElement.resolve(); |
| if ((!(target instanceof PsiField) || m_ignoreStaticFieldAccesses) && (!(target instanceof PsiMethod) || m_ignoreStaticMethodCalls)) { |
| return false; |
| } |
| if (m_ignoreStaticAccessFromStaticContext) { |
| final PsiMember containingMember = PsiTreeUtil.getParentOfType(referenceElement, PsiMember.class); |
| if (containingMember != null && !containingMember.hasModifierProperty(PsiModifier.STATIC)) { |
| return false; |
| } |
| } |
| final String referenceName = referenceElement.getReferenceName(); |
| if (referenceName == null) { |
| return false; |
| } |
| final PsiElement resolvedQualifier = qualifier.resolve(); |
| if (!(resolvedQualifier instanceof PsiClass)) { |
| return false; |
| } |
| final PsiClass containingClass = PsiTreeUtil.getParentOfType(referenceElement, PsiClass.class); |
| final PsiClass qualifyingClass = (PsiClass)resolvedQualifier; |
| if (containingClass == null || !PsiTreeUtil.isAncestor(qualifyingClass, containingClass, false)) { |
| return false; |
| } |
| final Project project = referenceElement.getProject(); |
| final JavaPsiFacade manager = JavaPsiFacade.getInstance(project); |
| final PsiResolveHelper resolveHelper = manager.getResolveHelper(); |
| final PsiMember member = (PsiMember)target; |
| final PsiClass memberClass; |
| if (target instanceof PsiField) { |
| final PsiVariable variable = resolveHelper.resolveReferencedVariable(referenceName, referenceElement); |
| if (variable == null || !variable.equals(member)) { |
| return false; |
| } |
| final TextRange referenceElementTextRange = referenceElement.getTextRange(); |
| if (referenceElementTextRange == null) { |
| return false; |
| } |
| final TextRange variableTextRange = variable.getTextRange(); |
| if (variableTextRange == null) { |
| return false; |
| } |
| //illegal forward ref |
| if (referenceElementTextRange.getStartOffset() < variableTextRange.getEndOffset()) { |
| return false; |
| } |
| final PsiMember memberVariable = (PsiMember)variable; |
| memberClass = memberVariable.getContainingClass(); |
| } |
| else if (target instanceof PsiClass) { |
| final PsiClass aClass = resolveHelper.resolveReferencedClass(referenceName, referenceElement); |
| if (aClass == null || !aClass.equals(member)) { |
| return false; |
| } |
| memberClass = aClass.getContainingClass(); |
| } |
| else { |
| return isMethodAccessibleWithoutQualifier(referenceElement, qualifyingClass); |
| } |
| return resolvedQualifier.equals(memberClass); |
| } |
| |
| private boolean isMethodAccessibleWithoutQualifier(PsiJavaCodeReferenceElement referenceElement, PsiClass qualifyingClass) { |
| final String referenceName = referenceElement.getReferenceName(); |
| if (referenceName == null) { |
| return false; |
| } |
| PsiClass containingClass = ClassUtils.getContainingClass(referenceElement); |
| while (containingClass != null) { |
| final PsiMethod[] methods = containingClass.findMethodsByName(referenceName, true); |
| for (final PsiMethod method : methods) { |
| final String name = method.getName(); |
| if (referenceName.equals(name)) { |
| return containingClass.equals(qualifyingClass); |
| } |
| } |
| containingClass = ClassUtils.getContainingClass(containingClass); |
| } |
| return false; |
| } |
| } |
| |
| static boolean isGenericReference(PsiJavaCodeReferenceElement referenceElement, PsiJavaCodeReferenceElement qualifierElement) { |
| final PsiReferenceParameterList qualifierParameterList = qualifierElement.getParameterList(); |
| if (qualifierParameterList != null) { |
| final PsiTypeElement[] typeParameterElements = qualifierParameterList.getTypeParameterElements(); |
| if (typeParameterElements.length > 0) { |
| return true; |
| } |
| } |
| final PsiReferenceParameterList parameterList = referenceElement.getParameterList(); |
| if (parameterList != null) { |
| final PsiTypeElement[] typeParameterElements = parameterList.getTypeParameterElements(); |
| if (typeParameterElements.length > 0) { |
| return true; |
| } |
| } |
| return false; |
| } |
| } |