diff --git a/rewrite-java-11/src/main/java/org/openrewrite/java/isolated/ReloadableJava11ParserVisitor.java b/rewrite-java-11/src/main/java/org/openrewrite/java/isolated/ReloadableJava11ParserVisitor.java index 4c8b2709c6..9e7e360216 100644 --- a/rewrite-java-11/src/main/java/org/openrewrite/java/isolated/ReloadableJava11ParserVisitor.java +++ b/rewrite-java-11/src/main/java/org/openrewrite/java/isolated/ReloadableJava11ParserVisitor.java @@ -2186,6 +2186,7 @@ private ReloadableJava11ModifierResults sortedModifiersAndAnnotations(ModifiersT boolean afterFirstModifier = false; boolean inComment = false; boolean inMultilineComment = false; + int multilineCommentStart = -1; final AtomicReference word = new AtomicReference<>(""); int afterLastModifierPosition = cursor; int lastAnnotationPosition = cursor; @@ -2208,16 +2209,18 @@ private ReloadableJava11ModifierResults sortedModifiersAndAnnotations(ModifiersT continue; } char c = source.charAt(i); - if (c == '/' && source.length() > i + 1) { + if (c == '/' && source.length() > i + 1 && !inComment && !inMultilineComment) { char next = source.charAt(i + 1); if (next == '*') { inMultilineComment = true; + multilineCommentStart = i; } else if (next == '/') { inComment = true; } } - if (inMultilineComment && c == '/' && source.charAt(i - 1) == '*') { + // The closing `/` cannot be part of the opener, so `/*/` does not terminate a block comment. + if (inMultilineComment && c == '/' && i >= multilineCommentStart + 3 && source.charAt(i - 1) == '*') { inMultilineComment = false; } else if (inComment && (c == '\n' || c == '\r')) { inComment = false; @@ -2321,6 +2324,7 @@ private List collectAnnotations(Map annotat List annotations = new ArrayList<>(); boolean inComment = false; boolean inMultilineComment = false; + int multilineCommentStart = -1; for (int i = cursor; i <= maxAnnotationPosition && i < source.length(); i++) { if (annotationPosTable.containsKey(i)) { JCAnnotation jcAnnotation = annotationPosTable.get(i); @@ -2333,16 +2337,18 @@ private List collectAnnotations(Map annotat continue; } char c = source.charAt(i); - if (c == '/' && source.length() > i + 1) { + if (c == '/' && source.length() > i + 1 && !inComment && !inMultilineComment) { char next = source.charAt(i + 1); if (next == '*') { inMultilineComment = true; + multilineCommentStart = i; } else if (next == '/') { inComment = true; } } - if (inMultilineComment && c == '/' && i > 0 && source.charAt(i - 1) == '*') { + // The closing `/` cannot be part of the opener, so `/*/` does not terminate a block comment. + if (inMultilineComment && c == '/' && i >= multilineCommentStart + 3 && source.charAt(i - 1) == '*') { inMultilineComment = false; } else if (inComment && (c == '\n' || c == '\r')) { inComment = false; diff --git a/rewrite-java-17/src/main/java/org/openrewrite/java/isolated/ReloadableJava17ParserVisitor.java b/rewrite-java-17/src/main/java/org/openrewrite/java/isolated/ReloadableJava17ParserVisitor.java index e857c8eddd..e19f03b369 100644 --- a/rewrite-java-17/src/main/java/org/openrewrite/java/isolated/ReloadableJava17ParserVisitor.java +++ b/rewrite-java-17/src/main/java/org/openrewrite/java/isolated/ReloadableJava17ParserVisitor.java @@ -2341,6 +2341,7 @@ private ReloadableJava17ModifierResults sortedModifiersAndAnnotations(ModifiersT boolean afterFirstModifier = false; boolean inComment = false; boolean inMultilineComment = false; + int multilineCommentStart = -1; int afterLastModifierPosition = cursor; int lastAnnotationPosition = cursor; @@ -2362,16 +2363,18 @@ private ReloadableJava17ModifierResults sortedModifiersAndAnnotations(ModifiersT continue; } char c = source.charAt(i); - if (c == '/' && source.length() > i + 1) { + if (c == '/' && source.length() > i + 1 && !inComment && !inMultilineComment) { char next = source.charAt(i + 1); if (next == '*') { inMultilineComment = true; + multilineCommentStart = i; } else if (next == '/') { inComment = true; } } - if (inMultilineComment && c == '/' && source.charAt(i - 1) == '*') { + // The closing `/` cannot be part of the opener, so `/*/` does not terminate a block comment. + if (inMultilineComment && c == '/' && i >= multilineCommentStart + 3 && source.charAt(i - 1) == '*') { inMultilineComment = false; } else if (inComment && (c == '\n' || c == '\r')) { inComment = false; @@ -2473,6 +2476,7 @@ private List collectAnnotations(Map annotat List annotations = new ArrayList<>(); boolean inComment = false; boolean inMultilineComment = false; + int multilineCommentStart = -1; for (int i = cursor; i <= maxAnnotationPosition && i < source.length(); i++) { if (annotationPosTable.containsKey(i)) { JCAnnotation jcAnnotation = annotationPosTable.get(i); @@ -2485,16 +2489,18 @@ private List collectAnnotations(Map annotat continue; } char c = source.charAt(i); - if (c == '/' && source.length() > i + 1) { + if (c == '/' && source.length() > i + 1 && !inComment && !inMultilineComment) { char next = source.charAt(i + 1); if (next == '*') { inMultilineComment = true; + multilineCommentStart = i; } else if (next == '/') { inComment = true; } } - if (inMultilineComment && c == '/' && i > 0 && source.charAt(i - 1) == '*') { + // The closing `/` cannot be part of the opener, so `/*/` does not terminate a block comment. + if (inMultilineComment && c == '/' && i >= multilineCommentStart + 3 && source.charAt(i - 1) == '*') { inMultilineComment = false; } else if (inComment && (c == '\n' || c == '\r')) { inComment = false; diff --git a/rewrite-java-21/src/main/java/org/openrewrite/java/isolated/ReloadableJava21ParserVisitor.java b/rewrite-java-21/src/main/java/org/openrewrite/java/isolated/ReloadableJava21ParserVisitor.java index cd3892c049..5060e081b6 100644 --- a/rewrite-java-21/src/main/java/org/openrewrite/java/isolated/ReloadableJava21ParserVisitor.java +++ b/rewrite-java-21/src/main/java/org/openrewrite/java/isolated/ReloadableJava21ParserVisitor.java @@ -2379,6 +2379,7 @@ private ReloadableJava21ModifierResults sortedModifiersAndAnnotations(ModifiersT boolean afterFirstModifier = false; boolean inComment = false; boolean inMultilineComment = false; + int multilineCommentStart = -1; int afterLastModifierPosition = cursor; int lastAnnotationPosition = cursor; @@ -2400,16 +2401,18 @@ private ReloadableJava21ModifierResults sortedModifiersAndAnnotations(ModifiersT continue; } char c = source.charAt(i); - if (c == '/' && source.length() > i + 1) { + if (c == '/' && source.length() > i + 1 && !inComment && !inMultilineComment) { char next = source.charAt(i + 1); if (next == '*') { inMultilineComment = true; + multilineCommentStart = i; } else if (next == '/') { inComment = true; } } - if (inMultilineComment && c == '/' && source.charAt(i - 1) == '*') { + // The closing `/` cannot be part of the opener, so `/*/` does not terminate a block comment. + if (inMultilineComment && c == '/' && i >= multilineCommentStart + 3 && source.charAt(i - 1) == '*') { inMultilineComment = false; } else if (inComment && (c == '\n' || c == '\r')) { inComment = false; @@ -2511,6 +2514,7 @@ private List collectAnnotations(Map annotat List annotations = new ArrayList<>(); boolean inComment = false; boolean inMultilineComment = false; + int multilineCommentStart = -1; for (int i = cursor; i <= maxAnnotationPosition && i < source.length(); i++) { if (annotationPosTable.containsKey(i)) { JCAnnotation jcAnnotation = annotationPosTable.get(i); @@ -2523,16 +2527,18 @@ private List collectAnnotations(Map annotat continue; } char c = source.charAt(i); - if (c == '/' && source.length() > i + 1) { + if (c == '/' && source.length() > i + 1 && !inComment && !inMultilineComment) { char next = source.charAt(i + 1); if (next == '*') { inMultilineComment = true; + multilineCommentStart = i; } else if (next == '/') { inComment = true; } } - if (inMultilineComment && c == '/' && i > 0 && source.charAt(i - 1) == '*') { + // The closing `/` cannot be part of the opener, so `/*/` does not terminate a block comment. + if (inMultilineComment && c == '/' && i >= multilineCommentStart + 3 && source.charAt(i - 1) == '*') { inMultilineComment = false; } else if (inComment && (c == '\n' || c == '\r')) { inComment = false; diff --git a/rewrite-java-25/src/main/java/org/openrewrite/java/isolated/ReloadableJava25ParserVisitor.java b/rewrite-java-25/src/main/java/org/openrewrite/java/isolated/ReloadableJava25ParserVisitor.java index 22c3fbe09f..1ec18d89c0 100644 --- a/rewrite-java-25/src/main/java/org/openrewrite/java/isolated/ReloadableJava25ParserVisitor.java +++ b/rewrite-java-25/src/main/java/org/openrewrite/java/isolated/ReloadableJava25ParserVisitor.java @@ -2411,6 +2411,7 @@ private ReloadableJava25ModifierResults sortedModifiersAndAnnotations(ModifiersT boolean afterFirstModifier = false; boolean inComment = false; boolean inMultilineComment = false; + int multilineCommentStart = -1; int afterLastModifierPosition = cursor; int lastAnnotationPosition = cursor; @@ -2432,16 +2433,18 @@ private ReloadableJava25ModifierResults sortedModifiersAndAnnotations(ModifiersT continue; } char c = source.charAt(i); - if (c == '/' && source.length() > i + 1) { + if (c == '/' && source.length() > i + 1 && !inComment && !inMultilineComment) { char next = source.charAt(i + 1); if (next == '*') { inMultilineComment = true; + multilineCommentStart = i; } else if (next == '/') { inComment = true; } } - if (inMultilineComment && c == '/' && source.charAt(i - 1) == '*') { + // The closing `/` cannot be part of the opener, so `/*/` does not terminate a block comment. + if (inMultilineComment && c == '/' && i >= multilineCommentStart + 3 && source.charAt(i - 1) == '*') { inMultilineComment = false; } else if (inComment && (c == '\n' || c == '\r')) { inComment = false; @@ -2543,6 +2546,7 @@ private List collectAnnotations(Map annotat List annotations = new ArrayList<>(); boolean inComment = false; boolean inMultilineComment = false; + int multilineCommentStart = -1; for (int i = cursor; i <= maxAnnotationPosition && i < source.length(); i++) { if (annotationPosTable.containsKey(i)) { JCAnnotation jcAnnotation = annotationPosTable.get(i); @@ -2555,16 +2559,18 @@ private List collectAnnotations(Map annotat continue; } char c = source.charAt(i); - if (c == '/' && source.length() > i + 1) { + if (c == '/' && source.length() > i + 1 && !inComment && !inMultilineComment) { char next = source.charAt(i + 1); if (next == '*') { inMultilineComment = true; + multilineCommentStart = i; } else if (next == '/') { inComment = true; } } - if (inMultilineComment && c == '/' && i > 0 && source.charAt(i - 1) == '*') { + // The closing `/` cannot be part of the opener, so `/*/` does not terminate a block comment. + if (inMultilineComment && c == '/' && i >= multilineCommentStart + 3 && source.charAt(i - 1) == '*') { inMultilineComment = false; } else if (inComment && (c == '\n' || c == '\r')) { inComment = false; diff --git a/rewrite-java-8/src/main/java/org/openrewrite/java/ReloadableJava8ParserVisitor.java b/rewrite-java-8/src/main/java/org/openrewrite/java/ReloadableJava8ParserVisitor.java index 719cb1271c..1a8cc07007 100644 --- a/rewrite-java-8/src/main/java/org/openrewrite/java/ReloadableJava8ParserVisitor.java +++ b/rewrite-java-8/src/main/java/org/openrewrite/java/ReloadableJava8ParserVisitor.java @@ -2168,6 +2168,7 @@ private Java8ModifierResults sortedModifiersAndAnnotations(ModifiersTree modifie boolean afterFirstModifier = false; boolean inComment = false; boolean inMultilineComment = false; + int multilineCommentStart = -1; final AtomicReference word = new AtomicReference<>(""); int afterLastModifierPosition = cursor; int lastAnnotationPosition = cursor; @@ -2190,16 +2191,18 @@ private Java8ModifierResults sortedModifiersAndAnnotations(ModifiersTree modifie continue; } char c = source.charAt(i); - if (c == '/' && source.length() > i + 1) { + if (c == '/' && source.length() > i + 1 && !inComment && !inMultilineComment) { char next = source.charAt(i + 1); if (next == '*') { inMultilineComment = true; + multilineCommentStart = i; } else if (next == '/') { inComment = true; } } - if (inMultilineComment && c == '/' && source.charAt(i - 1) == '*') { + // The closing `/` cannot be part of the opener, so `/*/` does not terminate a block comment. + if (inMultilineComment && c == '/' && i >= multilineCommentStart + 3 && source.charAt(i - 1) == '*') { inMultilineComment = false; } else if (inComment && (c == '\n' || c == '\r')) { inComment = false; @@ -2303,6 +2306,7 @@ private List collectAnnotations(Map annotat List annotations = new ArrayList<>(); boolean inComment = false; boolean inMultilineComment = false; + int multilineCommentStart = -1; for (int i = cursor; i <= maxAnnotationPosition && i < source.length(); i++) { if (annotationPosTable.containsKey(i)) { JCAnnotation jcAnnotation = annotationPosTable.get(i); @@ -2315,16 +2319,18 @@ private List collectAnnotations(Map annotat continue; } char c = source.charAt(i); - if (c == '/' && source.length() > i + 1) { + if (c == '/' && source.length() > i + 1 && !inComment && !inMultilineComment) { char next = source.charAt(i + 1); if (next == '*') { inMultilineComment = true; + multilineCommentStart = i; } else if (next == '/') { inComment = true; } } - if (inMultilineComment && c == '/' && i > 0 && source.charAt(i - 1) == '*') { + // The closing `/` cannot be part of the opener, so `/*/` does not terminate a block comment. + if (inMultilineComment && c == '/' && i >= multilineCommentStart + 3 && source.charAt(i - 1) == '*') { inMultilineComment = false; } else if (inComment && (c == '\n' || c == '\r')) { inComment = false; diff --git a/rewrite-java-tck/src/main/java/org/openrewrite/java/tree/CommentTest.java b/rewrite-java-tck/src/main/java/org/openrewrite/java/tree/CommentTest.java index 2cc970f222..fa9410542f 100644 --- a/rewrite-java-tck/src/main/java/org/openrewrite/java/tree/CommentTest.java +++ b/rewrite-java-tck/src/main/java/org/openrewrite/java/tree/CommentTest.java @@ -52,6 +52,82 @@ class Test {// /* ); } + @Test + void multilineOpenerInsideSingleLineCommentBeforeMethodModifier() { + rewriteRun( + java( + """ + class Test { + @Deprecated //*not a block comment + public String value() { + return null; + } + } + """ + ) + ); + } + + @Test + void multilineOpenerInsideSingleLineCommentBeforeMultipleModifiers() { + rewriteRun( + java( + """ + class Test { + @Deprecated //*not a block comment + public static String value() { + return null; + } + } + """ + ) + ); + } + + @Test + void multilineOpenerLaterInSingleLineCommentBeforeFieldModifier() { + rewriteRun( + java( + """ + class Test { + @Deprecated //see /*.java + public int value = 1; + } + """ + ) + ); + } + + @Test + void slashImmediatelyAfterMultilineOpenerBeforeModifier() { + rewriteRun( + java( + """ + class Test { + @Deprecated /*/ not the end of the comment */ public String value() { + return null; + } + } + """ + ) + ); + } + + @Test + void singleLineOpenerInsideMultilineCommentBeforeModifiers() { + rewriteRun( + java( + """ + class Test { + @Deprecated /* // */ public static String value() { + return null; + } + } + """ + ) + ); + } + @Issue("https://github.com/openrewrite/rewrite/issues/4995") @Test void trailingComment() {