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