IG: check for TestCase instead of inheritors of Assert
[idea/community.git] / plugins / InspectionGadgets / InspectionGadgetsAnalysis / src / com / siyeh / ig / junit / UseOfObsoleteAssertInspection.java
1 /*
2  * Copyright 2003-2014 Dave Griffith, Bas Leijdekkers
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.siyeh.ig.junit;
17
18 import com.intellij.codeInspection.ProblemDescriptor;
19 import com.intellij.openapi.module.Module;
20 import com.intellij.openapi.module.ModuleUtilCore;
21 import com.intellij.openapi.project.Project;
22 import com.intellij.openapi.util.Comparing;
23 import com.intellij.psi.*;
24 import com.intellij.psi.codeStyle.JavaCodeStyleManager;
25 import com.intellij.psi.search.GlobalSearchScope;
26 import com.intellij.psi.util.PsiTreeUtil;
27 import com.intellij.util.IncorrectOperationException;
28 import com.siyeh.InspectionGadgetsBundle;
29 import com.siyeh.ig.BaseInspection;
30 import com.siyeh.ig.BaseInspectionVisitor;
31 import com.siyeh.ig.InspectionGadgetsFix;
32 import org.jetbrains.annotations.NotNull;
33 import org.jetbrains.annotations.Nullable;
34
35 public class UseOfObsoleteAssertInspection extends BaseInspection {
36
37   @Override
38   @NotNull
39   public String getDisplayName() {
40     return InspectionGadgetsBundle.message("usage.of.obsolete.assert.display.name");
41   }
42
43   @Override
44   @NotNull
45   protected String buildErrorString(Object... infos) {
46     return InspectionGadgetsBundle.message("use.of.obsolete.assert.problem.descriptor");
47   }
48
49   @Override
50   protected InspectionGadgetsFix buildFix(Object... infos) {
51     return new ReplaceObsoleteAssertsFix();
52   }
53
54   @Override
55   public BaseInspectionVisitor buildVisitor() {
56     return new UseOfObsoleteAssertVisitor();
57   }
58
59   private static class UseOfObsoleteAssertVisitor extends BaseInspectionVisitor {
60
61     @Override
62     public void visitMethodCallExpression(PsiMethodCallExpression expression) {
63       final Project project = expression.getProject();
64       final Module module = ModuleUtilCore.findModuleForPsiElement(expression);
65       if (module == null) {
66         return;
67       }
68       final PsiClass newAssertClass = JavaPsiFacade.getInstance(project)
69         .findClass("org.junit.Assert", GlobalSearchScope.moduleWithDependenciesAndLibrariesScope(module));
70       if (newAssertClass == null) {
71         return;
72       }
73       final PsiMethod psiMethod = expression.resolveMethod();
74       if (psiMethod == null || !psiMethod.hasModifierProperty(PsiModifier.STATIC)) {
75         return;
76       }
77       final PsiClass containingClass = psiMethod.getContainingClass();
78       if (containingClass != null && Comparing.strEqual(containingClass.getQualifiedName(), "junit.framework.Assert")) {
79         registerMethodCallError(expression);
80       }
81     }
82   }
83
84   private static class ReplaceObsoleteAssertsFix extends InspectionGadgetsFix {
85     @Override
86     protected void doFix(Project project, ProblemDescriptor descriptor) throws IncorrectOperationException {
87       final PsiElement psiElement = PsiTreeUtil.getParentOfType(descriptor.getPsiElement(), PsiMethodCallExpression.class);
88       if (psiElement == null) {
89         return;
90       }
91       final PsiClass newAssertClass =
92         JavaPsiFacade.getInstance(project).findClass("org.junit.Assert", GlobalSearchScope.allScope(project));
93       final PsiClass oldAssertClass =
94         JavaPsiFacade.getInstance(project).findClass("junit.framework.Assert", GlobalSearchScope.allScope(project));
95
96       if (newAssertClass == null) {
97         return;
98       }
99       final PsiMethodCallExpression methodCallExpression = (PsiMethodCallExpression)psiElement;
100       final PsiReferenceExpression methodExpression = methodCallExpression.getMethodExpression();
101       final PsiExpression qualifierExpression = methodExpression.getQualifierExpression();
102       final PsiElement usedImport = qualifierExpression instanceof PsiReferenceExpression ?
103                                     ((PsiReferenceExpression)qualifierExpression).advancedResolve(true).getCurrentFileResolveScope() :
104                                     methodExpression.advancedResolve(true).getCurrentFileResolveScope();
105       final PsiMethod psiMethod = methodCallExpression.resolveMethod();
106
107       final boolean isImportUnused = isImportBecomeUnused(methodCallExpression, usedImport, psiMethod);
108
109       PsiImportStaticStatement staticStatement = null;
110       if (qualifierExpression == null) {
111         staticStatement = staticallyImported(oldAssertClass, methodExpression);
112       }
113
114       final JavaCodeStyleManager styleManager = JavaCodeStyleManager.getInstance(project);
115       if (staticStatement == null) {
116         methodExpression.setQualifierExpression(JavaPsiFacade.getElementFactory(project).createReferenceExpression(newAssertClass));
117
118         if (isImportUnused && usedImport instanceof PsiImportStatementBase) {
119           usedImport.delete();
120         }
121
122         styleManager.shortenClassReferences(methodExpression);
123       }
124       else {
125         if (isImportUnused) {
126           final PsiJavaCodeReferenceElement importReference = staticStatement.getImportReference();
127           if (importReference != null) {
128             if (staticStatement.isOnDemand()) {
129               importReference.bindToElement(newAssertClass);
130             }
131             else {
132               final PsiElement importQExpression = importReference.getQualifier();
133               if (importQExpression instanceof PsiJavaCodeReferenceElement) {
134                 ((PsiJavaCodeReferenceElement)importQExpression).bindToElement(newAssertClass);
135               }
136             }
137           }
138         }
139         else {
140           methodExpression
141             .setQualifierExpression(JavaPsiFacade.getElementFactory(project).createReferenceExpression(newAssertClass));
142           styleManager.shortenClassReferences(methodExpression);
143         }
144       }
145       /*
146           //refs can be optimized now but should we really?
147           if (isImportUnused) {
148             for (PsiReference reference : ReferencesSearch.search(newAssertClass, new LocalSearchScope(methodCallExpression.getContainingFile()))) {
149               final PsiElement element = reference.getElement();
150               styleManager.shortenClassReferences(element);
151             }
152           }*/
153     }
154
155     private static boolean isImportBecomeUnused(final PsiMethodCallExpression methodCallExpression,
156                                                 final PsiElement usedImport,
157                                                 final PsiMethod psiMethod) {
158       final boolean[] proceed = new boolean[]{true};
159       methodCallExpression.getContainingFile().accept(new JavaRecursiveElementWalkingVisitor() {
160         @Override
161         public void visitElement(PsiElement element) {
162           if (proceed[0]) {
163             super.visitElement(element);
164           }
165         }
166
167         @Override
168         public void visitMethodCallExpression(PsiMethodCallExpression expression) {
169           super.visitMethodCallExpression(expression);
170           if (expression == methodCallExpression) {
171             return;
172           }
173           final PsiMethod resolved = expression.resolveMethod();
174           if (resolved == psiMethod) {
175             proceed[0] = false;
176           }
177           else {
178             final PsiElement resolveScope =
179               expression.getMethodExpression().advancedResolve(false).getCurrentFileResolveScope();
180             if (resolveScope == usedImport) {
181               proceed[0] = false;
182             }
183           }
184         }
185       });
186       return proceed[0];
187     }
188
189     @Nullable
190     private static PsiImportStaticStatement staticallyImported(PsiClass oldAssertClass, PsiReferenceExpression methodExpression) {
191       final String referenceName = methodExpression.getReferenceName();
192       final PsiFile containingFile = methodExpression.getContainingFile();
193       if (!(containingFile instanceof PsiJavaFile)) {
194         return null;
195       }
196       final PsiImportList importList = ((PsiJavaFile)containingFile).getImportList();
197       if (importList == null) {
198         return null;
199       }
200       final PsiImportStaticStatement[] statements = importList.getImportStaticStatements();
201       for (PsiImportStaticStatement statement : statements) {
202         if (oldAssertClass != statement.resolveTargetClass()) {
203           continue;
204         }
205         final String importRefName = statement.getReferenceName();
206         final PsiJavaCodeReferenceElement importReference = statement.getImportReference();
207         if (importReference == null) {
208           continue;
209         }
210         if (Comparing.strEqual(importRefName, referenceName)) {
211           final PsiElement qualifier = importReference.getQualifier();
212           if (qualifier instanceof PsiJavaCodeReferenceElement) {
213             return statement;
214           }
215         }
216         else if (importRefName == null) {
217           return statement;
218         }
219       }
220       return null;
221     }
222
223     @NotNull
224     @Override
225     public String getFamilyName() {
226       return InspectionGadgetsBundle.message("use.of.obsolete.assert.quickfix");
227     }
228   }
229 }