2020import org .openrewrite .TreeVisitor ;
2121import org .openrewrite .internal .lang .Nullable ;
2222import org .openrewrite .java .JavaIsoVisitor ;
23+ import org .openrewrite .java .search .FindMissingTypes ;
2324import org .openrewrite .java .search .UsesType ;
2425import org .openrewrite .java .tree .J ;
26+ import org .openrewrite .java .tree .JavaSourceFile ;
2527
2628import java .time .Duration ;
29+ import java .util .Arrays ;
30+ import java .util .List ;
31+ import java .util .stream .Collectors ;
2732
2833/**
29- * Orders imports and removes unused imports from classes which import symbols from the "org.mockito" package .
34+ * Removes unused "org.mockito" imports .
3035 */
3136public class CleanupMockitoImports extends Recipe {
3237 @ Override
@@ -36,7 +41,7 @@ public String getDisplayName() {
3641
3742 @ Override
3843 public String getDescription () {
39- return "Removes unused imports `org.mockito` import symbols." ;
44+ return "Removes unused `org.mockito` import symbols, unless its possible they are associated with method invocations having null or unknown type information ." ;
4045 }
4146
4247 @ Override
@@ -56,12 +61,45 @@ protected TreeVisitor<?, ExecutionContext> getSingleSourceApplicableTest() {
5661 }
5762
5863 public static class CleanupMockitoImportsVisitor extends JavaIsoVisitor <ExecutionContext > {
64+ private static final List <String > MOCKITO_METHOD_NAMES = Arrays .asList ("mock" , "mockingDetails" , "spy" ,
65+ "stub" , "when" , "verify" , "reset" , "verifyNoMoreInteractions" , "verifyZeroInteractions" , "stubVoid" ,
66+ "doThrow" , "doCallRealMethod" , "doAnswer" , "doNothing" , "doReturn" , "inOrder" , "ignoreStubs" ,
67+ "times" , "never" , "atLeastOnce" , "atLeast" , "atMost" , "calls" , "only" , "timeout" , "after" );
5968 @ Override
60- public J .Import visitImport (J .Import _import , ExecutionContext executionContext ) {
61- if (_import .getPackageName ().startsWith ("org.mockito" )) {
62- maybeRemoveImport (_import .getPackageName () + "." + _import .getClassName ());
69+ public JavaSourceFile visitJavaSourceFile (JavaSourceFile cu , ExecutionContext executionContext ) {
70+ JavaSourceFile sf = super .visitJavaSourceFile (cu , executionContext );
71+
72+ final List <String > unknownTypeMethodInvocationNames = FindMissingTypes .findMissingTypes (cu )
73+ .stream ().map (FindMissingTypes .MissingTypeResult ::getJ )
74+ .filter (J .MethodInvocation .class ::isInstance )
75+ .map (J .MethodInvocation .class ::cast )
76+ .map (J .MethodInvocation ::getSimpleName ).collect (Collectors .toList ());
77+
78+ for (J .Import _import : cu .getImports ()) {
79+ if (_import .getPackageName ().startsWith ("org.mockito" )) {
80+ if (_import .isStatic ()) {
81+ String staticName = _import .getQualid ().getSimpleName ();
82+ if ("*" .equals (staticName ) && !possibleMockitoMethod (unknownTypeMethodInvocationNames )) {
83+ maybeRemoveImport (_import .getPackageName () + "." + _import .getClassName ());
84+ } else if (!unknownTypeMethodInvocationNames .contains (staticName )) {
85+ maybeRemoveImport (_import .getPackageName () + "." + _import .getClassName () + "." + staticName );
86+ }
87+ } else {
88+ maybeRemoveImport (_import .getPackageName () + "." + _import .getClassName ());
89+ }
90+ }
91+ }
92+ return sf ;
93+ }
94+
95+ private boolean possibleMockitoMethod (List <String > methodNamesHavingNullType ) {
96+ for (String missingMethod : methodNamesHavingNullType ) {
97+ if (MOCKITO_METHOD_NAMES .contains (missingMethod )) {
98+ return true ;
99+ }
63100 }
64- return _import ;
101+
102+ return false ;
65103 }
66104 }
67105}
0 commit comments