diff --git a/src/checkstyle/com/puppycrawl/tools/checkstyle/checks/coding/FallThroughCheck.java b/src/checkstyle/com/puppycrawl/tools/checkstyle/checks/coding/FallThroughCheck.java index 3f523c4ea..7ffcd49b2 100644 --- a/src/checkstyle/com/puppycrawl/tools/checkstyle/checks/coding/FallThroughCheck.java +++ b/src/checkstyle/com/puppycrawl/tools/checkstyle/checks/coding/FallThroughCheck.java @@ -18,9 +18,13 @@ //////////////////////////////////////////////////////////////////////////////// package com.puppycrawl.tools.checkstyle.checks.coding; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + import com.puppycrawl.tools.checkstyle.api.Check; -import com.puppycrawl.tools.checkstyle.api.TokenTypes; import com.puppycrawl.tools.checkstyle.api.DetailAST; +import com.puppycrawl.tools.checkstyle.api.TokenTypes; +import com.puppycrawl.tools.checkstyle.api.Utils; /** * Checks for fall through in switch statements @@ -28,10 +32,33 @@ import com.puppycrawl.tools.checkstyle.api.DetailAST; * but lacks a break, return, throw or continue statement. * *
+ * The check honors special comments to suppress warnings about + * the fall through. By default the comments "fallthru", + * "fall through", "falls through" and "fallthrough" are recognized. + *
+ *+ * The following fragment of code will NOT trigger the check, + * because of the comment "fallthru". + *
+ *+ * case 3: + * x = 2; + * // fallthru + * case 4: + *+ *
+ * The recognized relief comment can be configured with the property
+ * reliefPattern. Default value of this regular expression
+ * is "fallthru|fall through|fallthrough|falls through".
+ *
* An example of how to configure the check is: *
*- * <module name="FallThrough"/> + * <module name="FallThrough"> + * <property name="reliefPattern" + * value="Fall Through"/> + * </module> ** * @author o_sukhodolsky @@ -41,6 +68,12 @@ public class FallThroughCheck extends Check /** Do we need to check last case group. */ private boolean mCheckLastGroup; + /** Relief pattern to allow fall throught to the next case branch. */ + private String mReliefPattern = "fallthru|falls? ?through"; + + /** Relief regexp. */ + private Pattern mRegExp; + /** Creates new instance of the check. */ public FallThroughCheck() { @@ -59,6 +92,17 @@ public class FallThroughCheck extends Check return getDefaultTokens(); } + /** + * Set the relief pattern. + * + * @param aPattern + * The regular expression pattern. + */ + public void setReliefPattern(String aPattern) + { + mReliefPattern = aPattern; + } + /** * Configures whether we need to check last case group or not. * @param aValue new value of the property. @@ -68,6 +112,13 @@ public class FallThroughCheck extends Check mCheckLastGroup = aValue; } + /** {@inheritDoc} */ + public void init() + { + super.init(); + mRegExp = Utils.getPattern(mReliefPattern); + } + /** {@inheritDoc} */ public void visitToken(DetailAST aAST) { @@ -82,11 +133,13 @@ public class FallThroughCheck extends Check final DetailAST slist = aAST.findFirstToken(TokenTypes.SLIST); if (!isTerminated(slist, true, true)) { - if (!isLastGroup) { - log(nextGroup, "fall.through"); - } - else { - log(aAST, "fall.through.last"); + if (!hasFallTruComment(aAST, nextGroup)) { + if (!isLastGroup) { + log(nextGroup, "fall.through"); + } + else { + log(aAST, "fall.through.last"); + } } } } @@ -192,7 +245,7 @@ public class FallThroughCheck extends Check } /** - * Checks if a given try/cath/finally block terminated by return, throw or, + * Checks if a given try/catch/finally block terminated by return, throw or, * if allowed break, continue. * @param aAST loop to check * @param aUseBreak should we consider break as terminator. @@ -243,4 +296,91 @@ public class FallThroughCheck extends Check return isTerminated; } + /** + * Determines if the fall through case between
aCurrentCase and
+ * aNextCase is reliefed by a appropriate comment.
+ *
+ * @param aCurrentCase AST of the case that falls through to the next case.
+ * @param aNextCase AST of the next case.
+ * @return True if a relief comment was found
+ */
+ private boolean hasFallTruComment(DetailAST aCurrentCase,
+ DetailAST aNextCase)
+ {
+
+ final int startLineNo = aCurrentCase.getLineNo();
+ final int startColNo = aCurrentCase.getColumnNo();
+ final int endLineNo = aNextCase.getLineNo();
+ final int endColNo = aNextCase.getColumnNo();
+
+ /*
+ * Remember: The lines number returned from the AST is 1-based, but
+ * the lines number in this array are 0-based. So you will often
+ * see a "lineNo-1" etc.
+ */
+ final String[] lines = getLines();
+
+ /*
+ * Handle:
+ * case 1:
+ * /+ FALLTHRU +/ case 2:
+ * ....
+ * and
+ * switch(i) {
+ * default:
+ * /+ FALLTHRU +/}
+ */
+ String linepart = lines[endLineNo - 1].substring(0, endColNo);
+ if (commentMatch(mRegExp, linepart, endLineNo, 0)) {
+ return true;
+ }
+
+ /*
+ * Handle:
+ * case 1:
+ * .....
+ * // FALLTHRU
+ * case 2:
+ * ....
+ * and
+ * switch(i) {
+ * default:
+ * // FALLTHRU
+ * }
+ */
+ for (int i = endLineNo - 2; i > startLineNo - 1; i--) {
+ if (lines[i].trim().length() != 0) {
+ return commentMatch(mRegExp, lines[i], i + 1, 0);
+ }
+ }
+
+ // Well -- no relief comment found.
+ return false;
+ }
+
+ /**
+ * Does a regular expression match on the given line and checks that a
+ * possible match is within a comment.
+ * @param aPattern The regular expression pattern to use.
+ * @param aLine The line of test to do the match on.
+ * @param aLineNo The line number in the file.
+ * @param aStartColNo Column on that the regexp matching starts.
+ * @return True if a match was found inside a comment.
+ */
+ private boolean commentMatch(Pattern aPattern, String aLine, int aLineNo,
+ int aStartColNo)
+ {
+ Matcher matcher = aPattern.matcher(aLine);
+
+ boolean hit = matcher.find();
+
+ if (hit) {
+ final int startMatch = matcher.start();
+ // -1 because it returns the char position beyond the match
+ final int endMatch = matcher.end() - 1;
+ return getFileContents().hasIntersectionWithComment(aLineNo,
+ startMatch, aLineNo, endMatch);
+ }
+ return false;
+ }
}
diff --git a/src/testinputs/com/puppycrawl/tools/checkstyle/coding/InputFallThrough.java b/src/testinputs/com/puppycrawl/tools/checkstyle/coding/InputFallThrough.java
index ed071eb88..ed3647d9b 100644
--- a/src/testinputs/com/puppycrawl/tools/checkstyle/coding/InputFallThrough.java
+++ b/src/testinputs/com/puppycrawl/tools/checkstyle/coding/InputFallThrough.java
@@ -124,4 +124,278 @@ public class InputFallThrough
}
}
}
+
+
+
+ /* Like above, but all fall throughs with relief comment */
+ void methodFallThru(int i, int j, boolean cond) {
+ while (true) {
+ switch (i) {
+ case -1: // FALLTHRU
+
+ case 0: // no problem
+ case 1:
+ i++;
+ break;
+ case 2:
+ i++;
+ // fallthru
+ case 3:
+ i++;
+ break;
+ case 4:
+ return;
+ case 5:
+ throw new RuntimeException("");
+ case 6:
+ continue;
+ case 7: {
+ break;
+ }
+ case 8: {
+ return;
+ }
+ case 9: {
+ throw new RuntimeException("");
+ }
+ case 10: {
+ continue;
+ }
+ case 11: {
+ i++;
+ }
+ // fallthru
+ case 12:
+ if (false)
+ break;
+ else
+ break;
+ case 13:
+ if (true) {
+ return;
+ }
+ case 14:
+ if (true) {
+ return;
+ } else {
+ //do nothing
+ }
+ // fallthru
+ case 15:
+ do {
+ System.out.println("something");
+ return;
+ } while(true);
+ case 16:
+ for (int j1 = 0; j1 < 10; j1++) {
+ System.err.println("something");
+ return;
+ }
+ case 17:
+ while (cond)
+ throw new RuntimeException("");
+ case 18:
+ while(cond) {
+ break;
+ }
+ // fallthru
+ case 19:
+ try {
+ i++;
+ break;
+ } catch (RuntimeException e) {
+ break;
+ } catch (Error e) {
+ return;
+ }
+ case 20:
+ try {
+ i++;
+ break;
+ } catch (RuntimeException e) {
+ } catch (Error e) {
+ return;
+ }
+ // fallthru
+ case 21:
+ try {
+ i++;
+ } catch (RuntimeException e) {
+ i--;
+ } finally {
+ break;
+ }
+ case 22:
+ try {
+ i++;
+ break;
+ } catch (RuntimeException e) {
+ i--;
+ break;
+ } finally {
+ i++;
+ }
+ /* fallthru */
+ case 23:
+ switch (j) {
+ case 1:
+ continue;
+ case 2:
+ return;
+ default:
+ return;
+ }
+ case 24:
+ i++;
+ /* fallthru */ case 25:
+ i++;
+ break;
+
+ case 26:
+ switch (j) {
+ case 1:
+ continue;
+ case 2:
+ break;
+ default:
+ return;
+ }
+ // fallthru
+ default:
+ // this is the last label
+ i++;
+ // fallthru
+ }
+ }
+ }
+
+ /* Test relief comment. */
+ void methodFallThruCC(int i, int j, boolean cond) {
+ while (true) {
+ switch (i){
+ case 0:
+ i++; // fallthru
+
+ case 1:
+ i++;
+ // fallthru
+ case 2: {
+ i++;
+ }
+ // fallthru
+ case 3:
+ i++;
+ /* fallthru */case 4:
+ break;
+ case 5:
+ i++;
+ // fallthru
+ }
+ }
+ }
+
+ /* Like above, but C-style comments. */
+ void methodFallThruC(int i, int j, boolean cond) {
+ while (true) {
+ switch (i){
+ case 0:
+ i++; /* fallthru */
+
+ case 1:
+ i++;
+ /* fallthru */
+ case 2:
+ i++;
+ /* fallthru */case 3:
+ break;
+ case 4:
+ i++;
+ /* fallthru */
+ }
+ }
+ }
+
+ /* Like above, but C-style comments with no spaces. */
+ void methodFallThruC2(int i, int j, boolean cond) {
+ while (true) {
+ switch (i){
+ case 0:
+ i++; /*fallthru*/
+
+ case 1:
+ i++;
+ /*fallthru*/
+ case 2:
+ i++;
+ /*fallthru*/case 3:
+ break;
+ case 4:
+ i++;
+ /*fallthru*/
+ }
+ }
+ }
+
+ /* C-style comments with other default fallthru-comment. */
+ void methodFallThruCOtherWords(int i, int j, boolean cond) {
+ while (true) {
+ switch (i){
+ case 0:
+ i++; /* falls through */
+
+ case 1:
+ i++;
+ /* falls through */
+ case 2:
+ i++;
+ /* falls through */case 3:
+ break;
+ case 4:
+ i++;
+ /* falls through */
+ }
+ }
+ }
+
+ /* C-style comments with custom fallthru-comment. */
+ void methodFallThruCCustomWords(int i, int j, boolean cond) {
+ while (true) {
+ switch (i){
+ case 0:
+ i++; /* Continue with next case */
+
+ case 1:
+ i++;
+ /* Continue with next case */
+ case 2:
+ i++;
+ /* Continue with next case */case 3:
+ break;
+ case 4:
+ i++;
+ /* Continue with next case */
+ }
+ }
+ }
+
+ void methodFallThruLastCaseGroup(int i, int j, boolean cond) {
+ while (true) {
+ switch (i){
+ case 0:
+ i++; // fallthru
+ }
+ switch (i){
+ case 0:
+ i++;
+ // fallthru
+ }
+ switch (i){
+ case 0:
+ i++;
+ /* fallthru */ }
+ }
+ }
+
+
+
}
diff --git a/src/tests/com/puppycrawl/tools/checkstyle/checks/coding/FallThroughCheckTest.java b/src/tests/com/puppycrawl/tools/checkstyle/checks/coding/FallThroughCheckTest.java
index f2329a813..3ca4d8459 100644
--- a/src/tests/com/puppycrawl/tools/checkstyle/checks/coding/FallThroughCheckTest.java
+++ b/src/tests/com/puppycrawl/tools/checkstyle/checks/coding/FallThroughCheckTest.java
@@ -7,15 +7,30 @@ import java.io.File;
public class FallThroughCheckTest extends BaseCheckTestCase
{
- private DefaultConfiguration checkConfig;
- public void setUp()
+ public void testDefault() throws Exception
{
- checkConfig = createCheckConfig(FallThroughCheck.class);
+ DefaultConfiguration checkConfig = createCheckConfig(FallThroughCheck.class);
+ final String[] expected = {
+ "12:13: Fall through from previous branch of the switch statement.",
+ "36:13: Fall through from previous branch of the switch statement.",
+ "51:13: Fall through from previous branch of the switch statement.",
+ "68:13: Fall through from previous branch of the switch statement.",
+ "85:13: Fall through from previous branch of the switch statement.",
+ "103:13: Fall through from previous branch of the switch statement.",
+ "121:13: Fall through from previous branch of the switch statement.",
+ "367:11: Fall through from previous branch of the switch statement.",
+ "370:11: Fall through from previous branch of the switch statement.",
+ "372:40: Fall through from previous branch of the switch statement.",
+ };
+ verify(checkConfig,
+ getPath("coding" + File.separator + "InputFallThrough.java"),
+ expected);
}
-
- public void testIt() throws Exception
+
+ public void testLastCaseGroup() throws Exception
{
+ DefaultConfiguration checkConfig = createCheckConfig(FallThroughCheck.class);
checkConfig.addAttribute("checkLastCaseGroup", "true");
final String[] expected = {
"12:13: Fall through from previous branch of the switch statement.",
@@ -26,14 +41,23 @@ public class FallThroughCheckTest extends BaseCheckTestCase
"103:13: Fall through from previous branch of the switch statement.",
"121:13: Fall through from previous branch of the switch statement.",
"121:13: Fall through from the last branch of the switch statement.",
+ "367:11: Fall through from previous branch of the switch statement.",
+ "370:11: Fall through from previous branch of the switch statement.",
+ "372:40: Fall through from previous branch of the switch statement.",
+ "374:11: Fall through from the last branch of the switch statement.",
};
verify(checkConfig,
getPath("coding" + File.separator + "InputFallThrough.java"),
expected);
}
- public void testDefault() throws Exception
+ public void testOwnPattern() throws Exception
{
+ final String ownPattern = "Continue with next case";
+ final DefaultConfiguration checkConfig =
+ createCheckConfig(FallThroughCheck.class);
+ checkConfig.addAttribute("reliefPattern", ownPattern);
+
final String[] expected = {
"12:13: Fall through from previous branch of the switch statement.",
"36:13: Fall through from previous branch of the switch statement.",
@@ -42,9 +66,31 @@ public class FallThroughCheckTest extends BaseCheckTestCase
"85:13: Fall through from previous branch of the switch statement.",
"103:13: Fall through from previous branch of the switch statement.",
"121:13: Fall through from previous branch of the switch statement.",
+ "143:11: Fall through from previous branch of the switch statement.",
+ "168:11: Fall through from previous branch of the switch statement.",
+ "184:11: Fall through from previous branch of the switch statement.",
+ "202:11: Fall through from previous branch of the switch statement.",
+ "220:11: Fall through from previous branch of the switch statement.",
+ "239:11: Fall through from previous branch of the switch statement.",
+ "250:26: Fall through from previous branch of the switch statement.",
+ "264:11: Fall through from previous branch of the switch statement.",
+ "279:11: Fall through from previous branch of the switch statement.",
+ "282:11: Fall through from previous branch of the switch statement.",
+ "286:11: Fall through from previous branch of the switch statement.",
+ "288:25: Fall through from previous branch of the switch statement.",
+ "304:11: Fall through from previous branch of the switch statement.",
+ "307:11: Fall through from previous branch of the switch statement.",
+ "309:25: Fall through from previous branch of the switch statement.",
+ "325:11: Fall through from previous branch of the switch statement.",
+ "328:11: Fall through from previous branch of the switch statement.",
+ "330:23: Fall through from previous branch of the switch statement.",
+ "346:11: Fall through from previous branch of the switch statement.",
+ "349:11: Fall through from previous branch of the switch statement.",
+ "351:30: Fall through from previous branch of the switch statement.",
};
verify(checkConfig,
getPath("coding" + File.separator + "InputFallThrough.java"),
expected);
+
}
}
diff --git a/src/xdocs/config_coding.xml b/src/xdocs/config_coding.xml
index 5ffdf8294..cd90519f4 100755
--- a/src/xdocs/config_coding.xml
+++ b/src/xdocs/config_coding.xml
@@ -1851,12 +1851,40 @@ if ("something".equals(x))
Checks for fall through in switch
statements Finds locations where a case
- contains
Java code - but lacks a break, return, throw or continue
statement.
+ The check honores special comments to supress the warning. By + default the text "fallthru", "fall through", "fallthrough", + "falls through" and "fallsthrough" are recognized (case + sensitive). The comment containing this words must be a + one-liner and must be on the last none-empty line before the + case triggering the warning or on + the same line before the case + (urgly, but possible). +
+Note: the check works in assumption that there is no unreachable code in the case. @@ -1879,6 +1907,15 @@ if ("something".equals(x))
+ or +
+New features:
Fixed Bugs:
diff --git a/suppressions.xml b/suppressions.xml index 43338e3ac..480334584 100755 --- a/suppressions.xml +++ b/suppressions.xml @@ -11,9 +11,6 @@