diff --git a/.travis.yml b/.travis.yml
index f814e1b5c7..8a289d5c6b 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -46,7 +46,7 @@ before_install:
- rvm use 2.4.1
# Install OpenJDK 11 - see https://sormuras.github.io/blog/2018-03-20-jdk-matrix.html
install:
- - . ./install-jdk.sh -F 11 -L GPL -W $HOME/jdk
+ - . ./install-jdk.sh -F 12 -L GPL -W $HOME/jdk
- gem install bundler
- bundle install --with=release_notes_preprocessing --path=vendor/bundle
before_script: true
diff --git a/docs/pages/pmd/rules/java.md b/docs/pages/pmd/rules/java.md
index 29d0669364..80ca10856f 100644
--- a/docs/pages/pmd/rules/java.md
+++ b/docs/pages/pmd/rules/java.md
@@ -102,7 +102,7 @@ folder: pmd/rules
* [MethodArgumentCouldBeFinal](pmd_rules_java_codestyle.html#methodargumentcouldbefinal): A method argument that is never re-assigned within the method can be declared final.
* [MethodNamingConventions](pmd_rules_java_codestyle.html#methodnamingconventions): Configurable naming conventions for method declarations. This rule reports method decl...
* [MIsLeadingVariableName](pmd_rules_java_codestyle.html#misleadingvariablename): Deprecated Detects when a non-field has a name starting with 'm_'. This usually denotes a field and could b...
-* [NoPackage](pmd_rules_java_codestyle.html#nopackage): Detects when a class or interface does not have a package definition.
+* [NoPackage](pmd_rules_java_codestyle.html#nopackage): Detects when a class, interface, enum or annotation does not have a package definition.
* [OnlyOneReturn](pmd_rules_java_codestyle.html#onlyonereturn): A method should have only one exit point, and that should be the last statement in the method.
* [PackageCase](pmd_rules_java_codestyle.html#packagecase): Detects when a package definition contains uppercase characters.
* [PrematureDeclaration](pmd_rules_java_codestyle.html#prematuredeclaration): Checks for variables that are defined before they might be used. A reference is deemed to be prem...
diff --git a/docs/pages/pmd/rules/java/bestpractices.md b/docs/pages/pmd/rules/java/bestpractices.md
index 68feb9d383..4023ea302e 100644
--- a/docs/pages/pmd/rules/java/bestpractices.md
+++ b/docs/pages/pmd/rules/java/bestpractices.md
@@ -806,7 +806,7 @@ This rule checks for JUnit4, JUnit5 and TestNG Tests, as well as methods startin
**This rule is defined by the following XPath expression:**
``` xpath
-//MethodDeclarator[@Image[fn:matches(.,'^test')] or ../../Annotation/MarkerAnnotation/Name[
+//MethodDeclarator[@Image[matches(.,'^test')] or ../../Annotation/MarkerAnnotation/Name[
pmd-java:typeIs('org.junit.Test')
or pmd-java:typeIs('org.junit.jupiter.api.Test')
or pmd-java:typeIs('org.junit.jupiter.api.RepeatedTest')
@@ -815,7 +815,7 @@ This rule checks for JUnit4, JUnit5 and TestNG Tests, as well as methods startin
or pmd-java:typeIs('org.junit.jupiter.params.ParameterizedTest')
or pmd-java:typeIs('org.testng.annotations.Test')
]]
- [count(..//PrimaryPrefix/Name[@Image[fn:matches(.,'^assert')]]) > $maximumAsserts]
+ [count(..//PrimaryPrefix/Name[@Image[matches(.,'^assert')]]) > $maximumAsserts]
```
**Example(s):**
diff --git a/docs/pages/pmd/rules/java/codestyle.md b/docs/pages/pmd/rules/java/codestyle.md
index 1e267004bc..d2ff5b66c1 100644
--- a/docs/pages/pmd/rules/java/codestyle.md
+++ b/docs/pages/pmd/rules/java/codestyle.md
@@ -1689,11 +1689,11 @@ public class Foo {
**Priority:** Medium (3)
-Detects when a class or interface does not have a package definition.
+Detects when a class, interface, enum or annotation does not have a package definition.
**This rule is defined by the following XPath expression:**
``` xpath
-//ClassOrInterfaceDeclaration[count(preceding::PackageDeclaration) = 0]
+/CompilationUnit[not(./PackageDeclaration)]/TypeDeclaration[1]
```
**Example(s):**
diff --git a/docs/pages/release_notes.md b/docs/pages/release_notes.md
index 63e592ff36..e35c246a53 100644
--- a/docs/pages/release_notes.md
+++ b/docs/pages/release_notes.md
@@ -19,7 +19,7 @@ This is a {{ site.pmd.release_type }} release.
### New and noteworthy
-#### Dart support
+#### Dart support
Thanks to the contribution from [Maikel Steneker](https://github.com/maikelsteneker), and built on top of the ongoing efforts to fully support Antlr-based languages,
PMD now has CPD support for [Dart](https://www.dartlang.org/).
@@ -29,14 +29,31 @@ Being based on a proper Antlr grammar, CPD can:
* ignore imports / libraries
* honor [comment-based suppressions](pmd_userdocs_cpd.html#suppression)
+### Modified Rules
+
+* The Java rule {% rule "java/errorprone/AssignmentToNonFinalStatic" %} (`java-errorprone`) will now report on each
+ assignment made within a constructor rather than on the field declaration. This makes it easier for developers to
+ find the offending statements.
+
+* The Java rule {% rule "java/codestyle/NoPackage" %} (`java-codestyle`) will now report additionally enums
+ and annotations that do not have a package declaration.
+
### Fixed Issues
+* all
+ * [#1788](https://github.com/pmd/pmd/issues/1788): \[cpd] \[core] Use better `ClassLoader` for `ServiceLoader` in `LanguageFactory`
* go
* [#1751](https://github.com/pmd/pmd/issues/1751): \[go] Parsing errors encountered with escaped backslash
* java
+ * [#1532](https://github.com/pmd/pmd/issues/1532): \[java] NPE with incomplete auxclasspath
+ * [#1691](https://github.com/pmd/pmd/issues/1691): \[java] Possible Data Race in JavaTypeDefinitionSimple.getGenericType
* [#1729](https://github.com/pmd/pmd/issues/1729): \[java] JavaRuleViolation loses information in `className` field when class has package-private access level
* java-bestpractices
+ * [#1190](https://github.com/pmd/pmd/issues/1190): \[java] UnusedLocalVariable/UnusedPrivateField false-positive
* [#1720](https://github.com/pmd/pmd/issues/1720): \[java] UnusedImports false positive for Javadoc link with array type
+* java-codestyle
+ * [#1755](https://github.com/pmd/pmd/issues/1775): \[java] False negative in UnnecessaryLocalBeforeReturn when splitting statements across multiple lines
+ * [#1782](https://github.com/pmd/pmd/issues/1782): \[java] NoPackage: False Negative for enums
* java-design
* [#1760](https://github.com/pmd/pmd/issues/1760): \[java] UseObjectForClearerAPI flags private methods
@@ -48,6 +65,9 @@ Being based on a proper Antlr grammar, CPD can:
* [#1746](https://github.com/pmd/pmd/pull/1746): \[java] Update rule to prevent UnusedImport when using JavaDoc with array type - [itaigilo](https://github.com/itaigilo)
* [#1752](https://github.com/pmd/pmd/pull/1752): \[java] UseObjectForClearerAPI Only For Public - [Björn Kautler](https://github.com/Vampire)
* [#1761](https://github.com/pmd/pmd/pull/1761): \[dart] \[cpd] Added CPD support for Dart - [Maikel Steneker](https://github.com/maikelsteneker)
+* [#1776](https://github.com/pmd/pmd/pull/1776): \[java] Show more detailed message when can't resolve field type - [Andrey Fomin](https://github.com/andrey-fomin)
+* [#1781](https://github.com/pmd/pmd/pull/1781): \[java] Location change in AssignmentToNonFinalStatic - [Maikel Steneker](https://github.com/maikelsteneker)
+* [#1789](https://github.com/pmd/pmd/pull/1789): \[cpd] \[core] Use current classloader instead of Thread's classloader - [Andreas Schmid](https://github.com/aaschmid)
{% endtocmaker %}
diff --git a/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/codestyle/xml/VariableNamingConventions.xml b/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/codestyle/xml/VariableNamingConventions.xml
index a337efeaad..69441b8b3b 100644
--- a/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/codestyle/xml/VariableNamingConventions.xml
+++ b/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/codestyle/xml/VariableNamingConventions.xml
@@ -303,4 +303,21 @@ public class SerializerException extends Exception {
]]>
+
+ Enum members
+ 0
+
+
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/LanguageFactory.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/LanguageFactory.java
index 336a49a00c..a032ae7663 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/LanguageFactory.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/LanguageFactory.java
@@ -33,7 +33,8 @@ public final class LanguageFactory {
private LanguageFactory() {
List languagesList = new ArrayList<>();
- ServiceLoader languageLoader = ServiceLoader.load(Language.class);
+ // Use current class' classloader instead of the threads context classloader, see https://github.com/pmd/pmd/issues/1788
+ ServiceLoader languageLoader = ServiceLoader.load(Language.class, getClass().getClassLoader());
Iterator iterator = languageLoader.iterator();
while (iterator.hasNext()) {
try {
diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/xpath/SaxonXPathRuleQuery.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/xpath/SaxonXPathRuleQuery.java
index b369007c68..5dcc9e4364 100644
--- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/xpath/SaxonXPathRuleQuery.java
+++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/xpath/SaxonXPathRuleQuery.java
@@ -18,6 +18,7 @@ import net.sourceforge.pmd.lang.xpath.Initializer;
import net.sourceforge.pmd.properties.PropertyDescriptor;
import net.sf.saxon.om.Item;
+import net.sf.saxon.om.NamespaceConstant;
import net.sf.saxon.om.ValueRepresentation;
import net.sf.saxon.sxpath.AbstractStaticContext;
import net.sf.saxon.sxpath.IndependentContext;
@@ -193,6 +194,8 @@ public class SaxonXPathRuleQuery extends AbstractXPathRuleQuery {
((AbstractStaticContext) xpathStaticContext).setBackwardsCompatibilityMode(true);
}
+ ((IndependentContext) xpathEvaluator.getStaticContext()).declareNamespace("fn", NamespaceConstant.FN);
+
// Register PMD functions
Initializer.initialize((IndependentContext) xpathStaticContext);
diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/properties/MethodPropertyTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/properties/MethodPropertyTest.java
index 3ed12aa0fa..8e39e7da90 100644
--- a/pmd-core/src/test/java/net/sourceforge/pmd/properties/MethodPropertyTest.java
+++ b/pmd-core/src/test/java/net/sourceforge/pmd/properties/MethodPropertyTest.java
@@ -8,6 +8,7 @@ import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import java.lang.reflect.Method;
+import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -32,12 +33,23 @@ import net.sourceforge.pmd.util.ClassUtil;
*/
public class MethodPropertyTest extends AbstractPackagedPropertyDescriptorTester {
- private static final Method[] ALL_METHODS = String.class.getDeclaredMethods();
+ private static final Method[] ALL_METHODS;
private static final String[] METHOD_SIGNATURES = {"String#indexOf(int)", "String#substring(int,int)",
"java.lang.String#substring(int,int)", "Integer#parseInt(String)", "java.util.HashMap#put(Object,Object)",
"HashMap#containsKey(Object)", };
+ static {
+ List allMethods = new ArrayList<>();
+ for (Method m : String.class.getDeclaredMethods()) {
+ // exclude String.resolveConstantDesc to avoid random test failure with java12
+ // there are two methods with the same signature available, but different return types...
+ if (!m.getName().equals("resolveConstantDesc")) {
+ allMethods.add(m);
+ }
+ }
+ ALL_METHODS = allMethods.toArray(new Method[0]);
+ }
public MethodPropertyTest() {
super("Method");
diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/UnnecessaryLocalBeforeReturnRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/UnnecessaryLocalBeforeReturnRule.java
index 2890b968ca..056878a58f 100644
--- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/UnnecessaryLocalBeforeReturnRule.java
+++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/UnnecessaryLocalBeforeReturnRule.java
@@ -24,7 +24,6 @@ import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule;
import net.sourceforge.pmd.lang.java.symboltable.VariableNameDeclaration;
import net.sourceforge.pmd.lang.symboltable.NameOccurrence;
import net.sourceforge.pmd.lang.symboltable.Scope;
-import net.sourceforge.pmd.lang.symboltable.ScopedNode;
import net.sourceforge.pmd.properties.PropertyDescriptor;
@@ -115,7 +114,13 @@ public class UnnecessaryLocalBeforeReturnRule extends AbstractJavaRule {
final ASTReturnStatement rtn) {
final ASTVariableInitializer initializer = variableDeclaration.getAccessNodeParent()
.getFirstDescendantOfType(ASTVariableInitializer.class);
+
if (initializer != null) {
+ // Get the block statements for each, so we can compare apples to apples
+ final ASTBlockStatement initializerStmt = variableDeclaration.getAccessNodeParent()
+ .getFirstParentOfType(ASTBlockStatement.class);
+ final ASTBlockStatement rtnStmt = rtn.getFirstParentOfType(ASTBlockStatement.class);
+
final List referencedNames = initializer.findDescendantsOfType(ASTName.class);
for (final ASTName refName : referencedNames) {
// TODO : Shouldn't the scope allow us to search for a var name occurrences directly, moving up through parent scopes?
@@ -128,9 +133,10 @@ public class UnnecessaryLocalBeforeReturnRule extends AbstractJavaRule {
if (entry.getKey().getName().equals(refName.getImage())) {
// Variable found! Check usage locations
for (final NameOccurrence occ : entry.getValue()) {
- final ScopedNode location = occ.getLocation();
+ final ASTBlockStatement location = occ.getLocation().getFirstParentOfType(ASTBlockStatement.class);
+
// Is it used after initializing our "unnecessary" local but before the return statement?
- if (isAfter(location, initializer) && isAfter(rtn, location)) {
+ if (isAfter(location, initializerStmt) && isAfter(rtnStmt, location)) {
return true;
}
}
diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/AssignmentToNonFinalStaticRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/AssignmentToNonFinalStaticRule.java
index 4870e31778..9c3f01631e 100644
--- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/AssignmentToNonFinalStaticRule.java
+++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/AssignmentToNonFinalStaticRule.java
@@ -4,6 +4,7 @@
package net.sourceforge.pmd.lang.java.rule.errorprone;
+import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@@ -33,16 +34,16 @@ public class AssignmentToNonFinalStaticRule extends AbstractJavaRule {
continue;
}
- if (initializedInConstructor(entry.getValue())) {
- addViolation(data, decl.getNode(), decl.getImage());
+ final List locations = initializedInConstructor(entry.getValue());
+ for (final Node location : locations) {
+ addViolation(data, location, decl.getImage());
}
}
return super.visit(node, data);
}
- private boolean initializedInConstructor(List usages) {
- boolean initInConstructor = false;
-
+ private List initializedInConstructor(List usages) {
+ final List unsafeAssignments = new ArrayList<>();
for (NameOccurrence occ : usages) {
// specifically omitting prefix and postfix operators as there are
// legitimate usages of these with static fields, e.g. typesafe enum pattern.
@@ -50,12 +51,12 @@ public class AssignmentToNonFinalStaticRule extends AbstractJavaRule {
Node node = occ.getLocation();
Node constructor = node.getFirstParentOfType(ASTConstructorDeclaration.class);
if (constructor != null) {
- initInConstructor = true;
+ unsafeAssignments.add(node);
}
}
}
- return initInConstructor;
+ return unsafeAssignments;
}
}
diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symboltable/JavaNameOccurrence.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symboltable/JavaNameOccurrence.java
index 7dcc57fe91..4e7033de52 100644
--- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symboltable/JavaNameOccurrence.java
+++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symboltable/JavaNameOccurrence.java
@@ -14,6 +14,7 @@ import net.sourceforge.pmd.lang.java.ast.ASTPreDecrementExpression;
import net.sourceforge.pmd.lang.java.ast.ASTPreIncrementExpression;
import net.sourceforge.pmd.lang.java.ast.ASTPrimaryExpression;
import net.sourceforge.pmd.lang.java.ast.ASTPrimaryPrefix;
+import net.sourceforge.pmd.lang.java.ast.ASTResource;
import net.sourceforge.pmd.lang.java.ast.ASTStatementExpression;
import net.sourceforge.pmd.lang.java.ast.JavaNode;
import net.sourceforge.pmd.lang.symboltable.NameOccurrence;
@@ -87,9 +88,12 @@ public class JavaNameOccurrence implements NameOccurrence {
primaryExpression = location.jjtGetParent().jjtGetParent();
} else if (location.jjtGetParent().jjtGetParent() instanceof ASTPrimaryExpression) {
primaryExpression = location.jjtGetParent().jjtGetParent().jjtGetParent();
+ } else if (location.jjtGetParent() instanceof ASTResource) {
+ return false;
} else {
throw new RuntimeException(
- "Found a NameOccurrence (" + location + ") that didn't have an ASTPrimary Expression as parent or grandparent. Parent = "
+ "Found a NameOccurrence (" + location + ") that didn't have an ASTPrimary Expression"
+ + " as parent or grandparent nor is a concise resource. Parent = "
+ location.jjtGetParent() + " and grandparent = " + location.jjtGetParent().jjtGetParent()
+ " (location line " + location.getBeginLine() + " col " + location.getBeginColumn() + ")");
}
diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symboltable/OccurrenceFinder.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symboltable/OccurrenceFinder.java
index 37b6b819a5..13c770ab7e 100644
--- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symboltable/OccurrenceFinder.java
+++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symboltable/OccurrenceFinder.java
@@ -7,8 +7,11 @@ package net.sourceforge.pmd.lang.java.symboltable;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
+import java.util.StringTokenizer;
+import net.sourceforge.pmd.lang.java.ast.ASTName;
import net.sourceforge.pmd.lang.java.ast.ASTPrimaryExpression;
+import net.sourceforge.pmd.lang.java.ast.ASTResource;
import net.sourceforge.pmd.lang.java.ast.JavaParserVisitorAdapter;
import net.sourceforge.pmd.lang.symboltable.NameDeclaration;
import net.sourceforge.pmd.lang.symboltable.Scope;
@@ -21,6 +24,20 @@ public class OccurrenceFinder extends JavaParserVisitorAdapter {
private final Set additionalDeclarations = new HashSet<>();
+ @Override
+ public Object visit(ASTResource node, Object data) {
+ // is this a concise resource reference?
+ if (node.jjtGetNumChildren() == 1) {
+ ASTName nameNode = (ASTName) node.jjtGetChild(0);
+ for (StringTokenizer st = new StringTokenizer(nameNode.getImage(), "."); st.hasMoreTokens();) {
+ JavaNameOccurrence occ = new JavaNameOccurrence(nameNode, st.nextToken());
+ new Search(occ).execute();
+ }
+ }
+
+ return super.visit(node, data);
+ }
+
@Override
public Object visit(ASTPrimaryExpression node, Object data) {
NameFinder nameFinder = new NameFinder(node);
diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/ClassTypeResolver.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/ClassTypeResolver.java
index 4883668d0a..e3744835ff 100644
--- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/ClassTypeResolver.java
+++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/ClassTypeResolver.java
@@ -504,7 +504,9 @@ public class ClassTypeResolver extends JavaParserVisitorAdapter {
// swallow
} catch (final LinkageError e) {
if (LOG.isLoggable(Level.WARNING)) {
- LOG.log(Level.WARNING, "Error during type resolution due to: " + e);
+ String message = "Error during type resolution of field '" + fieldImage + "' in "
+ + typeToSearch.getType() + " due to: " + e;
+ LOG.log(Level.WARNING, message);
}
// TODO : report a missing class once we start doing that...
return null;
@@ -1407,7 +1409,9 @@ public class ClassTypeResolver extends JavaParserVisitorAdapter {
}
} else {
JavaTypeDefinition def = JavaTypeDefinition.forClass(myType);
- node.setTypeDefinition(def.withDimensions(arrayDimens));
+ if (def != null) {
+ node.setTypeDefinition(def.withDimensions(arrayDimens));
+ }
}
}
diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/typedefinition/JavaTypeDefinitionSimple.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/typedefinition/JavaTypeDefinitionSimple.java
index 1bed9a9f64..b1eca9da7f 100644
--- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/typedefinition/JavaTypeDefinitionSimple.java
+++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/typedefinition/JavaTypeDefinitionSimple.java
@@ -16,8 +16,7 @@ import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.lang.reflect.WildcardType;
-import java.util.ArrayList;
-import java.util.Collections;
+import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
@@ -28,7 +27,7 @@ import java.util.logging.Logger;
/* default */ class JavaTypeDefinitionSimple extends JavaTypeDefinition {
private final Class> clazz;
- private final List genericArgs;
+ private final JavaTypeDefinition[] genericArgs;
// cached because calling clazz.getTypeParameters().length create a new array every time
private final int typeParameterCount;
private final boolean isGeneric;
@@ -36,6 +35,7 @@ import java.util.logging.Logger;
private final JavaTypeDefinition enclosingClass;
private static final Logger LOG = Logger.getLogger(JavaTypeDefinitionSimple.class.getName());
+ private static final JavaTypeDefinition[] NO_GENERICS = {};
protected JavaTypeDefinitionSimple(Class> clazz, JavaTypeDefinition... boundGenerics) {
super(EXACT);
@@ -59,12 +59,10 @@ import java.util.logging.Logger;
isRawType = isGeneric && boundGenerics.length == 0;
if (isGeneric) {
- // Generics will be lazily loaded
- this.genericArgs = new ArrayList<>(typeParameters.length);
- // boundGenerics would be empty if this is a raw type, hence the lazy loading
- Collections.addAll(this.genericArgs, boundGenerics);
+ // Generics will be lazily loaded if not already known
+ this.genericArgs = Arrays.copyOf(boundGenerics, typeParameterCount);
} else {
- this.genericArgs = Collections.emptyList();
+ this.genericArgs = NO_GENERICS;
}
enclosingClass = forClass(clazz.getEnclosingClass());
@@ -82,7 +80,7 @@ import java.util.logging.Logger;
@Override
public boolean isGeneric() {
- return !genericArgs.isEmpty();
+ return isGeneric;
}
private JavaTypeDefinition getGenericType(final String parameterName, Method method,
@@ -126,29 +124,22 @@ import java.util.logging.Logger;
@Override
public JavaTypeDefinition getGenericType(final int index) {
// Check if it has been lazily initialized first
- if (genericArgs.size() > index) {
- final JavaTypeDefinition cachedDefinition = genericArgs.get(index);
- if (cachedDefinition != null) {
- return cachedDefinition;
- }
- }
-
- // Force the list to have enough elements
- for (int i = genericArgs.size(); i <= index; i++) {
- genericArgs.add(null);
+ final JavaTypeDefinition cachedDefinition = genericArgs[index];
+ if (cachedDefinition != null) {
+ return cachedDefinition;
}
/*
- * Set a default to circuit-brake any recursions (ie: raw types with no generic info)
+ * Set a default to circuit-break any recursions (ie: raw types with no generic info)
* Object.class is a right answer in those scenarios
*/
- genericArgs.set(index, forClass(Object.class));
+ genericArgs[index] = forClass(Object.class);
final TypeVariable> typeVariable = clazz.getTypeParameters()[index];
final JavaTypeDefinition typeDefinition = resolveTypeDefinition(typeVariable.getBounds()[0]);
// cache result
- genericArgs.set(index, typeDefinition);
+ genericArgs[index] = typeDefinition;
return typeDefinition;
}
@@ -279,7 +270,7 @@ import java.util.logging.Logger;
.append(", genericArgs=[");
// Forcefully resolve all generic types
- for (int i = 0; i < genericArgs.size(); i++) {
+ for (int i = 0; i < genericArgs.length; i++) {
getGenericType(i);
}
@@ -287,7 +278,7 @@ import java.util.logging.Logger;
sb.append(jtd.shallowString()).append(", ");
}
- if (!genericArgs.isEmpty()) {
+ if (genericArgs.length != 0) {
sb.replace(sb.length() - 3, sb.length() - 1, ""); // remove last comma
}
diff --git a/pmd-java/src/main/resources/category/java/bestpractices.xml b/pmd-java/src/main/resources/category/java/bestpractices.xml
index 92e683fd9a..1a8dfe3948 100644
--- a/pmd-java/src/main/resources/category/java/bestpractices.xml
+++ b/pmd-java/src/main/resources/category/java/bestpractices.xml
@@ -699,7 +699,7 @@ This rule checks for JUnit4, JUnit5 and TestNG Tests, as well as methods startin
$maximumAsserts]
+ [count(..//PrimaryPrefix/Name[@Image[matches(.,'^assert')]]) > $maximumAsserts]
]]>
+
-Detects when a class or interface does not have a package definition.
+Detects when a class, interface, enum or annotation does not have a package definition.
3
- //ClassOrInterfaceDeclaration[count(preceding::PackageDeclaration) = 0]
+ /CompilationUnit[not(./PackageDeclaration)]/TypeDeclaration[1]
+
}.
+ */
+ @Test
+ public void arrayListOfString() {
+ JavaTypeDefinition typeDef = JavaTypeDefinition.forClass(ArrayList.class, JavaTypeDefinition.forClass(String.class));
+ Assert.assertTrue(typeDef.isGeneric());
+ Assert.assertEquals(1, typeDef.getTypeParameterCount());
+ Assert.assertTrue(typeDef.isClassOrInterface());
+ Assert.assertFalse(typeDef.isArrayType());
+
+ JavaTypeDefinition genericType = typeDef.getGenericType(0);
+ Assert.assertFalse(genericType.isGeneric());
+ Assert.assertEquals(String.class, genericType.getType());
+
+ JavaTypeDefinition genericTypeByName = typeDef.getGenericType("E");
+ Assert.assertEquals(String.class, genericTypeByName.getType());
+ }
+
+ @Test
+ public void array() {
+ JavaTypeDefinition typeDef = JavaTypeDefinition.forClass(String[].class);
+ Assert.assertFalse(typeDef.isGeneric());
+ Assert.assertTrue(typeDef.isArrayType());
+ Assert.assertFalse(typeDef.isClassOrInterface());
+ Assert.assertEquals(String.class, typeDef.getElementType().getType());
+ Assert.assertFalse(typeDef.isPrimitive());
+ }
+
+ @Test
+ public void primitive() {
+ JavaTypeDefinition typeDef = JavaTypeDefinition.forClass(int.class);
+ Assert.assertTrue(typeDef.isPrimitive());
+ Assert.assertFalse(typeDef.isClassOrInterface());
+ }
+}
diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UnusedLocalVariable.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UnusedLocalVariable.xml
index 4883375d4e..32c62ecf2d 100644
--- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UnusedLocalVariable.xml
+++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UnusedLocalVariable.xml
@@ -386,6 +386,25 @@ public class Test {
double result = Math.sqrt((a) - b);
System.out.println(result);
}
+}
+ ]]>
+
+
+
+ #1190 [java] UnusedLocalVariable/UnusedPrivateField false-positive
+ 0
+
diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UnusedPrivateField.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UnusedPrivateField.xml
index 3124ab44b1..f5ff621713 100644
--- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UnusedPrivateField.xml
+++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UnusedPrivateField.xml
@@ -597,6 +597,29 @@ public class IssueUnusedPrivateField {
String helper = "some new string"; // hidden here
System.out.println("helper = " + helper);
}
+}
+ ]]>
+
+
+
+ #1190 [java] UnusedLocalVariable/UnusedPrivateField false-positive
+ 0
+
diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/NoPackage.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/NoPackage.xml
index 67c418e021..a851baee5f 100644
--- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/NoPackage.xml
+++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/NoPackage.xml
@@ -4,9 +4,7 @@
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://pmd.sourceforge.net/rule-tests http://pmd.sourceforge.net/rule-tests_1_0_0.xsd">
-
+ bad
1
-
+ good
0
-
+ nested package
0
+
+ #1782 no package in top-level enum
+ 1
+
+
+
+ #1782 no package with annotation
+ 1
+
+
diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/UnnecessaryLocalBeforeReturn.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/UnnecessaryLocalBeforeReturn.xml
index 34310160d2..a97b4efc4d 100644
--- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/UnnecessaryLocalBeforeReturn.xml
+++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/UnnecessaryLocalBeforeReturn.xml
@@ -229,6 +229,21 @@ public class UnnecessaryLocalBeforeReturnFP {
sideEffect(m);
return i;
}
+}
+ ]]>
+
+
+
+ #1775 [java] False negative in UnnecessaryLocalBeforeReturn when splitting statements across multiple lines
+ 1
+
diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/AssignmentToNonFinalStatic.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/AssignmentToNonFinalStatic.xml
index 7f4c7844d1..a0ccf0047b 100644
--- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/AssignmentToNonFinalStatic.xml
+++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/AssignmentToNonFinalStatic.xml
@@ -8,6 +8,7 @@
clear rule violation
]]>
1
+ 4
+
+
+
+ 2
+ 4,5
+
diff --git a/pom.xml b/pom.xml
index 172ecb0d37..be0d9a8804 100644
--- a/pom.xml
+++ b/pom.xml
@@ -271,7 +271,7 @@ Additionally it includes CPD, the copy-paste-detector. CPD finds duplicated code
5.0
2.22.1
3.0.0
- 3.11.0
+ 3.12.0
1.10.1
3.0.1
4.7