blob: 0c690e2b4f0ee69ec0b95d5cc73dab3dd1741a5f [file] [log] [blame]
/*
* 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.junit;
import com.intellij.codeInspection.ProblemDescriptor;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleUtilCore;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Comparing;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
import com.intellij.psi.search.GlobalSearchScope;
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 org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class UseOfObsoleteAssertInspection extends BaseInspection {
@Override
@NotNull
public String getDisplayName() {
return InspectionGadgetsBundle.message("usage.of.obsolete.assert.display.name");
}
@Override
@NotNull
protected String buildErrorString(Object... infos) {
return InspectionGadgetsBundle.message("use.of.obsolete.assert.problem.descriptor");
}
@Override
protected InspectionGadgetsFix buildFix(Object... infos) {
return new ReplaceObsoleteAssertsFix();
}
@Override
public BaseInspectionVisitor buildVisitor() {
return new UseOfObsoleteAssertVisitor();
}
private static class UseOfObsoleteAssertVisitor extends BaseInspectionVisitor {
@Override
public void visitMethodCallExpression(PsiMethodCallExpression expression) {
final Project project = expression.getProject();
final Module module = ModuleUtilCore.findModuleForPsiElement(expression);
if (module == null) {
return;
}
final PsiClass newAssertClass = JavaPsiFacade.getInstance(project)
.findClass("org.junit.Assert", GlobalSearchScope.moduleWithDependenciesAndLibrariesScope(module));
if (newAssertClass == null) {
return;
}
final PsiMethod psiMethod = expression.resolveMethod();
if (psiMethod == null || !psiMethod.hasModifierProperty(PsiModifier.STATIC)) {
return;
}
final PsiClass containingClass = psiMethod.getContainingClass();
if (containingClass != null && Comparing.strEqual(containingClass.getQualifiedName(), "junit.framework.Assert")) {
registerMethodCallError(expression);
}
}
}
private static class ReplaceObsoleteAssertsFix extends InspectionGadgetsFix {
@Override
protected void doFix(Project project, ProblemDescriptor descriptor) throws IncorrectOperationException {
final PsiElement psiElement = PsiTreeUtil.getParentOfType(descriptor.getPsiElement(), PsiMethodCallExpression.class);
if (psiElement == null) {
return;
}
final PsiClass newAssertClass =
JavaPsiFacade.getInstance(project).findClass("org.junit.Assert", GlobalSearchScope.allScope(project));
final PsiClass oldAssertClass =
JavaPsiFacade.getInstance(project).findClass("junit.framework.Assert", GlobalSearchScope.allScope(project));
if (newAssertClass == null) {
return;
}
final PsiMethodCallExpression methodCallExpression = (PsiMethodCallExpression)psiElement;
final PsiReferenceExpression methodExpression = methodCallExpression.getMethodExpression();
final PsiExpression qualifierExpression = methodExpression.getQualifierExpression();
final PsiElement usedImport = qualifierExpression instanceof PsiReferenceExpression ?
((PsiReferenceExpression)qualifierExpression).advancedResolve(true).getCurrentFileResolveScope() :
methodExpression.advancedResolve(true).getCurrentFileResolveScope();
final PsiMethod psiMethod = methodCallExpression.resolveMethod();
final boolean isImportUnused = isImportBecomeUnused(methodCallExpression, usedImport, psiMethod);
PsiImportStaticStatement staticStatement = null;
if (qualifierExpression == null) {
staticStatement = staticallyImported(oldAssertClass, methodExpression);
}
final JavaCodeStyleManager styleManager = JavaCodeStyleManager.getInstance(project);
if (staticStatement == null) {
methodExpression.setQualifierExpression(JavaPsiFacade.getElementFactory(project).createReferenceExpression(newAssertClass));
if (isImportUnused && usedImport instanceof PsiImportStatementBase) {
usedImport.delete();
}
styleManager.shortenClassReferences(methodExpression);
}
else {
if (isImportUnused) {
final PsiJavaCodeReferenceElement importReference = staticStatement.getImportReference();
if (importReference != null) {
if (staticStatement.isOnDemand()) {
importReference.bindToElement(newAssertClass);
}
else {
final PsiElement importQExpression = importReference.getQualifier();
if (importQExpression instanceof PsiJavaCodeReferenceElement) {
((PsiJavaCodeReferenceElement)importQExpression).bindToElement(newAssertClass);
}
}
}
}
else {
methodExpression
.setQualifierExpression(JavaPsiFacade.getElementFactory(project).createReferenceExpression(newAssertClass));
styleManager.shortenClassReferences(methodExpression);
}
}
/*
//refs can be optimized now but should we really?
if (isImportUnused) {
for (PsiReference reference : ReferencesSearch.search(newAssertClass, new LocalSearchScope(methodCallExpression.getContainingFile()))) {
final PsiElement element = reference.getElement();
styleManager.shortenClassReferences(element);
}
}*/
}
private static boolean isImportBecomeUnused(final PsiMethodCallExpression methodCallExpression,
final PsiElement usedImport,
final PsiMethod psiMethod) {
final boolean[] proceed = new boolean[]{true};
methodCallExpression.getContainingFile().accept(new JavaRecursiveElementWalkingVisitor() {
@Override
public void visitElement(PsiElement element) {
if (proceed[0]) {
super.visitElement(element);
}
}
@Override
public void visitMethodCallExpression(PsiMethodCallExpression expression) {
super.visitMethodCallExpression(expression);
if (expression == methodCallExpression) {
return;
}
final PsiMethod resolved = expression.resolveMethod();
if (resolved == psiMethod) {
proceed[0] = false;
}
else {
final PsiElement resolveScope =
expression.getMethodExpression().advancedResolve(false).getCurrentFileResolveScope();
if (resolveScope == usedImport) {
proceed[0] = false;
}
}
}
});
return proceed[0];
}
@Nullable
private static PsiImportStaticStatement staticallyImported(PsiClass oldAssertClass, PsiReferenceExpression methodExpression) {
final String referenceName = methodExpression.getReferenceName();
final PsiFile containingFile = methodExpression.getContainingFile();
if (!(containingFile instanceof PsiJavaFile)) {
return null;
}
final PsiImportList importList = ((PsiJavaFile)containingFile).getImportList();
if (importList == null) {
return null;
}
final PsiImportStaticStatement[] statements = importList.getImportStaticStatements();
for (PsiImportStaticStatement statement : statements) {
if (oldAssertClass != statement.resolveTargetClass()) {
continue;
}
final String importRefName = statement.getReferenceName();
final PsiJavaCodeReferenceElement importReference = statement.getImportReference();
if (importReference == null) {
continue;
}
if (Comparing.strEqual(importRefName, referenceName)) {
final PsiElement qualifier = importReference.getQualifier();
if (qualifier instanceof PsiJavaCodeReferenceElement) {
return statement;
}
}
else if (importRefName == null) {
return statement;
}
}
return null;
}
@NotNull
@Override
public String getName() {
return InspectionGadgetsBundle.message("use.of.obsolete.assert.quickfix");
}
@Override
@NotNull
public String getFamilyName() {
return getName();
}
}
}