[java] Allow CPD suppression through comments

This commit is contained in:
Juan Martín Sotuyo Dodero
2017-02-08 16:55:28 -03:00
parent 3797f7263f
commit 4d7938d821
3 changed files with 99 additions and 13 deletions

View File

@ -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;

View File

@ -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();

View File

@ -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.