2121import static com .sun .tools .javac .code .Flags .GENERATED_MEMBER ;
2222import static java .lang .Math .max ;
2323import static java .lang .Math .min ;
24- import static java .util .Comparator .comparingInt ;
2524
2625import com .google .common .collect .ImmutableList ;
2726import com .google .common .collect .ImmutableSet ;
3332import com .sun .source .tree .CaseTree ;
3433import com .sun .source .tree .ClassTree ;
3534import com .sun .source .tree .CompilationUnitTree ;
36- import com .sun .source .tree .DoWhileLoopTree ;
3735import com .sun .source .tree .EmptyStatementTree ;
38- import com .sun .source .tree .EnhancedForLoopTree ;
39- import com .sun .source .tree .ForLoopTree ;
40- import com .sun .source .tree .IfTree ;
4136import com .sun .source .tree .ImportTree ;
4237import com .sun .source .tree .NewClassTree ;
43- import com .sun .source .tree .StatementTree ;
4438import com .sun .source .tree .Tree ;
4539import com .sun .source .tree .VariableTree ;
46- import com .sun .source .tree .WhileLoopTree ;
4740import com .sun .source .util .TreePathScanner ;
4841import com .sun .tools .javac .tree .JCTree ;
4942import com .sun .tools .javac .tree .JCTree .JCCompilationUnit ;
5043import com .sun .tools .javac .tree .TreeInfo ;
5144import java .util .ArrayList ;
5245import java .util .Collection ;
5346import java .util .List ;
54- import java .util .Map ;
55- import java .util .NavigableMap ;
56- import java .util .TreeMap ;
5747import org .jspecify .annotations .Nullable ;
5848
59- /// Scans the compilation unit for non-whitespace cleanup opportunities: inserting missing
60- /// control-flow braces and removing redundant semicolons.
49+ /// Scans the compilation unit for non-whitespace cleanup opportunities, such as removing redundant
50+ /// semicolons.
6151final class CleanupScanner extends TreePathScanner <@ Nullable Void , @ Nullable Void > {
6252
6353 // Replace with Flags.IMPLICIT_CLASS once JDK 25 is the minimum supported version
@@ -69,27 +59,14 @@ static ImmutableList<Replacement> scan(
6959 RangeSet <Integer > targetRanges = TreeRangeSet .create (ranges );
7060 ImmutableList <Tok > toks = JavaInput .buildToks (contents , ImmutableSet .of ());
7161 RangeSet <Integer > activeDeletions = TreeRangeSet .create ();
72- NavigableMap <Integer , StringBuilder > activeInsertions = new TreeMap <>();
73- new CleanupScanner (contents , unit , toks , targetRanges , activeDeletions , activeInsertions )
74- .scan (unit , null );
75- if (activeDeletions .isEmpty () && activeInsertions .isEmpty ()) {
62+ new CleanupScanner (contents , unit , toks , targetRanges , activeDeletions ).scan (unit , null );
63+ if (activeDeletions .isEmpty ()) {
7664 return ImmutableList .of ();
7765 }
7866 List <Replacement > replacements = new ArrayList <>();
7967 for (Range <Integer > del : activeDeletions .asRanges ()) {
80- int dStart = del .lowerEndpoint ();
81- int dEnd = del .upperEndpoint ();
82- var subMap =
83- activeInsertions .subMap (dStart , /* fromInclusive= */ true , dEnd , /* toInclusive= */ true );
84- String rep = String .join ("" , subMap .values ());
85- subMap .clear ();
86- replacements .add (Replacement .create (dStart , dEnd , rep ));
68+ replacements .add (Replacement .create (del .lowerEndpoint (), del .upperEndpoint (), "" ));
8769 }
88- for (Map .Entry <Integer , StringBuilder > entry : activeInsertions .entrySet ()) {
89- int pos = entry .getKey ();
90- replacements .add (Replacement .create (pos , pos , entry .getValue ().toString ()));
91- }
92- replacements .sort (comparingInt (r -> r .replaceRange ().lowerEndpoint ()));
9370 verifyCommentsPreserved (toks , contents , replacements );
9471 return ImmutableList .copyOf (replacements );
9572 }
@@ -128,31 +105,24 @@ private static void verifyCommentsPreserved(
128105 private final ImmutableList <Tok > toks ;
129106 private final RangeSet <Integer > targetRanges ;
130107 private final RangeSet <Integer > activeDeletions ;
131- private final NavigableMap <Integer , StringBuilder > activeInsertions ;
132108
133109 CleanupScanner (
134110 String contents ,
135111 JCCompilationUnit unit ,
136112 ImmutableList <Tok > toks ,
137113 RangeSet <Integer > targetRanges ,
138- RangeSet <Integer > activeDeletions ,
139- NavigableMap <Integer , StringBuilder > activeInsertions ) {
114+ RangeSet <Integer > activeDeletions ) {
140115 this .contents = contents ;
141116 this .unit = unit ;
142117 this .toks = toks ;
143118 this .targetRanges = targetRanges ;
144119 this .activeDeletions = activeDeletions ;
145- this .activeInsertions = activeInsertions ;
146120 }
147121
148122 private boolean inRange (int startPos , int endPos ) {
149123 return targetRanges .intersects (Range .closedOpen (startPos , endPos ));
150124 }
151125
152- private void insert (int pos , String text ) {
153- activeInsertions .computeIfAbsent (pos , k -> new StringBuilder ()).append (text );
154- }
155-
156126 private void deleteRedundantSemicolon (int semiPos ) {
157127 int start = semiPos ;
158128 while (start > 0 && isHorizontalWhitespace (contents .charAt (start - 1 ))) {
@@ -301,85 +271,6 @@ private void deleteSemicolonsInGaps(int start, int end, RangeSet<Integer> validS
301271 }
302272 }
303273
304- @ Override
305- public @ Nullable Void visitIf (IfTree tree , @ Nullable Void unused ) {
306- super .visitIf (tree , null );
307- checkBraces (tree , tree .getThenStatement ());
308- if (!(tree .getElseStatement () instanceof IfTree )) {
309- checkBraces (tree , tree .getElseStatement ());
310- }
311- return null ;
312- }
313-
314- @ Override
315- public @ Nullable Void visitForLoop (ForLoopTree tree , @ Nullable Void unused ) {
316- super .visitForLoop (tree , null );
317- checkBraces (tree , tree .getStatement ());
318- return null ;
319- }
320-
321- @ Override
322- public @ Nullable Void visitEnhancedForLoop (EnhancedForLoopTree tree , @ Nullable Void unused ) {
323- super .visitEnhancedForLoop (tree , null );
324- checkBraces (tree , tree .getStatement ());
325- return null ;
326- }
327-
328- @ Override
329- public @ Nullable Void visitWhileLoop (WhileLoopTree tree , @ Nullable Void unused ) {
330- super .visitWhileLoop (tree , null );
331- checkBraces (tree , tree .getStatement ());
332- return null ;
333- }
334-
335- @ Override
336- public @ Nullable Void visitDoWhileLoop (DoWhileLoopTree tree , @ Nullable Void unused ) {
337- super .visitDoWhileLoop (tree , null );
338- checkBraces (tree , tree .getStatement ());
339- return null ;
340- }
341-
342- private void checkBraces (Tree owner , @ Nullable StatementTree body ) {
343- if (body == null || body instanceof BlockTree ) {
344- return ;
345- }
346- int rangeEnd =
347- owner instanceof DoWhileLoopTree ? getEndPosition (owner , unit ) : getEndPosition (body , unit );
348- if (!inRange (getStartPosition (owner ), rangeEnd )) {
349- return ;
350- }
351- Tok prev = prevToken (getStartPosition (body ));
352- if (prev == null ) {
353- return ;
354- }
355- insert (prev .getPosition () + prev .length (), "{" );
356- insert (closingBraceInsertionPos (body ), "}" );
357- if (body instanceof EmptyStatementTree ) {
358- activeDeletions .add (Range .closedOpen (getStartPosition (body ), getEndPosition (body , unit )));
359- }
360- }
361-
362- private int closingBraceInsertionPos (StatementTree stmt ) {
363- int stmtEnd = getEndPosition (stmt , unit );
364- if (stmt instanceof EmptyStatementTree ) {
365- return stmtEnd ;
366- }
367- boolean sawComment = false ;
368- for (int i = tokIndexAtOrAfter (stmtEnd ); i < toks .size (); i ++) {
369- Tok tok = toks .get (i );
370- if (tok .isToken ()) {
371- return stmtEnd ;
372- }
373- if (tok .isNewline ()) {
374- return sawComment ? tok .getPosition () + tok .length () : stmtEnd ;
375- }
376- if (tok .isComment ()) {
377- sawComment = true ;
378- }
379- }
380- return stmtEnd ;
381- }
382-
383274 @ Override
384275 public @ Nullable Void visitEmptyStatement (EmptyStatementTree tree , @ Nullable Void unused ) {
385276 // Include CaseTree for empty statements in colon-style switch statement groups
@@ -410,16 +301,6 @@ private int tokIndexAtOrAfter(int pos) {
410301 return lo ;
411302 }
412303
413- private @ Nullable Tok prevToken (int pos ) {
414- for (int i = tokIndexAtOrAfter (pos ) - 1 ; i >= 0 ; i --) {
415- Tok tok = toks .get (i );
416- if (tok .isToken () && !tok .getText ().isEmpty ()) {
417- return tok ;
418- }
419- }
420- return null ;
421- }
422-
423304 private @ Nullable Tok findTokenAtOrAfter (int startPos , int endPos , String text ) {
424305 for (int i = tokIndexAtOrAfter (startPos ); i < toks .size (); i ++) {
425306 Tok tok = toks .get (i );
0 commit comments