diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/cpd/JavaTokenizer.java b/pmd-java/src/main/java/net/sourceforge/pmd/cpd/JavaTokenizer.java index 2be4b4ca82..0b62ca6ade 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/cpd/JavaTokenizer.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/cpd/JavaTokenizer.java @@ -127,7 +127,7 @@ public class JavaTokenizer implements Tokenizer { } } - public void skipPackageAndImport(Token currentToken) { + private void skipPackageAndImport(Token currentToken) { if (currentToken.kind == JavaParserConstants.PACKAGE || currentToken.kind == JavaParserConstants.IMPORT) { discardingKeywords = true; } else if (discardingKeywords && currentToken.kind == JavaParserConstants.SEMICOLON) { @@ -135,7 +135,7 @@ public class JavaTokenizer implements Tokenizer { } } - public void skipSemicolon(Token currentToken) { + private void skipSemicolon(Token currentToken) { if (currentToken.kind == JavaParserConstants.SEMICOLON) { discardingSemicolon = true; } else if (discardingSemicolon && currentToken.kind != JavaParserConstants.SEMICOLON) { @@ -143,7 +143,21 @@ public class JavaTokenizer implements Tokenizer { } } - public void skipCPDSuppression(Token currentToken) { + private void skipCPDSuppression(Token currentToken) { + // Check if a comment is altering the suppression state + Token st = currentToken.specialToken; + while (st != null) { + if (st.image.contains("CPD-OFF")) { + discardingSuppressing = true; + break; + } + if (st.image.contains("CPD-ON")) { + discardingSuppressing = false; + break; + } + st = st.specialToken; + } + // if processing an annotation, look for a CPD-START or CPD-END if (isAnnotation) { if (!discardingSuppressing && currentToken.kind == JavaParserConstants.STRING_LITERAL @@ -156,7 +170,7 @@ public class JavaTokenizer implements Tokenizer { } } - public void skipAnnotations() { + private void skipAnnotations() { if (!discardingAnnotations && isAnnotation) { discardingAnnotations = true; } else if (discardingAnnotations && !isAnnotation) { @@ -170,7 +184,7 @@ public class JavaTokenizer implements Tokenizer { return result; } - public void detectAnnotations(Token currentToken) { + private void detectAnnotations(Token currentToken) { if (isAnnotation && nextTokenEndsAnnotation) { isAnnotation = false; nextTokenEndsAnnotation = false; diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/cpd/JavaTokensTokenizerTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/cpd/JavaTokensTokenizerTest.java index fa0fa9908e..3291c53ad6 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/cpd/JavaTokensTokenizerTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/cpd/JavaTokensTokenizerTest.java @@ -87,11 +87,6 @@ public class JavaTokensTokenizerTest { assertEquals(6, tokens.size()); } - /** - * Comments are discarded already by the Java parser. It would be nice, - * however, to use simple comments like //CPD-START or //CPD-END to enable - * discard-mode of CPD - */ @Test public void testIgnoreComments() { JavaTokenizer t = new JavaTokenizer(); @@ -117,6 +112,55 @@ public class JavaTokensTokenizerTest { assertEquals(6, tokens.size()); } + @Test + public void testIgnoreBetweenSpecialComments() throws IOException { + JavaTokenizer t = new JavaTokenizer(); + t.setIgnoreAnnotations(false); + SourceCode sourceCode = new SourceCode(new SourceCode.StringCodeLoader("package foo.bar.baz;" + PMD.EOL + + "// CPD-OFF" + PMD.EOL + "// CPD-OFF" + PMD.EOL + + "@ MyAnnotation (\"ugh\")" + PMD.EOL + "@NamedQueries({" + PMD.EOL + "@NamedQuery(" + PMD.EOL + ")})" + + PMD.EOL + "public class Foo {" + "// CPD-ON" + PMD.EOL + + "}" + )); + Tokens tokens = new Tokens(); + t.tokenize(sourceCode, tokens); + TokenEntry.getEOF(); + assertEquals(2, tokens.size()); // 2 tokens: "}" + EOF + } + + @Test + public void testIgnoreBetweenSpecialCommentsMultiple() throws IOException { + JavaTokenizer t = new JavaTokenizer(); + t.setIgnoreAnnotations(false); + SourceCode sourceCode = new SourceCode(new SourceCode.StringCodeLoader("package foo.bar.baz;" + PMD.EOL + + "// CPD-OFF" + PMD.EOL + "// another irrelevant comment" + PMD.EOL + + "@ MyAnnotation (\"ugh\")" + PMD.EOL + "@NamedQueries({" + PMD.EOL + "@NamedQuery(" + PMD.EOL + ")})" + + PMD.EOL + "public class Foo {" + "// CPD-ON" + PMD.EOL + + "}" + )); + Tokens tokens = new Tokens(); + t.tokenize(sourceCode, tokens); + TokenEntry.getEOF(); + assertEquals(2, tokens.size()); // 2 tokens: "}" + EOF + } + + @Test + public void testIgnoreBetweenSpecialCommentsMultiline() throws IOException { + JavaTokenizer t = new JavaTokenizer(); + t.setIgnoreAnnotations(false); + SourceCode sourceCode = new SourceCode(new SourceCode.StringCodeLoader("package foo.bar.baz;" + PMD.EOL + + "/* " + PMD.EOL + " * CPD-OFF" + PMD.EOL + "*/" + PMD.EOL + + "@ MyAnnotation (\"ugh\")" + PMD.EOL + "@NamedQueries({" + PMD.EOL + "@NamedQuery(" + PMD.EOL + ")})" + + PMD.EOL + "public class Foo {" + PMD.EOL + + "/* " + PMD.EOL + " * CPD-ON" + PMD.EOL + "*/" + PMD.EOL + + "}" + )); + Tokens tokens = new Tokens(); + t.tokenize(sourceCode, tokens); + TokenEntry.getEOF(); + assertEquals(2, tokens.size()); // 2 tokens: "}" + EOF + } + @Test public void testIgnoreBetweenSpecialAnnotation() throws IOException { JavaTokenizer t = new JavaTokenizer(); diff --git a/src/site/markdown/usage/cpd-usage.md b/src/site/markdown/usage/cpd-usage.md index d22865c681..23fb35425b 100644 --- a/src/site/markdown/usage/cpd-usage.md +++ b/src/site/markdown/usage/cpd-usage.md @@ -443,9 +443,33 @@ Here's a [screenshot](../images/screenshot_cpd.png) of CPD after running on the ## Suppression -By adding the annotations **@SuppressWarnings("CPD-START")** and **@SuppressWarnings("CPD-END")** -all code within will be ignored by CPD - thus you can avoid false positivs. -This provides the ability to ignore sections of source code, such as switch/case statements or parameterized factories. +Arbitrary blocks of code can be ignored through comments on **Java** by including the keywords `CPD-OFF` and `CPD-ON`. + + public Object someParameterizedFactoryMethod(int x) throws Exception { + // some unignored code + + // tell cpd to start ignoring code - CPD-OFF + + // mission critical code, manually loop unroll + goDoSomethingAwesome(x + x / 2); + goDoSomethingAwesome(x + x / 2); + goDoSomethingAwesome(x + x / 2); + goDoSomethingAwesome(x + x / 2); + goDoSomethingAwesome(x + x / 2); + goDoSomethingAwesome(x + x / 2); + + // resume CPD analysis - CPD-ON + + // further code will *not* be ignored + } + + +Additionally, **Java** allows to toggle suppression by adding the annotations +**`@SuppressWarnings("CPD-START")`** and **`@SuppressWarnings("CPD-END")`** +all code within will be ignored by CPD. + +This approach however, is limited to the locations were `@SuppressWarnings` is accepted. +It's legacy and the new comment's based approch should be favored. //enable suppression @SuppressWarnings("CPD-START") @@ -457,3 +481,7 @@ This provides the ability to ignore sections of source code, such as switch/case public void nextMethod() { } + +Other languages currently have no support to suppress CPD reports. In the future, +the comment based approach will be extended to those of them that can support it. +