Coverage Report - com.puppycrawl.tools.checkstyle.checks.TrailingCommentCheck
 
Classes in this File Line Coverage Branch Coverage Complexity
TrailingCommentCheck
93%
41/44
90%
20/22
3.667
 
 1  
 ////////////////////////////////////////////////////////////////////////////////
 2  
 // checkstyle: Checks Java source code for adherence to a set of rules.
 3  
 // Copyright (C) 2001-2014  Oliver Burn
 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  
 package com.puppycrawl.tools.checkstyle.checks;
 20  
 
 21  
 import com.google.common.collect.Sets;
 22  
 import com.puppycrawl.tools.checkstyle.api.DetailAST;
 23  
 import com.puppycrawl.tools.checkstyle.api.TextBlock;
 24  
 import com.puppycrawl.tools.checkstyle.api.Utils;
 25  
 import java.util.List;
 26  
 import java.util.Map;
 27  
 import java.util.Set;
 28  
 import java.util.regex.Pattern;
 29  
 import java.util.regex.PatternSyntaxException;
 30  
 import org.apache.commons.beanutils.ConversionException;
 31  
 
 32  
 /**
 33  
  * <p>
 34  
  * The check to ensure that requires that comments be the only thing on a line.
 35  
  * For the case of // comments that means that the only thing that should
 36  
  * precede it is whitespace.
 37  
  * It doesn't check comments if they do not end line, i.e. it accept
 38  
  * the following:
 39  
  * <code>Thread.sleep( 10 &lt;some comment here&gt; );</code>
 40  
  * Format property is intended to deal with the "} // while" example.
 41  
  * </p>
 42  
  * <p>
 43  
  * Rationale: Steve McConnel in &quot;Code Complete&quot; suggests that endline
 44  
  * comments are a bad practice. An end line comment would
 45  
  * be one that is on the same line as actual code. For example:
 46  
  * <pre>
 47  
  *  a = b + c;      // Some insightful comment
 48  
  *  d = e / f;        // Another comment for this line
 49  
  * </pre>
 50  
  * Quoting &quot;Code Complete&quot; for the justification:
 51  
  * <ul>
 52  
  * <li>
 53  
  * &quot;The comments have to be aligned so that they do not
 54  
  * interfere with the visual structure of the code. If you don't
 55  
  * align them neatly, they'll make your listing look like it's been
 56  
  * through a washing machine.&quot;
 57  
  * </li>
 58  
  * <li>
 59  
  * &quot;Endline comments tend to be hard to format...It takes time
 60  
  * to align them. Such time is not spent learning more about
 61  
  * the code; it's dedicated solely to the tedious task of
 62  
  * pressing the spacebar or tab key.&quot;
 63  
  * </li>
 64  
  * <li>
 65  
  * &quot;Endline comments are also hard to maintain. If the code on
 66  
  * any line containing an endline comment grows, it bumps the
 67  
  * comment farther out, and all the other endline comments will
 68  
  * have to bumped out to match. Styles that are hard to
 69  
  * maintain aren't maintained....&quot;
 70  
  * </li>
 71  
  * <li>
 72  
  * &quot;Endline comments also tend to be cryptic. The right side of
 73  
  * the line doesn't offer much room and the desire to keep the
 74  
  * comment on one line means the comment must be short.
 75  
  * Work then goes into making the line as short as possible
 76  
  * instead of as clear as possible. The comment usually ends
 77  
  * up as cryptic as possible....&quot;
 78  
  * </li>
 79  
  * <li>
 80  
  * &quot;A systemic problem with endline comments is that it's hard
 81  
  * to write a meaningful comment for one line of code. Most
 82  
  * endline comments just repeat the line of code, which hurts
 83  
  * more than it helps.&quot;
 84  
  * </li>
 85  
  * </ul>
 86  
  * His comments on being hard to maintain when the size of
 87  
  * the line changes are even more important in the age of
 88  
  * automated refactorings.
 89  
  * </p>
 90  
  * <p>
 91  
  * To configure the check so it enforces only comment on a line:
 92  
  * <pre>
 93  
  * &lt;module name=&quot;TrailingComment&quot;&gt;
 94  
  *    &lt;property name=&quot;format&quot; value=&quot;^\\s*$&quot;/&gt;
 95  
  * &lt;/module&gt;
 96  
  * </pre>
 97  
  * </p>
 98  
  * @author o_sukhodolsky
 99  
  */
 100  
 public class TrailingCommentCheck extends AbstractFormatCheck
 101  
 {
 102  
     /** default format for allowed blank line. */
 103  
     private static final String DEFAULT_FORMAT = "^[\\s\\}\\);]*$";
 104  
 
 105  
     /** pattern for legal trailing comment. */
 106  
     private Pattern mLegalComment;
 107  
 
 108  
     /**
 109  
      * Sets patter for legal trailing comments.
 110  
      * @param aFormat format to set.
 111  
      * @throws ConversionException unable to parse a given format.
 112  
      */
 113  
     public void setLegalComment(final String aFormat)
 114  
         throws ConversionException
 115  
     {
 116  
         try {
 117  1
             mLegalComment = Utils.getPattern(aFormat);
 118  
         }
 119  0
         catch (final PatternSyntaxException e) {
 120  0
             throw new ConversionException("unable to parse " + aFormat, e);
 121  1
         }
 122  1
     }
 123  
     /**
 124  
      * Creates new instance of the check.
 125  
      * @throws ConversionException unable to parse DEFAULT_FORMAT.
 126  
      */
 127  
     public TrailingCommentCheck() throws ConversionException
 128  
     {
 129  2
         super(DEFAULT_FORMAT);
 130  2
     }
 131  
 
 132  
     @Override
 133  
     public int[] getDefaultTokens()
 134  
     {
 135  2
         return new int[0];
 136  
     }
 137  
 
 138  
     @Override
 139  
     public void visitToken(DetailAST aAST)
 140  
     {
 141  0
         throw new IllegalStateException("visitToken() shouldn't be called.");
 142  
     }
 143  
 
 144  
     @Override
 145  
     public void beginTree(DetailAST aRootAST)
 146  
     {
 147  2
         final Pattern blankLinePattern = getRegexp();
 148  2
         final Map<Integer, TextBlock> cppComments = getFileContents()
 149  
                 .getCppComments();
 150  2
         final Map<Integer, List<TextBlock>> cComments = getFileContents()
 151  
                 .getCComments();
 152  2
         final Set<Integer> lines = Sets.newHashSet();
 153  2
         lines.addAll(cppComments.keySet());
 154  2
         lines.addAll(cComments.keySet());
 155  
 
 156  2
         for (Integer lineNo : lines) {
 157  30
             final String line = getLines()[lineNo.intValue() - 1];
 158  30
             String lineBefore = "";
 159  30
             TextBlock comment = null;
 160  30
             if (cppComments.containsKey(lineNo)) {
 161  10
                 comment = cppComments.get(lineNo);
 162  10
                 lineBefore = line.substring(0, comment.getStartColNo());
 163  
             }
 164  20
             else if (cComments.containsKey(lineNo)) {
 165  20
                 final List<TextBlock> commentList = cComments.get(lineNo);
 166  20
                 comment = commentList.get(commentList.size() - 1);
 167  20
                 lineBefore = line.substring(0, comment.getStartColNo());
 168  20
                 if (comment.getText().length == 1) {
 169  14
                     final String lineAfter =
 170  
                         line.substring(comment.getEndColNo() + 1).trim();
 171  14
                     if (!"".equals(lineAfter)) {
 172  
                         // do not check comment which doesn't end line
 173  6
                         continue;
 174  
                     }
 175  
                 }
 176  
             }
 177  24
             if ((comment != null)
 178  
                 && !blankLinePattern.matcher(lineBefore).find()
 179  
                 && !isLegalComment(comment))
 180  
             {
 181  11
                 log(lineNo.intValue(), "trailing.comments");
 182  
             }
 183  24
         }
 184  2
     }
 185  
 
 186  
     /**
 187  
      * Checks if given comment is legal (single-line and matches to the
 188  
      * pattern).
 189  
      * @param aComment comment to check.
 190  
      * @return true if the comment if legal.
 191  
      */
 192  
     private boolean isLegalComment(final TextBlock aComment)
 193  
     {
 194  12
         if (mLegalComment == null) {
 195  6
             return false;
 196  
         }
 197  
         // multi-line comment can not be legal
 198  6
         if (aComment.getStartLineNo() != aComment.getEndLineNo()) {
 199  1
             return false;
 200  
         }
 201  5
         String commentText = aComment.getText()[0];
 202  
         // remove chars which start comment
 203  5
         commentText = commentText.substring(2);
 204  
         // if this is a C-style comment we need to remove its end
 205  5
         if (commentText.endsWith("*/")) {
 206  2
             commentText = commentText.substring(0, commentText.length() - 2);
 207  
         }
 208  5
         commentText = commentText.trim();
 209  5
         return mLegalComment.matcher(commentText).find();
 210  
     }
 211  
 }