Merge branch 'pmd/5.3.x' into master

This commit is contained in:
Andreas Dangel
2015-05-22 19:46:19 +02:00
23 changed files with 337 additions and 30 deletions

View File

@ -47,8 +47,11 @@ public class ASTCompilationUnit extends AbstractJavaTypeNode implements RootNode
}
public ASTPackageDeclaration getPackageDeclaration() {
Node n = jjtGetChild(0);
return n instanceof ASTPackageDeclaration ? (ASTPackageDeclaration) n : null;
if (jjtGetNumChildren() > 0) {
Node n = jjtGetChild(0);
return n instanceof ASTPackageDeclaration ? (ASTPackageDeclaration) n : null;
}
return null;
}
public ClassTypeResolver getClassTypeResolver() {

View File

@ -104,14 +104,32 @@ public abstract class AbstractInefficientZeroCheck extends AbstractJavaRule {
private boolean isCompare(Node equality) {
if (isLiteralLeftHand(equality)) {
return checkComparison(inverse.get(equality.getImage()), equality, 0);
} else {
} else if (isLiteralRightHand(equality)) {
return checkComparison(equality.getImage(), equality, 1);
}
return false;
}
private boolean isLiteralLeftHand(Node equality) {
return equality.jjtGetChild(0).jjtGetChild(0).jjtGetNumChildren() > 0
&& equality.jjtGetChild(0).jjtGetChild(0).jjtGetChild(0) instanceof ASTLiteral;
return isLiteral(equality, 0);
}
private boolean isLiteralRightHand(Node equality) {
return isLiteral(equality, 1);
}
private boolean isLiteral(Node equality, int child) {
Node target = equality.jjtGetChild(child);
target = getFirstChildOrThis(target);
target = getFirstChildOrThis(target);
return target instanceof ASTLiteral;
}
private Node getFirstChildOrThis(Node node) {
if (node.jjtGetNumChildren() > 0) {
return node.jjtGetChild(0);
}
return node;
}
/**
@ -126,7 +144,10 @@ public abstract class AbstractInefficientZeroCheck extends AbstractJavaRule {
* @see #getComparisonTargets()
*/
private boolean checkComparison(String operator, Node equality, int i) {
Node target = equality.jjtGetChild(i).jjtGetChild(0).jjtGetChild(0);
Node target = equality
.jjtGetChild(i)
.jjtGetChild(0)
.jjtGetChild(0);
return target instanceof ASTLiteral && getComparisonTargets().get(operator).contains(target.getImage());
}

View File

@ -9,6 +9,7 @@ import java.util.List;
import net.sourceforge.pmd.lang.ast.Node;
import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTLambdaExpression;
import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTReturnStatement;
import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule;
@ -31,9 +32,11 @@ public class OnlyOneReturnRule extends AbstractJavaRule {
List<ASTReturnStatement> returnNodes = new ArrayList<ASTReturnStatement>();
node.findDescendantsOfType(ASTReturnStatement.class, returnNodes, false);
returnNodes = filterLambdaExpressions(returnNodes);
if (returnNodes.size() > 1) {
for (Iterator<ASTReturnStatement> i = returnNodes.iterator(); i.hasNext();) {
Node problem = i.next();
Node problem = i.next();
// skip the last one, it's OK
if (!i.hasNext()) {
continue;
@ -44,4 +47,21 @@ public class OnlyOneReturnRule extends AbstractJavaRule {
return data;
}
/**
* Checks whether the return statement is inside a lambda expression, and if
* so, this return statement is removed.
*
* @param returnNodes
* all the return statements inside the method
* @return all return statements, that are NOT within a lambda expression.
*/
private List<ASTReturnStatement> filterLambdaExpressions(List<ASTReturnStatement> returnNodes) {
List<ASTReturnStatement> filtered = new ArrayList<ASTReturnStatement>();
for (ASTReturnStatement ret : returnNodes) {
if (ret.getFirstParentOfType(ASTLambdaExpression.class) == null) {
filtered.add(ret);
}
}
return filtered;
}
}

View File

@ -32,6 +32,7 @@ public class AvoidReassigningParametersRule extends AbstractJavaRule {
if ((jocc.isOnLeftHandSide() || jocc.isSelfAssignment())
&& jocc.getNameForWhichThisIsAQualifier() == null
&& !jocc.useThisOrSuper()
&& !decl.isVarargs()
&& (!decl.isArray() || jocc.getLocation().jjtGetParent().jjtGetParent().jjtGetNumChildren() == 1)) {
// not an array or no primary suffix to access the array
// values

View File

@ -4,6 +4,7 @@
package net.sourceforge.pmd.lang.java.rule.design;
import net.sourceforge.pmd.lang.ast.Node;
import net.sourceforge.pmd.lang.java.ast.ASTAnnotation;
import net.sourceforge.pmd.lang.java.ast.ASTAnnotationTypeDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceBodyDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration;
@ -50,7 +51,11 @@ public class FieldDeclarationsShouldBeAtStartOfClassRule extends AbstractJavaRul
for (int i = 0; i < parent.jjtGetNumChildren(); i++) {
Node child = parent.jjtGetChild(i);
if (child.jjtGetNumChildren() > 0) {
child = child.jjtGetChild(0);
if (!(child.jjtGetChild(0) instanceof ASTAnnotation) || child.jjtGetNumChildren() == 1) {
child = child.jjtGetChild(0);
} else {
child = child.jjtGetChild(1);
}
}
if (child.equals(node)) {
break;

View File

@ -3,9 +3,11 @@
*/
package net.sourceforge.pmd.lang.java.rule.logging;
import java.util.List;
import java.util.logging.Level;
import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit;
import net.sourceforge.pmd.lang.java.ast.ASTImportDeclaration;
public class GuardLogStatementJavaUtilRule extends GuardLogStatementRule {
@ -24,6 +26,10 @@ public class GuardLogStatementJavaUtilRule extends GuardLogStatementRule {
@Override
public Object visit(ASTCompilationUnit unit, Object data) {
if (isSlf4jImported(unit)) {
return data;
}
String[] logLevels = getProperty(LOG_LEVELS);
String[] guardMethods = getProperty(GUARD_METHODS);
@ -37,6 +43,16 @@ public class GuardLogStatementJavaUtilRule extends GuardLogStatementRule {
return super.visit(unit,data);
}
private boolean isSlf4jImported(ASTCompilationUnit unit) {
List<ASTImportDeclaration> imports = unit.findChildrenOfType(ASTImportDeclaration.class);
for (ASTImportDeclaration i : imports) {
if (i.getImportedName().startsWith("org.slf4j")) {
return true;
}
}
return false;
}
private void configureGuards(String[] logLevels, String[] guardMethods) {
String[] methods = guardMethods;
if (methods.length != logLevels.length) {

View File

@ -3,9 +3,14 @@
*/
package net.sourceforge.pmd.lang.java.rule.naming;
import java.util.List;
import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceBodyDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit;
import net.sourceforge.pmd.lang.java.ast.ASTMarkerAnnotation;
import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclarator;
import net.sourceforge.pmd.lang.java.ast.ASTName;
import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule;
import net.sourceforge.pmd.lang.rule.properties.BooleanProperty;
@ -30,6 +35,10 @@ public class MethodNamingConventionsRule extends AbstractJavaRule {
return data;
}
if (isOverriddenMethod(node)) {
return data;
}
String methodName = node.getImage();
if (Character.isUpperCase(methodName.charAt(0))) {
@ -41,4 +50,15 @@ public class MethodNamingConventionsRule extends AbstractJavaRule {
return data;
}
private boolean isOverriddenMethod(ASTMethodDeclarator node) {
ASTClassOrInterfaceBodyDeclaration declaration = node.getFirstParentOfType(ASTClassOrInterfaceBodyDeclaration.class);
List<ASTMarkerAnnotation> annotations = declaration.findDescendantsOfType(ASTMarkerAnnotation.class);
for (ASTMarkerAnnotation ann : annotations) {
ASTName name = ann.getFirstChildOfType(ASTName.class);
if (name != null && name.hasImageEqualTo("Override")) {
return true;
}
}
return false;
}
}

View File

@ -36,6 +36,12 @@ public class VariableNameDeclaration extends AbstractNameDeclaration implements
}
}
public boolean isVarargs() {
ASTVariableDeclaratorId astVariableDeclaratorId = (ASTVariableDeclaratorId) node;
ASTFormalParameter parameter = astVariableDeclaratorId.getFirstParentOfType(ASTFormalParameter.class);
return parameter != null && parameter.isVarargs();
}
public boolean isExceptionBlockParameter() {
return ((ASTVariableDeclaratorId) node).isExceptionBlockParameter();
}

View File

@ -851,7 +851,8 @@ perform efficient map reads without blocking other threads.
<property name="xpath">
<value>
<![CDATA[
//Type[../VariableDeclarator/VariableInitializer//AllocationExpression]/ReferenceType/ClassOrInterfaceType[@Image = 'Map']
//Type[../VariableDeclarator/VariableInitializer//AllocationExpression/ClassOrInterfaceType[@Image != 'ConcurrentHashMap']]
/ReferenceType/ClassOrInterfaceType[@Image = 'Map']
]]>
</value>
</property>

View File

@ -70,21 +70,21 @@ public class ParserCornersTest extends ParserTst {
*/
@Test
public void testLambdaBug1333() {
parseJava18("final class Bug1333 {\n" +
" private static final Logger LOG = LoggerFactory.getLogger(Foo.class);\n" +
"\n" +
" public void deleteDirectoriesByNamePattern() {\n" +
" delete(path -> deleteDirectory(path));\n" +
" }\n" +
"\n" +
" private void delete(Consumer<? super String> consumer) {\n" +
" LOG.debug(consumer.toString());\n" +
" }\n" +
"\n" +
" private void deleteDirectory(String path) {\n" +
" LOG.debug(path);\n" +
" }\n" +
"}");
parseJava18("final class Bug1333 {\n"
+ " private static final Logger LOG = LoggerFactory.getLogger(Foo.class);\n" + "\n"
+ " public void deleteDirectoriesByNamePattern() {\n"
+ " delete(path -> deleteDirectory(path));\n" + " }\n" + "\n"
+ " private void delete(Consumer<? super String> consumer) {\n"
+ " LOG.debug(consumer.toString());\n" + " }\n" + "\n"
+ " private void deleteDirectory(String path) {\n" + " LOG.debug(path);\n" + " }\n" + "}");
}
/**
* Test for https://sourceforge.net/p/pmd/bugs/1355/
*/
@Test
public void emptyFileJustComment() {
parseJava18("// just a comment");
}
@Test

View File

@ -94,4 +94,29 @@ public class Foo {
}
]]></code>
</test-code>
<test-code>
<description>#1353 False positive "Only One Return" with lambda</description>
<expected-problems>0</expected-problems>
<code><![CDATA[
public class OnlyOneReturn {
public Try<SearchHit[]> search(final String indexName, final SearchRequest searchRequest) {
final SearchHit[] empty = new SearchHit[0];
final Optional<SearchDefinition> searchDefinition = settingsService.getSearchDefinition(indexName);
return searchDefinition.<Try<SearchHit[]>>map(
i -> {
final List<Try<ProviderSearchHit[]>> res = i.getSearchMapping().stream()
.peek(m -> LOGGER.debug("Treating backend \"{}\"", m.getProviderRef()))
.map(m -> invokeAdapter(getProviderSearchService(m.getProviderRef()), m, searchRequest))
.collect(Collectors.toList());
return TryCollections.pull(res).map(l -> sortReturning(l.stream().collect(ArrayCollectors.arrayMerging(
SearchServiceImpl::toSearchHit,
SearchHit::getInternalId,
Function.identity(),
SearchServiceImpl::merge)).orElse(Collections.emptyList()), SearchServiceImpl.searchHitComparator()))
.map(list -> list.toArray(empty));
}).orElse(Try.success(empty));
}
}
]]></code>
</test-code>
</test-data>

View File

@ -27,6 +27,22 @@ public class Foo {
public void m() {
final Map myMap = myObject.methodThatReturnMap();
}
}
]]></code>
</test-code>
<test-code>
<description>#1342 UseConcurrentHashMap false positive (with documentation example)</description>
<expected-problems>1</expected-problems>
<expected-linenumbers>3</expected-linenumbers>
<code><![CDATA[
public class ConcurrentApp {
public void getMyInstance() {
Map map1 = new HashMap(); // fine for single-threaded access --- violation on this line
Map map2 = new ConcurrentHashMap(); // preferred for use with multiple threads
// the following case will be ignored by this rule
Map map3 = someModule.methodThatReturnMap(); // might be OK, if the returned map is already thread-safe
}
}
]]></code>
</test-code>

View File

@ -234,6 +234,17 @@ public class PmdBug {
public static void main(String[] args) {
new PmdBug().foo("Hello world");
}
}
]]></code>
</test-code>
<test-code>
<description>#1330 AvoidReassigningParameters does not work with varargs</description>
<expected-problems>0</expected-problems>
<code><![CDATA[
public class AvoidReassigningParameters {
public void a(String... s) {
s[0] = "";
}
}
]]></code>
</test-code>

View File

@ -153,6 +153,23 @@ public class MyClass {
// something
}
};
}
]]></code>
</test-code>
<test-code>
<description>#1354 Complex FieldDeclarationsShouldBeAtStartOfClass false positive with Spring annotations</description>
<expected-problems>0</expected-problems>
<code><![CDATA[
public class MyClass {
private static final String MY_STRING = "STRING";
@Autowired
private MyPrivate myPrivate;
@Bean
public void myPublicBean() {}
private static void myPrivateStatic() {}
}
]]></code>
</test-code>

View File

@ -291,6 +291,29 @@ public class PMDIsEmptyFalsePositive {
// do something
}
}
}
]]></code>
</test-code>
<test-code>
<description>#1345 UseCollectionIsEmpty throws NullPointerException</description>
<expected-problems>0</expected-problems>
<code><![CDATA[
package org.oikarinen.pmdbugbait;
import java.util.List;
public class PmdBugBait {
public int getSize() {
return 0;
}
/**
* "this." before the method call triggers the bug
*/
public void compareSizeToThisPointMethod(List<String> list) {
if (list.size() < this.getSize()) {
throw new IllegalArgumentException();
}
}
}
]]></code>
</test-code>

View File

@ -119,6 +119,18 @@ public class Test {
__log.debug("bla" + "",e );
}
}
}
]]></code>
</test-code>
<test-code>
<description>#1341 pmd:GuardDebugLogging violates LOGGER.debug with format "{}"</description>
<expected-problems>0</expected-problems>
<code><![CDATA[
public class GuardDebugFalsePositive {
public void test() {
String tempSelector = "a";
LOGGER.debug("MessageSelector={}" , tempSelector);
}
}
]]></code>
</test-code>

View File

@ -51,6 +51,7 @@ public class Foo {
<test-code>
<description>#1227 GuardLogStatementJavaUtil doesn't catch log(Level.FINE, "msg" + " msg") calls</description>
<expected-problems>1</expected-problems>
<expected-linenumbers>8</expected-linenumbers>
<code><![CDATA[
import java.util.logging.Logger;
import java.util.logging.Level;
@ -64,6 +65,38 @@ public class Foo {
LOGGER.log(Level.FINE, "This is a severe message" + " and concat"); // no violation
}
}
}
]]></code>
</test-code>
<test-code>
<description>#1335 GuardLogStatementJavaUtil should not apply to SLF4J Logger</description>
<expected-problems>0</expected-problems>
<code><![CDATA[
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class GuardLogTest {
Logger LOGGER = LoggerFactory.getLogger(getClass());
public void foo() {
LOGGER.info("foo" + "bar");
}
}
]]></code>
</test-code>
<test-code>
<description>#1347 False positive for GuardLogStatementJavaUtil</description>
<expected-problems>0</expected-problems>
<code><![CDATA[
import org.slf4j.Logger;
public class GuardLogTest {
Logger LOG;
public void foo() {
if (LOG.isInfoEnabled()) {
LOG.info("update: After spool map size: " + map.size());
}
}
}
]]></code>
</test-code>

View File

@ -51,6 +51,17 @@ public class Foo {
<code><![CDATA[
public class Foo {
protected final native void __surfunc__(float[] data);
}
]]></code>
</test-code>
<test-code>
<description>#1343 MethodNamingConventions for overrided methods</description>
<expected-problems>0</expected-problems>
<code><![CDATA[
public class MethodNamingConventions implements SomeInterface {
@Override
public void _foo() {
}
}
]]></code>
</test-code>

View File

@ -330,6 +330,31 @@ public class X {
public class X {
private native void stream_dip_set_data(long stream, long dip_x,
long dip_z);
}
]]></code>
</test-code>
<test-code>
<description>#1346 VariableNamingConventions do not work for method parameters</description>
<expected-problems>1</expected-problems>
<expected-linenumbers>3</expected-linenumbers>
<rule-property name="checkParameters">true</rule-property>
<rule-property name="checkNativeMethodParameters">false</rule-property>
<code><![CDATA[
public class X {
private long horsTout;
public void setHorsTout(long hors_tout) {
this.horsTout = hors_tout;
}
}
]]></code>
</test-code>
<test-code>
<description>#1349 VariableNamingConventions : underscore in final but at first position ?</description>
<expected-problems>0</expected-problems>
<code><![CDATA[
public class X {
private final String _projection;
private final String _projectionID;
}
]]></code>
</test-code>

View File

@ -134,4 +134,17 @@ public class Foo {
}
]]></code>
</test-code>
<test-code>
<description>#1340 UseStringBufferForStringAppends False Positive with ternary operator</description>
<expected-problems>0</expected-problems>
<code><![CDATA[
public class UseStringBuffer {
public void foo() {
String country = (country == null || "".equals(country))
? ((String) getCountry())
: country;
}
}
]]></code>
</test-code>
</test-data>

View File

@ -4,6 +4,39 @@ Previous versions of PMD can be downloaded here:
http://sourceforge.net/projects/pmd/files/pmd/
## 22-May-2015 - 5.3.2
**Bugfixes:**
* [#1330](https://sourceforge.net/p/pmd/bugs/1330/): AvoidReassigningParameters does not work with varargs
* [#1335](https://sourceforge.net/p/pmd/bugs/1335/): GuardLogStatementJavaUtil should not apply to SLF4J Logger
* [#1342](https://sourceforge.net/p/pmd/bugs/1342/): UseConcurrentHashMap false positive (with documentation example)
* [#1343](https://sourceforge.net/p/pmd/bugs/1343/): MethodNamingConventions for overrided methods
* [#1345](https://sourceforge.net/p/pmd/bugs/1345/): UseCollectionIsEmpty throws NullPointerException
* [#1353](https://sourceforge.net/p/pmd/bugs/1353/): False positive "Only One Return" with lambda
* [#1354](https://sourceforge.net/p/pmd/bugs/1354/): Complex FieldDeclarationsShouldBeAtStartOfClass false positive with Spring annotations
* [#1355](https://sourceforge.net/p/pmd/bugs/1355/): NullPointerException in a java file having a single comment line
## 20-April-2015 - 5.3.1
**New/Modified/Deprecated Rules:**
* Language Java, ruleset design.xml: The rule "UseSingleton" *has been renamed* to "UseUtilityClass".
See also bugs [#1059](https://sourceforge.net/p/pmd/bugs/1059) and [#1339](https://sourceforge.net/p/pmd/bugs/1339/).
**Pull Requests:**
* [#53](https://github.com/pmd/pmd/pull/53): Fix some NullPointerExceptions
**Bugfixes:**
* [#1332](https://sourceforge.net/p/pmd/bugs/1332/): False Positive: UnusedPrivateMethod
* [#1333](https://sourceforge.net/p/pmd/bugs/1333/): Error while processing Java file with Lambda expressions
* [#1337](https://sourceforge.net/p/pmd/bugs/1337/): False positive "Avoid throwing raw exception types" when exception is not thrown
* [#1338](https://sourceforge.net/p/pmd/bugs/1338/): The pmd-java8 POM bears the wrong parent module version
## April 1, 2015 - 5.3.0
**New Supported Languages:**

View File

@ -34,14 +34,9 @@
**Pull Requests:**
* [#21](https://github.com/adangel/pmd/pull/21): Added PMD Rules for Singleton pattern violations.
* [#53](https://github.com/pmd/pmd/pull/53): Fix some NullPointerExceptions
* [#54](https://github.com/pmd/pmd/pull/54): Add a new rulesets for Maven's POM rules
**Bugfixes:**
* [#1332](https://sourceforge.net/p/pmd/bugs/1332/): False Positive: UnusedPrivateMethod
* [#1333](https://sourceforge.net/p/pmd/bugs/1333/): Error while processing Java file with Lambda expressions
* [#1337](https://sourceforge.net/p/pmd/bugs/1337/): False positive "Avoid throwing raw exception types" when exception is not thrown
* [#1338](https://sourceforge.net/p/pmd/bugs/1338/): The pmd-java8 POM bears the wrong parent module version
**API Changes:**

View File

@ -8,7 +8,7 @@
<skin>
<groupId>org.apache.maven.skins</groupId>
<artifactId>maven-fluido-skin</artifactId>
<version>1.3.1</version>
<version>1.4</version>
</skin>
<custom>