23
23
import org .openrewrite .java .tree .*;
24
24
import org .openrewrite .staticanalysis .java .JavaFileChecker ;
25
25
26
+ import java .util .Collections ;
26
27
import java .util .List ;
27
28
import java .util .Optional ;
29
+ import java .util .stream .Collectors ;
28
30
29
31
public class LambdaBlockToExpression extends Recipe {
30
32
@ Override
@@ -70,7 +72,7 @@ public J.Lambda visitLambda(J.Lambda lambda, ExecutionContext ctx) {
70
72
71
73
@ Override
72
74
public J .MethodInvocation visitMethodInvocation (J .MethodInvocation method , ExecutionContext ctx ) {
73
- if (hasLambdaArgument (method ) && hasMethodOverloading (method )) {
75
+ if (hasLambdaArgument (method ) && hasAmbiguousMethodOverloading (method )) {
74
76
return method ;
75
77
}
76
78
return super .visitMethodInvocation (method , ctx );
@@ -90,34 +92,53 @@ private static boolean hasLambdaArgument(J.MethodInvocation method) {
90
92
return hasLambdaArgument ;
91
93
}
92
94
93
- // Check whether a method has overloading methods in the declaring class
94
- private static boolean hasMethodOverloading (J .MethodInvocation method ) {
95
+ // Check whether a method has overloading methods in the declaring class
96
+ static boolean hasAmbiguousMethodOverloading (J .MethodInvocation method ) {
95
97
JavaType .Method methodType = method .getMethodType ();
98
+
99
+ if (methodType == null ) {
100
+ return false ;
101
+ }
96
102
int numberOfArguments = method .getArguments ().size ();
97
- return methodType != null && hasMethodOverloading (methodType , numberOfArguments );
98
- }
99
103
100
104
// TODO this is actually more complex in the presence of generics and inheritance
101
- static boolean hasMethodOverloading (JavaType .Method methodType , int numberOfArguments ) {
102
105
String methodName = methodType .getName ();
103
- return Optional .of (methodType )
106
+
107
+ //all methods of the given type
108
+ List <JavaType .Method > methodsOfType = Optional .of (methodType )
104
109
.map (JavaType .Method ::getDeclaringType )
105
110
.filter (JavaType .Class .class ::isInstance )
106
111
.map (JavaType .Class .class ::cast )
107
112
.map (JavaType .Class ::getMethods )
108
- .map (methods -> {
109
- int overloadingCount = 0 ;
110
- for (JavaType .Method dm : methods ) {
111
- if (dm .getName ().equals (methodName ) &&
112
- dm .getParameterTypes ().size () == numberOfArguments ) {
113
- if (++overloadingCount > 1 ) {
114
- return true ;
115
- }
116
- }
117
- }
113
+ .orElse (Collections .emptyList ());
114
+
115
+ List <JavaType .Method > potentiallyOverLoadedMethods = methodsOfType .stream ()
116
+ .filter (dm -> dm .getName ().equals (methodName ))
117
+ .filter (dm -> dm .getParameterTypes ().size () == numberOfArguments )
118
+ .collect (Collectors .toList ());
119
+
120
+ //if there are less than 2 such methods, then there is no ambiguity
121
+ if (potentiallyOverLoadedMethods .size () <= 1 ) {
122
+ return false ;
123
+ }
124
+
125
+ //if there is a position where
126
+ // - the argument is a lambda
127
+ // - the parameters of all potential methods have the same type
128
+ // then there is no ambiguity
129
+ for (int i = 0 ; i < numberOfArguments ; i ++) {
130
+ int finalI = i ;
131
+ if (method .getArguments ().get (i ) instanceof J .Lambda ) {
132
+ long distinctElementsCount = potentiallyOverLoadedMethods .stream ()
133
+ .map (m -> m .getParameterTypes ().get (finalI ))
134
+ .distinct ().count ();
135
+ if (distinctElementsCount == 1 ) {
118
136
return false ;
119
- })
120
- .orElse (false );
137
+ }
138
+ }
139
+ }
140
+ //otherwise, there must be ambiguity
141
+ return true ;
121
142
}
122
143
123
144
}
0 commit comments