Merge branch 'pr/3663' into 7.0.x

This commit is contained in:
Clément Fournier
2022-01-26 19:46:02 +01:00
4 changed files with 53 additions and 36 deletions

View File

@ -180,7 +180,7 @@
<rule ref="category/java/errorprone.xml/AvoidCatchingNPE"/>
<rule ref="category/java/errorprone.xml/AvoidCatchingThrowable"/>
<rule ref="category/java/errorprone.xml/AvoidDecimalLiteralsInBigDecimalConstructor"/>
<!-- <rule ref="category/java/errorprone.xml/AvoidDuplicateLiterals"/> -->
<rule ref="category/java/errorprone.xml/AvoidDuplicateLiterals"/>
<rule ref="category/java/errorprone.xml/AvoidEnumAsIdentifier"/>
<rule ref="category/java/errorprone.xml/AvoidFieldNameMatchingMethodName"/>
<rule ref="category/java/errorprone.xml/AvoidFieldNameMatchingTypeName"/>

View File

@ -9,23 +9,24 @@ import static net.sourceforge.pmd.properties.PropertyFactory.intProperty;
import static net.sourceforge.pmd.properties.PropertyFactory.stringListProperty;
import static net.sourceforge.pmd.properties.constraints.NumericConstraints.positive;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import net.sourceforge.pmd.RuleContext;
import net.sourceforge.pmd.lang.ast.Node;
import net.sourceforge.pmd.lang.java.ast.ASTAnnotation;
import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit;
import net.sourceforge.pmd.lang.java.ast.ASTLiteral;
import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule;
import net.sourceforge.pmd.lang.java.ast.ASTStringLiteral;
import net.sourceforge.pmd.lang.java.rule.AbstractJavaRulechainRule;
import net.sourceforge.pmd.properties.PropertyDescriptor;
@SuppressWarnings("PMD")
public class AvoidDuplicateLiteralsRule extends AbstractJavaRule {
public class AvoidDuplicateLiteralsRule extends AbstractJavaRulechainRule {
public static final PropertyDescriptor<Integer> THRESHOLD_DESCRIPTOR
= intProperty("maxDuplicateLiterals")
@ -47,11 +48,12 @@ public class AvoidDuplicateLiteralsRule extends AbstractJavaRule {
.delim(',')
.build();
private Map<String, List<ASTLiteral>> literals = new HashMap<>();
private Map<String, SortedSet<ASTStringLiteral>> literals = new HashMap<>();
private Set<String> exceptions = new HashSet<>();
private int minLength;
public AvoidDuplicateLiteralsRule() {
super(ASTStringLiteral.class);
definePropertyDescriptor(THRESHOLD_DESCRIPTOR);
definePropertyDescriptor(MINIMUM_LENGTH_DESCRIPTOR);
definePropertyDescriptor(SKIP_ANNOTATIONS_DESCRIPTOR);
@ -59,7 +61,9 @@ public class AvoidDuplicateLiteralsRule extends AbstractJavaRule {
}
@Override
public Object visit(ASTCompilationUnit node, Object data) {
public void start(RuleContext ctx) {
super.start(ctx);
literals.clear();
if (getProperty(EXCEPTION_LIST_DESCRIPTOR) != null) {
@ -68,34 +72,31 @@ public class AvoidDuplicateLiteralsRule extends AbstractJavaRule {
minLength = 2 + getProperty(MINIMUM_LENGTH_DESCRIPTOR);
super.visit(node, data);
}
processResults(data);
return data;
@Override
public void end(RuleContext ctx) {
processResults(ctx);
super.end(ctx);
}
private void processResults(Object data) {
int threshold = getProperty(THRESHOLD_DESCRIPTOR);
for (Map.Entry<String, List<ASTLiteral>> entry : literals.entrySet()) {
List<ASTLiteral> occurrences = entry.getValue();
for (Map.Entry<String, SortedSet<ASTStringLiteral>> entry : literals.entrySet()) {
SortedSet<ASTStringLiteral> occurrences = entry.getValue();
if (occurrences.size() >= threshold) {
ASTLiteral first = occurrences.get(0);
// FIXME - REVERT ME
// String rawImage = first.getEscapedStringLiteral();
// Object[] args = {rawImage, occurrences.size(), first.getBeginLine(), };
// addViolation(data, first, args);
ASTStringLiteral first = occurrences.first();
String rawImage = first.getImage();
Object[] args = {rawImage, occurrences.size(), first.getBeginLine(), };
addViolation(data, first, args);
}
}
}
@Override
public Object visit(ASTLiteral node, Object data) {
if (!node.isStringLiteral()) {
return data;
}
public Object visit(ASTStringLiteral node, Object data) {
String image = node.getImage();
// just catching strings of 'minLength' chars or more (including the
@ -110,18 +111,14 @@ public class AvoidDuplicateLiteralsRule extends AbstractJavaRule {
}
// Skip literals in annotations
if (getProperty(SKIP_ANNOTATIONS_DESCRIPTOR) && node.getFirstParentOfType(ASTAnnotation.class) != null) {
if (getProperty(SKIP_ANNOTATIONS_DESCRIPTOR) && node.ancestors(ASTAnnotation.class).nonEmpty()) {
return data;
}
if (literals.containsKey(image)) {
List<ASTLiteral> occurrences = literals.get(image);
occurrences.add(node);
} else {
List<ASTLiteral> occurrences = new ArrayList<>();
occurrences.add(node);
literals.put(image, occurrences);
}
// This is a rulechain rule - the nodes might be visited out of order. Therefore sort the occurrences.
SortedSet<ASTStringLiteral> occurrences = literals.computeIfAbsent(image,
key -> new TreeSet<>(Node.COORDS_COMPARATOR));
occurrences.add(node);
return data;
}

View File

@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.errorprone;
import net.sourceforge.pmd.testframework.PmdRuleTst;
@org.junit.Ignore("Rule has not been updated yet")
public class AvoidDuplicateLiteralsTest extends PmdRuleTst {
// no additional unit tests
}

View File

@ -7,6 +7,7 @@
<test-code>
<description>duplicate literals in argument list</description>
<expected-problems>1</expected-problems>
<expected-linenumbers>3</expected-linenumbers>
<code><![CDATA[
public class Foo {
private void bar() {
@ -34,6 +35,7 @@ public class Foo {
<test-code>
<description>duplicate literals in field decl</description>
<expected-problems>1</expected-problems>
<expected-linenumbers>2</expected-linenumbers>
<code><![CDATA[
public class Foo {
String[] FOO = {"foo", "foo", "foo", "foo", "foo", "foo", "foo", "foo", "foo"};
@ -42,8 +44,9 @@ public class Foo {
</test-code>
<test-code>
<description>duplicate literals in annotations</description>
<description>duplicate literals in annotations, default case</description>
<expected-problems>1</expected-problems>
<expected-linenumbers>1</expected-linenumbers>
<code><![CDATA[
@SuppressWarnings("foo")
@SuppressWarnings("foo")
@ -80,6 +83,7 @@ public class Foo {
<description>threshold property</description>
<rule-property name="maxDuplicateLiterals">2</rule-property>
<expected-problems>1</expected-problems>
<expected-linenumbers>3</expected-linenumbers>
<code><![CDATA[
public class Foo {
private void bar() {
@ -123,6 +127,7 @@ public class Foo {
<description>minimum length property, minimum length reached</description>
<rule-property name="minimumLength">5</rule-property>
<expected-problems>1</expected-problems>
<expected-linenumbers>3</expected-linenumbers>
<code><![CDATA[
public class Foo {
private void bar() {
@ -148,6 +153,7 @@ public class Foo {
<test-code>
<description>minimum length property, default value</description>
<expected-problems>1</expected-problems>
<expected-linenumbers>3</expected-linenumbers>
<code><![CDATA[
public class Foo {
private void bar() {
@ -161,6 +167,7 @@ public class Foo {
<test-code>
<description>#1425 Invalid XML Characters in Output</description>
<expected-problems>1</expected-problems>
<expected-linenumbers>2</expected-linenumbers>
<expected-messages>
<message>The String literal "Tokenizer \ud801\udc1ctest" appears 4 times in this file; the first occurrence is on line 2</message>
</expected-messages>
@ -172,6 +179,20 @@ public class Duplicate {
String s4 = "Tokenizer \ud801\udc1ctest";
char c = '\uffef';
char c\u0030 = 'a';
}
]]></code>
</test-code>
<test-code>
<description>Duplicated string literals as annotation parameter</description>
<rule-property name="skipAnnotations">true</rule-property>
<expected-problems>0</expected-problems>
<code><![CDATA[
public class AnnotationParams {
public static void foo(@SuppressWarnings("unused") Object str,
@SuppressWarnings("unused") String str2,
@SuppressWarnings("unused") String str3,
@SuppressWarnings("unused") String str4) {}
}
]]></code>
</test-code>