Coverage Report - com.puppycrawl.tools.checkstyle.checks.TrailingCommentCheck
 
Classes in this File Line Coverage Branch Coverage Complexity
TrailingCommentCheck
100%
43/43
100%
18/18
2.25
 
 1  
 ////////////////////////////////////////////////////////////////////////////////
 2  
 // checkstyle: Checks Java source code for adherence to a set of rules.
 3  
 // Copyright (C) 2001-2017 the original author or authors.
 4  
 //
 5  
 // This library is free software; you can redistribute it and/or
 6  
 // modify it under the terms of the GNU Lesser General Public
 7  
 // License as published by the Free Software Foundation; either
 8  
 // version 2.1 of the License, or (at your option) any later version.
 9  
 //
 10  
 // This library is distributed in the hope that it will be useful,
 11  
 // but WITHOUT ANY WARRANTY; without even the implied warranty of
 12  
 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 13  
 // Lesser General Public License for more details.
 14  
 //
 15  
 // You should have received a copy of the GNU Lesser General Public
 16  
 // License along with this library; if not, write to the Free Software
 17  
 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 18  
 ////////////////////////////////////////////////////////////////////////////////
 19  
 
 20  
 package com.puppycrawl.tools.checkstyle.checks;
 21  
 
 22  
 import java.util.HashSet;
 23  
 import java.util.List;
 24  
 import java.util.Map;
 25  
 import java.util.Set;
 26  
 import java.util.regex.Pattern;
 27  
 
 28  
 import com.puppycrawl.tools.checkstyle.StatelessCheck;
 29  
 import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
 30  
 import com.puppycrawl.tools.checkstyle.api.DetailAST;
 31  
 import com.puppycrawl.tools.checkstyle.api.TextBlock;
 32  
 import com.puppycrawl.tools.checkstyle.utils.CommonUtils;
 33  
 
 34  
 /**
 35  
  * <p>
 36  
  * The check to ensure that comments are the only thing on a line.
 37  
  * For the case of // comments that means that the only thing that should
 38  
  * precede it is whitespace.
 39  
  * It doesn't check comments if they do not end line, i.e. it accept
 40  
  * the following:
 41  
  * {@code Thread.sleep( 10 &lt;some comment here&gt; );}
 42  
  * Format property is intended to deal with the "} // while" example.
 43  
  * </p>
 44  
  *
 45  
  * <p>Rationale: Steve McConnell in &quot;Code Complete&quot; suggests that endline
 46  
  * comments are a bad practice. An end line comment would
 47  
  * be one that is on the same line as actual code. For example:
 48  
  * <pre>
 49  
  *  a = b + c;      // Some insightful comment
 50  
  *  d = e / f;        // Another comment for this line
 51  
  * </pre>
 52  
  * Quoting &quot;Code Complete&quot; for the justification:
 53  
  * <ul>
 54  
  * <li>
 55  
  * &quot;The comments have to be aligned so that they do not
 56  
  * interfere with the visual structure of the code. If you don't
 57  
  * align them neatly, they'll make your listing look like it's been
 58  
  * through a washing machine.&quot;
 59  
  * </li>
 60  
  * <li>
 61  
  * &quot;Endline comments tend to be hard to format...It takes time
 62  
  * to align them. Such time is not spent learning more about
 63  
  * the code; it's dedicated solely to the tedious task of
 64  
  * pressing the spacebar or tab key.&quot;
 65  
  * </li>
 66  
  * <li>
 67  
  * &quot;Endline comments are also hard to maintain. If the code on
 68  
  * any line containing an endline comment grows, it bumps the
 69  
  * comment farther out, and all the other endline comments will
 70  
  * have to bumped out to match. Styles that are hard to
 71  
  * maintain aren't maintained....&quot;
 72  
  * </li>
 73  
  * <li>
 74  
  * &quot;Endline comments also tend to be cryptic. The right side of
 75  
  * the line doesn't offer much room and the desire to keep the
 76  
  * comment on one line means the comment must be short.
 77  
  * Work then goes into making the line as short as possible
 78  
  * instead of as clear as possible. The comment usually ends
 79  
  * up as cryptic as possible....&quot;
 80  
  * </li>
 81  
  * <li>
 82  
  * &quot;A systemic problem with endline comments is that it's hard
 83  
  * to write a meaningful comment for one line of code. Most
 84  
  * endline comments just repeat the line of code, which hurts
 85  
  * more than it helps.&quot;
 86  
  * </li>
 87  
  * </ul>
 88  
  * His comments on being hard to maintain when the size of
 89  
  * the line changes are even more important in the age of
 90  
  * automated refactorings.
 91  
  *
 92  
  * <p>To configure the check so it enforces only comment on a line:
 93  
  * <pre>
 94  
  * &lt;module name=&quot;TrailingComment&quot;&gt;
 95  
  *    &lt;property name=&quot;format&quot; value=&quot;^\\s*$&quot;/&gt;
 96  
  * &lt;/module&gt;
 97  
  * </pre>
 98  
  *
 99  
  * @author o_sukhodolsky
 100  
  */
 101  
 @StatelessCheck
 102  14
 public class TrailingCommentCheck extends AbstractCheck {
 103  
 
 104  
     /**
 105  
      * A key is pointing to the warning message text in "messages.properties"
 106  
      * file.
 107  
      */
 108  
     public static final String MSG_KEY = "trailing.comments";
 109  
 
 110  
     /** Pattern for legal trailing comment. */
 111  
     private Pattern legalComment;
 112  
 
 113  
     /** The regexp to match against. */
 114  14
     private Pattern format = Pattern.compile("^[\\s});]*$");
 115  
 
 116  
     /**
 117  
      * Sets patter for legal trailing comments.
 118  
      * @param legalComment pattern to set.
 119  
      */
 120  
     public void setLegalComment(final Pattern legalComment) {
 121  1
         this.legalComment = legalComment;
 122  1
     }
 123  
 
 124  
     /**
 125  
      * Set the format for the specified regular expression.
 126  
      * @param pattern a pattern
 127  
      */
 128  
     public final void setFormat(Pattern pattern) {
 129  2
         format = pattern;
 130  2
     }
 131  
 
 132  
     @Override
 133  
     public int[] getDefaultTokens() {
 134  10
         return getRequiredTokens();
 135  
     }
 136  
 
 137  
     @Override
 138  
     public int[] getAcceptableTokens() {
 139  5
         return getRequiredTokens();
 140  
     }
 141  
 
 142  
     @Override
 143  
     public int[] getRequiredTokens() {
 144  26
         return CommonUtils.EMPTY_INT_ARRAY;
 145  
     }
 146  
 
 147  
     @Override
 148  
     public void visitToken(DetailAST ast) {
 149  1
         throw new IllegalStateException("visitToken() shouldn't be called.");
 150  
     }
 151  
 
 152  
     @Override
 153  
     public void beginTree(DetailAST rootAST) {
 154  4
         final Map<Integer, TextBlock> cppComments = getFileContents()
 155  4
                 .getSingleLineComments();
 156  4
         final Map<Integer, List<TextBlock>> cComments = getFileContents()
 157  4
                 .getBlockComments();
 158  4
         final Set<Integer> lines = new HashSet<>();
 159  4
         lines.addAll(cppComments.keySet());
 160  4
         lines.addAll(cComments.keySet());
 161  
 
 162  4
         for (Integer lineNo : lines) {
 163  45
             final String line = getLines()[lineNo - 1];
 164  
             final String lineBefore;
 165  
             final TextBlock comment;
 166  45
             if (cppComments.containsKey(lineNo)) {
 167  15
                 comment = cppComments.get(lineNo);
 168  15
                 lineBefore = line.substring(0, comment.getStartColNo());
 169  
             }
 170  
             else {
 171  30
                 final List<TextBlock> commentList = cComments.get(lineNo);
 172  30
                 comment = commentList.get(commentList.size() - 1);
 173  30
                 lineBefore = line.substring(0, comment.getStartColNo());
 174  
 
 175  
                 // do not check comment which doesn't end line
 176  30
                 if (comment.getText().length == 1
 177  21
                         && !CommonUtils.isBlank(line
 178  21
                             .substring(comment.getEndColNo() + 1))) {
 179  9
                     continue;
 180  
                 }
 181  
             }
 182  36
             if (!format.matcher(lineBefore).find()
 183  24
                 && !isLegalComment(comment)) {
 184  23
                 log(lineNo, MSG_KEY);
 185  
             }
 186  36
         }
 187  4
     }
 188  
 
 189  
     /**
 190  
      * Checks if given comment is legal (single-line and matches to the
 191  
      * pattern).
 192  
      * @param comment comment to check.
 193  
      * @return true if the comment if legal.
 194  
      */
 195  
     private boolean isLegalComment(final TextBlock comment) {
 196  
         final boolean legal;
 197  
 
 198  
         // multi-line comment can not be legal
 199  24
         if (legalComment == null || comment.getStartLineNo() != comment.getEndLineNo()) {
 200  19
             legal = false;
 201  
         }
 202  
         else {
 203  5
             String commentText = comment.getText()[0];
 204  
             // remove chars which start comment
 205  5
             commentText = commentText.substring(2);
 206  
             // if this is a C-style comment we need to remove its end
 207  5
             if (commentText.endsWith("*/")) {
 208  2
                 commentText = commentText.substring(0, commentText.length() - 2);
 209  
             }
 210  5
             commentText = commentText.trim();
 211  5
             legal = legalComment.matcher(commentText).find();
 212  
         }
 213  24
         return legal;
 214  
     }
 215  
 }