How to add a line comment just after a method with OpenRewrite

316 Views Asked by At

What I want to do

I am trying to add single-line comments around a method annotated with Lombok's @Generated to tell Parasoft Jtest to suppress the reporting of findings in the method like this:

class FooBar {
    // parasoft-begin-suppress ALL
    @Generated
    public String hello() {
        return "Hello from com.yourorg.FooBar!";
    }
    //parasoft-end-suppress ALL
}

What I tried

I have found that JavaParser breaks up single-line comments at the end of the line (see How to prepend to move a line comment to the middle of field declaration with JavaParser), so I have tried to write an OpenRewrite recipe. I have successfully added the comment before the annotation with this code:

public class JtestSuppressDelombokVisitor extends JavaIsoVisitor<ExecutionContext> {
    @Override
    public MethodDeclaration visitMethodDeclaration(MethodDeclaration methodDecl, ExecutionContext context) {
        boolean hasGeneratedAnnotation = methodDecl.getLeadingAnnotations().stream()
                .anyMatch(annotation -> annotation.getType().toString().equals("lombok.Generated"));

        Iterator<Comment> it = methodDecl.getPrefix().getComments().iterator();
        boolean alreadyHasSuppressComment = false;
        while (it.hasNext()) {
            Comment comment = it.next();
            PrintOutputCapture<String> p = new PrintOutputCapture<String>("");
            comment.printComment(this.getCursor(), p);
            if (p.out.toString().matches(".*parasoft-begin-suppress\sALL.*")) {
                alreadyHasSuppressComment = true;
                break;
            }
        }

        if (hasGeneratedAnnotation && !alreadyHasSuppressComment) {
            String methodDeclWhitespace = methodDecl.getPrefix().getWhitespace();
            if (methodDecl.getPrefix().getComments().size() == 0) {
                methodDecl = methodDecl.withComments(Arrays.asList(new TextComment(false, "parasoft-begin-suppress ALL", methodDeclWhitespace, Markers.EMPTY)));
            } else {
                methodDecl.getPrefix().getComments().add(new TextComment(false, "parasoft-begin-suppress ALL", methodDeclWhitespace, Markers.EMPTY));
            }
            return methodDecl;
        }

        return methodDecl;
    }
}

Problem

I couldn't add the comment after the method. I have tried three ways:

1. Create a comment with JavaTemplate and add it to the method

I have tried the following code, but nothing is added to the code.

final JavaTemplate endSuppressCommentTemplate =
        JavaTemplate.builder(this::getCursor, "// parasoft-end-suppress ALL").build();
methodDecl = methodDecl.withBody(methodDecl.getBody().withTemplate(
        endSuppressCommentTemplate,
        methodDecl.getBody().getCoordinates().lastStatement()
        ));

2. Added comment at the end of the method body

I have tried the following code.

methodDecl.getBody().getEnd().getComments().add(new TextComment(false, "parasoft-end-suppress ALL", "\n", Markers.EMPTY));

This code added the comment on the last line of the method (not after the method). This code also inserted the comment not only on the last line of the method but also on the other line and overwrote the comment before the annotation like this:

class FooBar {
    //parasoft-end-suppress ALL
@Generated
    //parasoft-end-suppress ALL
public String hello() {
        return "Hello from com.yourorg.FooBar!";
    //parasoft-end-suppress ALL
}

3. Create a Space instance with a comment and add it to the method

The following code resulted in the same as 2.

Space currentSpace = methodDecl.getBody().getEnd();
Space newSpace = Space.build(currentSpace.getWhitespace(), currentSpace.getComments());
newSpace.getComments().add(new TextComment(false, "parasoft-end-suppress ALL", "\n", Markers.EMPTY));
methodDecl = methodDecl.withBody(methodDecl.getBody().withEnd(newSpace));

Question

How can I add a line comment just after a method (or, at the end of a method) with an OpenRewrite recipe?

1

There are 1 best solutions below

0
On

By the given example code below

import lombok.Generated;

class A {
  // parasoft-begin-suppress ALL
  @Generated
  public String hello() {
      return "Hello from com.yourorg.FooBar!";
  }
  //parasoft-end-suppress ALL
  
  void method2() {
  }
  
  // comment2
}

If you debug and put a breakpoint anywhere in the method visitMethodDeclaration, evaluate expression TreeVisitingPrinter.printTree(getCursor());, you can get a printed LST tree looks like this

----J.CompilationUnit
    |-------J.Import | "import lombok.Generated"
    |       \---J.FieldAccess | "lombok.Generated"
    |           |---J.Identifier | "lombok"
    |           \-------J.Identifier | "Generated"
    \---J.ClassDeclaration
        |---J.Identifier | "A"
        \---J.Block                                                                                                     // J.Block.end.comments.get(0).text = "comment2"
            |-------J.MethodDeclaration | "MethodDeclaration{A{name=hello,return=java.lang.String,parameters=[]}}"      // J.MethodDeclaration.prefix.comments.get(0).text = "parasoft-begin-suppress ALL"
            |       |---J.Annotation | "@Generated"
            |       |   \---J.Identifier | "Generated"
            |       |---J.Modifier | "public"
            |       |---J.Identifier | "String"
            |       |---J.Identifier | "hello"
            |       |-----------J.Empty
            |       \---J.Block
            |           \-------J.Return | "return "Hello from com.yourorg.FooBar!""
            |                   \---J.Literal
            \-------J.MethodDeclaration | "MethodDeclaration{A{name=method2,return=void,parameters=[]}}"                // J.MethodDeclaration.prefix.comments.get(0).text = "parasoft-end-suppress ALL"
                    |---J.Primitive | "void"
                    |---J.Identifier | "method2"
                    |-----------J.Empty
                    \---J.Block

I put 3 comments there to indicate where is the comment located in the tree. you can see the expected method end comment parasoft-end-suppress ALL actually doesn't belong to the 1st method declaration, but the 2nd method declaration, or if there is only one method there, the comment comment2 is at the end of the J.Block. So the solution should be to add a method-end comment to the next method declaration prefix. or the parent block's end if there is not following method.

Hope this helps.

If you have further questions, welcome to ask in the openRewrite slack here