Merge branch 'master' into update-classnamingconventions
This commit is contained in:
@ -308,7 +308,7 @@ entries:
|
||||
url: /pmd_devdocs_building.html
|
||||
output: web, pdf
|
||||
- title: Contributing
|
||||
url: /pmd_devdocs_contributing.html
|
||||
url: https://github.com/pmd/pmd/blob/master/CONTRIBUTING.md
|
||||
output: web, pdf
|
||||
- title: Writing documentation
|
||||
url: /pmd_devdocs_writing_documentation.html
|
||||
|
@ -1,24 +0,0 @@
|
||||
---
|
||||
title: Contributing to PMD
|
||||
sidebar: pmd_sidebar
|
||||
permalink: pmd_devdocs_contributing.html
|
||||
folder: pmd/devdocs
|
||||
---
|
||||
|
||||
## Contributing via pull requests
|
||||
|
||||
First off, thanks for taking the time to contribute!
|
||||
|
||||
* Please create your pull request against the `master` branch. We will rebase/merge it to the maintenance
|
||||
branches, if necessary. Just fork the [pmd repo](https://github.com/pmd/pmd/) and
|
||||
create a [pull request](https://github.com/pmd/pmd/pulls).
|
||||
|
||||
* We are using [checkstyle](http://checkstyle.sourceforge.net/) to enforce a common code style.
|
||||
The check is integrated into the default build - so, make sure, you can [build PMD](pmd_devdocs_building.html)
|
||||
without errors.
|
||||
|
||||
The following section goes into more details about our Checkstyle configuration.
|
||||
|
||||
## Code style
|
||||
|
||||
* Checkstyle
|
@ -153,7 +153,7 @@ Maven plugin will use and benefit from the latest bugfixes and enhancements:
|
||||
``` xml
|
||||
<project>
|
||||
<properties>
|
||||
<pmdVersion>...choose your version...</version>
|
||||
<pmdVersion>...choose your version...</pmdVersion>
|
||||
</properties>
|
||||
...
|
||||
<build>
|
||||
|
@ -13,6 +13,7 @@ This is a minor release.
|
||||
### Table Of Contents
|
||||
|
||||
* [New and noteworthy](#new-and-noteworthy)
|
||||
* [Tree transversal revision](#tree-transversal-revision)
|
||||
* [Naming rules enhancements](#naming-rules-enhancements)
|
||||
* [Fixed Issues](#fixed-issues)
|
||||
* [API Changes](#api-changes)
|
||||
@ -21,6 +22,18 @@ This is a minor release.
|
||||
|
||||
### New and noteworthy
|
||||
|
||||
#### Tree transversal revision
|
||||
|
||||
As described in [#904](https://github.com/pmd/pmd/issues/904), when searching for child nodes of the AST methods
|
||||
such as `hasDescendantOfType`, `getFirstDescendantOfType` and `findDescendantsOfType` were found to behave inconsistently,
|
||||
not all of them honoring find boundaries; that is, nodes that define a self-contained entity which should be considered separately
|
||||
(think of lambdas, nested classes, anonymous classes, etc.). We have modified these methods to ensure all of them honor
|
||||
find boundaries.
|
||||
|
||||
This change implies several false positives / unexpected results (ie: `ASTBlockStatement` falsely returning `true` to `isAllocation()`)
|
||||
have been fixed; and lots of searches are now restricted to smaller search areas, which improves performance (depending on the project,
|
||||
we have measured up to 10% improvements during Type Resolution, Symbol Table analysis, and some rule's application).
|
||||
|
||||
|
||||
#### Naming rules enhancements
|
||||
|
||||
@ -34,6 +47,11 @@ This is a minor release.
|
||||
|
||||
### Fixed Issues
|
||||
|
||||
* documentation
|
||||
* [#994](https://github.com/pmd/pmd/issues/994): \[doc] Delete duplicate page contributing.md on the website
|
||||
* java-bestpracrtices
|
||||
* [#370](https://github.com/pmd/pmd/issues/370): \[java] GuardLogStatementJavaUtil not considering lambdas
|
||||
|
||||
### API Changes
|
||||
|
||||
#### Deprecated Rules
|
||||
@ -46,3 +64,6 @@ This is a minor release.
|
||||
|
||||
|
||||
### External Contributions
|
||||
|
||||
* [#1002](https://github.com/pmd/pmd/pull/1002): \[doc] Delete duplicate page contributing.md on the website - [Ishan Srivastava](https://github.com/ishanSrt)
|
||||
* [#1008](https://github.com/pmd/pmd/pull/1008): \[core] DOC: fix closing tag for <pmdVersion> - [stonio](https://github.com/stonio)
|
||||
|
@ -237,7 +237,6 @@ public abstract class AbstractNode implements Node {
|
||||
return parents;
|
||||
}
|
||||
|
||||
|
||||
@SafeVarargs
|
||||
@Override
|
||||
public final <T> T getFirstParentOfAnyType(Class<? extends T>... parentTypes) {
|
||||
@ -253,14 +252,19 @@ public abstract class AbstractNode implements Node {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public <T> List<T> findDescendantsOfType(Class<T> targetType) {
|
||||
List<T> list = new ArrayList<>();
|
||||
findDescendantsOfType(this, targetType, list, true);
|
||||
findDescendantsOfType(this, targetType, list, false);
|
||||
return list;
|
||||
}
|
||||
|
||||
// TODO : Add to Node interface in 7.0.0
|
||||
public <T> List<T> findDescendantsOfType(final Class<T> targetType, final boolean crossBoundaries) {
|
||||
final List<T> list = new ArrayList<>();
|
||||
findDescendantsOfType(this, targetType, list, crossBoundaries);
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void findDescendantsOfType(Class<T> targetType, List<T> results, boolean crossBoundaries) {
|
||||
@ -270,17 +274,15 @@ public abstract class AbstractNode implements Node {
|
||||
private static <T> void findDescendantsOfType(Node node, Class<T> targetType, List<T> results,
|
||||
boolean crossFindBoundaries) {
|
||||
|
||||
if (!crossFindBoundaries && node.isFindBoundary()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < node.jjtGetNumChildren(); i++) {
|
||||
Node child = node.jjtGetChild(i);
|
||||
if (child.getClass() == targetType) {
|
||||
results.add(targetType.cast(child));
|
||||
}
|
||||
|
||||
findDescendantsOfType(child, targetType, results, crossFindBoundaries);
|
||||
if (crossFindBoundaries || !child.isFindBoundary()) {
|
||||
findDescendantsOfType(child, targetType, results, crossFindBoundaries);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -356,16 +358,18 @@ public abstract class AbstractNode implements Node {
|
||||
}
|
||||
|
||||
|
||||
private static <T> T getFirstDescendantOfType(Class<T> descendantType, Node node) {
|
||||
int n = node.jjtGetNumChildren();
|
||||
private static <T> T getFirstDescendantOfType(final Class<T> descendantType, final Node node) {
|
||||
final int n = node.jjtGetNumChildren();
|
||||
for (int i = 0; i < n; i++) {
|
||||
Node n1 = node.jjtGetChild(i);
|
||||
if (descendantType.isAssignableFrom(n1.getClass())) {
|
||||
return descendantType.cast(n1);
|
||||
}
|
||||
T n2 = getFirstDescendantOfType(descendantType, n1);
|
||||
if (n2 != null) {
|
||||
return n2;
|
||||
if (!n1.isFindBoundary()) {
|
||||
final T n2 = getFirstDescendantOfType(descendantType, n1);
|
||||
if (n2 != null) {
|
||||
return n2;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
@ -175,7 +175,7 @@ public interface Node {
|
||||
|
||||
/**
|
||||
* Traverses down the tree to find all the descendant instances of type
|
||||
* descendantType.
|
||||
* descendantType without crossing find boundaries.
|
||||
*
|
||||
* @param targetType
|
||||
* class which you want to find.
|
||||
@ -212,7 +212,7 @@ public interface Node {
|
||||
|
||||
/**
|
||||
* Traverses down the tree to find the first descendant instance of type
|
||||
* descendantType.
|
||||
* descendantType without crossing find boundaries.
|
||||
*
|
||||
* @param descendantType
|
||||
* class which you want to find.
|
||||
@ -222,7 +222,7 @@ public interface Node {
|
||||
<T> T getFirstDescendantOfType(Class<T> descendantType);
|
||||
|
||||
/**
|
||||
* Finds if this node contains a descendant of the given type.
|
||||
* Finds if this node contains a descendant of the given type without crossing find boundaries.
|
||||
*
|
||||
* @param type
|
||||
* the node type to search
|
||||
|
@ -0,0 +1,72 @@
|
||||
/**
|
||||
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
|
||||
*/
|
||||
|
||||
package net.sourceforge.pmd.lang.ast;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* Unit test for {@link AbstractNode} tree transversal methods
|
||||
*/
|
||||
public class AbstractNodeTransversalTest {
|
||||
private int id;
|
||||
private Node rootNode;
|
||||
|
||||
private int nextId() {
|
||||
return id++;
|
||||
}
|
||||
|
||||
private Node newDummyNode(boolean boundary) {
|
||||
return new DummyNode(nextId(), boundary);
|
||||
}
|
||||
|
||||
private Node addChild(final Node parent, final Node child) {
|
||||
parent.jjtAddChild(child, parent.jjtGetNumChildren()); // Append child at the end
|
||||
child.jjtSetParent(parent);
|
||||
return parent;
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUpSampleNodeTree() {
|
||||
id = 0;
|
||||
rootNode = newDummyNode(false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBoundaryIsHonored() {
|
||||
addChild(rootNode, addChild(newDummyNode(true), newDummyNode(false)));
|
||||
|
||||
List<DummyNode> descendantsOfType = rootNode.findDescendantsOfType(DummyNode.class);
|
||||
assertEquals(1, descendantsOfType.size());
|
||||
assertTrue(descendantsOfType.get(0).isFindBoundary());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchFromBoundary() {
|
||||
addChild(rootNode, addChild(newDummyNode(true), newDummyNode(false)));
|
||||
|
||||
List<DummyNode> descendantsOfType = rootNode.findDescendantsOfType(DummyNode.class).get(0).findDescendantsOfType(DummyNode.class);
|
||||
assertEquals(1, descendantsOfType.size());
|
||||
assertFalse(descendantsOfType.get(0).isFindBoundary());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchIgnoringBoundary() {
|
||||
addChild(rootNode, addChild(newDummyNode(true), newDummyNode(false)));
|
||||
|
||||
List<DummyNode> descendantsOfType = new ArrayList<>();
|
||||
rootNode.findDescendantsOfType(DummyNode.class, descendantsOfType, true);
|
||||
assertEquals(2, descendantsOfType.size());
|
||||
assertTrue(descendantsOfType.get(0).isFindBoundary());
|
||||
assertFalse(descendantsOfType.get(1).isFindBoundary());
|
||||
}
|
||||
}
|
@ -5,8 +5,15 @@
|
||||
package net.sourceforge.pmd.lang.ast;
|
||||
|
||||
public class DummyNode extends AbstractNode {
|
||||
private final boolean findBoundary;
|
||||
|
||||
public DummyNode(int id) {
|
||||
this(id, false);
|
||||
}
|
||||
|
||||
public DummyNode(int id, boolean findBoundary) {
|
||||
super(id);
|
||||
this.findBoundary = findBoundary;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -18,4 +25,9 @@ public class DummyNode extends AbstractNode {
|
||||
public String getXPathNodeName() {
|
||||
return "dummyNode";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFindBoundary() {
|
||||
return findBoundary;
|
||||
}
|
||||
}
|
||||
|
@ -5,8 +5,6 @@
|
||||
|
||||
package net.sourceforge.pmd.lang.java.ast;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import net.sourceforge.pmd.lang.ast.Node;
|
||||
|
||||
public class ASTClassOrInterfaceType extends AbstractJavaTypeNode {
|
||||
@ -35,14 +33,12 @@ public class ASTClassOrInterfaceType extends AbstractJavaTypeNode {
|
||||
*/
|
||||
public boolean isReferenceToClassSameCompilationUnit() {
|
||||
ASTCompilationUnit root = getFirstParentOfType(ASTCompilationUnit.class);
|
||||
List<ASTClassOrInterfaceDeclaration> classes = root.findDescendantsOfType(ASTClassOrInterfaceDeclaration.class);
|
||||
for (ASTClassOrInterfaceDeclaration c : classes) {
|
||||
for (ASTClassOrInterfaceDeclaration c : root.findDescendantsOfType(ASTClassOrInterfaceDeclaration.class, true)) {
|
||||
if (c.hasImageEqualTo(getImage())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
List<ASTEnumDeclaration> enums = root.findDescendantsOfType(ASTEnumDeclaration.class);
|
||||
for (ASTEnumDeclaration e : enums) {
|
||||
for (ASTEnumDeclaration e : root.findDescendantsOfType(ASTEnumDeclaration.class, true)) {
|
||||
if (e.hasImageEqualTo(getImage())) {
|
||||
return true;
|
||||
}
|
||||
|
@ -103,7 +103,7 @@ public class AtfdBaseVisitor extends JavaParserVisitorAdapter {
|
||||
|
||||
|
||||
private boolean isAttributeAccess(ASTPrimaryExpression node) {
|
||||
return node.findDescendantsOfType(ASTPrimarySuffix.class).isEmpty();
|
||||
return !node.hasDescendantOfType(ASTPrimarySuffix.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
package net.sourceforge.pmd.lang.java.rule.bestpractices;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
@ -139,7 +140,9 @@ public class PreserveStackTraceRule extends AbstractJavaRule {
|
||||
private boolean checkForTargetUsage(String target, Node baseNode) {
|
||||
boolean match = false;
|
||||
if (target != null && baseNode != null) {
|
||||
List<ASTName> nameNodes = baseNode.findDescendantsOfType(ASTName.class);
|
||||
// TODO : use Node.findDescendantsOfType(ASTName.class, true) on 7.0.0
|
||||
List<ASTName> nameNodes = new ArrayList<>();
|
||||
baseNode.findDescendantsOfType(ASTName.class, nameNodes, true);
|
||||
for (ASTName nameNode : nameNodes) {
|
||||
if (target.equals(nameNode.getImage())) {
|
||||
boolean isPartOfStringConcatenation = isStringConcat(nameNode, baseNode);
|
||||
|
@ -18,6 +18,7 @@ import net.sourceforge.pmd.lang.java.ast.ASTEnumDeclaration;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTName;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTPrimaryPrefix;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTPrimarySuffix;
|
||||
import net.sourceforge.pmd.lang.java.ast.AbstractJavaNode;
|
||||
import net.sourceforge.pmd.lang.java.ast.AccessNode;
|
||||
import net.sourceforge.pmd.lang.java.ast.Annotatable;
|
||||
import net.sourceforge.pmd.lang.java.ast.JavaNode;
|
||||
@ -90,20 +91,18 @@ public class UnusedPrivateFieldRule extends AbstractLombokAwareRule {
|
||||
List<ASTClassOrInterfaceBodyDeclaration> classOrInterfaceBodyDeclarations = body
|
||||
.findChildrenOfType(ASTClassOrInterfaceBodyDeclaration.class);
|
||||
List<ASTEnumConstant> enumConstants = body.findChildrenOfType(ASTEnumConstant.class);
|
||||
List<JavaNode> nodes = new ArrayList<>();
|
||||
List<AbstractJavaNode> nodes = new ArrayList<>();
|
||||
nodes.addAll(classOrInterfaceBodyDeclarations);
|
||||
nodes.addAll(enumConstants);
|
||||
|
||||
for (JavaNode node : nodes) {
|
||||
List<ASTPrimarySuffix> primarySuffixes = node.findDescendantsOfType(ASTPrimarySuffix.class);
|
||||
for (ASTPrimarySuffix primarySuffix : primarySuffixes) {
|
||||
for (AbstractJavaNode node : nodes) {
|
||||
for (ASTPrimarySuffix primarySuffix : node.findDescendantsOfType(ASTPrimarySuffix.class, true)) {
|
||||
if (decl.getImage().equals(primarySuffix.getImage())) {
|
||||
return true; // No violation
|
||||
}
|
||||
}
|
||||
|
||||
List<ASTPrimaryPrefix> primaryPrefixes = node.findDescendantsOfType(ASTPrimaryPrefix.class);
|
||||
for (ASTPrimaryPrefix primaryPrefix : primaryPrefixes) {
|
||||
for (ASTPrimaryPrefix primaryPrefix : node.findDescendantsOfType(ASTPrimaryPrefix.class, true)) {
|
||||
ASTName name = primaryPrefix.getFirstDescendantOfType(ASTName.class);
|
||||
|
||||
if (name != null) {
|
||||
|
@ -117,14 +117,9 @@ public class CommentDefaultAccessModifierRule extends AbstractCommentRule {
|
||||
if (parent != null) {
|
||||
List<ASTAnnotation> annotations = parent.findChildrenOfType(ASTAnnotation.class);
|
||||
for (ASTAnnotation annotation : annotations) {
|
||||
List<ASTName> names = annotation.findDescendantsOfType(ASTName.class);
|
||||
for (ASTName name : names) {
|
||||
if (name.hasImageEqualTo("VisibleForTesting")) {
|
||||
result = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (result == false) {
|
||||
final ASTName name = annotation.getFirstDescendantOfType(ASTName.class);
|
||||
if (name.hasImageEqualTo("VisibleForTesting")) {
|
||||
result = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -4,13 +4,11 @@
|
||||
|
||||
package net.sourceforge.pmd.lang.java.rule.codestyle;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
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 +29,7 @@ public class OnlyOneReturnRule extends AbstractJavaRule {
|
||||
return data;
|
||||
}
|
||||
|
||||
List<ASTReturnStatement> returnNodes = new ArrayList<>();
|
||||
node.findDescendantsOfType(ASTReturnStatement.class, returnNodes, false);
|
||||
returnNodes = filterLambdaExpressions(returnNodes);
|
||||
List<ASTReturnStatement> returnNodes = node.findDescendantsOfType(ASTReturnStatement.class);
|
||||
|
||||
if (returnNodes.size() > 1) {
|
||||
for (Iterator<ASTReturnStatement> i = returnNodes.iterator(); i.hasNext();) {
|
||||
@ -47,22 +43,4 @@ 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<>();
|
||||
for (ASTReturnStatement ret : returnNodes) {
|
||||
if (ret.getFirstParentOfType(ASTLambdaExpression.class) == null) {
|
||||
filtered.add(ret);
|
||||
}
|
||||
}
|
||||
return filtered;
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,6 @@
|
||||
|
||||
package net.sourceforge.pmd.lang.java.rule.design;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@ -136,9 +135,7 @@ public class ImmutableFieldRule extends AbstractLombokAwareRule {
|
||||
}
|
||||
|
||||
private List<ASTConstructorDeclaration> findAllConstructors(ASTClassOrInterfaceDeclaration node) {
|
||||
List<ASTConstructorDeclaration> cons = new ArrayList<>();
|
||||
node.getFirstChildOfType(ASTClassOrInterfaceBody.class).findDescendantsOfType(ASTConstructorDeclaration.class,
|
||||
cons, false);
|
||||
return cons;
|
||||
return node.getFirstChildOfType(ASTClassOrInterfaceBody.class)
|
||||
.findDescendantsOfType(ASTConstructorDeclaration.class);
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,6 @@
|
||||
|
||||
package net.sourceforge.pmd.lang.java.rule.design;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import net.sourceforge.pmd.lang.ast.Node;
|
||||
@ -161,8 +160,7 @@ public class SingularFieldRule extends AbstractLombokAwareRule {
|
||||
private boolean isInAssignment(Node potentialStatement) {
|
||||
if (potentialStatement instanceof ASTStatementExpression) {
|
||||
ASTStatementExpression statement = (ASTStatementExpression) potentialStatement;
|
||||
List<ASTAssignmentOperator> assignments = new ArrayList<>();
|
||||
statement.findDescendantsOfType(ASTAssignmentOperator.class, assignments, false);
|
||||
List<ASTAssignmentOperator> assignments = statement.findDescendantsOfType(ASTAssignmentOperator.class);
|
||||
return !assignments.isEmpty() && "=".equals(assignments.get(0).getImage());
|
||||
} else {
|
||||
return false;
|
||||
|
@ -4,68 +4,43 @@
|
||||
|
||||
package net.sourceforge.pmd.lang.java.rule.design;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import net.sourceforge.pmd.lang.ast.Node;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTFieldDeclaration;
|
||||
import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule;
|
||||
import net.sourceforge.pmd.properties.IntegerProperty;
|
||||
import net.sourceforge.pmd.util.NumericConstants;
|
||||
|
||||
public class TooManyFieldsRule extends AbstractJavaRule {
|
||||
|
||||
private static final int DEFAULT_MAXFIELDS = 15;
|
||||
|
||||
private Map<String, Integer> stats;
|
||||
private Map<String, ASTClassOrInterfaceDeclaration> nodes;
|
||||
|
||||
private static final IntegerProperty MAX_FIELDS_DESCRIPTOR = new IntegerProperty("maxfields",
|
||||
"Max allowable fields", 1, 300, DEFAULT_MAXFIELDS, 1.0f);
|
||||
|
||||
public TooManyFieldsRule() {
|
||||
definePropertyDescriptor(MAX_FIELDS_DESCRIPTOR);
|
||||
addRuleChainVisit(ASTClassOrInterfaceDeclaration.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object visit(ASTCompilationUnit node, Object data) {
|
||||
public Object visit(ASTClassOrInterfaceDeclaration node, Object data) {
|
||||
final int maxFields = getProperty(MAX_FIELDS_DESCRIPTOR);
|
||||
int counter = 0;
|
||||
|
||||
int maxFields = getProperty(MAX_FIELDS_DESCRIPTOR);
|
||||
|
||||
stats = new HashMap<>(5);
|
||||
nodes = new HashMap<>(5);
|
||||
|
||||
List<ASTFieldDeclaration> l = node.findDescendantsOfType(ASTFieldDeclaration.class);
|
||||
final List<ASTFieldDeclaration> l = node.findDescendantsOfType(ASTFieldDeclaration.class);
|
||||
|
||||
for (ASTFieldDeclaration fd : l) {
|
||||
if (fd.isFinal() && fd.isStatic()) {
|
||||
continue;
|
||||
}
|
||||
ASTClassOrInterfaceDeclaration clazz = fd.getFirstParentOfType(ASTClassOrInterfaceDeclaration.class);
|
||||
if (clazz != null && !clazz.isInterface()) {
|
||||
bumpCounterFor(clazz);
|
||||
}
|
||||
counter++;
|
||||
}
|
||||
for (Map.Entry<String, Integer> entry : stats.entrySet()) {
|
||||
int val = entry.getValue();
|
||||
Node n = nodes.get(entry.getKey());
|
||||
if (val > maxFields) {
|
||||
addViolation(data, n);
|
||||
}
|
||||
|
||||
if (counter > maxFields) {
|
||||
addViolation(data, node);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
private void bumpCounterFor(ASTClassOrInterfaceDeclaration clazz) {
|
||||
String key = clazz.getImage();
|
||||
if (!stats.containsKey(key)) {
|
||||
stats.put(key, NumericConstants.ZERO);
|
||||
nodes.put(key, clazz);
|
||||
}
|
||||
Integer i = Integer.valueOf(stats.get(key) + 1);
|
||||
stats.put(key, i);
|
||||
}
|
||||
}
|
||||
|
@ -196,26 +196,19 @@ public abstract class AbstractCommentRule extends AbstractJavaRule {
|
||||
}
|
||||
|
||||
protected SortedMap<Integer, Node> orderedCommentsAndDeclarations(ASTCompilationUnit cUnit) {
|
||||
|
||||
SortedMap<Integer, Node> itemsByLineNumber = new TreeMap<>();
|
||||
|
||||
List<ASTClassOrInterfaceDeclaration> packageDecl = cUnit
|
||||
.findDescendantsOfType(ASTClassOrInterfaceDeclaration.class);
|
||||
addDeclarations(itemsByLineNumber, packageDecl);
|
||||
addDeclarations(itemsByLineNumber, cUnit.findDescendantsOfType(ASTClassOrInterfaceDeclaration.class, true));
|
||||
|
||||
addDeclarations(itemsByLineNumber, cUnit.getComments());
|
||||
|
||||
List<ASTFieldDeclaration> fields = cUnit.findDescendantsOfType(ASTFieldDeclaration.class);
|
||||
addDeclarations(itemsByLineNumber, fields);
|
||||
addDeclarations(itemsByLineNumber, cUnit.findDescendantsOfType(ASTFieldDeclaration.class, true));
|
||||
|
||||
List<ASTMethodDeclaration> methods = cUnit.findDescendantsOfType(ASTMethodDeclaration.class);
|
||||
addDeclarations(itemsByLineNumber, methods);
|
||||
addDeclarations(itemsByLineNumber, cUnit.findDescendantsOfType(ASTMethodDeclaration.class, true));
|
||||
|
||||
List<ASTConstructorDeclaration> constructors = cUnit.findDescendantsOfType(ASTConstructorDeclaration.class);
|
||||
addDeclarations(itemsByLineNumber, constructors);
|
||||
addDeclarations(itemsByLineNumber, cUnit.findDescendantsOfType(ASTConstructorDeclaration.class, true));
|
||||
|
||||
List<ASTEnumDeclaration> enumDecl = cUnit.findDescendantsOfType(ASTEnumDeclaration.class);
|
||||
addDeclarations(itemsByLineNumber, enumDecl);
|
||||
addDeclarations(itemsByLineNumber, cUnit.findDescendantsOfType(ASTEnumDeclaration.class, true));
|
||||
|
||||
return itemsByLineNumber;
|
||||
}
|
||||
|
@ -104,13 +104,13 @@ public class CloseResourceRule extends AbstractJavaRule {
|
||||
@Override
|
||||
public Object visit(ASTConstructorDeclaration node, Object data) {
|
||||
checkForResources(node, data);
|
||||
return data;
|
||||
return super.visit(node, data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object visit(ASTMethodDeclaration node, Object data) {
|
||||
checkForResources(node, data);
|
||||
return data;
|
||||
return super.visit(node, data);
|
||||
}
|
||||
|
||||
private void checkForResources(Node node, Object data) {
|
||||
|
@ -23,8 +23,9 @@ public class TestClassWithoutTestCasesRule extends AbstractJUnitRule {
|
||||
|
||||
if (m != null) {
|
||||
for (ASTMethodDeclaration md : m) {
|
||||
if (!isInInnerClassOrInterface(md) && isJUnitMethod(md, data)) {
|
||||
if (isJUnitMethod(md, data)) {
|
||||
testsFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -35,9 +36,4 @@ public class TestClassWithoutTestCasesRule extends AbstractJUnitRule {
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
private boolean isInInnerClassOrInterface(ASTMethodDeclaration md) {
|
||||
ASTClassOrInterfaceDeclaration p = md.getFirstParentOfType(ASTClassOrInterfaceDeclaration.class);
|
||||
return p != null && p.isNested();
|
||||
}
|
||||
}
|
||||
|
@ -47,7 +47,8 @@ public class ASTVariableDeclaratorIdTest {
|
||||
@Test
|
||||
public void testLambdaWithType() throws Exception {
|
||||
ASTCompilationUnit acu = parseJava18(TEST_LAMBDA_WITH_TYPE);
|
||||
ASTVariableDeclaratorId f = acu.findDescendantsOfType(ASTVariableDeclaratorId.class).get(1);
|
||||
ASTLambdaExpression lambda = acu.getFirstDescendantOfType(ASTLambdaExpression.class);
|
||||
ASTVariableDeclaratorId f = lambda.getFirstDescendantOfType(ASTVariableDeclaratorId.class);
|
||||
assertEquals("File", f.getTypeNode().getTypeImage());
|
||||
assertEquals("File", f.getTypeNameNode().jjtGetChild(0).getImage());
|
||||
}
|
||||
@ -55,7 +56,8 @@ public class ASTVariableDeclaratorIdTest {
|
||||
@Test
|
||||
public void testLambdaWithoutType() throws Exception {
|
||||
ASTCompilationUnit acu = parseJava18(TEST_LAMBDA_WITHOUT_TYPE);
|
||||
ASTVariableDeclaratorId f = acu.findDescendantsOfType(ASTVariableDeclaratorId.class).get(1);
|
||||
ASTLambdaExpression lambda = acu.getFirstDescendantOfType(ASTLambdaExpression.class);
|
||||
ASTVariableDeclaratorId f = lambda.getFirstDescendantOfType(ASTVariableDeclaratorId.class);
|
||||
assertNull(f.getTypeNode());
|
||||
assertNull(f.getTypeNameNode());
|
||||
}
|
||||
|
@ -276,7 +276,7 @@ public class JDKVersionTest {
|
||||
@Test
|
||||
public final void jdk7PrivateMethodInnerClassInterface1() {
|
||||
ASTCompilationUnit acu = parseJava17(loadSource("private_method_in_inner_class_interface1.java"));
|
||||
List<ASTMethodDeclaration> methods = acu.findDescendantsOfType(ASTMethodDeclaration.class);
|
||||
List<ASTMethodDeclaration> methods = acu.findDescendantsOfType(ASTMethodDeclaration.class, true);
|
||||
assertEquals(3, methods.size());
|
||||
for (ASTMethodDeclaration method : methods) {
|
||||
assertFalse(method.isInterfaceMember());
|
||||
|
@ -13,7 +13,6 @@ import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
@ -209,8 +208,7 @@ public class SimpleNodeTest {
|
||||
@Test
|
||||
public void testContainsNoInner() {
|
||||
ASTCompilationUnit c = getNodes(ASTCompilationUnit.class, CONTAINS_NO_INNER).iterator().next();
|
||||
List<ASTFieldDeclaration> res = new ArrayList<>();
|
||||
c.findDescendantsOfType(ASTFieldDeclaration.class, res, false);
|
||||
List<ASTFieldDeclaration> res = c.findDescendantsOfType(ASTFieldDeclaration.class);
|
||||
assertTrue(res.isEmpty());
|
||||
/*
|
||||
* String expectedXml =
|
||||
@ -254,8 +252,7 @@ public class SimpleNodeTest {
|
||||
@Test
|
||||
public void testContainsNoInnerWithAnonInner() {
|
||||
ASTCompilationUnit c = getNodes(ASTCompilationUnit.class, CONTAINS_NO_INNER_WITH_ANON_INNER).iterator().next();
|
||||
List<ASTFieldDeclaration> res = new ArrayList<>();
|
||||
c.findDescendantsOfType(ASTFieldDeclaration.class, res, false);
|
||||
List<ASTFieldDeclaration> res = c.findDescendantsOfType(ASTFieldDeclaration.class);
|
||||
assertTrue(res.isEmpty());
|
||||
}
|
||||
|
||||
|
@ -18,6 +18,7 @@ import net.sourceforge.pmd.PMD;
|
||||
import net.sourceforge.pmd.lang.ast.Node;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTBlock;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTCatchStatement;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTEqualityExpression;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTInitializer;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration;
|
||||
@ -129,7 +130,8 @@ public class AcceptanceTest extends STBBaseTst {
|
||||
@Test
|
||||
public void testInnerOuterClass() {
|
||||
parseCode(TEST_INNER_CLASS);
|
||||
ASTVariableDeclaratorId vdi = acu.findDescendantsOfType(ASTVariableDeclaratorId.class).get(0);
|
||||
ASTVariableDeclaratorId vdi = acu.findDescendantsOfType(ASTClassOrInterfaceDeclaration.class).get(1) // get inner class
|
||||
.getFirstDescendantOfType(ASTVariableDeclaratorId.class); // get first declaration
|
||||
List<NameOccurrence> usages = vdi.getUsages();
|
||||
assertEquals(2, usages.size());
|
||||
assertEquals(5, usages.get(0).getLocation().getBeginLine());
|
||||
|
@ -13,6 +13,7 @@ import org.junit.Test;
|
||||
import net.sourceforge.pmd.PMD;
|
||||
import net.sourceforge.pmd.lang.LanguageRegistry;
|
||||
import net.sourceforge.pmd.lang.java.JavaLanguageModule;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceBody;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTLambdaExpression;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclarator;
|
||||
@ -65,7 +66,9 @@ public class ScopeAndDeclarationFinderTest extends STBBaseTst {
|
||||
ClassScope cs = (ClassScope) acu.getFirstDescendantOfType(ASTClassOrInterfaceDeclaration.class).getScope();
|
||||
Assert.assertEquals(1, cs.getClassDeclarations().size()); // There should be 1 anonymous class
|
||||
|
||||
List<ASTMethodDeclarator> methods = acu.findDescendantsOfType(ASTMethodDeclarator.class);
|
||||
List<ASTMethodDeclarator> methods = acu.getFirstDescendantOfType(ASTClassOrInterfaceBody.class) // outer class
|
||||
.getFirstDescendantOfType(ASTClassOrInterfaceBody.class) // inner class
|
||||
.findDescendantsOfType(ASTMethodDeclarator.class, true); // inner class methods
|
||||
Assert.assertEquals(2, methods.size());
|
||||
ClassScope scope1 = methods.get(0).getScope().getEnclosingScope(ClassScope.class);
|
||||
ClassScope scope2 = methods.get(1).getScope().getEnclosingScope(ClassScope.class);
|
||||
|
@ -38,6 +38,7 @@ import net.sourceforge.pmd.lang.java.ParserTstUtil;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTAllocationExpression;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTArgumentList;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTBooleanLiteral;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceBodyDeclaration;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceType;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit;
|
||||
@ -244,7 +245,8 @@ public class ClassTypeResolverTest {
|
||||
Node acu = parseAndTypeResolveForClass(NestedAnonymousClass.class, "1.8");
|
||||
ASTAllocationExpression allocationExpression = acu.getFirstDescendantOfType(ASTAllocationExpression.class);
|
||||
ASTAllocationExpression nestedAllocation
|
||||
= allocationExpression.getFirstDescendantOfType(ASTAllocationExpression.class);
|
||||
= allocationExpression.getFirstDescendantOfType(ASTClassOrInterfaceBodyDeclaration.class) // get the declaration (boundary)
|
||||
.getFirstDescendantOfType(ASTAllocationExpression.class); // and dive for the nested allocation
|
||||
TypeNode child = (TypeNode) nestedAllocation.jjtGetChild(0);
|
||||
Assert.assertTrue(Converter.class.isAssignableFrom(child.getType()));
|
||||
Assert.assertSame(String.class, child.getTypeDefinition().getGenericType(0).getType());
|
||||
|
@ -311,6 +311,26 @@ public class Foo {
|
||||
LOGGER.log(Level.FINE, "This is a severe message" + " and concat");
|
||||
}
|
||||
}
|
||||
}
|
||||
]]></code>
|
||||
</test-code>
|
||||
<test-code>
|
||||
<description>#370 rule not considering lambdas</description>
|
||||
<expected-problems>0</expected-problems>
|
||||
<code><![CDATA[
|
||||
public class Logger {
|
||||
private static final Logger LOGGER = new Logger();
|
||||
|
||||
public void bar() {
|
||||
LOGGER.info(() -> "Bla " + " bla"); // The lambda is free to do whatever it likes
|
||||
}
|
||||
|
||||
@Override
|
||||
public void info(Supplier<String> message) {
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info(message.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
]]></code>
|
||||
</test-code>
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
package net.sourceforge.pmd.util.fxdesigner.util.codearea;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
@ -82,6 +83,30 @@ public abstract class SimpleRegexSyntaxHighlighter implements SyntaxHighlighter
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns a regex alternation for the given words.
|
||||
* The words must not begin with an escaped character.
|
||||
*
|
||||
* @param alternatives Words to join
|
||||
*/
|
||||
protected static String alternation(String[] alternatives) {
|
||||
// first characters of each alternative, to optimise the regex
|
||||
String firstChars = Arrays.stream(alternatives)
|
||||
.map(s -> s.substring(0, 1))
|
||||
.distinct()
|
||||
.reduce((s1, s2) -> s1 + s2)
|
||||
.get();
|
||||
|
||||
String alt = "(?=[" + firstChars + "])(?:" + String.join("|", alternatives) + ")";
|
||||
|
||||
return asWord(alt);
|
||||
}
|
||||
|
||||
|
||||
protected static String asWord(String regex) {
|
||||
return "(?:\\b" + regex + "\\b)";
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a builder to make a grammar to build a highlighter.
|
||||
*
|
||||
|
@ -15,7 +15,6 @@ import static net.sourceforge.pmd.util.fxdesigner.util.codearea.syntaxhighlighti
|
||||
import static net.sourceforge.pmd.util.fxdesigner.util.codearea.syntaxhighlighting.HighlightClasses.SINGLEL_COMMENT;
|
||||
import static net.sourceforge.pmd.util.fxdesigner.util.codearea.syntaxhighlighting.HighlightClasses.STRING;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import net.sourceforge.pmd.util.fxdesigner.util.codearea.SimpleRegexSyntaxHighlighter;
|
||||
@ -29,7 +28,7 @@ import net.sourceforge.pmd.util.fxdesigner.util.codearea.SimpleRegexSyntaxHighli
|
||||
public class ApexSyntaxHighlighter extends SimpleRegexSyntaxHighlighter {
|
||||
|
||||
|
||||
private static final String[] KEYWORDS = new String[] {
|
||||
private static final String[] KEYWORDS = {
|
||||
"abstract", "activate", "and", "any", "array", "as",
|
||||
"asc", "autonomous", "begin", "bigdecimal", "blob",
|
||||
"break", "bulk", "by", "byte", "case", "cast", "catch",
|
||||
@ -55,24 +54,16 @@ public class ApexSyntaxHighlighter extends SimpleRegexSyntaxHighlighter {
|
||||
"last", "order", "sharing", "with",
|
||||
};
|
||||
|
||||
|
||||
/** First characters of the keywords, used to optimise the regex. */
|
||||
private static final String KEYWORDS_START_CHARS = Arrays.stream(KEYWORDS)
|
||||
.map(s -> s.substring(0, 1))
|
||||
.distinct()
|
||||
.reduce((s1, s2) -> s1 + s2)
|
||||
.get();
|
||||
|
||||
private static final RegexHighlightGrammar GRAMMAR
|
||||
= grammarBuilder(SINGLEL_COMMENT.css, "//[^\r\n]*")
|
||||
.or(MULTIL_COMMENT.css, "/\\*.*?\\*/")
|
||||
.or(KEYWORD.css, "\\b(?i)(?=[" + KEYWORDS_START_CHARS + "])(" + String.join("|", KEYWORDS) + ")\\b")
|
||||
.or(KEYWORD.css, "(?i)" + alternation(KEYWORDS))
|
||||
.or(PAREN.css, "[()]")
|
||||
.or(BRACE.css, "[{}]")
|
||||
.or(BRACKET.css, "[\\[]]")
|
||||
.or(SEMICOLON.css, ";")
|
||||
.or(STRING.css, "'[^'\\\\]*(\\\\.[^'\\\\]*)*'")
|
||||
.or(BOOLEAN.css, "\\b(?i)true|false\\b")
|
||||
.or(BOOLEAN.css, asWord("(?i)true|false"))
|
||||
.or(ANNOTATION.css, "@[\\w]+")
|
||||
.create(Pattern.DOTALL | Pattern.CASE_INSENSITIVE);
|
||||
|
||||
|
@ -28,6 +28,7 @@ public enum HighlightClasses {
|
||||
LITERAL(Constants.LITERAL),
|
||||
BOOLEAN("boolean", Constants.LITERAL),
|
||||
STRING("string", Constants.LITERAL),
|
||||
URI("uri", "string", Constants.LITERAL),
|
||||
CHAR("char", Constants.LITERAL),
|
||||
NULL("null", Constants.LITERAL),
|
||||
NUMBER("number", Constants.LITERAL),
|
||||
@ -41,7 +42,8 @@ public enum HighlightClasses {
|
||||
XPATH_AXIS("axis", Constants.XPATH),
|
||||
XPATH_FUNCTION("function", Constants.XPATH),
|
||||
XPATH_PATH("path", Constants.XPATH, Constants.PUNCTUATION),
|
||||
|
||||
XPATH_KIND_TEST("kind-test", "function", Constants.XPATH),
|
||||
|
||||
XML_CDATA_TAG("cdata-tag", Constants.XML),
|
||||
XML_CDATA_CONTENT("cdata-content", Constants.XML),
|
||||
XML_PROLOG("xml-prolog", Constants.XML),
|
||||
@ -59,7 +61,7 @@ public enum HighlightClasses {
|
||||
}
|
||||
|
||||
|
||||
private static class Constants {
|
||||
private static final class Constants {
|
||||
static final String LITERAL = "literal";
|
||||
static final String COMMENT = "comment";
|
||||
static final String PUNCTUATION = "punctuation";
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user