21
21
import static com .google .common .collect .Iterables .getOnlyElement ;
22
22
import static com .google .errorprone .BugPattern .SeverityLevel .WARNING ;
23
23
import static com .google .errorprone .bugpatterns .flogger .FloggerHelpers .inferFormatSpecifier ;
24
+ import static com .google .errorprone .bugpatterns .formatstring .LenientFormatStringUtils .getLenientFormatStringPosition ;
24
25
import static com .google .errorprone .matchers .Description .NO_MATCH ;
25
26
import static com .google .errorprone .matchers .Matchers .allOf ;
26
27
import static com .google .errorprone .matchers .Matchers .anyOf ;
29
30
import static com .google .errorprone .matchers .method .MethodMatchers .staticMethod ;
30
31
import static com .google .errorprone .util .ASTHelpers .getReceiver ;
31
32
import static com .google .errorprone .util .ASTHelpers .getType ;
32
- import static java .util .Arrays .stream ;
33
33
34
34
import com .google .common .base .Ascii ;
35
35
import com .google .common .collect .ImmutableMap ;
46
46
import com .sun .source .tree .MethodInvocationTree ;
47
47
import com .sun .tools .javac .code .Type ;
48
48
import com .sun .tools .javac .code .Type .ArrayType ;
49
+ import java .util .EnumSet ;
49
50
import java .util .List ;
50
51
import java .util .regex .Pattern ;
51
52
import java .util .stream .Stream ;
@@ -281,34 +282,58 @@ private static ExpressionTree getOnlyArgument(MethodInvocationTree invocation) {
281
282
private static final Matcher <ExpressionTree > LOG_MATCHER =
282
283
instanceMethod ().onDescendantOf ("com.google.common.flogger.LoggingApi" ).named ("log" );
283
284
284
- static final Matcher <ExpressionTree > UNWRAPPABLE =
285
- anyOf (stream (Unwrapper .values ()).map (u -> u .matcher ).collect (toImmutableList ()));
286
-
287
285
@ Override
288
286
public Description matchMethodInvocation (MethodInvocationTree tree , VisitorState state ) {
289
- if (!LOG_MATCHER .matches (tree , state )) {
290
- return NO_MATCH ;
291
- }
292
287
List <? extends ExpressionTree > arguments = tree .getArguments ();
293
- if (arguments .isEmpty ()) {
294
- return NO_MATCH ;
295
- }
296
- String formatString = ASTHelpers .constValue (arguments .get (0 ), String .class );
297
- if (formatString == null ) {
298
- return NO_MATCH ;
299
- }
300
- arguments = arguments .subList (1 , arguments .size ());
301
- if (arguments .stream ().noneMatch (argument -> UNWRAPPABLE .matches (argument , state ))) {
302
- return NO_MATCH ;
288
+ if (LOG_MATCHER .matches (tree , state )) {
289
+ if (arguments .isEmpty ()) {
290
+ return NO_MATCH ;
291
+ }
292
+ String formatString = ASTHelpers .constValue (arguments .get (0 ), String .class );
293
+ if (formatString == null ) {
294
+ return NO_MATCH ;
295
+ }
296
+ arguments = arguments .subList (1 , arguments .size ());
297
+ return unwrapArguments (formatString , tree , arguments , EnumSet .allOf (Unwrapper .class ), state );
298
+ } else {
299
+ var lenientFormatPosition = getLenientFormatStringPosition (tree , state );
300
+ EnumSet <Unwrapper > unwrappers =
301
+ EnumSet .of (Unwrapper .TO_STRING , Unwrapper .STRING_VALUE_OF , Unwrapper .STATIC_TO_STRING );
302
+ if (lenientFormatPosition != -1 ) {
303
+ for (int i = lenientFormatPosition + 1 ; i < arguments .size (); ++i ) {
304
+ var argument = arguments .get (i );
305
+ var unwrapper =
306
+ unwrappers .stream ()
307
+ .filter (u -> u .matcher .matches (argument , state ))
308
+ .findFirst ()
309
+ .orElse (null );
310
+ if (unwrapper == null ) {
311
+ continue ;
312
+ }
313
+ Parameter unwrapped = unwrapper .unwrap ((MethodInvocationTree ) argument , 's' );
314
+ state .reportMatch (
315
+ buildDescription (argument )
316
+ .setMessage (
317
+ "Avoid eagerly stringifying arguments to lenient format methods. The method"
318
+ + " will stringify if needed, and the evaluation may be lazy." )
319
+ .addFix (SuggestedFix .replace (argument , unwrapped .source .get (state )))
320
+ .build ());
321
+ }
322
+ }
303
323
}
304
- return unwrapArguments ( formatString , tree , arguments , state ) ;
324
+ return NO_MATCH ;
305
325
}
306
326
307
- Description unwrapArguments (
327
+ private Description unwrapArguments (
308
328
String formatString ,
309
329
MethodInvocationTree tree ,
310
330
List <? extends ExpressionTree > arguments ,
331
+ EnumSet <Unwrapper > unwrappers ,
311
332
VisitorState state ) {
333
+ if (arguments .stream ()
334
+ .noneMatch (arg -> unwrappers .stream ().anyMatch (u -> u .matcher .matches (arg , state )))) {
335
+ return NO_MATCH ;
336
+ }
312
337
SuggestedFix .Builder fix = SuggestedFix .builder ();
313
338
int start = 0 ;
314
339
java .util .regex .Matcher matcher = PRINTF_TERM_CAPTURE_PATTERN .matcher (formatString );
@@ -332,7 +357,7 @@ Description unwrapArguments(
332
357
if (term .length () == 2 ) {
333
358
ExpressionTree argument = arguments .get (idx );
334
359
char placeholder = term .charAt (1 );
335
- Parameter unwrapped = unwrap (argument , placeholder , state );
360
+ Parameter unwrapped = unwrap (argument , placeholder , unwrappers , state );
336
361
if (unwrapped != null ) {
337
362
fix .replace (argument , unwrapped .source .get (state ));
338
363
placeholder = firstNonNull (unwrapped .placeholder , 's' );
@@ -357,13 +382,15 @@ Description unwrapArguments(
357
382
}
358
383
359
384
private static @ Nullable Parameter unwrap (
360
- ExpressionTree argument , char placeholder , VisitorState state ) {
361
- for (Unwrapper unwrapper : Unwrapper .values ()) {
362
- if (unwrapper .matcher .matches (argument , state )) {
363
- return unwrapper .unwrap ((MethodInvocationTree ) argument , placeholder );
364
- }
365
- }
366
- return null ;
385
+ ExpressionTree argument ,
386
+ char placeholder ,
387
+ EnumSet <Unwrapper > unwrappers ,
388
+ VisitorState state ) {
389
+ return unwrappers .stream ()
390
+ .filter (unwrapper -> unwrapper .matcher .matches (argument , state ))
391
+ .map (unwrapper -> unwrapper .unwrap ((MethodInvocationTree ) argument , placeholder ))
392
+ .findFirst ()
393
+ .orElse (null );
367
394
}
368
395
369
396
private static boolean hasSingleVarargsCompatibleArgument (
0 commit comments