[java] Allow CPD suppression through comments
This commit is contained in:
@ -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) {
|
if (currentToken.kind == JavaParserConstants.PACKAGE || currentToken.kind == JavaParserConstants.IMPORT) {
|
||||||
discardingKeywords = true;
|
discardingKeywords = true;
|
||||||
} else if (discardingKeywords && currentToken.kind == JavaParserConstants.SEMICOLON) {
|
} 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) {
|
if (currentToken.kind == JavaParserConstants.SEMICOLON) {
|
||||||
discardingSemicolon = true;
|
discardingSemicolon = true;
|
||||||
} else if (discardingSemicolon && currentToken.kind != JavaParserConstants.SEMICOLON) {
|
} 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 processing an annotation, look for a CPD-START or CPD-END
|
||||||
if (isAnnotation) {
|
if (isAnnotation) {
|
||||||
if (!discardingSuppressing && currentToken.kind == JavaParserConstants.STRING_LITERAL
|
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) {
|
if (!discardingAnnotations && isAnnotation) {
|
||||||
discardingAnnotations = true;
|
discardingAnnotations = true;
|
||||||
} else if (discardingAnnotations && !isAnnotation) {
|
} else if (discardingAnnotations && !isAnnotation) {
|
||||||
@ -170,7 +184,7 @@ public class JavaTokenizer implements Tokenizer {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void detectAnnotations(Token currentToken) {
|
private void detectAnnotations(Token currentToken) {
|
||||||
if (isAnnotation && nextTokenEndsAnnotation) {
|
if (isAnnotation && nextTokenEndsAnnotation) {
|
||||||
isAnnotation = false;
|
isAnnotation = false;
|
||||||
nextTokenEndsAnnotation = false;
|
nextTokenEndsAnnotation = false;
|
||||||
|
@ -87,11 +87,6 @@ public class JavaTokensTokenizerTest {
|
|||||||
assertEquals(6, tokens.size());
|
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
|
@Test
|
||||||
public void testIgnoreComments() {
|
public void testIgnoreComments() {
|
||||||
JavaTokenizer t = new JavaTokenizer();
|
JavaTokenizer t = new JavaTokenizer();
|
||||||
@ -117,6 +112,55 @@ public class JavaTokensTokenizerTest {
|
|||||||
assertEquals(6, tokens.size());
|
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
|
@Test
|
||||||
public void testIgnoreBetweenSpecialAnnotation() throws IOException {
|
public void testIgnoreBetweenSpecialAnnotation() throws IOException {
|
||||||
JavaTokenizer t = new JavaTokenizer();
|
JavaTokenizer t = new JavaTokenizer();
|
||||||
|
@ -443,9 +443,33 @@ Here's a [screenshot](../images/screenshot_cpd.png) of CPD after running on the
|
|||||||
|
|
||||||
## Suppression
|
## Suppression
|
||||||
|
|
||||||
By adding the annotations **@SuppressWarnings("CPD-START")** and **@SuppressWarnings("CPD-END")**
|
Arbitrary blocks of code can be ignored through comments on **Java** by including the keywords `CPD-OFF` and `CPD-ON`.
|
||||||
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.
|
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
|
//enable suppression
|
||||||
@SuppressWarnings("CPD-START")
|
@SuppressWarnings("CPD-START")
|
||||||
@ -457,3 +481,7 @@ This provides the ability to ignore sections of source code, such as switch/case
|
|||||||
public void nextMethod() {
|
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.
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user