Fixed false positive in UnusedImports: javadoc comments are parsed to check @see and other tags

git-svn-id: https://pmd.svn.sourceforge.net/svnroot/pmd/branches/pmd/4.2.x@6018 51baf565-9d33-0410-a72c-fc3788e3496d
This commit is contained in:
Xavier Le Vourch
2008-04-16 21:18:35 +00:00
parent d157b08bee
commit cdb5525716
8 changed files with 123 additions and 10 deletions

View File

@ -1,3 +1,7 @@
???? - 4.2.2:
Fixed false positive in UnusedImports: javadoc comments are parsed to check @see and other tags
April 11, 2008 - 4.2.1:
'41' and '42' shortcuts for rulesets added

View File

@ -163,6 +163,8 @@ public class JavaParser {
}
PARSER_END(JavaParser)
TOKEN_MGR_DECLS : {
protected List<Token> formalComments = new ArrayList<Token>();
private Map<Integer, String> excludeMap = new HashMap<Integer, String>();
private String excludeMarker = PMD.EXCLUDE_MARKER;
@ -204,7 +206,7 @@ MORE :
<IN_FORMAL_COMMENT>
SPECIAL_TOKEN :
{
<FORMAL_COMMENT: "*/" > : DEFAULT
<FORMAL_COMMENT: "*/" > { formalComments.add(matchedToken); } : DEFAULT
}
<IN_MULTI_LINE_COMMENT>
@ -1077,6 +1079,7 @@ ASTCompilationUnit CompilationUnit() :
( < "~[]" > )?
<EOF>
{
jjtThis.setFormalComments(token_source.formalComments);
return jjtThis;
}
}

View File

@ -178,6 +178,27 @@ On demand import
import java.util.*;
public class Foo {
List list = new ArrayList();
}
]]></code>
</test-code>
<test-code>
<description><![CDATA[
imports used in javadoc comment
]]></description>
<expected-problems>0</expected-problems>
<code><![CDATA[
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.File;
public class Foo {
/**
* {@linkplain List list}
* {@link ArrayList arraylist}
* {@value Calendar#DATE}
* @see File
*/
public void test() {}
}
]]></code>
</test-code>

View File

@ -154,4 +154,25 @@ public class Foo {
}
]]></code>
</test-code>
<test-code>
<description><![CDATA[
imports used in javadoc comment
]]></description>
<expected-problems>0</expected-problems>
<code><![CDATA[
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.File;
public class Foo {
/**
* {@linkplain List list}
* {@link ArrayList arraylist}
* {@value Calendar#DATE}
* @see File
*/
public void test() {}
}
]]></code>
</test-code>
</test-data>

View File

@ -2,6 +2,7 @@
package net.sourceforge.pmd.ast;
import java.util.List;
import net.sourceforge.pmd.typeresolution.ClassTypeResolver;
// FUTURE Change this class to extend from SimpleJavaNode, as TypeNode is not appropriate (unless I'm wrong)
@ -17,6 +18,15 @@ public class ASTCompilationUnit extends SimpleJavaTypeNode implements Compilatio
super(p, id);
}
private List<Token> formalComments;
public List<Token> getFormalComments() {
return formalComments;
}
public void setFormalComments(List<Token> formalComments) {
this.formalComments = formalComments;
}
/**
* Accept the visitor. *

View File

@ -169,6 +169,7 @@ public class JavaParser/*@bgen(jjtree)*/implements JavaParserTreeConstants, Java
jj_consume_token(0);
jjtree.closeNodeScope(jjtn000, true);
jjtc000 = false;
jjtn000.setFormalComments(token_source.formalComments);
{if (true) return jjtn000;}
} catch (Throwable jjte000) {
if (jjtc000) {
@ -8001,6 +8002,15 @@ jjtn000.setModifiers(modifiers);
return false;
}
private boolean jj_3R_188() {
if (jj_scan_token(WHILE)) return true;
if (jj_scan_token(LPAREN)) return true;
if (jj_3R_88()) return true;
if (jj_scan_token(RPAREN)) return true;
if (jj_3R_91()) return true;
return false;
}
private boolean jj_3_1() {
Token xsp;
while (true) {
@ -8011,15 +8021,6 @@ jjtn000.setModifiers(modifiers);
return false;
}
private boolean jj_3R_188() {
if (jj_scan_token(WHILE)) return true;
if (jj_scan_token(LPAREN)) return true;
if (jj_3R_88()) return true;
if (jj_scan_token(RPAREN)) return true;
if (jj_3R_91()) return true;
return false;
}
private boolean jj_3_44() {
if (jj_3R_74()) return true;
return false;

View File

@ -6,6 +6,8 @@ import net.sourceforge.pmd.PMD;
/** Token Manager. */
public class JavaParserTokenManager implements JavaParserConstants
{
protected List<Token> formalComments = new ArrayList<Token>();
private Map<Integer, String> excludeMap = new HashMap<Integer, String>();
private String excludeMarker = PMD.EXCLUDE_MARKER;
@ -2051,6 +2053,10 @@ void SkipLexicalActions(Token matchedToken)
excludeMap.put(matchedToken.beginLine, matchedToken.image.substring(startOfNOPMD + excludeMarker.length()));
}
break;
case 9 :
image.append(input_stream.GetSuffix(jjimageLen + (lengthOfMatch = jjmatchedPos + 1)));
formalComments.add(matchedToken);
break;
default :
break;
}

View File

@ -10,10 +10,14 @@ import net.sourceforge.pmd.ast.ASTImportDeclaration;
import net.sourceforge.pmd.ast.ASTName;
import net.sourceforge.pmd.ast.SimpleJavaNode;
import net.sourceforge.pmd.ast.SimpleNode;
import net.sourceforge.pmd.ast.Token;
import net.sourceforge.pmd.rules.ImportWrapper;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class UnusedImportsRule extends AbstractRule {
@ -22,12 +26,55 @@ public class UnusedImportsRule extends AbstractRule {
public Object visit(ASTCompilationUnit node, Object data) {
imports.clear();
super.visit(node, data);
visitFormalComments(node);
for (ImportWrapper wrapper : imports) {
addViolation(data, wrapper.getNode(), wrapper.getFullName());
}
return data;
}
/*
* Patterns to match the following constructs:
*
* @see package.class#member label
* {@linkplain package.class#member label}
* {@link package.class#member label}
* {@value package.class#field}
*/
private static final Pattern SEE_PATTERN = Pattern.compile(
"@see\\s+(\\p{Alpha}\\p{Alnum}*)[\\s#]");
private static final Pattern LINK_PATTERNS = Pattern.compile(
"\\{@link(?:plain)?\\s+(\\p{Alpha}\\p{Alnum}*)[\\s#]");
private static final Pattern VALUE_PATTERN = Pattern.compile(
"\\{@value\\s+(\\p{Alpha}\\p{Alnum}*)[\\s#]");
private static final Pattern[] PATTERNS = { SEE_PATTERN, LINK_PATTERNS, VALUE_PATTERN };
private void visitFormalComments(ASTCompilationUnit node) {
if (imports.isEmpty()) {
return;
}
List<Token> formals = node.getFormalComments();
for (Token formal: formals) {
for (Pattern p: PATTERNS) {
Matcher m = p.matcher(formal.image);
while (m.find()) {
String s = m.group(1);
ImportWrapper candidate = new ImportWrapper(s, s, new SimpleJavaNode(-1));
if (imports.contains(candidate)) {
imports.remove(candidate);
if (imports.isEmpty()) {
return;
}
}
}
}
}
}
public Object visit(ASTImportDeclaration node, Object data) {
if (!node.isImportOnDemand()) {
ASTName importedType = (ASTName) node.jjtGetChild(0);