20
20
import static com .google .common .collect .Iterables .getOnlyElement ;
21
21
import static com .google .errorprone .BugPattern .SeverityLevel .WARNING ;
22
22
import static com .google .errorprone .matchers .Description .NO_MATCH ;
23
+ import static com .google .errorprone .matchers .Matchers .anyOf ;
23
24
import static com .google .errorprone .matchers .method .MethodMatchers .instanceMethod ;
24
25
import static com .google .errorprone .util .ASTHelpers .getStartPosition ;
26
+ import static com .google .errorprone .util .ASTHelpers .getType ;
27
+ import static com .google .errorprone .util .ASTHelpers .isSubtype ;
25
28
import static com .google .errorprone .util .Regexes .convertRegexToLiteral ;
26
29
import static java .lang .String .format ;
27
30
46
49
import com .sun .source .util .TreePath ;
47
50
import com .sun .source .util .TreePathScanner ;
48
51
import com .sun .tools .javac .code .Symbol .VarSymbol ;
52
+ import com .sun .tools .javac .code .Type ;
49
53
import java .util .ArrayList ;
50
54
import java .util .List ;
51
55
import java .util .Objects ;
59
63
public class StringSplitter extends BugChecker implements MethodInvocationTreeMatcher {
60
64
61
65
private static final Matcher <ExpressionTree > MATCHER =
62
- instanceMethod ()
63
- .onExactClass ("java.lang.String" )
64
- .named ("split" )
65
- .withParameters ("java.lang.String" );
66
+ anyOf (
67
+ instanceMethod ()
68
+ .onExactClass ("java.lang.String" )
69
+ .named ("split" )
70
+ .withParameters ("java.lang.String" ),
71
+ instanceMethod ()
72
+ .onExactClass ("java.util.regex.Pattern" )
73
+ .named ("split" )
74
+ .withParameters ("java.lang.CharSequence" ));
66
75
67
76
@ Override
68
77
public Description matchMethodInvocation (MethodInvocationTree tree , VisitorState state ) {
@@ -78,20 +87,14 @@ public Description matchMethodInvocation(MethodInvocationTree tree, VisitorState
78
87
}
79
88
80
89
public Optional <Fix > buildFix (MethodInvocationTree tree , VisitorState state ) {
81
- Tree arg = getOnlyElement (tree .getArguments ());
82
- String methodAndArgument = getMethodAndArgument (arg , state );
90
+ ExpressionTree arg = getOnlyElement (tree .getArguments ());
83
91
Tree parent = state .getPath ().getParentPath ().getLeaf ();
84
92
if (parent instanceof EnhancedForLoopTree
85
93
&& ((EnhancedForLoopTree ) parent ).getExpression ().equals (tree )) {
86
94
// fix for `for (... : s.split(...)) {}` -> `for (... : Splitter.on(...).split(s)) {}`
87
95
return Optional .of (
88
96
replaceWithSplitter (
89
- SuggestedFix .builder (),
90
- tree ,
91
- methodAndArgument ,
92
- state ,
93
- "split" ,
94
- /* mutableList= */ false )
97
+ SuggestedFix .builder (), tree , arg , state , "split" , /* mutableList= */ false )
95
98
.build ());
96
99
}
97
100
if (parent instanceof ArrayAccessTree ) {
@@ -115,9 +118,7 @@ public Optional<Fix> buildFix(MethodInvocationTree tree, VisitorState state) {
115
118
state .getEndPosition (arrayAccessTree ),
116
119
")" );
117
120
return Optional .of (
118
- replaceWithSplitter (
119
- fix , tree , methodAndArgument , state , "split" , /* mutableList= */ false )
120
- .build ());
121
+ replaceWithSplitter (fix , tree , arg , state , "split" , /* mutableList= */ false ).build ());
121
122
}
122
123
// If the result of split is assigned to a variable, try to fix all uses of the variable in the
123
124
// enclosing method. If we don't know how to fix any of them, bail out.
@@ -218,12 +219,12 @@ public Boolean visitMemberSelect(MemberSelectTree tree, Void unused) {
218
219
if (!isImplicitlyTyped ) {
219
220
fix .replace (varType , "List<String>" ).addImport ("java.util.List" );
220
221
}
221
- replaceWithSplitter (fix , tree , methodAndArgument , state , "splitToList" , needsMutableList [0 ]);
222
+ replaceWithSplitter (fix , tree , arg , state , "splitToList" , needsMutableList [0 ]);
222
223
} else {
223
224
if (!isImplicitlyTyped ) {
224
225
fix .replace (varType , "Iterable<String>" );
225
226
}
226
- replaceWithSplitter (fix , tree , methodAndArgument , state , "split" , needsMutableList [0 ]);
227
+ replaceWithSplitter (fix , tree , arg , state , "split" , needsMutableList [0 ]);
227
228
}
228
229
return Optional .of (fix .build ());
229
230
}
@@ -258,24 +259,42 @@ private static String getMethodAndArgument(Tree origArg, VisitorState state) {
258
259
private SuggestedFix .Builder replaceWithSplitter (
259
260
SuggestedFix .Builder fix ,
260
261
MethodInvocationTree tree ,
261
- String methodAndArgument ,
262
+ ExpressionTree arg ,
262
263
VisitorState state ,
263
264
String splitMethod ,
264
265
boolean mutableList ) {
265
266
ExpressionTree receiver = ASTHelpers .getReceiver (tree );
266
267
if (mutableList ) {
267
268
fix .addImport ("java.util.ArrayList" );
268
269
}
269
- return fix .addImport ("com.google.common.base.Splitter" )
270
- .prefixWith (
271
- receiver ,
272
- String .format (
273
- "%sSplitter.%s.%s(" ,
274
- (mutableList ? "new ArrayList<>(" : "" ), methodAndArgument , splitMethod ))
275
- .replace (
276
- state .getEndPosition (receiver ),
277
- state .getEndPosition (tree ),
278
- (mutableList ? ")" : "" ) + ")" );
270
+ fix .addImport ("com.google.common.base.Splitter" );
271
+ Type receiverType = getType (receiver );
272
+ if (isSubtype (receiverType , state .getSymtab ().stringType , state )) {
273
+ String methodAndArgument = getMethodAndArgument (arg , state );
274
+ return fix .prefixWith (
275
+ receiver ,
276
+ String .format (
277
+ "%sSplitter.%s.%s(" ,
278
+ (mutableList ? "new ArrayList<>(" : "" ), methodAndArgument , splitMethod ))
279
+ .replace (
280
+ state .getEndPosition (receiver ),
281
+ state .getEndPosition (tree ),
282
+ (mutableList ? ")" : "" ) + ")" );
283
+ }
284
+ if (isSubtype (receiverType , state .getTypeFromString ("java.util.regex.Pattern" ), state )) {
285
+ return fix .prefixWith (
286
+ receiver , String .format ("%sSplitter.on(" , (mutableList ? "new ArrayList<>(" : "" )))
287
+ .postfixWith (receiver , ")" )
288
+ .replace (
289
+ /* startPos= */ state .getEndPosition (receiver ),
290
+ /* endPos= */ getStartPosition (arg ),
291
+ String .format (".%s(" , splitMethod ))
292
+ .replace (
293
+ state .getEndPosition (arg ),
294
+ state .getEndPosition (tree ),
295
+ (mutableList ? ")" : "" ) + ")" );
296
+ }
297
+ throw new AssertionError (receiver );
279
298
}
280
299
281
300
private TreePath findEnclosing (VisitorState state ) {
0 commit comments