IG: check for TestCase too
[idea/community.git] / plugins / InspectionGadgets / InspectionGadgetsAnalysis / src / com / siyeh / ig / junit / UseOfObsoleteAssertInspection.java
1 /*
2  * Copyright 2003-2016 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     String name = (String)infos[0];
47     return InspectionGadgetsBundle.message("use.of.obsolete.assert.problem.descriptor", name);
48   }
49
50   @Override
51   protected InspectionGadgetsFix buildFix(Object... infos) {
52     return new ReplaceObsoleteAssertsFix();
53   }
54
55   @Override
56   public BaseInspectionVisitor buildVisitor() {
57     return new UseOfObsoleteAssertVisitor();
58   }
59
60   private static class UseOfObsoleteAssertVisitor extends BaseInspectionVisitor {
61
62     @Override
63     public void visitMethodCallExpression(PsiMethodCallExpression expression) {
64       final Project project = expression.getProject();
65       final Module module = ModuleUtilCore.findModuleForPsiElement(expression);
66       if (module == null) {
67         return;
68       }
69       final PsiClass newAssertClass = JavaPsiFacade.getInstance(project)
70         .findClass("org.junit.Assert", GlobalSearchScope.moduleWithDependenciesAndLibrariesScope(module));
71       if (newAssertClass == null) {
72         return;
73       }
74       final PsiMethod psiMethod = expression.resolveMethod();
75       if (psiMethod == null || !psiMethod.hasModifierProperty(PsiModifier.STATIC)) {
76         return;
77       }
78       final PsiClass containingClass = psiMethod.getContainingClass();
79       if (containingClass == null) {
80         return;
81       }
82       final String name = containingClass.getQualifiedName();
83       if ("junit.framework.Assert".equals(name) || "junit.framework.TestCase".equals(name)) {
84         registerMethodCallError(expression, name);
85       }
86     }
87   }
88
89   private static class ReplaceObsoleteAssertsFix extends InspectionGadgetsFix {
90     @Override
91     protected void doFix(Project project, ProblemDescriptor descriptor) throws IncorrectOperationException {
92       final PsiElement psiElement = PsiTreeUtil.getParentOfType(descriptor.getPsiElement(), PsiMethodCallExpression.class);
93       if (psiElement == null) {
94         return;
95       }
96       final PsiClass newAssertClass =
97         JavaPsiFacade.getInstance(project).findClass("org.junit.Assert", GlobalSearchScope.allScope(project));
98       final PsiClass oldAssertClass =
99         JavaPsiFacade.getInstance(project).findClass("junit.framework.Assert", GlobalSearchScope.allScope(project));
100
101       if (newAssertClass == null) {
102         return;
103       }
104       final PsiMethodCallExpression methodCallExpression = (PsiMethodCallExpression)psiElement;
105       final PsiReferenceExpression methodExpression = methodCallExpression.getMethodExpression();
106       final PsiExpression qualifierExpression = methodExpression.getQualifierExpression();
107       final PsiElement usedImport = qualifierExpression instanceof PsiReferenceExpression ?
108                                     ((PsiReferenceExpression)qualifierExpression).advancedResolve(true).getCurrentFileResolveScope() :
109                                     methodExpression.advancedResolve(true).getCurrentFileResolveScope();
110       final PsiMethod psiMethod = methodCallExpression.resolveMethod();
111
112       final boolean isImportUnused = isImportBecomeUnused(methodCallExpression, usedImport, psiMethod);
113
114       PsiImportStaticStatement staticStatement = null;
115       if (qualifierExpression == null) {
116         staticStatement = staticallyImported(oldAssertClass, methodExpression);
117       }
118
119       final JavaCodeStyleManager styleManager = JavaCodeStyleManager.getInstance(project);
120       if (staticStatement == null) {
121         methodExpression.setQualifierExpression(JavaPsiFacade.getElementFactory(project).createReferenceExpression(newAssertClass));
122
123         if (isImportUnused && usedImport instanceof PsiImportStatementBase) {
124           usedImport.delete();
125         }
126
127         styleManager.shortenClassReferences(methodExpression);
128       }
129       else {
130         if (isImportUnused) {
131           final PsiJavaCodeReferenceElement importReference = staticStatement.getImportReference();
132           if (importReference != null) {
133             if (staticStatement.isOnDemand()) {
134               importReference.bindToElement(newAssertClass);
135             }
136             else {
137               final PsiElement importQExpression = importReference.getQualifier();
138               if (importQExpression instanceof PsiJavaCodeReferenceElement) {
139                 ((PsiJavaCodeReferenceElement)importQExpression).bindToElement(newAssertClass);
140               }
141             }
142           }
143         }
144         else {
145           methodExpression
146             .setQualifierExpression(JavaPsiFacade.getElementFactory(project).createReferenceExpression(newAssertClass));
147           styleManager.shortenClassReferences(methodExpression);
148         }
149       }
150       /*
151           //refs can be optimized now but should we really?
152           if (isImportUnused) {
153             for (PsiReference reference : ReferencesSearch.search(newAssertClass, new LocalSearchScope(methodCallExpression.getContainingFile()))) {
154               final PsiElement element = reference.getElement();
155               styleManager.shortenClassReferences(element);
156             }
157           }*/
158     }
159
160     private static boolean isImportBecomeUnused(final PsiMethodCallExpression methodCallExpression,
161                                                 final PsiElement usedImport,
162                                                 final PsiMethod psiMethod) {
163       final boolean[] proceed = new boolean[]{true};
164       methodCallExpression.getContainingFile().accept(new JavaRecursiveElementWalkingVisitor() {
165         @Override
166         public void visitElement(PsiElement element) {
167           if (proceed[0]) {
168             super.visitElement(element);
169           }
170         }
171
172         @Override
173         public void visitMethodCallExpression(PsiMethodCallExpression expression) {
174           super.visitMethodCallExpression(expression);
175           if (expression == methodCallExpression) {
176             return;
177           }
178           final PsiMethod resolved = expression.resolveMethod();
179           if (resolved == psiMethod) {
180             proceed[0] = false;
181           }
182           else {
183             final PsiElement resolveScope =
184               expression.getMethodExpression().advancedResolve(false).getCurrentFileResolveScope();
185             if (resolveScope == usedImport) {
186               proceed[0] = false;
187             }
188           }
189         }
190       });
191       return proceed[0];
192     }
193
194     @Nullable
195     private static PsiImportStaticStatement staticallyImported(PsiClass oldAssertClass, PsiReferenceExpression methodExpression) {
196       final String referenceName = methodExpression.getReferenceName();
197       final PsiFile containingFile = methodExpression.getContainingFile();
198       if (!(containingFile instanceof PsiJavaFile)) {
199         return null;
200       }
201       final PsiImportList importList = ((PsiJavaFile)containingFile).getImportList();
202       if (importList == null) {
203         return null;
204       }
205       final PsiImportStaticStatement[] statements = importList.getImportStaticStatements();
206       for (PsiImportStaticStatement statement : statements) {
207         if (oldAssertClass != statement.resolveTargetClass()) {
208           continue;
209         }
210         final String importRefName = statement.getReferenceName();
211         final PsiJavaCodeReferenceElement importReference = statement.getImportReference();
212         if (importReference == null) {
213           continue;
214         }
215         if (Comparing.strEqual(importRefName, referenceName)) {
216           final PsiElement qualifier = importReference.getQualifier();
217           if (qualifier instanceof PsiJavaCodeReferenceElement) {
218             return statement;
219           }
220         }
221         else if (importRefName == null) {
222           return statement;
223         }
224       }
225       return null;
226     }
227
228     @NotNull
229     @Override
230     public String getFamilyName() {
231       return InspectionGadgetsBundle.message("use.of.obsolete.assert.quickfix");
232     }
233   }
234 }