1 package com.puppycrawl.tools.checkstyle.checks.coding; 20 21 import java.util.regex.Matcher ; 22 import java.util.regex.Pattern ; 23 24 import com.puppycrawl.tools.checkstyle.api.Check; 25 import com.puppycrawl.tools.checkstyle.api.DetailAST; 26 import com.puppycrawl.tools.checkstyle.api.TokenTypes; 27 import com.puppycrawl.tools.checkstyle.api.Utils; 28 29 66 public class FallThroughCheck extends Check 67 { 68 69 private boolean mCheckLastGroup; 70 71 72 private String mReliefPattern = "fallthru|falls? ?through"; 73 74 75 private Pattern mRegExp; 76 77 78 public FallThroughCheck() 79 { 80 } 82 83 84 public int[] getDefaultTokens() 85 { 86 return new int[]{TokenTypes.CASE_GROUP}; 87 } 88 89 90 public int[] getRequiredTokens() 91 { 92 return getDefaultTokens(); 93 } 94 95 101 public void setReliefPattern(String aPattern) 102 { 103 mReliefPattern = aPattern; 104 } 105 106 110 public void setCheckLastCaseGroup(boolean aValue) 111 { 112 mCheckLastGroup = aValue; 113 } 114 115 116 public void init() 117 { 118 super.init(); 119 mRegExp = Utils.getPattern(mReliefPattern); 120 } 121 122 123 public void visitToken(DetailAST aAST) 124 { 125 final DetailAST nextGroup = (DetailAST) aAST.getNextSibling(); 126 final boolean isLastGroup = 127 ((nextGroup == null) 128 || (nextGroup.getType() != TokenTypes.CASE_GROUP)); 129 if (isLastGroup && !mCheckLastGroup) { 130 return; 132 } 133 134 final DetailAST slist = aAST.findFirstToken(TokenTypes.SLIST); 135 136 if (!isTerminated(slist, true, true)) { 137 if (!hasFallTruComment(aAST, nextGroup)) { 138 if (!isLastGroup) { 139 log(nextGroup, "fall.through"); 140 } 141 else { 142 log(aAST, "fall.through.last"); 143 } 144 } 145 } 146 } 147 148 156 private boolean isTerminated(final DetailAST aAST, boolean aUseBreak, 157 boolean aUseContinue) 158 { 159 switch (aAST.getType()) { 160 case TokenTypes.LITERAL_RETURN: 161 case TokenTypes.LITERAL_THROW: 162 return true; 163 case TokenTypes.LITERAL_BREAK: 164 return aUseBreak; 165 case TokenTypes.LITERAL_CONTINUE: 166 return aUseContinue; 167 case TokenTypes.SLIST: 168 return checkSlist(aAST, aUseBreak, aUseContinue); 169 case TokenTypes.LITERAL_IF: 170 return checkIf(aAST, aUseBreak, aUseContinue); 171 case TokenTypes.LITERAL_FOR: 172 case TokenTypes.LITERAL_WHILE: 173 case TokenTypes.LITERAL_DO: 174 return checkLoop(aAST); 175 case TokenTypes.LITERAL_TRY: 176 return checkTry(aAST, aUseBreak, aUseContinue); 177 case TokenTypes.LITERAL_SWITCH: 178 return checkSwitch(aAST, aUseContinue); 179 default: 180 return false; 181 } 182 } 183 184 192 private boolean checkSlist(final DetailAST aAST, boolean aUseBreak, 193 boolean aUseContinue) 194 { 195 DetailAST lastStmt = aAST.getLastChild(); 196 197 if (lastStmt.getType() == TokenTypes.RCURLY) { 198 lastStmt = lastStmt.getPreviousSibling(); 199 } 200 201 return (lastStmt != null) 202 && isTerminated(lastStmt, aUseBreak, aUseContinue); 203 } 204 205 213 private boolean checkIf(final DetailAST aAST, boolean aUseBreak, 214 boolean aUseContinue) 215 { 216 final DetailAST thenStmt = (DetailAST) 217 aAST.findFirstToken(TokenTypes.RPAREN).getNextSibling(); 218 final DetailAST elseStmt = (DetailAST) thenStmt.getNextSibling(); 219 boolean isTerminated = isTerminated(thenStmt, aUseBreak, aUseContinue); 220 221 if (isTerminated && (elseStmt != null)) { 222 isTerminated = isTerminated((DetailAST) elseStmt.getFirstChild(), 223 aUseBreak, aUseContinue); 224 } 225 return isTerminated; 226 } 227 228 234 private boolean checkLoop(final DetailAST aAST) 235 { 236 DetailAST loopBody = null; 237 if (aAST.getType() == TokenTypes.LITERAL_DO) { 238 final DetailAST lparen = aAST.findFirstToken(TokenTypes.DO_WHILE); 239 loopBody = lparen.getPreviousSibling(); 240 } 241 else { 242 final DetailAST rparen = aAST.findFirstToken(TokenTypes.RPAREN); 243 loopBody = (DetailAST) rparen.getNextSibling(); 244 } 245 return isTerminated(loopBody, false, false); 246 } 247 248 256 private boolean checkTry(final DetailAST aAST, boolean aUseBreak, 257 boolean aUseContinue) 258 { 259 final DetailAST finalStmt = aAST.getLastChild(); 260 if (finalStmt.getType() == TokenTypes.LITERAL_FINALLY) { 261 return isTerminated(finalStmt.findFirstToken(TokenTypes.SLIST), 262 aUseBreak, aUseContinue); 263 } 264 265 boolean isTerminated = isTerminated((DetailAST) aAST.getFirstChild(), 266 aUseBreak, aUseContinue); 267 268 DetailAST catchStmt = aAST.findFirstToken(TokenTypes.LITERAL_CATCH); 269 while ((catchStmt != null) && isTerminated) { 270 final DetailAST catchBody = 271 catchStmt.findFirstToken(TokenTypes.SLIST); 272 isTerminated &= isTerminated(catchBody, aUseBreak, aUseContinue); 273 catchStmt = (DetailAST) catchStmt.getNextSibling(); 274 } 275 return isTerminated; 276 } 277 278 285 private boolean checkSwitch(final DetailAST aAST, boolean aUseContinue) 286 { 287 DetailAST caseGroup = aAST.findFirstToken(TokenTypes.CASE_GROUP); 288 boolean isTerminated = (caseGroup != null); 289 while (isTerminated && (caseGroup != null) 290 && (caseGroup.getType() != TokenTypes.RCURLY)) 291 { 292 final DetailAST caseBody = 293 caseGroup.findFirstToken(TokenTypes.SLIST); 294 isTerminated &= isTerminated(caseBody, false, aUseContinue); 295 caseGroup = (DetailAST) caseGroup.getNextSibling(); 296 } 297 return isTerminated; 298 } 299 300 308 private boolean hasFallTruComment(DetailAST aCurrentCase, 309 DetailAST aNextCase) 310 { 311 312 final int startLineNo = aCurrentCase.getLineNo(); 313 final int endLineNo = aNextCase.getLineNo(); 314 final int endColNo = aNextCase.getColumnNo(); 315 316 321 final String [] lines = getLines(); 322 323 333 final String linepart = lines[endLineNo - 1].substring(0, endColNo); 334 if (commentMatch(mRegExp, linepart, endLineNo)) { 335 return true; 336 } 337 338 351 for (int i = endLineNo - 2; i > startLineNo - 1; i--) { 352 if (lines[i].trim().length() != 0) { 353 return commentMatch(mRegExp, lines[i], i + 1); 354 } 355 } 356 357 return false; 359 } 360 361 369 private boolean commentMatch(Pattern aPattern, String aLine, int aLineNo 370 ) 371 { 372 final Matcher matcher = aPattern.matcher(aLine); 373 374 final boolean hit = matcher.find(); 375 376 if (hit) { 377 final int startMatch = matcher.start(); 378 final int endMatch = matcher.end() - 1; 380 return getFileContents().hasIntersectionWithComment(aLineNo, 381 startMatch, aLineNo, endMatch); 382 } 383 return false; 384 } 385 } 386 | Popular Tags |