34
34
import com .google .errorprone .bugpatterns .BadShiftAmount ;
35
35
import com .google .errorprone .bugpatterns .BugChecker ;
36
36
import com .google .errorprone .bugpatterns .BugChecker .ClassTreeMatcher ;
37
+ import com .google .errorprone .bugpatterns .BugChecker .VariableTreeMatcher ;
37
38
import com .google .errorprone .bugpatterns .ChainingConstructorIgnoresParameter ;
38
39
import com .google .errorprone .bugpatterns .Finally ;
39
40
import com .google .errorprone .fixes .SuggestedFix ;
42
43
import com .google .errorprone .util .ASTHelpers ;
43
44
import com .sun .source .tree .ClassTree ;
44
45
import com .sun .source .tree .MethodTree ;
46
+ import com .sun .source .tree .VariableTree ;
45
47
import com .sun .tools .javac .file .JavacFileManager ;
48
+ import com .sun .tools .javac .util .Constants ;
46
49
import java .io .ByteArrayOutputStream ;
47
50
import java .io .IOException ;
48
51
import java .io .InputStream ;
49
52
import java .io .OutputStream ;
50
53
import java .io .OutputStreamWriter ;
51
54
import java .io .PrintWriter ;
55
+ import java .nio .file .Files ;
56
+ import java .nio .file .Path ;
52
57
import java .util .Arrays ;
53
58
import java .util .Collections ;
54
59
import java .util .List ;
55
60
import java .util .Locale ;
61
+ import javax .inject .Inject ;
56
62
import javax .lang .model .SourceVersion ;
57
63
import javax .tools .DiagnosticListener ;
58
64
import javax .tools .JavaCompiler ;
59
65
import javax .tools .JavaFileObject ;
66
+ import javax .tools .SimpleJavaFileObject ;
60
67
import org .junit .Rule ;
61
68
import org .junit .Test ;
62
69
import org .junit .rules .TemporaryFolder ;
@@ -410,6 +417,150 @@ public void withExcludedPaths() {
410
417
assertThat (result .succeeded ).isFalse ();
411
418
}
412
419
420
+ @ BugPattern (summary = "Test bug pattern to test custom patch functionality" , severity = ERROR )
421
+ public static final class AssignmentUpdater extends BugChecker implements VariableTreeMatcher {
422
+ private final String newValue ;
423
+
424
+ @ Inject
425
+ AssignmentUpdater (ErrorProneFlags flags ) {
426
+ newValue = flags .get ("AssignmentUpdater:NewValue" ).orElse ("flag-not-set" );
427
+ }
428
+
429
+ @ Override
430
+ public Description matchVariable (VariableTree tree , VisitorState state ) {
431
+ return describeMatch (
432
+ tree , SuggestedFix .replace (tree .getInitializer (), Constants .format (newValue )));
433
+ }
434
+ }
435
+
436
+ @ Test
437
+ public void patchAll () throws IOException {
438
+ JavaFileObject fileObject =
439
+ createOnDiskFileObject (
440
+ "StringConstantWrapper.java" ,
441
+ """
442
+ class StringConstantWrapper {
443
+ String s = "old-value";
444
+ }
445
+ """ );
446
+
447
+ CompilationResult result =
448
+ doCompile (
449
+ Collections .singleton (fileObject ),
450
+ Arrays .asList ("-XepPatchChecks:" , "-XepPatchLocation:IN_PLACE" ),
451
+ ImmutableList .of (AssignmentUpdater .class ));
452
+ assertThat (result .succeeded ).isTrue ();
453
+ assertThat (Files .readString (Path .of (fileObject .toUri ())))
454
+ .isEqualTo (
455
+ """
456
+ class StringConstantWrapper {
457
+ String s = "flag-not-set";
458
+ }
459
+ """ );
460
+ }
461
+
462
+ @ Test
463
+ public void patchAllWithCheckDisabled () throws IOException {
464
+ JavaFileObject fileObject =
465
+ createOnDiskFileObject (
466
+ "StringConstantWrapper.java" ,
467
+ """
468
+ class StringConstantWrapper {
469
+ String s = "old-value";
470
+ }
471
+ """ );
472
+
473
+ CompilationResult result =
474
+ doCompile (
475
+ Collections .singleton (fileObject ),
476
+ Arrays .asList (
477
+ "-XepPatchChecks:" , "-XepPatchLocation:IN_PLACE" , "-Xep:AssignmentUpdater:OFF" ),
478
+ ImmutableList .of (AssignmentUpdater .class ));
479
+ assertThat (result .succeeded ).isTrue ();
480
+ assertThat (Files .readString (Path .of (fileObject .toUri ())))
481
+ .isEqualTo (
482
+ """
483
+ class StringConstantWrapper {
484
+ String s = "old-value";
485
+ }
486
+ """ );
487
+ }
488
+
489
+ @ Test
490
+ public void patchSingleWithCheckDisabled () throws IOException {
491
+ JavaFileObject fileObject =
492
+ createOnDiskFileObject (
493
+ "StringConstantWrapper.java" ,
494
+ """
495
+ class StringConstantWrapper {
496
+ String s = "old-value";
497
+ }
498
+ """ );
499
+
500
+ CompilationResult result =
501
+ doCompile (
502
+ Collections .singleton (fileObject ),
503
+ Arrays .asList (
504
+ "-XepPatchChecks:AssignmentUpdater" ,
505
+ "-XepPatchLocation:IN_PLACE" ,
506
+ "-Xep:AssignmentUpdater:OFF" ),
507
+ ImmutableList .of (AssignmentUpdater .class ));
508
+ assertThat (result .succeeded ).isTrue ();
509
+ assertThat (Files .readString (Path .of (fileObject .toUri ())))
510
+ .isEqualTo (
511
+ """
512
+ class StringConstantWrapper {
513
+ String s = "old-value";
514
+ }
515
+ """ );
516
+ }
517
+
518
+ @ Test
519
+ public void patchSingleWithBugPatternCustomization () throws IOException {
520
+ JavaFileObject fileObject =
521
+ createOnDiskFileObject (
522
+ "StringConstantWrapper.java" ,
523
+ """
524
+ class StringConstantWrapper {
525
+ String s = "old-value";
526
+ }
527
+ """ );
528
+
529
+ CompilationResult result =
530
+ doCompile (
531
+ Collections .singleton (fileObject ),
532
+ Arrays .asList (
533
+ "-XepPatchChecks:AssignmentUpdater" ,
534
+ "-XepPatchLocation:IN_PLACE" ,
535
+ "-XepOpt:AssignmentUpdater:NewValue=new-value" ),
536
+ ImmutableList .of (AssignmentUpdater .class ));
537
+ assertThat (result .succeeded ).isTrue ();
538
+ assertThat (Files .readString (Path .of (fileObject .toUri ())))
539
+ .isEqualTo (
540
+ """
541
+ class StringConstantWrapper {
542
+ String s = "new-value";
543
+ }
544
+ """ );
545
+ }
546
+
547
+ /**
548
+ * Creates a {@link JavaFileObject} with matching on-disk contents.
549
+ *
550
+ * <p>This method aids in testing patching functionality, as {@code IN_PLACE} patching requires
551
+ * that compiled code actually exists on disk.
552
+ */
553
+ private JavaFileObject createOnDiskFileObject (String fileName , String source ) throws IOException {
554
+ Path location = tempDir .getRoot ().toPath ().resolve (fileName );
555
+ Files .writeString (location , source );
556
+ return new SimpleJavaFileObject (location .toUri (), SimpleJavaFileObject .Kind .SOURCE ) {
557
+ @ Override
558
+ public String getCharContent (boolean ignoreEncodingErrors ) {
559
+ return source ;
560
+ }
561
+ };
562
+ }
563
+
413
564
private static class CompilationResult {
414
565
public final boolean succeeded ;
415
566
public final String output ;
@@ -427,6 +578,14 @@ private CompilationResult doCompile(
427
578
List <String > fileNames ,
428
579
List <String > extraArgs ,
429
580
List <Class <? extends BugChecker >> customCheckers ) {
581
+ return doCompile (
582
+ forResources (getClass (), fileNames .toArray (new String [0 ])), extraArgs , customCheckers );
583
+ }
584
+
585
+ private CompilationResult doCompile (
586
+ Iterable <? extends JavaFileObject > files ,
587
+ List <String > extraArgs ,
588
+ List <Class <? extends BugChecker >> customCheckers ) {
430
589
DiagnosticTestHelper diagnosticHelper = new DiagnosticTestHelper ();
431
590
ByteArrayOutputStream outputStream = new ByteArrayOutputStream ();
432
591
PrintWriter printWriter = new PrintWriter (new OutputStreamWriter (outputStream , UTF_8 ), true );
@@ -441,12 +600,7 @@ private CompilationResult doCompile(
441
600
: new ErrorProneJavaCompiler (ScannerSupplier .fromBugCheckerClasses (customCheckers ));
442
601
JavaCompiler .CompilationTask task =
443
602
errorProneJavaCompiler .getTask (
444
- printWriter ,
445
- fileManager ,
446
- diagnosticHelper .collector ,
447
- args ,
448
- null ,
449
- forResources (getClass (), fileNames .toArray (new String [0 ])));
603
+ printWriter , fileManager , diagnosticHelper .collector , args , null , files );
450
604
451
605
return new CompilationResult (
452
606
task .call (), new String (outputStream .toByteArray (), UTF_8 ), diagnosticHelper );
0 commit comments