From 8cf61b4579c00371a3bb57fc89e990cde9b6a425 Mon Sep 17 00:00:00 2001 From: Bas Date: Mon, 19 Oct 2009 12:35:57 +0200 Subject: [PATCH] add static import if possible --- .../SimplifiableJUnitAssertionInspection.java | 137 ++++----- .../com/siyeh/ig/psiutils/ImportUtils.java | 270 ++++++++++++++++-- 2 files changed, 325 insertions(+), 82 deletions(-) diff --git a/plugins/InspectionGadgets/src/com/siyeh/ig/junit/SimplifiableJUnitAssertionInspection.java b/plugins/InspectionGadgets/src/com/siyeh/ig/junit/SimplifiableJUnitAssertionInspection.java index 4dd77f7a69eb..71ce06fe07a8 100644 --- a/plugins/InspectionGadgets/src/com/siyeh/ig/junit/SimplifiableJUnitAssertionInspection.java +++ b/plugins/InspectionGadgets/src/com/siyeh/ig/junit/SimplifiableJUnitAssertionInspection.java @@ -26,10 +26,7 @@ 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 com.siyeh.ig.psiutils.ComparisonUtils; -import com.siyeh.ig.psiutils.MethodCallUtils; -import com.siyeh.ig.psiutils.TestUtils; +import com.siyeh.ig.psiutils.*; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; @@ -65,7 +62,9 @@ public class SimplifiableJUnitAssertionInspection extends BaseInspection { throws IncorrectOperationException { final PsiElement methodNameIdentifier = descriptor.getPsiElement(); final PsiElement parent = methodNameIdentifier.getParent(); - assert parent != null; + if (parent == null) { + return; + } final PsiMethodCallExpression callExpression = (PsiMethodCallExpression)parent.getParent(); if (isAssertThatCouldBeAssertNull(callExpression)) { @@ -84,27 +83,21 @@ public class SimplifiableJUnitAssertionInspection extends BaseInspection { private static void replaceAssertWithFail( PsiMethodCallExpression callExpression) throws IncorrectOperationException { - final PsiReferenceExpression methodExpression = - callExpression.getMethodExpression(); - final PsiMethod method = (PsiMethod)methodExpression.resolve(); - assert method != null; - final PsiParameterList parameterList = method.getParameterList(); - final PsiParameter[] parameters = parameterList.getParameters(); + final PsiMethod method = callExpression.resolveMethod(); + if (method == null) { + return; + } final PsiExpressionList argumentList = callExpression.getArgumentList(); final PsiExpression[] arguments = argumentList.getExpressions(); final PsiExpression message; - if (parameters.length == 2) { + if (arguments.length == 2) { message = arguments[0]; } else { message = null; } @NonNls final StringBuilder newExpression = new StringBuilder(); - final PsiMethod containingMethod = - PsiTreeUtil.getParentOfType(callExpression, PsiMethod.class); - if (TestUtils.isJUnit4TestMethod(containingMethod)) { - newExpression.append("org.junit.Assert."); - } + addStaticImportIfPossible(callExpression, "fail", newExpression); newExpression.append("fail("); if (message != null) { newExpression.append(message.getText()); @@ -117,10 +110,10 @@ public class SimplifiableJUnitAssertionInspection extends BaseInspection { private static void replaceAssertTrueWithAssertEquals( PsiMethodCallExpression callExpression, Project project) throws IncorrectOperationException { - final PsiReferenceExpression methodExpression = - callExpression.getMethodExpression(); - final PsiMethod method = (PsiMethod)methodExpression.resolve(); - assert method != null; + final PsiMethod method = callExpression.resolveMethod(); + if (method == null) { + return; + } final PsiParameterList parameterList = method.getParameterList(); final PsiParameter[] parameters = parameterList.getParameters(); final PsiManager psiManager = callExpression.getManager(); @@ -166,21 +159,18 @@ public class SimplifiableJUnitAssertionInspection extends BaseInspection { lhs = rhs; rhs = temp; } - @NonNls final StringBuilder newExpression = new StringBuilder(); - final PsiMethod containingMethod = PsiTreeUtil.getParentOfType( - callExpression, PsiMethod.class); - if (TestUtils.isJUnit4TestMethod(containingMethod)) { - newExpression.append("org.junit.Assert."); + if (lhs == null || rhs == null) { + return; } + @NonNls final StringBuilder newExpression = new StringBuilder(); + addStaticImportIfPossible(callExpression, "assertEquals", newExpression); newExpression.append("assertEquals("); if (message != null) { newExpression.append(message.getText()); newExpression.append(','); } - assert lhs != null; newExpression.append(lhs.getText()); newExpression.append(','); - assert rhs != null; newExpression.append(rhs.getText()); if (isFloatingPoint(lhs) || isFloatingPoint(rhs)) { newExpression.append(",0.0"); @@ -193,10 +183,10 @@ public class SimplifiableJUnitAssertionInspection extends BaseInspection { private static void replaceAssertWithAssertNull( PsiMethodCallExpression callExpression, Project project) throws IncorrectOperationException { - final PsiReferenceExpression methodExpression = - callExpression.getMethodExpression(); - final PsiMethod method = (PsiMethod)methodExpression.resolve(); - assert method != null; + final PsiMethod method = callExpression.resolveMethod(); + if (method == null) { + return; + } final PsiParameterList parameterList = method.getParameterList(); final PsiParameter[] parameters = parameterList.getParameters(); final PsiManager psiManager = callExpression.getManager(); @@ -230,18 +220,18 @@ public class SimplifiableJUnitAssertionInspection extends BaseInspection { rhs = lhs; } @NonNls final StringBuilder newExpression = new StringBuilder(); - final PsiMethod containingMethod = PsiTreeUtil.getParentOfType( - callExpression, PsiMethod.class); - if (TestUtils.isJUnit4TestMethod(containingMethod)) { - newExpression.append("org.junit.Assert."); - } + final PsiReferenceExpression methodExpression = + callExpression.getMethodExpression(); final String methodName = methodExpression.getReferenceName(); - if ("assertFalse".equals(methodName) ^ - tokenType.equals(JavaTokenType.NE)) { - newExpression.append("assertNotNull("); + final String memberName; + if ("assertFalse".equals(methodName) ^ tokenType.equals(JavaTokenType.NE)) { + memberName = "assertNotNull"; } else { - newExpression.append("assertNull("); + memberName = "assertNull"; } + addStaticImportIfPossible(callExpression, memberName, newExpression); + newExpression.append(memberName); + newExpression.append('('); if (message != null) { newExpression.append(message.getText()); newExpression.append(','); @@ -255,10 +245,10 @@ public class SimplifiableJUnitAssertionInspection extends BaseInspection { private static void replaceAssertWithAssertSame( PsiMethodCallExpression callExpression, Project project) throws IncorrectOperationException { - final PsiReferenceExpression methodExpression = - callExpression.getMethodExpression(); - final PsiMethod method = (PsiMethod)methodExpression.resolve(); - assert method != null; + final PsiMethod method = callExpression.resolveMethod(); + if (method == null) { + return; + } final PsiParameterList parameterList = method.getParameterList(); final PsiParameter[] parameters = parameterList.getParameters(); final PsiManager psiManager = callExpression.getManager(); @@ -290,26 +280,29 @@ public class SimplifiableJUnitAssertionInspection extends BaseInspection { lhs = rhs; rhs = temp; } - @NonNls final StringBuilder newExpression = new StringBuilder(); - final PsiMethod containingMethod = PsiTreeUtil.getParentOfType( - callExpression, PsiMethod.class); - if (TestUtils.isJUnit4TestMethod(containingMethod)) { - newExpression.append("org.junit.Assert."); + if (rhs == null) { + return; } + @NonNls final StringBuilder newExpression = new StringBuilder(); + final PsiReferenceExpression methodExpression = + callExpression.getMethodExpression(); final String methodName = methodExpression.getReferenceName(); + final String memberName; if ("assertFalse".equals(methodName) ^ tokenType.equals(JavaTokenType.NE)) { - newExpression.append("assertNotSame("); + memberName = "assertNotSame"; } else { - newExpression.append("assertSame("); + memberName = "assertSame"; } + addStaticImportIfPossible(callExpression, memberName, newExpression); + newExpression.append(memberName); + newExpression.append('('); if (message != null) { newExpression.append(message.getText()); newExpression.append(','); } newExpression.append(lhs.getText()); newExpression.append(','); - assert rhs != null; newExpression.append(rhs.getText()); newExpression.append(')'); replaceExpressionAndShorten(callExpression, @@ -319,10 +312,10 @@ public class SimplifiableJUnitAssertionInspection extends BaseInspection { private static void replaceAssertEqualsWithAssertLiteral( PsiMethodCallExpression callExpression, Project project) throws IncorrectOperationException { - final PsiReferenceExpression methodExpression = - callExpression.getMethodExpression(); - final PsiMethod method = (PsiMethod)methodExpression.resolve(); - assert method != null; + final PsiMethod method = callExpression.resolveMethod(); + if (method == null) { + return; + } final PsiParameterList parameterList = method.getParameterList(); final PsiParameter[] parameters = parameterList.getParameters(); final PsiManager psiManager = callExpression.getManager(); @@ -360,13 +353,9 @@ public class SimplifiableJUnitAssertionInspection extends BaseInspection { Character.toUpperCase(literalValue.charAt(0)) + literalValue.substring(1); @NonNls final StringBuilder newExpression = new StringBuilder(); - final PsiMethod containingMethod = PsiTreeUtil.getParentOfType( - callExpression, PsiMethod.class); - if (TestUtils.isJUnit4TestMethod(containingMethod)) { - newExpression.append("org.junit.Assert."); - } - newExpression.append("assert"); - newExpression.append(uppercaseLiteralValue); + final String methodName = "assert" + uppercaseLiteralValue; + addStaticImportIfPossible(callExpression, methodName, newExpression); + newExpression.append(methodName); newExpression.append('('); if (message != null) { newExpression.append(message.getText()); @@ -378,6 +367,24 @@ public class SimplifiableJUnitAssertionInspection extends BaseInspection { newExpression.toString()); } + private static void addStaticImportIfPossible(PsiMethodCallExpression context, + String memberName, StringBuilder out) { + final PsiMethod containingMethod = PsiTreeUtil.getParentOfType( + context, PsiMethod.class); + if (TestUtils.isJUnit4TestMethod(containingMethod)) { + final PsiReferenceExpression methodExpression = + context.getMethodExpression(); + if (methodExpression.isQualified() || + !ImportUtils.nameCanBeStaticallyImported( + "org.junit.Assert", memberName, context)) { + out.append("org.junit.Assert."); + } else { + ImportUtils.addStaticImport( + "org.junit.Assert", memberName, context); + } + } + } + private static boolean isFloatingPoint(PsiExpression expression) { final PsiType type = expression.getType(); return PsiType.FLOAT.equals(type) || PsiType.DOUBLE.equals(type); @@ -672,7 +679,7 @@ public class SimplifiableJUnitAssertionInspection extends BaseInspection { return false; } final PsiType type = expression2.getType(); - return PsiType.BOOLEAN.equals(type); + return PsiType.BOOLEAN.equals(type); } private static boolean isEqualityComparison(PsiExpression expression) { diff --git a/plugins/InspectionGadgets/src/com/siyeh/ig/psiutils/ImportUtils.java b/plugins/InspectionGadgets/src/com/siyeh/ig/psiutils/ImportUtils.java index f868c4907e74..20c254379654 100644 --- a/plugins/InspectionGadgets/src/com/siyeh/ig/psiutils/ImportUtils.java +++ b/plugins/InspectionGadgets/src/com/siyeh/ig/psiutils/ImportUtils.java @@ -15,21 +15,51 @@ */ package com.siyeh.ig.psiutils; +import com.intellij.openapi.project.Project; import com.intellij.psi.*; +import com.intellij.psi.codeStyle.CodeStyleSettings; +import com.intellij.psi.codeStyle.CodeStyleSettingsManager; +import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.util.ClassUtil; import com.intellij.psi.util.PsiTreeUtil; -import com.intellij.openapi.project.Project; +import com.intellij.util.IncorrectOperationException; import com.siyeh.HardcodedMethodConstants; import org.jetbrains.annotations.NotNull; +import java.util.ArrayList; +import java.util.List; + public class ImportUtils{ private ImportUtils(){ } + public static boolean nameCanBeStaticallyImported( + @NotNull String fqName, @NotNull String memberName, @NotNull PsiElement context) { + final PsiClass containingClass = PsiTreeUtil.getParentOfType(context, PsiClass.class); + if (ClassUtils.isSubclass(containingClass, fqName)) { + return true; + } + final PsiField field = containingClass.findFieldByName(memberName, true); + if (field != null) { + return false; + } + final PsiMethod[] methods = containingClass.findMethodsByName(memberName, true); + if (methods.length > 0) { + return false; + } + if (hasOnDemandImportStaticConflict(fqName, memberName, context, true)) { + return false; + } + if (hasExactImportStaticConflict(fqName, memberName, context)) { + return false; + } + return true; + } + public static boolean nameCanBeImported(@NotNull String fqName, @NotNull PsiElement context){ - final PsiClass containingClass = ClassUtils.getContainingClass(context); + final PsiClass containingClass = PsiTreeUtil.getParentOfType(context, PsiClass.class); if (containingClass != null) { if (fqName.equals(containingClass.getQualifiedName())) { return true; @@ -87,27 +117,57 @@ public class ImportUtils{ if(imports == null){ return false; } - final PsiImportStatement[] importStatements = imports - .getImportStatements(); + final PsiImportStatement[] importStatements = + imports.getImportStatements(); final int lastDotIndex = fqName.lastIndexOf((int) '.'); final String shortName = fqName.substring(lastDotIndex + 1); final String dottedShortName = '.' + shortName; for(final PsiImportStatement importStatement : importStatements){ - if(!importStatement.isOnDemand()){ - final String importName = importStatement.getQualifiedName(); - if (importName == null){ - return false; - } - if(!importName.equals(fqName)){ - if(importName.endsWith(dottedShortName)){ - return true; - } + if (importStatement.isOnDemand()) { + continue; + } + final String importName = importStatement.getQualifiedName(); + if (importName == null){ + return false; + } + if(!importName.equals(fqName)){ + if(importName.endsWith(dottedShortName)){ + return true; } } } return false; } + private static boolean hasExactImportStaticConflict(String qualifierClass, String memberName, + PsiElement context) { + final PsiFile file = context.getContainingFile(); + if (!(file instanceof PsiJavaFile)) { + return false; + } + final PsiJavaFile javaFile = (PsiJavaFile) file; + final PsiImportList importList = javaFile.getImportList(); + final PsiImportStaticStatement[] importStaticStatements = + importList.getImportStaticStatements(); + for (PsiImportStaticStatement importStaticStatement : importStaticStatements) { + if (importStaticStatement.isOnDemand()) { + continue; + } + final String name = importStaticStatement.getReferenceName(); + if (!memberName.equals(name)) { + continue; + } + final PsiJavaCodeReferenceElement importReference = + importStaticStatement.getImportReference(); + final PsiElement qualifier = importReference.getQualifier(); + final String qualifierText = qualifier.getText(); + if (!qualifierClass.equals(qualifierText)) { + return true; + } + } + return false; + } + public static boolean hasOnDemandImportConflict(@NotNull String fqName, @NotNull PsiJavaFile file){ return hasOnDemandImportConflict(fqName, file, false); @@ -167,6 +227,47 @@ public class ImportUtils{ return hasJavaLangImportConflict(fqName, file); } + public static boolean hasOnDemandImportStaticConflict(String fqName, String memberName, + PsiElement context) { + return hasOnDemandImportStaticConflict(fqName, memberName, context, false); + } + + private static boolean hasOnDemandImportStaticConflict(String fqName, String memberName, + PsiElement context, boolean strict) { + final PsiFile file = context.getContainingFile(); + if (!(file instanceof PsiJavaFile)) { + return false; + } + final PsiJavaFile javaFile = (PsiJavaFile) file; + final PsiImportList importList = javaFile.getImportList(); + final PsiImportStaticStatement[] importStaticStatements = + importList.getImportStaticStatements(); + for (PsiImportStaticStatement importStaticStatement : importStaticStatements) { + if (!importStaticStatement.isOnDemand()) { + continue; + } + final PsiClass targetClass = importStaticStatement.resolveTargetClass(); + final String name = targetClass.getQualifiedName(); + if (fqName.equals(name)) { + continue; + } + final PsiField field = targetClass.findFieldByName(memberName, true); + if (field != null) { + if (!strict || memberReferenced(field, javaFile)) { + return true; + } + } + final PsiMethod[] methods = targetClass.findMethodsByName(memberName, true); + if (methods.length > 0) { + if (!strict || membersReferenced(methods, javaFile)) { + return true; + } + } + + } + return false; + } + public static boolean hasDefaultImportConflict(String fqName, PsiJavaFile file) { final String shortName = ClassUtil.extractClassName(fqName); @@ -242,9 +343,9 @@ public class ImportUtils{ PsiClass aClass){ final String shortName = ClassUtil.extractClassName(fqName); if(shortName.equals(aClass.getName())){ - if(!fqName.equals(aClass.getQualifiedName())){ - return true; - } + if(!fqName.equals(aClass.getQualifiedName())){ + return true; + } } final PsiClass[] classes = aClass.getInnerClasses(); for (PsiClass innerClass : classes) { @@ -255,6 +356,142 @@ public class ImportUtils{ return false; } + public static void addStaticImport(@NotNull String qualifierClass, @NotNull String memberName, + @NotNull PsiElement context) + throws IncorrectOperationException { + final PsiClass containingClass = PsiTreeUtil.getParentOfType(context, PsiClass.class); + if (ClassUtils.isSubclass(containingClass, qualifierClass)) { + return; + } + final PsiFile psiFile = context.getContainingFile(); + if (!(psiFile instanceof PsiJavaFile)) { + return; + } + final PsiJavaFile javaFile = (PsiJavaFile)psiFile; + final PsiImportList importList = javaFile.getImportList(); + if (importList == null) { + return; + } + final PsiImportStatementBase existingImportStatement = + importList.findSingleImportStatement(memberName); + if (existingImportStatement != null) { + return; + } else { + final PsiImportStaticStatement onDemandImportStatement = + findOnDemandImportStaticStatement(importList, qualifierClass); + if (onDemandImportStatement != null) { + if (!hasOnDemandImportStaticConflict(qualifierClass, memberName, context)) { + return; + } + } + } + final Project project = context.getProject(); + final GlobalSearchScope scope = context.getResolveScope(); + final JavaPsiFacade psiFacade = JavaPsiFacade.getInstance(project); + final PsiClass aClass = psiFacade.findClass(qualifierClass, scope); + if (aClass == null) { + return; + } + final String qualifiedName = aClass.getQualifiedName(); + if (qualifiedName == null) { + return; + } + final List imports = + getMatchingImports(importList, qualifiedName); + final CodeStyleSettings codeStyleSettings = CodeStyleSettingsManager.getSettings(project); + final PsiElementFactory elementFactory = psiFacade.getElementFactory(); + if (imports.size() < codeStyleSettings.NAMES_COUNT_TO_USE_IMPORT_ON_DEMAND) { + importList.add(elementFactory.createImportStaticStatement(aClass, memberName)); + } else { + for (PsiImportStaticStatement importStatement : imports) { + importStatement.delete(); + } + importList.add(elementFactory.createImportStaticStatement(aClass, "*")); + } + } + + private static PsiImportStaticStatement findOnDemandImportStaticStatement( + PsiImportList importList, String qualifierClass) { + final PsiImportStaticStatement[] importStaticStatements = + importList.getImportStaticStatements(); + for (PsiImportStaticStatement importStaticStatement : importStaticStatements) { + if (!importStaticStatement.isOnDemand()) { + continue; + } + final PsiJavaCodeReferenceElement importReference = + importStaticStatement.getImportReference(); + final String text = importReference.getText(); + if (qualifierClass.equals(text)) { + return importStaticStatement; + } + } + return null; + } + + private static List getMatchingImports( + @NotNull PsiImportList importList, @NotNull String className){ + final List imports = new ArrayList(); + for (PsiImportStaticStatement staticStatement : importList.getImportStaticStatements()) { + final PsiClass psiClass = staticStatement.resolveTargetClass(); + if (psiClass == null) { + continue; + } + if (!className.equals(psiClass.getQualifiedName())) { + continue; + } + imports.add(staticStatement); + } + return imports; + } + + private static boolean memberReferenced(PsiField field, PsiJavaFile javaFile) { + final MemberReferenceVisitor visitor = new MemberReferenceVisitor(field); + javaFile.accept(visitor); + return visitor.isReferenceFound(); + } + + private static boolean membersReferenced(PsiMethod[] methods, PsiJavaFile javaFile) { + final MemberReferenceVisitor visitor = new MemberReferenceVisitor(methods); + javaFile.accept(visitor); + return visitor.isReferenceFound(); + } + + private static class MemberReferenceVisitor extends JavaRecursiveElementVisitor { + + private final PsiMember[] members; + private boolean referenceFound = false; + + public MemberReferenceVisitor(PsiField field) { + this.members = new PsiMember[]{field}; + } + + public MemberReferenceVisitor(PsiMethod[] methods) { + this.members = methods; + } + + @Override + public void visitReferenceElement(PsiJavaCodeReferenceElement reference) { + if (referenceFound) { + return; + } + super.visitReferenceElement(reference); + if (reference.isQualified()) { + return; + } + final PsiElement target = reference.resolve(); + for (PsiMember member : members) { + if (member.equals(target)) { + referenceFound = true; + return; + } + } + } + + public boolean isReferenceFound() { + return referenceFound; + } + } + private static class ClassReferenceVisitor extends JavaRecursiveElementVisitor{ @@ -268,7 +505,6 @@ public class ImportUtils{ this.fullyQualifiedName = fullyQualifiedName; } - @Override public void visitReferenceElement( PsiJavaCodeReferenceElement reference) { super.visitReferenceElement(reference);