View Javadoc
1   /*
2    * SPDX-FileCopyrightText: Copyright (c) 2011-2026 Yegor Bugayenko
3    * SPDX-License-Identifier: MIT
4    */
5   package com.qulice.checkstyle;
6   
7   import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
8   import com.puppycrawl.tools.checkstyle.api.DetailAST;
9   import com.puppycrawl.tools.checkstyle.api.TokenTypes;
10  import java.util.Arrays;
11  
12  /**
13   * Checks method bodies for comments. All comments in method bodies are
14   * prohibited.
15   *
16   * <p>We believe that in-code comments and empty lines are evil. If you
17   * need to use
18   * a comment inside a method - your code needs refactoring. Either move that
19   * comment to a method javadoc block or add a logging mechanism with the same
20   * text.</p>
21   *
22   * @since 0.3
23   */
24  public final class MethodBodyCommentsCheck extends AbstractCheck {
25  
26      /**
27       * Default constructor.
28       */
29      public MethodBodyCommentsCheck() {
30          // nothing to initialize
31      }
32  
33      @Override
34      public int[] getDefaultTokens() {
35          return new int[] {
36              TokenTypes.CTOR_DEF,
37              TokenTypes.METHOD_DEF,
38          };
39      }
40  
41      @Override
42      public int[] getAcceptableTokens() {
43          return this.getDefaultTokens();
44      }
45  
46      @Override
47      public int[] getRequiredTokens() {
48          return this.getDefaultTokens();
49      }
50  
51      @Override
52      public void visitToken(final DetailAST ast) {
53          final DetailAST start = ast.findFirstToken(TokenTypes.SLIST);
54          if (start != null) {
55              final String[] lines = Arrays.copyOf(
56                  this.getLines(), this.getLines().length
57              );
58              this.maskAnonymous(start, lines);
59              this.checkMethod(
60                  lines,
61                  start.getLineNo(),
62                  start.findFirstToken(TokenTypes.RCURLY).getLineNo() - 1
63              );
64          }
65      }
66  
67      private void maskAnonymous(final DetailAST node, final String... lines) {
68          for (DetailAST child = node.getFirstChild(); child != null;
69              child = child.getNextSibling()) {
70              if (child.getType() == TokenTypes.LITERAL_NEW) {
71                  final DetailAST block = child.findFirstToken(
72                      TokenTypes.OBJBLOCK
73                  );
74                  if (block != null) {
75                      Arrays.fill(
76                          lines, block.getLineNo(),
77                          block.findFirstToken(TokenTypes.RCURLY).getLineNo(),
78                          ""
79                      );
80                  }
81              }
82              this.maskAnonymous(child, lines);
83          }
84      }
85  
86      private void checkMethod(final String[] lines, final int start,
87          final int end) {
88          final boolean oneliner = start == end - 1;
89          for (int pos = start; pos < end; ++pos) {
90              final String line = lines[pos].trim();
91              if (line.startsWith("//") || line.startsWith("/*")) {
92                  final String comment = line.substring(2).trim();
93                  if (!comment.startsWith("@checkstyle") && !oneliner
94                      && !MethodBodyCommentsCheck.alone(lines, pos)) {
95                      this.log(pos + 1, "Comments in method body are prohibited");
96                  }
97              }
98          }
99      }
100 
101     private static boolean alone(final String[] lines, final int pos) {
102         return pos > 0 && pos + 1 < lines.length
103             && lines[pos - 1].trim().endsWith("{")
104             && lines[pos + 1].trim().startsWith("}");
105     }
106 }