Merge branch 'master' into update-classnamingconventions

This commit is contained in:
Clément Fournier
2018-03-29 18:48:31 +02:00
33 changed files with 282 additions and 208 deletions

View File

@ -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

View File

@ -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

View File

@ -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>

View File

@ -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 &lt;pmdVersion> - [stonio](https://github.com/stonio)

View File

@ -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;

View File

@ -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

View File

@ -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());
}
}

View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -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);
}
}

View File

@ -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);

View File

@ -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) {

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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;

View File

@ -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);
}
}

View File

@ -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;
}

View File

@ -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) {

View File

@ -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();
}
}

View File

@ -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());
}

View File

@ -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());

View File

@ -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());
}

View File

@ -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());

View File

@ -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);

View File

@ -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());

View File

@ -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>

View File

@ -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.
*

View File

@ -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);

View File

@ -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