diff --git a/docs/pages/release_notes.md b/docs/pages/release_notes.md index 1344f1f6b5..195239bccd 100644 --- a/docs/pages/release_notes.md +++ b/docs/pages/release_notes.md @@ -41,16 +41,29 @@ the implementation based on your feedback. at runtime. So, if you try to do this, you'll get an error at runtime, hence this is error prone. See also the discussion on [#2064](https://github.com/pmd/pmd/issues/2064). +* The Java rule {% rule "java/documentation/CommentRequired" %} (`java-documentation`) has a new property + `classCommentRequirement`. This replaces the now deprecated property `headerCommentRequirement`, since + the name was misleading. (File) header comments are not checked, but class comments are. + ### Fixed Issues +* core + * [#2006](https://github.com/pmd/pmd/issues/2006): \[core] PMD should warn about multiple instances of the same rule in a ruleset + * [#2161](https://github.com/pmd/pmd/issues/2161): \[core] ResourceLoader is deprecated and marked as internal but is exposed + * [#2170](https://github.com/pmd/pmd/issues/2170): \[core] DocumentFile doesn't preserve newlines * java-bestpractices * [#2149](https://github.com/pmd/pmd/issues/2149): \[java] JUnitAssertionsShouldIncludeMessage - False positive with assertEquals and JUnit5 * java-codestyle * [#2167](https://github.com/pmd/pmd/issues/2167): \[java] UnnecessaryLocalBeforeReturn false positive with variable captured by method reference +* java-documentation + * [#1683](https://github.com/pmd/pmd/issues/1683): \[java] CommentRequired property names are inconsistent * java-errorprone * [#2140](https://github.com/pmd/pmd/issues/2140): \[java] AvoidLiteralsInIfCondition: false negative for expressions * java-performance * [#2141](https://github.com/pmd/pmd/issues/2141): \[java] StringInstatiation: False negative with String-array access +* plsql + * [#2008](https://github.com/pmd/pmd/issues/2008): \[plsql] In StringLiteral using alternative quoting mechanism single quotes cause parsing errors + * [#2009](https://github.com/pmd/pmd/issues/2009): \[plsql] Multiple DDL commands are skipped during parsing ### API Changes @@ -72,6 +85,11 @@ You can identify them with the `@InternalApi` annotation. You'll also get a depr eg {% jdoc java::lang.java.rule.JavaRuleViolation %}. See javadoc of {% jdoc core::RuleViolation %}. +* {% jdoc core::rules.RuleFactory %} +* {% jdoc core::rules.RuleBuilder %} +* Constructors of {% jdoc core::RuleSetFactory %}, use factory methods from {% jdoc core::RulesetsFactoryUtils %} instead +* {% jdoc core::RulesetsFactoryUtils#getRulesetFactory(core::PMDConfiguration, core::util.ResourceLoader) %} + ##### For removal * {% jdoc java::lang.java.AbstractJavaParser %} @@ -93,13 +111,21 @@ You can identify them with the `@InternalApi` annotation. You'll also get a depr instead. This affects {% jdoc !!java::lang.java.ast.ASTAnnotationTypeDeclaration#getImage() %}, {% jdoc !!java::lang.java.ast.ASTClassOrInterfaceDeclaration#getImage() %}, and {% jdoc !!java::lang.java.ast.ASTEnumDeclaration#getImage() %}. - +* Several methods of {% jdoc java::lang.java.ast.ASTTryStatement %}, replacements with other names + have been added. This includes the XPath attribute `@Finally`, replace it with a test for `child::FinallyStatement`. +* Several methods named `getGuardExpressionNode` are replaced with `getCondition`. This affects the + following nodes: WhileStatement, DoStatement, ForStatement, IfStatement, AssertStatement, ConditionalExpression. +* {% jdoc java::lang.java.ast.ASTYieldStatement %} will not implement {% jdoc java::lang.java.ast.TypeNode %} + anymore come 7.0.0. Test the type of the expression nested within it. ### External Contributions * [#2041](https://github.com/pmd/pmd/pull/2041): \[modelica] Initial implementation for PMD - [Anatoly Trosinenko](https://github.com/atrosinenko) * [#2051](https://github.com/pmd/pmd/pull/2051): \[doc] Update the docs on adding a new language - [Anatoly Trosinenko](https://github.com/atrosinenko) +* [#2069](https://github.com/pmd/pmd/pull/2069): \[java] CommentRequired: make property names consistent - [snuyanzin](https://github.com/snuyanzin) * [#2169](https://github.com/pmd/pmd/pull/2169): \[modelica] Follow-up fixes for Modelica language module - [Anatoly Trosinenko](https://github.com/atrosinenko) +* [#2193](https://github.com/pmd/pmd/pull/2193): \[core] Fix odd logic in test runner - [Egor Bredikhin](https://github.com/Egor18) +* [#2194](https://github.com/pmd/pmd/pull/2194): \[java] Fix odd logic in AvoidUsingHardCodedIPRule - [Egor Bredikhin](https://github.com/Egor18) {% endtocmaker %} diff --git a/pmd-core/pom.xml b/pmd-core/pom.xml index 9443265f3b..f46e5bf834 100644 --- a/pmd-core/pom.xml +++ b/pmd-core/pom.xml @@ -24,7 +24,7 @@ - + diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSet.java b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSet.java index 9d8b45fe64..ec8f98166d 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSet.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSet.java @@ -152,6 +152,32 @@ public class RuleSet implements ChecksumAware { return this; } + /** + * Finds an already added rule by same name and language, if it already exists. + * @param rule the rule to search + * @return the already added rule or null if no rule was added yet to the builder. + */ + Rule getExistingRule(final Rule rule) { + for (Rule r : rules) { + if (r.getName().equals(rule.getName()) && r.getLanguage() == rule.getLanguage()) { + return r; + } + } + + return null; + } + + /** + * Checks, whether a rule with the same name and language already exists in the + * ruleset. + * @param rule to rule to check + * @return true if the rule already exists, false if the given + * rule is the first configuration of this rule. + */ + boolean hasRule(final Rule rule) { + return getExistingRule(rule) != null; + } + /** * Adds a rule. If a rule with the same name and language already * existed before in the ruleset, then the new rule will replace it. @@ -197,13 +223,7 @@ public class RuleSet implements ChecksumAware { rule = ((RuleReference) rule).getRule(); } - boolean exists = false; - for (final Rule r : rules) { - if (r.getName().equals(rule.getName()) && r.getLanguage() == rule.getLanguage()) { - exists = true; - break; - } - } + boolean exists = hasRule(rule); if (!exists) { addRule(ruleOrRef); } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java index 96a36fda88..6d9f41e970 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java @@ -60,22 +60,31 @@ public class RuleSetFactory { private final boolean warnDeprecated; private final RuleSetFactoryCompatibility compatibilityFilter; + /** + * @deprecated Use {@link RulesetsFactoryUtils#defaultFactory()} + */ + @Deprecated // to be removed with PMD 7.0.0. public RuleSetFactory() { this(new ResourceLoader(), RulePriority.LOW, false, true); } /** - * @deprecated Use {@link #RuleSetFactory(ResourceLoader, RulePriority, boolean, boolean)} with - * {@link ResourceLoader} instead of a {@link ClassLoader}. + * @deprecated Use {@link RulesetsFactoryUtils#createFactory(ClassLoader, RulePriority, boolean, boolean)} + * or {@link RulesetsFactoryUtils#createFactory(RulePriority, boolean, boolean)} */ @Deprecated // to be removed with PMD 7.0.0. public RuleSetFactory(final ClassLoader classLoader, final RulePriority minimumPriority, - final boolean warnDeprecated, final boolean enableCompatibility) { + final boolean warnDeprecated, final boolean enableCompatibility) { this(new ResourceLoader(classLoader), minimumPriority, warnDeprecated, enableCompatibility); } + /** + * @deprecated Use {@link RulesetsFactoryUtils#createFactory(ClassLoader, RulePriority, boolean, boolean)} + * or {@link RulesetsFactoryUtils#createFactory(RulePriority, boolean, boolean)} + */ + @Deprecated // to be hidden with PMD 7.0.0. public RuleSetFactory(final ResourceLoader resourceLoader, final RulePriority minimumPriority, - final boolean warnDeprecated, final boolean enableCompatibility) { + final boolean warnDeprecated, final boolean enableCompatibility) { this.resourceLoader = resourceLoader; this.minimumPriority = minimumPriority; this.warnDeprecated = warnDeprecated; @@ -374,6 +383,8 @@ public class RuleSetFactory { ruleSetBuilder.withName("Missing RuleSet Name"); } + Set rulesetReferences = new HashSet<>(); + NodeList nodeList = ruleSetElement.getChildNodes(); for (int i = 0; i < nodeList.getLength(); i++) { Node node = nodeList.item(i); @@ -395,7 +406,7 @@ public class RuleSetFactory { } ruleSetBuilder.withFileExclusions(pattern); } else if ("rule".equals(nodeName)) { - parseRuleNode(ruleSetReferenceId, ruleSetBuilder, node, withDeprecatedRuleReferences); + parseRuleNode(ruleSetReferenceId, ruleSetBuilder, node, withDeprecatedRuleReferences, rulesetReferences); } else { throw new IllegalArgumentException(UNEXPECTED_ELEMENT + node.getNodeName() + "> encountered as child of element."); @@ -481,14 +492,15 @@ public class RuleSetFactory { * @param withDeprecatedRuleReferences * whether rule references that are deprecated should be ignored * or not + * @param rulesetReferences keeps track of already processed complete ruleset references in order to log a warning */ private void parseRuleNode(RuleSetReferenceId ruleSetReferenceId, RuleSetBuilder ruleSetBuilder, Node ruleNode, - boolean withDeprecatedRuleReferences) + boolean withDeprecatedRuleReferences, Set rulesetReferences) throws ClassNotFoundException, InstantiationException, IllegalAccessException, RuleSetNotFoundException { Element ruleElement = (Element) ruleNode; String ref = ruleElement.getAttribute("ref"); if (ref.endsWith("xml")) { - parseRuleSetReferenceNode(ruleSetBuilder, ruleElement, ref); + parseRuleSetReferenceNode(ruleSetBuilder, ruleElement, ref, rulesetReferences); } else if (StringUtils.isBlank(ref)) { parseSingleRuleNode(ruleSetReferenceId, ruleSetBuilder, ruleNode); } else { @@ -508,8 +520,9 @@ public class RuleSetFactory { * Must be a rule element node. * @param ref * The RuleSet reference. + * @param rulesetReferences keeps track of already processed complete ruleset references in order to log a warning */ - private void parseRuleSetReferenceNode(RuleSetBuilder ruleSetBuilder, Element ruleElement, String ref) + private void parseRuleSetReferenceNode(RuleSetBuilder ruleSetBuilder, Element ruleElement, String ref, Set rulesetReferences) throws RuleSetNotFoundException { String priority = null; NodeList childNodes = ruleElement.getChildNodes(); @@ -571,6 +584,12 @@ public class RuleSetFactory { + "; perhaps the rule name is mispelled or the rule doesn't exist anymore?"); } } + + if (rulesetReferences.contains(ref)) { + LOG.warning("The ruleset " + ref + " is referenced multiple times in \"" + + ruleSetBuilder.getName() + "\"."); + } + rulesetReferences.add(ref); } /** @@ -694,6 +713,19 @@ public class RuleSetFactory { } if (withDeprecatedRuleReferences || !isSameRuleSet || !ruleReference.isDeprecated()) { + Rule existingRule = ruleSetBuilder.getExistingRule(ruleReference); + if (existingRule instanceof RuleReference) { + RuleReference existingRuleReference = (RuleReference) existingRule; + // the only valid use case is: the existing rule does not override anything yet + // which means, it is a plain reference. And the new reference overrides. + // for all other cases, we should log a warning + if (existingRuleReference.hasOverriddenAttributes() || !ruleReference.hasOverriddenAttributes()) { + LOG.warning("The rule " + ruleReference.getName() + " is referenced multiple times in \"" + + ruleSetBuilder.getName() + "\". " + + "Only the last rule configuration is used."); + } + } + ruleSetBuilder.addRuleReplaceIfExists(ruleReference); } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/RulesetsFactoryUtils.java b/pmd-core/src/main/java/net/sourceforge/pmd/RulesetsFactoryUtils.java index e13384111a..2fb7dba856 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RulesetsFactoryUtils.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RulesetsFactoryUtils.java @@ -7,6 +7,7 @@ package net.sourceforge.pmd; import java.util.logging.Level; import java.util.logging.Logger; +import net.sourceforge.pmd.annotation.InternalApi; import net.sourceforge.pmd.benchmark.TimeTracker; import net.sourceforge.pmd.benchmark.TimedOperation; import net.sourceforge.pmd.benchmark.TimedOperationCategory; @@ -61,24 +62,119 @@ public final class RulesetsFactoryUtils { * @throws IllegalArgumentException * if rulesets is empty (means, no rules have been found) or if * a ruleset couldn't be found. + * @deprecated Is internal API */ + @InternalApi + @Deprecated public static RuleSets getRuleSetsWithBenchmark(String rulesets, RuleSetFactory factory) { try (TimedOperation to = TimeTracker.startOperation(TimedOperationCategory.LOAD_RULES)) { return getRuleSets(rulesets, factory); } } + /** + * @deprecated Use {@link #createFactory(PMDConfiguration)} or {@link #createFactory(PMDConfiguration, ClassLoader)} + */ + @InternalApi + @Deprecated public static RuleSetFactory getRulesetFactory(final PMDConfiguration configuration, - final ResourceLoader resourceLoader) { + final ResourceLoader resourceLoader) { return new RuleSetFactory(resourceLoader, configuration.getMinimumPriority(), true, - configuration.isRuleSetFactoryCompatibilityEnabled()); + configuration.isRuleSetFactoryCompatibilityEnabled()); + } + + /** + * Returns a ruleset factory which uses the classloader for PMD + * classes to resolve resource references. + * + * @param configuration PMD configuration, contains info about the + * factory parameters + * + * @return A ruleset factory + * + * @see #createFactory(PMDConfiguration, ClassLoader) + */ + public static RuleSetFactory createFactory(final PMDConfiguration configuration) { + return createFactory(configuration, RulesetsFactoryUtils.class.getClassLoader()); + } + + /** + * Returns a ruleset factory with default parameters. It doesn't prune + * rules based on priority, and doesn't warn for deprecations. + * + * @return A ruleset factory + * + * @see #createFactory(PMDConfiguration, ClassLoader) + */ + public static RuleSetFactory defaultFactory() { + return new RuleSetFactory(); + } + + /** + * Returns a ruleset factory which uses the provided {@link ClassLoader} + * to resolve resource references. It warns for deprecated rule usages. + * + * @param configuration PMD configuration, contains info about the + * factory parameters + * @param classLoader Class loader to load resources + * + * @return A ruleset factory + * + * @see #createFactory(PMDConfiguration) + */ + public static RuleSetFactory createFactory(final PMDConfiguration configuration, ClassLoader classLoader) { + return createFactory(classLoader, + configuration.getMinimumPriority(), + true, + configuration.isRuleSetFactoryCompatibilityEnabled()); + } + + /** + * Returns a ruleset factory which uses the provided {@link ClassLoader} + * to resolve resource references. + * + * @param minimumPriority Minimum priority for rules to be included + * @param warnDeprecated If true, print warnings when deprecated rules are included + * @param enableCompatibility If true, rule references to moved rules are mapped to their + * new location if they are known + * @param classLoader Class loader to load resources + * + * @return A ruleset factory + * + * @see #createFactory(PMDConfiguration) + */ + public static RuleSetFactory createFactory(ClassLoader classLoader, + RulePriority minimumPriority, + boolean warnDeprecated, + boolean enableCompatibility) { + + return new RuleSetFactory(new ResourceLoader(classLoader), minimumPriority, warnDeprecated, enableCompatibility); + } + + /** + * Returns a ruleset factory which uses the classloader for PMD + * classes to resolve resource references. + * + * @param minimumPriority Minimum priority for rules to be included + * @param warnDeprecated If true, print warnings when deprecated rules are included + * @param enableCompatibility If true, rule references to moved rules are mapped to their + * new location if they are known + * + * @return A ruleset factory + * + * @see #createFactory(PMDConfiguration) + */ + public static RuleSetFactory createFactory(RulePriority minimumPriority, + boolean warnDeprecated, + boolean enableCompatibility) { + + return new RuleSetFactory(new ResourceLoader(), minimumPriority, warnDeprecated, enableCompatibility); } /** * If in debug modus, print the names of the rules. * - * @param rulesets - * the RuleSets to print + * @param rulesets the RuleSets to print */ private static void printRuleNamesInDebug(RuleSets rulesets) { if (LOG.isLoggable(Level.FINER)) { diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/benchmark/Benchmarker.java b/pmd-core/src/main/java/net/sourceforge/pmd/benchmark/Benchmarker.java index 383244a334..42baa667b6 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/benchmark/Benchmarker.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/benchmark/Benchmarker.java @@ -27,6 +27,7 @@ import net.sourceforge.pmd.RuleSet; import net.sourceforge.pmd.RuleSetFactory; import net.sourceforge.pmd.RuleSetNotFoundException; import net.sourceforge.pmd.RuleSets; +import net.sourceforge.pmd.RulesetsFactoryUtils; import net.sourceforge.pmd.SourceCodeProcessor; import net.sourceforge.pmd.lang.Language; import net.sourceforge.pmd.lang.LanguageFilenameFilter; @@ -116,7 +117,7 @@ public final class Benchmarker { System.out.println("Checking directory " + srcDir); } Set results = new TreeSet<>(); - RuleSetFactory factory = new RuleSetFactory(); + RuleSetFactory factory = RulesetsFactoryUtils.defaultFactory(); if (StringUtils.isNotBlank(ruleset)) { stress(languageVersion, factory.createRuleSet(ruleset), dataSources, results, debug); } else { @@ -174,7 +175,7 @@ public final class Benchmarker { private static void stress(LanguageVersion languageVersion, RuleSet ruleSet, List dataSources, Set results, boolean debug) throws PMDException, IOException { - final RuleSetFactory factory = new RuleSetFactory(); + final RuleSetFactory factory = RulesetsFactoryUtils.defaultFactory(); for (Rule rule: ruleSet.getRules()) { if (debug) { System.out.println("Starting " + rule.getName()); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/document/DocumentFile.java b/pmd-core/src/main/java/net/sourceforge/pmd/document/DocumentFile.java index 1aba2c795b..7c3e1640c0 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/document/DocumentFile.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/document/DocumentFile.java @@ -21,6 +21,8 @@ import java.util.Scanner; import java.util.logging.Level; import java.util.logging.Logger; +import org.apache.commons.io.IOUtils; + /** * Implementation that handles a Document as a file in the filesystem and receives operations in a sorted manner * (i.e. the regions are sorted). This improves the efficiency of reading the file by only scanning it once while @@ -157,11 +159,7 @@ public class DocumentFile implements Document, Closeable { } private void writeUntilEOF() throws IOException { - String line; - - while ((line = reader.readLine()) != null) { - writer.write(line); - } + IOUtils.copy(reader, writer); } /* package-private */ List getLineToOffset() { diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/AssertionUtil.java b/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/AssertionUtil.java index 3a229a5a54..a63687c276 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/AssertionUtil.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/AssertionUtil.java @@ -5,16 +5,38 @@ package net.sourceforge.pmd.internal.util; +import org.checkerframework.checker.nullness.qual.NonNull; + public final class AssertionUtil { private AssertionUtil() { // utility class } - public static void assertArgNonNegative(int n) { - if (n < 0) { - throw new IllegalArgumentException("Argument should be positive or null, got " + n); + public static int requireOver1(String name, final int value) { + if (value < 1) { + throw mustBe(name, value, ">= 1"); } + return value; } + public static int requireNonNegative(String name, int value) { + if (value < 0) { + throw mustBe(name, value, "non-negative"); + } + return value; + } + + public static RuntimeException mustBe(String name, Object value, String condition) { + return new IllegalArgumentException(String.format("%s must be %s, got %s", name, condition, value)); + } + + @NonNull + public static T requireParamNotNull(String paramName, T obj) { + if (obj == null) { + throw new NullPointerException("Parameter " + paramName + " is null"); + } + + return obj; + } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/IteratorUtil.java b/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/IteratorUtil.java index d77b11841f..451f1c792d 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/IteratorUtil.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/IteratorUtil.java @@ -201,7 +201,7 @@ public final class IteratorUtil { /** Advance {@code n} times. */ public static void advance(Iterator iterator, int n) { - AssertionUtil.assertArgNonNegative(n); + AssertionUtil.requireNonNegative("n", n); while (n > 0 && iterator.hasNext()) { iterator.next(); @@ -211,7 +211,7 @@ public final class IteratorUtil { /** Limit the number of elements yielded by this iterator to the given number. */ public static Iterator take(Iterator iterator, final int n) { - AssertionUtil.assertArgNonNegative(n); + AssertionUtil.requireNonNegative("n", n); if (n == 0) { return Collections.emptyIterator(); } @@ -233,7 +233,7 @@ public final class IteratorUtil { /** Produce an iterator whose first element is the nth element of the given source. */ public static Iterator drop(Iterator source, final int n) { - AssertionUtil.assertArgNonNegative(n); + AssertionUtil.requireNonNegative("n", n); if (n == 0) { return source; } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/internal/AxisStream.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/internal/AxisStream.java index 2561e16877..7c7cf64293 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/internal/AxisStream.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/internal/AxisStream.java @@ -86,7 +86,7 @@ abstract class AxisStream extends IteratorBasedNStream { @Override public NodeStream drop(int n) { - AssertionUtil.assertArgNonNegative(n); + AssertionUtil.requireNonNegative("n", n); switch (n) { case 0: return this; @@ -306,13 +306,13 @@ abstract class AxisStream extends IteratorBasedNStream { @Override public NodeStream take(int maxSize) { - AssertionUtil.assertArgNonNegative(maxSize); + AssertionUtil.requireNonNegative("maxSize", maxSize); return StreamImpl.sliceChildren(node, filter, low, min(maxSize, len)); } @Override public NodeStream drop(int n) { - AssertionUtil.assertArgNonNegative(n); + AssertionUtil.requireNonNegative("n", n); int newLow = min(low + n, node.jjtGetNumChildren()); int newLen = max(len - n, 0); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/internal/IteratorBasedNStream.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/internal/IteratorBasedNStream.java index 18c0e53642..9e37ce98d5 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/internal/IteratorBasedNStream.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/internal/IteratorBasedNStream.java @@ -78,13 +78,13 @@ abstract class IteratorBasedNStream implements NodeStream { @Override public NodeStream drop(int n) { - AssertionUtil.assertArgNonNegative(n); + AssertionUtil.requireNonNegative("n", n); return n == 0 ? this : mapIter(iter -> IteratorUtil.drop(iter, n)); } @Override public NodeStream take(int maxSize) { - AssertionUtil.assertArgNonNegative(maxSize); + AssertionUtil.requireNonNegative("maxSize", maxSize); return maxSize == 0 ? NodeStream.empty() : mapIter(iter -> IteratorUtil.take(iter, maxSize)); } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/internal/SingletonNodeStream.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/internal/SingletonNodeStream.java index 5b69b78e63..87230911fe 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/internal/SingletonNodeStream.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/internal/SingletonNodeStream.java @@ -92,13 +92,13 @@ final class SingletonNodeStream extends IteratorBasedNStream @Override public NodeStream drop(int n) { - AssertionUtil.assertArgNonNegative(n); + AssertionUtil.requireNonNegative("n", n); return n == 0 ? this : NodeStream.empty(); } @Override public NodeStream take(int maxSize) { - AssertionUtil.assertArgNonNegative(maxSize); + AssertionUtil.requireNonNegative("maxSize", maxSize); return maxSize >= 1 ? this : NodeStream.empty(); } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/internal/StreamImpl.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/internal/StreamImpl.java index bb0e72033f..660fe11061 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/internal/StreamImpl.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/internal/StreamImpl.java @@ -28,6 +28,7 @@ import net.sourceforge.pmd.lang.ast.internal.AxisStream.FilteredDescendantStream public final class StreamImpl { + @SuppressWarnings("rawtypes") private static final NodeStream EMPTY = new IteratorBasedNStream() { @Override public Iterator iterator() { diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/RuleReference.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/RuleReference.java index d24394931a..8b1d189f36 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/RuleReference.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/RuleReference.java @@ -389,4 +389,17 @@ public class RuleReference extends AbstractDelegateRule { public Rule deepCopy() { return new RuleReference(this); } + + /** + * Checks whether this rule reference explicitly overrides any of the possible + * attributes of the referenced rule. + * @return true if there is at least one attribute overridden. false if + * the referenced rule is referenced without any change. + */ + public boolean hasOverriddenAttributes() { + return deprecated != null || description != null || examples != null || externalInfoUrl != null + || maximumLanguageVersion != null || minimumLanguageVersion != null + || message != null || name != null || priority != null + || propertyDescriptors != null || propertyValues != null; + } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleBuilder.java b/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleBuilder.java index 16e2088a5e..94491ad631 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleBuilder.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleBuilder.java @@ -14,6 +14,7 @@ import org.w3c.dom.Element; import net.sourceforge.pmd.Rule; import net.sourceforge.pmd.RulePriority; import net.sourceforge.pmd.RuleSetReference; +import net.sourceforge.pmd.annotation.InternalApi; import net.sourceforge.pmd.lang.Language; import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.lang.LanguageVersion; @@ -28,6 +29,8 @@ import net.sourceforge.pmd.util.ResourceLoader; * @author Clément Fournier * @since 6.0.0 */ +@InternalApi +@Deprecated public class RuleBuilder { private List> definedProperties = new ArrayList<>(); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java b/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java index 9a11c36927..217bfe2084 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/rules/RuleFactory.java @@ -26,6 +26,7 @@ import org.w3c.dom.NodeList; import net.sourceforge.pmd.Rule; import net.sourceforge.pmd.RulePriority; import net.sourceforge.pmd.RuleSetReference; +import net.sourceforge.pmd.annotation.InternalApi; import net.sourceforge.pmd.lang.rule.RuleReference; import net.sourceforge.pmd.properties.PropertyDescriptor; import net.sourceforge.pmd.properties.PropertyDescriptorField; @@ -40,6 +41,8 @@ import net.sourceforge.pmd.util.ResourceLoader; * @author Clément Fournier * @since 6.0.0 */ +@InternalApi +@Deprecated public class RuleFactory { private static final Logger LOG = Logger.getLogger(RuleFactory.class.getName()); @@ -57,7 +60,7 @@ public class RuleFactory { private static final String DESCRIPTION = "description"; private static final String PROPERTY = "property"; private static final String CLASS = "class"; - + private static final List REQUIRED_ATTRIBUTES = Collections.unmodifiableList(Arrays.asList(NAME, CLASS)); private final ResourceLoader resourceLoader; @@ -337,7 +340,7 @@ public class RuleFactory { Attr a = (Attr) atts.item(i); values.put(PropertyDescriptorField.getConstant(a.getName()), a.getValue()); } - + if (StringUtils.isBlank(values.get(DEFAULT_VALUE))) { NodeList children = propertyElement.getElementsByTagName(DEFAULT_VALUE.attributeName()); if (children.getLength() == 1) { diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/util/StringUtil.java b/pmd-core/src/main/java/net/sourceforge/pmd/util/StringUtil.java index d24165fa0a..533624f11d 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/util/StringUtil.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/util/StringUtil.java @@ -53,10 +53,8 @@ public final class StringUtil { * Return whether the non-null text arg starts with any of the prefix * values. * - * @param text - * @param prefixes - * * @return boolean + * * @deprecated {@link StringUtils#startsWithAny(CharSequence, CharSequence...)} */ @Deprecated @@ -75,9 +73,6 @@ public final class StringUtil { /** * Returns whether the non-null text arg matches any of the test values. * - * @param text - * @param tests - * * @return boolean */ public static boolean isAnyOf(String text, String... tests) { @@ -96,9 +91,6 @@ public final class StringUtil { * Checks for the existence of any of the listed prefixes on the non-null * text and removes them. * - * @param text - * @param prefixes - * * @return String */ public static String withoutPrefixes(String text, String... prefixes) { @@ -117,6 +109,7 @@ public final class StringUtil { * @param value String * * @return boolean + * * @deprecated {@link StringUtils#isNotBlank(CharSequence)} */ @Deprecated @@ -133,6 +126,7 @@ public final class StringUtil { * @param value String to test * * @return true if the value is empty, false otherwise. + * * @deprecated {@link StringUtils#isBlank(CharSequence)} */ @Deprecated @@ -147,6 +141,7 @@ public final class StringUtil { * @param value String to test * * @return True if the argument is null or the empty string + * * @deprecated {@link StringUtils#isEmpty(CharSequence)} */ @Deprecated @@ -159,9 +154,6 @@ public final class StringUtil { * Returns true if both strings are effectively null or whitespace, returns * false otherwise if they have actual text that differs. * - * @param a - * @param b - * * @return boolean */ @Deprecated @@ -184,6 +176,7 @@ public final class StringUtil { * @param newString String * * @return String + * * @deprecated {@link StringUtils#replace(String, String, String)} */ @Deprecated @@ -207,11 +200,8 @@ public final class StringUtil { } /** - * @param buf - * @param src * @param supportUTF8 override the default setting, whether special characters should be replaced with entities ( * false) or should be included as is ( true). - * */ public static void appendXmlEscaped(StringBuilder buf, String src, boolean supportUTF8) { char c; @@ -249,8 +239,6 @@ public final class StringUtil { /** * Replace some whitespace characters so they are visually apparent. * - * @param o - * * @return String */ public static String escapeWhitespace(Object o) { @@ -271,6 +259,7 @@ public final class StringUtil { * @param newString String * * @return String + * * @deprecated {@link StringUtils#replace(String, String, String)} or {@link StringUtils#replaceChars(String, char, char)} */ @Deprecated @@ -304,6 +293,7 @@ public final class StringUtil { * @param delimiter char * * @return String[] + * * @deprecated {@link StringUtils#split(String, char)} */ @Deprecated @@ -352,6 +342,7 @@ public final class StringUtil { * @param separator char * * @return String[] + * * @deprecated {@link StringUtils#split(String, String)} */ @Deprecated @@ -386,6 +377,7 @@ public final class StringUtil { * @param sb StringBuffer * @param iter Iterator * @param separator String + * * @deprecated {@link StringUtils#join(Iterator, String)} */ @Deprecated @@ -411,6 +403,7 @@ public final class StringUtil { * @param sb StringBuilder * @param items Object[] * @param separator String + * * @deprecated {@link StringUtils#join(Iterable, String)} */ @Deprecated @@ -497,9 +490,6 @@ public final class StringUtil { * Trims off the leading characters off the strings up to the trimDepth * specified. Returns the same strings if trimDepth = 0 * - * @param strings - * @param trimDepth - * * @return String[] */ public static String[] trimStartOn(String[] strings, int trimDepth) { @@ -523,6 +513,7 @@ public final class StringUtil { * @param length The desired minimum length of the resulting padded String * * @return The resulting left padded String + * * @deprecated {@link StringUtils#leftPad(String, int)} */ @Deprecated diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryCompatibilityTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryCompatibilityTest.java index 8feadfdc10..dd7104c561 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryCompatibilityTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryCompatibilityTest.java @@ -28,7 +28,7 @@ public class RuleSetFactoryCompatibilityTest { + " Test\n" + "\n" + " \n" + "\n"; - RuleSetFactory factory = new RuleSetFactory(); + RuleSetFactory factory = RulesetsFactoryUtils.defaultFactory(); factory.getCompatibilityFilter().addFilterRuleMoved("dummy", "notexisting", "basic", "DummyBasicMockRule"); RuleSet createdRuleSet = createRulesetFromString(ruleset, factory); @@ -66,7 +66,7 @@ public class RuleSetFactoryCompatibilityTest { + " Test\n" + "\n" + " \n" + " \n" + " \n" + "\n"; - RuleSetFactory factory = new RuleSetFactory(); + RuleSetFactory factory = RulesetsFactoryUtils.defaultFactory(); factory.getCompatibilityFilter().addFilterRuleRenamed("dummy", "basic", "OldNameOfSampleXPathRule", "SampleXPathRule"); diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryDuplicatedRuleLoggingTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryDuplicatedRuleLoggingTest.java new file mode 100644 index 0000000000..38f58273c5 --- /dev/null +++ b/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryDuplicatedRuleLoggingTest.java @@ -0,0 +1,81 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; +import org.junit.rules.ExpectedException; + +import net.sourceforge.pmd.junit.JavaUtilLoggingRule; +import net.sourceforge.pmd.junit.LocaleRule; + +public class RuleSetFactoryDuplicatedRuleLoggingTest { + @org.junit.Rule + public ExpectedException ex = ExpectedException.none(); + + @org.junit.Rule + public LocaleRule localeRule = LocaleRule.en(); + + @org.junit.Rule + public JavaUtilLoggingRule logging = new JavaUtilLoggingRule(RuleSetFactory.class.getName()); + + @Test + public void duplicatedRuleReferenceShouldWarn() throws RuleSetNotFoundException { + RuleSet ruleset = loadRuleSet("duplicatedRuleReference.xml"); + + assertEquals(1, ruleset.getRules().size()); + Rule mockRule = ruleset.getRuleByName("DummyBasicMockRule"); + assertNotNull(mockRule); + assertEquals(RulePriority.MEDIUM, mockRule.getPriority()); + assertTrue(logging.getLog().contains("The rule DummyBasicMockRule is referenced multiple times in \"Custom Rules\". " + + "Only the last rule configuration is used.")); + } + + @Test + public void duplicatedRuleReferenceWithOverrideShouldNotWarn() throws RuleSetNotFoundException { + RuleSet ruleset = loadRuleSet("duplicatedRuleReferenceWithOverride.xml"); + + assertEquals(2, ruleset.getRules().size()); + Rule mockRule = ruleset.getRuleByName("DummyBasicMockRule"); + assertNotNull(mockRule); + assertEquals(RulePriority.HIGH, mockRule.getPriority()); + assertNotNull(ruleset.getRuleByName("SampleXPathRule")); + assertTrue(logging.getLog().isEmpty()); + } + + @Test + public void duplicatedRuleReferenceWithOverrideBeforeShouldNotWarn() throws RuleSetNotFoundException { + RuleSet ruleset = loadRuleSet("duplicatedRuleReferenceWithOverrideBefore.xml"); + + assertEquals(2, ruleset.getRules().size()); + Rule mockRule = ruleset.getRuleByName("DummyBasicMockRule"); + assertNotNull(mockRule); + assertEquals(RulePriority.HIGH, mockRule.getPriority()); + assertNotNull(ruleset.getRuleByName("SampleXPathRule")); + assertTrue(logging.getLog().isEmpty()); + } + + @Test + public void multipleDuplicates() throws RuleSetNotFoundException { + RuleSet ruleset = loadRuleSet("multipleDuplicates.xml"); + + assertEquals(2, ruleset.getRules().size()); + Rule mockRule = ruleset.getRuleByName("DummyBasicMockRule"); + assertNotNull(mockRule); + assertEquals(RulePriority.MEDIUM_HIGH, mockRule.getPriority()); + assertNotNull(ruleset.getRuleByName("SampleXPathRule")); + assertTrue(logging.getLog().contains("The rule DummyBasicMockRule is referenced multiple times in \"Custom Rules\". " + + "Only the last rule configuration is used.")); + assertTrue(logging.getLog().contains("The ruleset rulesets/dummy/basic.xml is referenced multiple times in \"Custom Rules\".")); + } + + private RuleSet loadRuleSet(String ruleSetFilename) throws RuleSetNotFoundException { + RuleSetFactory rsf = RulesetsFactoryUtils.defaultFactory(); + return rsf.createRuleSet("net/sourceforge/pmd/rulesets/duplicatedRuleLoggingTest/" + ruleSetFilename); + } +} diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryTest.java index a1257dd023..9115341ccf 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryTest.java @@ -46,7 +46,7 @@ public class RuleSetFactoryTest { RuleSet rs = loadRuleSet(EMPTY_RULESET); assertNull("RuleSet file name not expected", rs.getFileName()); - RuleSetFactory rsf = new RuleSetFactory(); + RuleSetFactory rsf = RulesetsFactoryUtils.defaultFactory(); rs = rsf.createRuleSet("net/sourceforge/pmd/TestRuleset1.xml"); assertEquals("wrong RuleSet file name", rs.getFileName(), "net/sourceforge/pmd/TestRuleset1.xml"); } @@ -59,7 +59,7 @@ public class RuleSetFactoryTest { @Test public void testRefs() throws Exception { - RuleSetFactory rsf = new RuleSetFactory(); + RuleSetFactory rsf = RulesetsFactoryUtils.defaultFactory(); RuleSet rs = rsf.createRuleSet("net/sourceforge/pmd/TestRuleset1.xml"); assertNotNull(rs.getRuleByName("TestRuleRef")); } @@ -70,7 +70,7 @@ public class RuleSetFactoryTest { assertNotNull("Test ruleset not found - can't continue with test!", in); in.close(); - RuleSetFactory rsf = new RuleSetFactory(); + RuleSetFactory rsf = RulesetsFactoryUtils.defaultFactory(); RuleSets rs = rsf.createRuleSets("net/sourceforge/pmd/rulesets/reference-ruleset.xml"); // added by referencing a complete ruleset (TestRuleset1.xml) assertNotNull(rs.getRuleByName("MockRule1")); @@ -128,7 +128,7 @@ public class RuleSetFactoryTest { @Test(expected = RuleSetNotFoundException.class) public void testRuleSetNotFound() throws RuleSetNotFoundException { - RuleSetFactory rsf = new RuleSetFactory(); + RuleSetFactory rsf = RulesetsFactoryUtils.defaultFactory(); rsf.createRuleSet("fooooo"); } @@ -535,7 +535,7 @@ public class RuleSetFactoryTest { assertNotNull(ruleset.getRuleByName("SampleXPathRule")); // now, load with default minimum priority - rsf = new RuleSetFactory(); + rsf = RulesetsFactoryUtils.defaultFactory(); ruleset = rsf.createRuleSet("net/sourceforge/pmd/rulesets/ruleset-minimum-priority.xml"); assertEquals("Number of Rules", 2, ruleset.getRules().size()); Rule dummyBasicMockRule = ruleset.getRuleByName("DummyBasicMockRule"); @@ -544,13 +544,13 @@ public class RuleSetFactoryTest { @Test public void testExcludeWithMinimumPriority() throws RuleSetNotFoundException { - RuleSetFactory rsf = new RuleSetFactory(new ResourceLoader(), RulePriority.HIGH, true, true); + RuleSetFactory rsf = RulesetsFactoryUtils.createFactory(RulePriority.HIGH, true, true); RuleSet ruleset = rsf.createRuleSet("net/sourceforge/pmd/rulesets/ruleset-minimum-priority-exclusion.xml"); // no rules should be loaded assertEquals("Number of Rules", 0, ruleset.getRules().size()); // now, load with default minimum priority - rsf = new RuleSetFactory(); + rsf = RulesetsFactoryUtils.defaultFactory(); ruleset = rsf.createRuleSet("net/sourceforge/pmd/rulesets/ruleset-minimum-priority-exclusion.xml"); // only one rule, we have excluded one... assertEquals("Number of Rules", 1, ruleset.getRules().size()); @@ -663,7 +663,7 @@ public class RuleSetFactoryTest { @Test public void testDeprecatedRuleSetReference() throws RuleSetNotFoundException { - RuleSetFactory ruleSetFactory = new RuleSetFactory(); + RuleSetFactory ruleSetFactory = RulesetsFactoryUtils.defaultFactory(); RuleSet ruleSet = ruleSetFactory.createRuleSet("net/sourceforge/pmd/rulesets/ruleset-deprecated.xml"); assertEquals(2, ruleSet.getRules().size()); } @@ -705,7 +705,7 @@ public class RuleSetFactoryTest { + " \n" + " \n" + " \n" + " \n" + " \n" + "\n"); - RuleSetFactory ruleSetFactory = new RuleSetFactory(); + RuleSetFactory ruleSetFactory = RulesetsFactoryUtils.defaultFactory(); ruleSetFactory.createRuleSet(ref); } @@ -723,7 +723,7 @@ public class RuleSetFactoryTest { + " xsi:schemaLocation=\"http://pmd.sourceforge.net/ruleset/2.0.0 https://pmd.sourceforge.io/ruleset_2_0_0.xsd\">\n" + " PMD Ruleset.\n" + "\n" + " .*Test.*\n" + "\n" + "\n"); - RuleSetFactory ruleSetFactory = new RuleSetFactory(); + RuleSetFactory ruleSetFactory = RulesetsFactoryUtils.defaultFactory(); RuleSet ruleset = ruleSetFactory.createRuleSet(ref); assertEquals(0, ruleset.getRules().size()); } @@ -766,7 +766,7 @@ public class RuleSetFactoryTest { + " xsi:schemaLocation=\"http://pmd.sourceforge.net/ruleset/2.0.0 https://pmd.sourceforge.io/ruleset_2_0_0.xsd\">\n" + " Custom ruleset for tests\n" + " \n" + "\n"); - RuleSetFactory ruleSetFactory = new RuleSetFactory(); + RuleSetFactory ruleSetFactory = RulesetsFactoryUtils.defaultFactory(); ruleSetFactory.createRuleSet(ref); } @@ -786,7 +786,7 @@ public class RuleSetFactoryTest { + " PMD Plugin preferences rule set\n" + "\n" + "\n" + "\n" + "\n" + ""); - RuleSetFactory ruleSetFactory = new RuleSetFactory(); + RuleSetFactory ruleSetFactory = RulesetsFactoryUtils.defaultFactory(); RuleSet rs = ruleSetFactory.createRuleSet(ref); Rule r = rs.getRules().toArray(new Rule[1])[0]; @@ -813,7 +813,7 @@ public class RuleSetFactoryTest { + " Custom ruleset for tests\n" + " \n" + " \n" + " \n" + "\n"); - RuleSetFactory ruleSetFactory = new RuleSetFactory(); + RuleSetFactory ruleSetFactory = RulesetsFactoryUtils.defaultFactory(); RuleSet ruleset = ruleSetFactory.createRuleSet(ref); assertEquals(4, ruleset.getRules().size()); } @@ -843,7 +843,7 @@ public class RuleSetFactoryTest { + " Custom ruleset for tests\n" + " \n" + " \n" + " \n" + "\n"); - RuleSetFactory ruleSetFactory = new RuleSetFactory(); + RuleSetFactory ruleSetFactory = RulesetsFactoryUtils.defaultFactory(); RuleSet ruleset = ruleSetFactory.createRuleSet(ref1); assertNull(ruleset.getRuleByName("DummyBasicMockRule")); @@ -855,7 +855,7 @@ public class RuleSetFactoryTest { + " Custom ruleset for tests\n" + " \n" + " \n" + " \n" + " \n" + "\n"); - RuleSetFactory ruleSetFactory2 = new RuleSetFactory(); + RuleSetFactory ruleSetFactory2 = RulesetsFactoryUtils.defaultFactory(); RuleSet ruleset2 = ruleSetFactory2.createRuleSet(ref2); assertNotNull(ruleset2.getRuleByName("DummyBasicMockRule")); @@ -867,7 +867,7 @@ public class RuleSetFactoryTest { + " Custom ruleset for tests\n" + " \n" + " \n" + " \n" + " \n" + "\n"); - RuleSetFactory ruleSetFactory3 = new RuleSetFactory(); + RuleSetFactory ruleSetFactory3 = RulesetsFactoryUtils.defaultFactory(); RuleSet ruleset3 = ruleSetFactory3.createRuleSet(ref3); assertNotNull(ruleset3.getRuleByName("DummyBasicMockRule")); } @@ -885,7 +885,7 @@ public class RuleSetFactoryTest { + " Custom ruleset for tests\n" + " \n" + " \n"); - RuleSetFactory ruleSetFactory = new RuleSetFactory(); + RuleSetFactory ruleSetFactory = RulesetsFactoryUtils.defaultFactory(); ruleSetFactory.createRuleSet(ref); assertTrue(logging.getLog().contains("RuleSet name is missing.")); @@ -900,7 +900,7 @@ public class RuleSetFactoryTest { + " xsi:schemaLocation=\"http://pmd.sourceforge.net/ruleset/2.0.0 https://pmd.sourceforge.io/ruleset_2_0_0.xsd\">\n" + " \n" + " \n"); - RuleSetFactory ruleSetFactory = new RuleSetFactory(); + RuleSetFactory ruleSetFactory = RulesetsFactoryUtils.defaultFactory(); ruleSetFactory.createRuleSet(ref); assertTrue(logging.getLog().contains("RuleSet description is missing.")); } @@ -1100,12 +1100,12 @@ public class RuleSetFactoryTest { } private RuleSet loadRuleSet(String ruleSetXml) throws RuleSetNotFoundException { - RuleSetFactory rsf = new RuleSetFactory(); + RuleSetFactory rsf = RulesetsFactoryUtils.defaultFactory(); return rsf.createRuleSet(createRuleSetReferenceId(ruleSetXml)); } private RuleSet loadRuleSetWithDeprecationWarnings(String ruleSetXml) throws RuleSetNotFoundException { - RuleSetFactory rsf = new RuleSetFactory(new ResourceLoader(), RulePriority.LOW, true, false); + RuleSetFactory rsf = RulesetsFactoryUtils.createFactory(RulePriority.LOW, true, false); return rsf.createRuleSet(createRuleSetReferenceId(ruleSetXml)); } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetTest.java index 8a5f498594..baa66c0637 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetTest.java @@ -60,7 +60,7 @@ public class RuleSetTest { public void testNoDFA() { MockRule mock = new MockRule("name", "desc", "msg", "rulesetname"); mock.setLanguage(LanguageRegistry.getLanguage(DummyLanguageModule.NAME)); - RuleSet rs = new RuleSetFactory().createSingleRuleRuleSet(mock); + RuleSet rs = RulesetsFactoryUtils.defaultFactory().createSingleRuleRuleSet(mock); assertFalse(rs.usesDFA(LanguageRegistry.getLanguage(DummyLanguageModule.NAME))); } @@ -69,7 +69,7 @@ public class RuleSetTest { MockRule mock = new MockRule("name", "desc", "msg", "rulesetname"); mock.setLanguage(LanguageRegistry.getLanguage(DummyLanguageModule.NAME)); mock.setDfa(true); - RuleSet rs = new RuleSetFactory().createSingleRuleRuleSet(mock); + RuleSet rs = RulesetsFactoryUtils.defaultFactory().createSingleRuleRuleSet(mock); assertTrue(rs.usesDFA(LanguageRegistry.getLanguage(DummyLanguageModule.NAME))); } @@ -88,21 +88,21 @@ public class RuleSetTest { @Test public void testGetRuleByName() { MockRule mock = new MockRule("name", "desc", "msg", "rulesetname"); - RuleSet rs = new RuleSetFactory().createSingleRuleRuleSet(mock); + RuleSet rs = RulesetsFactoryUtils.defaultFactory().createSingleRuleRuleSet(mock); assertEquals("unable to fetch rule by name", mock, rs.getRuleByName("name")); } @Test public void testGetRuleByName2() { MockRule mock = new MockRule("name", "desc", "msg", "rulesetname"); - RuleSet rs = new RuleSetFactory().createSingleRuleRuleSet(mock); + RuleSet rs = RulesetsFactoryUtils.defaultFactory().createSingleRuleRuleSet(mock); assertNull("the rule FooRule must not be found!", rs.getRuleByName("FooRule")); } @Test public void testRuleList() { MockRule rule = new MockRule("name", "desc", "msg", "rulesetname"); - RuleSet ruleset = new RuleSetFactory().createSingleRuleRuleSet(rule); + RuleSet ruleset = RulesetsFactoryUtils.defaultFactory().createSingleRuleRuleSet(rule); assertEquals("Size of RuleSet isn't one.", 1, ruleset.size()); diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetWriterTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetWriterTest.java index a451ecb150..d488f30fcc 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetWriterTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetWriterTest.java @@ -51,7 +51,7 @@ public class RuleSetWriterTest { */ @Test public void testWrite() throws Exception { - RuleSet braces = new RuleSetFactory().createRuleSet("net/sourceforge/pmd/TestRuleset1.xml"); + RuleSet braces = RulesetsFactoryUtils.defaultFactory().createRuleSet("net/sourceforge/pmd/TestRuleset1.xml"); RuleSet ruleSet = new RuleSetBuilder(new Random().nextLong()) .withName("ruleset") .withDescription("ruleset description") @@ -72,7 +72,7 @@ public class RuleSetWriterTest { */ @Test public void testRuleReferenceOverriddenName() throws Exception { - RuleSetFactory ruleSetFactory = new RuleSetFactory(); + RuleSetFactory ruleSetFactory = RulesetsFactoryUtils.defaultFactory(); RuleSet rs = ruleSetFactory.createRuleSet("dummy-basic"); RuleSetReference ruleSetReference = new RuleSetReference("rulesets/dummy/basic.xml"); diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/document/DocumentFileTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/document/DocumentFileTest.java index 738d886936..ece529938d 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/document/DocumentFileTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/document/DocumentFileTest.java @@ -16,6 +16,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import org.apache.commons.io.IOUtils; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -48,6 +49,22 @@ public class DocumentFileTest { } } + @Test + public void shouldPreserveNewlines() throws IOException { + final String testFileContent = IOUtils.toString( + DocumentFileTest.class.getResource("ShouldPreserveNewlines.java"), StandardCharsets.UTF_8); + writeContentToTemporaryFile(testFileContent); + + try (DocumentFile documentFile = new DocumentFile(temporaryFile, StandardCharsets.UTF_8)) { + documentFile.insert(0, 0, "public "); + } + + try (FileInputStream stream = new FileInputStream(temporaryFile)) { + final String actualContent = new String(readAllBytes(stream)); + assertEquals("public " + testFileContent, actualContent); + } + } + private byte[] readAllBytes(final FileInputStream stream) throws IOException { final int defaultBufferSize = 8192; final int maxBufferSize = Integer.MAX_VALUE - 8; diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/lang/ast/internal/NodeStreamBlanketTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/lang/ast/internal/NodeStreamBlanketTest.java index 76033830e7..bcef611273 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/lang/ast/internal/NodeStreamBlanketTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/lang/ast/internal/NodeStreamBlanketTest.java @@ -17,6 +17,7 @@ import java.util.stream.Collectors; import java.util.stream.Stream; import org.junit.Assert; +import org.junit.Assume; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -206,9 +207,7 @@ public class NodeStreamBlanketTest { @SafeVarargs private static void assertImplication(T input, Prop precond, Prop... properties) { - if (!precond.test(input)) { - return; - } + Assume.assumeTrue(precond.test(input)); for (Prop prop2 : properties) { Assert.assertTrue( diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/processor/MultiThreadProcessorTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/processor/MultiThreadProcessorTest.java index 311deb2ba6..020cd21c89 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/processor/MultiThreadProcessorTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/processor/MultiThreadProcessorTest.java @@ -22,6 +22,7 @@ import net.sourceforge.pmd.Report.ConfigurationError; import net.sourceforge.pmd.RuleContext; import net.sourceforge.pmd.RuleSetFactory; import net.sourceforge.pmd.RuleViolation; +import net.sourceforge.pmd.RulesetsFactoryUtils; import net.sourceforge.pmd.ThreadSafeReportListener; import net.sourceforge.pmd.lang.ast.Node; import net.sourceforge.pmd.lang.rule.AbstractRule; @@ -51,9 +52,9 @@ public class MultiThreadProcessorTest { ctx.getReport().addListener(reportListener); processor = new MultiThreadProcessor(configuration); - ruleSetFactory = new RuleSetFactory(); + ruleSetFactory = RulesetsFactoryUtils.defaultFactory(); } - + @Test public void testRulesDysnfunctionalLog() throws IOException { setUpForTest("rulesets/MultiThreadProcessorTest/dysfunctional.xml"); diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/properties/PropertyDescriptorTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/properties/PropertyDescriptorTest.java index 954af6bf7e..11e005c622 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/properties/PropertyDescriptorTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/properties/PropertyDescriptorTest.java @@ -30,7 +30,7 @@ import org.junit.rules.ExpectedException; import net.sourceforge.pmd.FooRule; import net.sourceforge.pmd.Rule; import net.sourceforge.pmd.RuleSet; -import net.sourceforge.pmd.RuleSetFactory; +import net.sourceforge.pmd.RulesetsFactoryUtils; import net.sourceforge.pmd.properties.constraints.PropertyConstraint; @@ -57,7 +57,7 @@ public class PropertyDescriptorTest { FooRule rule = new FooRule(); rule.definePropertyDescriptor(intProperty); rule.setProperty(intProperty, 1000); - RuleSet ruleSet = new RuleSetFactory().createSingleRuleRuleSet(rule); + RuleSet ruleSet = RulesetsFactoryUtils.defaultFactory().createSingleRuleRuleSet(rule); List dysfunctional = new ArrayList<>(); ruleSet.removeDysfunctionalRules(dysfunctional); @@ -78,7 +78,7 @@ public class PropertyDescriptorTest { FooRule rule = new FooRule(); rule.definePropertyDescriptor(descriptor); rule.setProperty(descriptor, Collections.singletonList(1000d)); // not in range - RuleSet ruleSet = new RuleSetFactory().createSingleRuleRuleSet(rule); + RuleSet ruleSet = RulesetsFactoryUtils.defaultFactory().createSingleRuleRuleSet(rule); List dysfunctional = new ArrayList<>(); ruleSet.removeDysfunctionalRules(dysfunctional); diff --git a/pmd-core/src/test/resources/net/sourceforge/pmd/document/ShouldPreserveNewlines.java b/pmd-core/src/test/resources/net/sourceforge/pmd/document/ShouldPreserveNewlines.java new file mode 100644 index 0000000000..265d32f5d1 --- /dev/null +++ b/pmd-core/src/test/resources/net/sourceforge/pmd/document/ShouldPreserveNewlines.java @@ -0,0 +1,8 @@ +class ShouldPreserveNewlines { + public static void main(String[] args) { + System.out.println("Test"); + } +} +// note: multiple empty lines at the end + + diff --git a/pmd-core/src/test/resources/net/sourceforge/pmd/rulesets/duplicatedRuleLoggingTest/duplicatedRuleReference.xml b/pmd-core/src/test/resources/net/sourceforge/pmd/rulesets/duplicatedRuleLoggingTest/duplicatedRuleReference.xml new file mode 100644 index 0000000000..7555a87a01 --- /dev/null +++ b/pmd-core/src/test/resources/net/sourceforge/pmd/rulesets/duplicatedRuleLoggingTest/duplicatedRuleReference.xml @@ -0,0 +1,12 @@ + + + + + My custom rules + + + + diff --git a/pmd-core/src/test/resources/net/sourceforge/pmd/rulesets/duplicatedRuleLoggingTest/duplicatedRuleReferenceWithOverride.xml b/pmd-core/src/test/resources/net/sourceforge/pmd/rulesets/duplicatedRuleLoggingTest/duplicatedRuleReferenceWithOverride.xml new file mode 100644 index 0000000000..0c2a78cb2b --- /dev/null +++ b/pmd-core/src/test/resources/net/sourceforge/pmd/rulesets/duplicatedRuleLoggingTest/duplicatedRuleReferenceWithOverride.xml @@ -0,0 +1,14 @@ + + + + + My custom rules + + + + 1 + + diff --git a/pmd-core/src/test/resources/net/sourceforge/pmd/rulesets/duplicatedRuleLoggingTest/duplicatedRuleReferenceWithOverrideBefore.xml b/pmd-core/src/test/resources/net/sourceforge/pmd/rulesets/duplicatedRuleLoggingTest/duplicatedRuleReferenceWithOverrideBefore.xml new file mode 100644 index 0000000000..c72e2fda3d --- /dev/null +++ b/pmd-core/src/test/resources/net/sourceforge/pmd/rulesets/duplicatedRuleLoggingTest/duplicatedRuleReferenceWithOverrideBefore.xml @@ -0,0 +1,14 @@ + + + + + My custom rules + + + 1 + + + diff --git a/pmd-core/src/test/resources/net/sourceforge/pmd/rulesets/duplicatedRuleLoggingTest/multipleDuplicates.xml b/pmd-core/src/test/resources/net/sourceforge/pmd/rulesets/duplicatedRuleLoggingTest/multipleDuplicates.xml new file mode 100644 index 0000000000..8e2899d6ef --- /dev/null +++ b/pmd-core/src/test/resources/net/sourceforge/pmd/rulesets/duplicatedRuleLoggingTest/multipleDuplicates.xml @@ -0,0 +1,19 @@ + + + + + My custom rules + + + + + + 1 + + + 2 + + diff --git a/pmd-cpp/pom.xml b/pmd-cpp/pom.xml index 253f4b79b3..2ca5b92458 100644 --- a/pmd-cpp/pom.xml +++ b/pmd-cpp/pom.xml @@ -34,7 +34,7 @@ - + diff --git a/pmd-doc/src/main/java/net/sourceforge/pmd/docs/GenerateRuleDocsCmd.java b/pmd-doc/src/main/java/net/sourceforge/pmd/docs/GenerateRuleDocsCmd.java index e58f1d72a7..fbb78a6898 100644 --- a/pmd-doc/src/main/java/net/sourceforge/pmd/docs/GenerateRuleDocsCmd.java +++ b/pmd-doc/src/main/java/net/sourceforge/pmd/docs/GenerateRuleDocsCmd.java @@ -22,6 +22,7 @@ import org.apache.commons.io.FilenameUtils; import net.sourceforge.pmd.RuleSet; import net.sourceforge.pmd.RuleSetFactory; import net.sourceforge.pmd.RuleSetNotFoundException; +import net.sourceforge.pmd.RulesetsFactoryUtils; public final class GenerateRuleDocsCmd { private GenerateRuleDocsCmd() { @@ -33,7 +34,7 @@ public final class GenerateRuleDocsCmd { Path output = FileSystems.getDefault().getPath(args[0]).resolve("..").toAbsolutePath().normalize(); System.out.println("Generating docs into " + output); - RuleSetFactory ruleSetFactory = new RuleSetFactory(); + RuleSetFactory ruleSetFactory = RulesetsFactoryUtils.defaultFactory(); Iterator registeredRuleSets = ruleSetFactory.getRegisteredRuleSets(); List additionalRulesets = findAdditionalRulesets(output); diff --git a/pmd-doc/src/main/java/net/sourceforge/pmd/docs/RuleDocGenerator.java b/pmd-doc/src/main/java/net/sourceforge/pmd/docs/RuleDocGenerator.java index 8b6f68f16a..4d1ffa2dee 100644 --- a/pmd-doc/src/main/java/net/sourceforge/pmd/docs/RuleDocGenerator.java +++ b/pmd-doc/src/main/java/net/sourceforge/pmd/docs/RuleDocGenerator.java @@ -38,6 +38,7 @@ import net.sourceforge.pmd.Rule; import net.sourceforge.pmd.RuleSet; import net.sourceforge.pmd.RuleSetFactory; import net.sourceforge.pmd.RuleSetNotFoundException; +import net.sourceforge.pmd.RulesetsFactoryUtils; import net.sourceforge.pmd.lang.Language; import net.sourceforge.pmd.lang.rule.RuleReference; import net.sourceforge.pmd.lang.rule.XPathRule; @@ -111,7 +112,7 @@ public class RuleDocGenerator { } List rulesets = new ArrayList<>(); - RuleSetFactory ruleSetFactory = new RuleSetFactory(); + RuleSetFactory ruleSetFactory = RulesetsFactoryUtils.defaultFactory(); for (String filename : additionalRulesets) { try { // do not take rulesets from pmd-test or pmd-core diff --git a/pmd-doc/src/test/java/net/sourceforge/pmd/docs/RuleDocGeneratorTest.java b/pmd-doc/src/test/java/net/sourceforge/pmd/docs/RuleDocGeneratorTest.java index 65ee4ef4a7..47e3d21f23 100644 --- a/pmd-doc/src/test/java/net/sourceforge/pmd/docs/RuleDocGeneratorTest.java +++ b/pmd-doc/src/test/java/net/sourceforge/pmd/docs/RuleDocGeneratorTest.java @@ -26,6 +26,7 @@ import org.junit.Test; import net.sourceforge.pmd.RuleSet; import net.sourceforge.pmd.RuleSetFactory; import net.sourceforge.pmd.RuleSetNotFoundException; +import net.sourceforge.pmd.RulesetsFactoryUtils; import net.sourceforge.pmd.docs.MockedFileWriter.FileEntry; public class RuleDocGeneratorTest { @@ -76,7 +77,7 @@ public class RuleDocGeneratorTest { public void testSingleRuleset() throws RuleSetNotFoundException, IOException { RuleDocGenerator generator = new RuleDocGenerator(writer, root); - RuleSetFactory rsf = new RuleSetFactory(); + RuleSetFactory rsf = RulesetsFactoryUtils.defaultFactory(); RuleSet ruleset = rsf.createRuleSet("rulesets/ruledoctest/sample.xml"); generator.generate(Arrays.asList(ruleset).iterator(), diff --git a/pmd-doc/src/test/java/net/sourceforge/pmd/docs/RuleSetResolverTest.java b/pmd-doc/src/test/java/net/sourceforge/pmd/docs/RuleSetResolverTest.java index f6fa28da16..c2c082c44f 100644 --- a/pmd-doc/src/test/java/net/sourceforge/pmd/docs/RuleSetResolverTest.java +++ b/pmd-doc/src/test/java/net/sourceforge/pmd/docs/RuleSetResolverTest.java @@ -17,6 +17,7 @@ import org.junit.Test; import net.sourceforge.pmd.RuleSetFactory; import net.sourceforge.pmd.RuleSetNotFoundException; +import net.sourceforge.pmd.RulesetsFactoryUtils; public class RuleSetResolverTest { @@ -30,10 +31,10 @@ public class RuleSetResolverTest { public void resolveAllRulesets() { Path basePath = FileSystems.getDefault().getPath(".").resolve("..").toAbsolutePath().normalize(); List additionalRulesets = GenerateRuleDocsCmd.findAdditionalRulesets(basePath); - + filterRuleSets(additionalRulesets); - RuleSetFactory ruleSetFactory = new RuleSetFactory(); + RuleSetFactory ruleSetFactory = RulesetsFactoryUtils.defaultFactory(); for (String filename : additionalRulesets) { try { ruleSetFactory.createRuleSet(filename); diff --git a/pmd-doc/src/test/java/net/sourceforge/pmd/docs/SidebarGeneratorTest.java b/pmd-doc/src/test/java/net/sourceforge/pmd/docs/SidebarGeneratorTest.java index 474e8683f6..ea01604b3c 100644 --- a/pmd-doc/src/test/java/net/sourceforge/pmd/docs/SidebarGeneratorTest.java +++ b/pmd-doc/src/test/java/net/sourceforge/pmd/docs/SidebarGeneratorTest.java @@ -25,7 +25,7 @@ import org.yaml.snakeyaml.DumperOptions.LineBreak; import org.yaml.snakeyaml.Yaml; import net.sourceforge.pmd.RuleSet; -import net.sourceforge.pmd.RuleSetFactory; +import net.sourceforge.pmd.RulesetsFactoryUtils; import net.sourceforge.pmd.lang.Language; import net.sourceforge.pmd.lang.LanguageRegistry; @@ -40,8 +40,8 @@ public class SidebarGeneratorTest { @Test public void testSidebar() throws IOException { Map> rulesets = new HashMap<>(); - RuleSet ruleSet1 = new RuleSetFactory().createNewRuleSet("test", "test", "bestpractices.xml", Collections.emptyList(), Collections.emptyList(), Collections.emptyList()); - RuleSet ruleSet2 = new RuleSetFactory().createNewRuleSet("test2", "test", "codestyle.xml", Collections.emptyList(), Collections.emptyList(), Collections.emptyList()); + RuleSet ruleSet1 = RulesetsFactoryUtils.defaultFactory().createNewRuleSet("test", "test", "bestpractices.xml", Collections.emptyList(), Collections.emptyList(), Collections.emptyList()); + RuleSet ruleSet2 = RulesetsFactoryUtils.defaultFactory().createNewRuleSet("test2", "test", "codestyle.xml", Collections.emptyList(), Collections.emptyList(), Collections.emptyList()); rulesets.put(LanguageRegistry.findLanguageByTerseName("java"), Arrays.asList(ruleSet1, ruleSet2)); rulesets.put(LanguageRegistry.findLanguageByTerseName("ecmascript"), Arrays.asList(ruleSet1)); diff --git a/pmd-java/pom.xml b/pmd-java/pom.xml index 956fafa8d0..52b960630a 100644 --- a/pmd-java/pom.xml +++ b/pmd-java/pom.xml @@ -62,7 +62,7 @@ - + diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTAssertStatement.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTAssertStatement.java index 73f206d203..07e237ae97 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTAssertStatement.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTAssertStatement.java @@ -37,8 +37,18 @@ public final class ASTAssertStatement extends AbstractJavaNode { /** * Returns the expression tested by this assert statement. + * + * @deprecated Use {@link #getCondition()} */ + @Deprecated public ASTExpression getGuardExpressionNode() { + return getCondition(); + } + + /** + * Returns the expression tested by this assert statement. + */ + public ASTExpression getCondition() { return (ASTExpression) jjtGetChild(0); } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTDoStatement.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTDoStatement.java index dafc5b986c..bc5fc994f3 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTDoStatement.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTDoStatement.java @@ -28,8 +28,19 @@ public final class ASTDoStatement extends AbstractJavaNode { /** * Returns the node that represents the guard of this loop. * This may be any expression of type boolean. + * + * @deprecated Use {@link #getCondition()} */ + @Deprecated public ASTExpression getGuardExpressionNode() { + return getCondition(); + } + + /** + * Returns the node that represents the guard of this loop. + * This may be any expression of type boolean. + */ + public ASTExpression getCondition() { return (ASTExpression) jjtGetChild(1); } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTFinallyStatement.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTFinallyStatement.java index b95ff39453..373d859a34 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTFinallyStatement.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTFinallyStatement.java @@ -24,4 +24,12 @@ public final class ASTFinallyStatement extends AbstractJavaNode { public void jjtAccept(SideEffectingVisitor visitor, T data) { visitor.visit(this, data); } + + + /** + * Returns the body of this finally clause. + */ + public ASTBlock getBody() { + return (ASTBlock) jjtGetChild(0); + } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTForStatement.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTForStatement.java index 6a9ecc8527..9fb14d3ae9 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTForStatement.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTForStatement.java @@ -46,8 +46,23 @@ public final class ASTForStatement extends AbstractJavaNode { * *

If this node represents a foreach loop, or if there is * no specified guard, then returns null. + * + * @deprecated Use {@link #getCondition()} */ + @Deprecated public ASTExpression getGuardExpressionNode() { + return getCondition(); + } + + + /** + * Returns the node that represents the guard of this loop. + * This may be any expression of type boolean. + * + *

If this node represents a foreach loop, or if there is + * no specified guard, then returns null. + */ + public ASTExpression getCondition() { if (isForeach()) { return null; } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTIfStatement.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTIfStatement.java index 508d9299c0..ab80eb3cc8 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTIfStatement.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTIfStatement.java @@ -45,11 +45,22 @@ public final class ASTIfStatement extends AbstractJavaNode { /** * Returns the node that represents the guard of this conditional. * This may be any expression of type boolean. + * + * @deprecated Use {@link #getCondition()} */ + @Deprecated public ASTExpression getGuardExpressionNode() { return (ASTExpression) jjtGetChild(0); } + /** + * Returns the node that represents the guard of this conditional. + * This may be any expression of type boolean. + */ + public ASTExpression getCondition() { + return (ASTExpression) jjtGetChild(0); + } + /** * Returns the statement that will be run if the guard evaluates diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTTryStatement.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTTryStatement.java index f62eef5f5b..c8b8ee6cd9 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTTryStatement.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTTryStatement.java @@ -67,19 +67,40 @@ public final class ASTTryStatement extends AbstractJavaNode { } + /** + * Returns the body of this try statement. + */ + public ASTBlock getBody() { + return (ASTBlock) jjtGetChild(1); + } + /** * Returns the catch statement nodes of this try statement. * If there are none, returns an empty list. + * + * @deprecated Use {@link #getCatchClauses()} */ + @Deprecated public List getCatchStatements() { return findChildrenOfType(ASTCatchStatement.class); } + /** + * Returns the catch clauses of this try statement. + * If there are none, returns an empty list. + */ + public List getCatchClauses() { + return findChildrenOfType(ASTCatchStatement.class); + } + /** * Returns true if this try statement has a {@code finally} statement, - * in which case {@link #getFinally()} won't return {@code null}. + * in which case {@link #getFinallyClause()} won't return {@code null}. + * + * @deprecated Check for nullity of {@link #getFinallyClause()} */ + @Deprecated public boolean hasFinally() { return getFirstChildOfType(ASTFinallyStatement.class) != null; } @@ -89,9 +110,20 @@ public final class ASTTryStatement extends AbstractJavaNode { * Returns the {@code finally} statement of this try statement, if any. * * @return The finally statement, or null if there is none + * + * @deprecated Use {@link #getFinallyClause()} */ public ASTFinallyStatement getFinally() { return getFirstChildOfType(ASTFinallyStatement.class); } + /** + * Returns the {@code finally} clause of this try statement, if any. + * + * @return The finally statement, or null if there is none + */ + public ASTFinallyStatement getFinallyClause() { + return getFirstChildOfType(ASTFinallyStatement.class); + } + } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTWhileStatement.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTWhileStatement.java index e51ee30528..fefe26c909 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTWhileStatement.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTWhileStatement.java @@ -27,11 +27,22 @@ public final class ASTWhileStatement extends AbstractJavaNode { /** * Returns the node that represents the guard of this loop. * This may be any expression of type boolean. + * + * @deprecated Use {@link #getCondition()} */ + @Deprecated public ASTExpression getGuardExpressionNode() { return (ASTExpression) jjtGetChild(0); } + /** + * Returns the node that represents the guard of this loop. + * This may be any expression of type boolean. + */ + public ASTExpression getCondition() { + return (ASTExpression) jjtGetChild(0); + } + /** * Returns the statement that will be run while the guard diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTYieldStatement.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTYieldStatement.java index fca637f634..25ee1aa425 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTYieldStatement.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTYieldStatement.java @@ -4,6 +4,8 @@ package net.sourceforge.pmd.lang.java.ast; +import net.sourceforge.pmd.lang.java.typeresolution.typedefinition.JavaTypeDefinition; + public class ASTYieldStatement extends AbstractJavaTypeNode { ASTYieldStatement(int id) { @@ -34,4 +36,30 @@ public class ASTYieldStatement extends AbstractJavaTypeNode { } return result; } + + + /** Returns the yielded expression. */ + public ASTExpression getExpr() { + return (ASTExpression) jjtGetChild(0); + } + + + /** + * @deprecated Use the type of the expression yielded by {@link #getExpr()} + */ + @Deprecated + @Override + public Class getType() { + return super.getType(); + } + + /** + * @deprecated Use the type of the expression yielded by {@link #getExpr()} + */ + @Deprecated + @Override + public JavaTypeDefinition getTypeDefinition() { + return super.getTypeDefinition(); + } + } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/metrics/internal/visitors/CycloVisitor.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/metrics/internal/visitors/CycloVisitor.java index edee8381f3..dd3fdf84e2 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/metrics/internal/visitors/CycloVisitor.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/metrics/internal/visitors/CycloVisitor.java @@ -90,7 +90,7 @@ public class CycloVisitor extends JavaParserVisitorAdapter { public Object visit(ASTWhileStatement node, Object data) { ((MutableInt) data).increment(); if (considerBooleanPaths) { - ((MutableInt) data).add(CycloMetric.booleanExpressionComplexity(node.getGuardExpressionNode())); + ((MutableInt) data).add(CycloMetric.booleanExpressionComplexity(node.getCondition())); } return super.visit(node, data); } @@ -100,7 +100,7 @@ public class CycloVisitor extends JavaParserVisitorAdapter { public Object visit(ASTIfStatement node, Object data) { ((MutableInt) data).increment(); if (considerBooleanPaths) { - ((MutableInt) data).add(CycloMetric.booleanExpressionComplexity(node.getGuardExpressionNode())); + ((MutableInt) data).add(CycloMetric.booleanExpressionComplexity(node.getCondition())); } return super.visit(node, data); @@ -112,7 +112,7 @@ public class CycloVisitor extends JavaParserVisitorAdapter { ((MutableInt) data).increment(); if (considerBooleanPaths && !node.isForeach()) { - ((MutableInt) data).add(CycloMetric.booleanExpressionComplexity(node.getGuardExpressionNode())); + ((MutableInt) data).add(CycloMetric.booleanExpressionComplexity(node.getCondition())); } return super.visit(node, data); @@ -123,7 +123,7 @@ public class CycloVisitor extends JavaParserVisitorAdapter { public Object visit(ASTDoStatement node, Object data) { ((MutableInt) data).increment(); if (considerBooleanPaths) { - ((MutableInt) data).add(CycloMetric.booleanExpressionComplexity(node.getGuardExpressionNode())); + ((MutableInt) data).add(CycloMetric.booleanExpressionComplexity(node.getCondition())); } return super.visit(node, data); @@ -150,7 +150,7 @@ public class CycloVisitor extends JavaParserVisitorAdapter { ((MutableInt) data).add(2); // equivalent to if (condition) { throw .. } if (considerBooleanPaths) { - ((MutableInt) data).add(CycloMetric.booleanExpressionComplexity(node.getGuardExpressionNode())); + ((MutableInt) data).add(CycloMetric.booleanExpressionComplexity(node.getCondition())); } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/AvoidUsingHardCodedIPRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/AvoidUsingHardCodedIPRule.java index 364162fecf..998651feef 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/AvoidUsingHardCodedIPRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/AvoidUsingHardCodedIPRule.java @@ -104,11 +104,11 @@ public class AvoidUsingHardCodedIPRule extends AbstractJavaRule { } protected boolean isLatinDigit(char c) { - return '0' <= c || c <= '9'; + return '0' <= c && c <= '9'; } protected boolean isHexCharacter(char c) { - return isLatinDigit(c) || 'A' <= c || c <= 'F' || 'a' <= c || c <= 'f'; + return isLatinDigit(c) || 'A' <= c && c <= 'F' || 'a' <= c && c <= 'f'; } protected boolean isIPv4(final char firstChar, final String s) { @@ -190,7 +190,7 @@ public class AvoidUsingHardCodedIPRule extends AbstractJavaRule { } } catch (NumberFormatException e) { // The last part can be a standard IPv4 address. - if (i != parts.length - 1 || !isIPv4(firstChar, part)) { + if (i != parts.length - 1 || !isIPv4(part.charAt(0), part)) { return false; } ipv4Mapped = true; diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/IdenticalCatchBranchesRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/IdenticalCatchBranchesRule.java index ba10b3f3c7..9701dbfd37 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/IdenticalCatchBranchesRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/IdenticalCatchBranchesRule.java @@ -88,7 +88,7 @@ public class IdenticalCatchBranchesRule extends AbstractJavaRule { @Override public Object visit(ASTTryStatement node, Object data) { - List catchStatements = node.getCatchStatements(); + List catchStatements = node.getCatchClauses(); Set> equivClasses = equivalenceClasses(catchStatements); for (List identicalStmts : equivClasses) { diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/documentation/AbstractCommentRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/documentation/AbstractCommentRule.java index f18f27e273..afa10c1d35 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/documentation/AbstractCommentRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/documentation/AbstractCommentRule.java @@ -19,11 +19,13 @@ import net.sourceforge.pmd.lang.java.ast.ASTConstructorDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTEnumDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTFieldDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTPackageDeclaration; import net.sourceforge.pmd.lang.java.ast.AccessNode; import net.sourceforge.pmd.lang.java.ast.Comment; import net.sourceforge.pmd.lang.java.ast.CommentUtil; import net.sourceforge.pmd.lang.java.ast.FormalComment; import net.sourceforge.pmd.lang.java.ast.InternalApiBridge; +import net.sourceforge.pmd.lang.java.ast.JavaNode; import net.sourceforge.pmd.lang.java.ast.JavadocElement; import net.sourceforge.pmd.lang.java.ast.TypeNode; import net.sourceforge.pmd.lang.java.javadoc.JavadocTag; @@ -66,17 +68,16 @@ public abstract class AbstractCommentRule extends AbstractJavaRule { SortedMap itemsByLineNumber = orderedCommentsAndDeclarations(cUnit); FormalComment lastComment = null; - AccessNode lastNode = null; + JavaNode lastNode = null; for (Entry entry : itemsByLineNumber.entrySet()) { Node value = entry.getValue(); - - if (value instanceof AccessNode) { - AccessNode node = (AccessNode) value; + if (value instanceof AccessNode || value instanceof ASTPackageDeclaration) { + JavaNode node = (JavaNode) value; // maybe the last comment is within the last node - if (lastComment != null && isCommentNotWithin(lastComment, lastNode, node) - && isCommentBefore(lastComment, node)) { + if (lastComment != null && isCommentNotWithin(lastComment, lastNode, value) + && isCommentBefore(lastComment, value)) { InternalApiBridge.setComment(node, lastComment); lastComment = null; } @@ -110,6 +111,8 @@ public abstract class AbstractCommentRule extends AbstractJavaRule { protected SortedMap orderedCommentsAndDeclarations(ASTCompilationUnit cUnit) { SortedMap itemsByLineNumber = new TreeMap<>(); + addDeclarations(itemsByLineNumber, cUnit.findDescendantsOfType(ASTPackageDeclaration.class, true)); + addDeclarations(itemsByLineNumber, cUnit.findDescendantsOfType(ASTClassOrInterfaceDeclaration.class, true)); addDeclarations(itemsByLineNumber, cUnit.getComments()); diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/documentation/CommentRequiredRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/documentation/CommentRequiredRule.java index dabeab5589..0aa317d2c8 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/documentation/CommentRequiredRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/documentation/CommentRequiredRule.java @@ -11,7 +11,9 @@ import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.logging.Logger; +import net.sourceforge.pmd.RuleContext; import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit; import net.sourceforge.pmd.lang.java.ast.ASTConstructorDeclaration; @@ -32,11 +34,11 @@ import net.sourceforge.pmd.properties.PropertyFactory; * @author Brian Remedios */ public class CommentRequiredRule extends AbstractCommentRule { + private static final Logger LOG = Logger.getLogger(CommentRequiredRule.class.getName()); // Used to pretty print a message private static final Map DESCRIPTOR_NAME_TO_COMMENT_TYPE = new HashMap<>(); - private static final PropertyDescriptor ACCESSOR_CMT_DESCRIPTOR = requirementPropertyBuilder("accessorCommentRequirement", "Comments on getters and setters\"") .defaultValue(CommentRequirement.Ignored).build(); @@ -44,7 +46,9 @@ public class CommentRequiredRule extends AbstractCommentRule { = requirementPropertyBuilder("methodWithOverrideCommentRequirement", "Comments on @Override methods") .defaultValue(CommentRequirement.Ignored).build(); private static final PropertyDescriptor HEADER_CMT_REQUIREMENT_DESCRIPTOR - = requirementPropertyBuilder("headerCommentRequirement", "Header comments").build(); + = requirementPropertyBuilder("headerCommentRequirement", "Deprecated! Header comments. Please use the property \"classCommentRequired\" instead.").build(); + private static final PropertyDescriptor CLASS_CMT_REQUIREMENT_DESCRIPTOR + = requirementPropertyBuilder("classCommentRequirement", "Class comments").build(); private static final PropertyDescriptor FIELD_CMT_REQUIREMENT_DESCRIPTOR = requirementPropertyBuilder("fieldCommentRequirement", "Field comments").build(); private static final PropertyDescriptor PUB_METHOD_CMT_REQUIREMENT_DESCRIPTOR @@ -60,10 +64,13 @@ public class CommentRequiredRule extends AbstractCommentRule { = requirementPropertyBuilder("serialPersistentFieldsCommentRequired", "Serial persistent fields comments") .defaultValue(CommentRequirement.Ignored).build(); + /** stores the resolved property values. This is necessary in order to transparently use deprecated properties. */ + private final Map, CommentRequirement> propertyValues = new HashMap<>(); public CommentRequiredRule() { definePropertyDescriptor(OVERRIDE_CMT_DESCRIPTOR); definePropertyDescriptor(ACCESSOR_CMT_DESCRIPTOR); + definePropertyDescriptor(CLASS_CMT_REQUIREMENT_DESCRIPTOR); definePropertyDescriptor(HEADER_CMT_REQUIREMENT_DESCRIPTOR); definePropertyDescriptor(FIELD_CMT_REQUIREMENT_DESCRIPTOR); definePropertyDescriptor(PUB_METHOD_CMT_REQUIREMENT_DESCRIPTOR); @@ -73,10 +80,37 @@ public class CommentRequiredRule extends AbstractCommentRule { definePropertyDescriptor(SERIAL_PERSISTENT_FIELDS_CMT_REQUIREMENT_DESCRIPTOR); } + @Override + public void start(RuleContext ctx) { + propertyValues.put(ACCESSOR_CMT_DESCRIPTOR, getProperty(ACCESSOR_CMT_DESCRIPTOR)); + propertyValues.put(OVERRIDE_CMT_DESCRIPTOR, getProperty(OVERRIDE_CMT_DESCRIPTOR)); + propertyValues.put(FIELD_CMT_REQUIREMENT_DESCRIPTOR, getProperty(FIELD_CMT_REQUIREMENT_DESCRIPTOR)); + propertyValues.put(PUB_METHOD_CMT_REQUIREMENT_DESCRIPTOR, getProperty(PUB_METHOD_CMT_REQUIREMENT_DESCRIPTOR)); + propertyValues.put(PROT_METHOD_CMT_REQUIREMENT_DESCRIPTOR, getProperty(PROT_METHOD_CMT_REQUIREMENT_DESCRIPTOR)); + propertyValues.put(ENUM_CMT_REQUIREMENT_DESCRIPTOR, getProperty(ENUM_CMT_REQUIREMENT_DESCRIPTOR)); + propertyValues.put(SERIAL_VERSION_UID_CMT_REQUIREMENT_DESCRIPTOR, + getProperty(SERIAL_VERSION_UID_CMT_REQUIREMENT_DESCRIPTOR)); + propertyValues.put(SERIAL_PERSISTENT_FIELDS_CMT_REQUIREMENT_DESCRIPTOR, + getProperty(SERIAL_PERSISTENT_FIELDS_CMT_REQUIREMENT_DESCRIPTOR)); + + CommentRequirement headerCommentRequirementValue = getProperty(HEADER_CMT_REQUIREMENT_DESCRIPTOR); + boolean headerCommentRequirementValueOverridden = headerCommentRequirementValue != CommentRequirement.Required; + CommentRequirement classCommentRequirementValue = getProperty(CLASS_CMT_REQUIREMENT_DESCRIPTOR); + boolean classCommentRequirementValueOverridden = classCommentRequirementValue != CommentRequirement.Required; + + if (headerCommentRequirementValueOverridden && !classCommentRequirementValueOverridden) { + LOG.warning("Rule CommentRequired uses deprecated property 'headerCommentRequirement'. " + + "Future versions of PMD will remove support for this property. " + + "Please use 'classCommentRequirement' instead!"); + propertyValues.put(CLASS_CMT_REQUIREMENT_DESCRIPTOR, headerCommentRequirementValue); + } else { + propertyValues.put(CLASS_CMT_REQUIREMENT_DESCRIPTOR, classCommentRequirementValue); + } + } private void checkCommentMeetsRequirement(Object data, JavaNode node, PropertyDescriptor descriptor) { - switch (getProperty(descriptor)) { + switch (propertyValues.get(descriptor)) { case Ignored: break; case Required: @@ -109,7 +143,7 @@ public class CommentRequiredRule extends AbstractCommentRule { @Override public Object visit(ASTClassOrInterfaceDeclaration decl, Object data) { - checkCommentMeetsRequirement(data, decl, HEADER_CMT_REQUIREMENT_DESCRIPTOR); + checkCommentMeetsRequirement(data, decl, CLASS_CMT_REQUIREMENT_DESCRIPTOR); return super.visit(decl, data); } @@ -181,9 +215,9 @@ public class CommentRequiredRule extends AbstractCommentRule { * This field must be initialized with an array of ObjectStreamField objects. * The modifiers for the field are required to be private, static, and final. * - * @see Oracle docs * @param field the field, must not be null - * @return true if the field ia a serialPersistentFields variable, otherwise false + * @return true if the field is a serialPersistentFields variable, otherwise false + * @see Oracle docs */ private boolean isSerialPersistentFields(final ASTFieldDeclaration field) { return "serialPersistentFields".equals(field.getVariableName()) @@ -200,7 +234,6 @@ public class CommentRequiredRule extends AbstractCommentRule { return super.visit(decl, data); } - @Override public Object visit(ASTCompilationUnit cUnit, Object data) { assignCommentsToDeclarations(cUnit); @@ -211,7 +244,8 @@ public class CommentRequiredRule extends AbstractCommentRule { return getProperty(OVERRIDE_CMT_DESCRIPTOR) == CommentRequirement.Ignored && getProperty(ACCESSOR_CMT_DESCRIPTOR) == CommentRequirement.Ignored - && getProperty(HEADER_CMT_REQUIREMENT_DESCRIPTOR) == CommentRequirement.Ignored + && (getProperty(CLASS_CMT_REQUIREMENT_DESCRIPTOR) == CommentRequirement.Ignored + || getProperty(HEADER_CMT_REQUIREMENT_DESCRIPTOR) == CommentRequirement.Ignored) && getProperty(FIELD_CMT_REQUIREMENT_DESCRIPTOR) == CommentRequirement.Ignored && getProperty(PUB_METHOD_CMT_REQUIREMENT_DESCRIPTOR) == CommentRequirement.Ignored && getProperty(PROT_METHOD_CMT_REQUIREMENT_DESCRIPTOR) == CommentRequirement.Ignored diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/CloseResourceRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/CloseResourceRule.java index b77de0d316..84adc4f279 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/CloseResourceRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/CloseResourceRule.java @@ -26,6 +26,7 @@ import net.sourceforge.pmd.lang.java.ast.ASTBlockStatement; import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceType; import net.sourceforge.pmd.lang.java.ast.ASTConstructorDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTExpression; +import net.sourceforge.pmd.lang.java.ast.ASTFinallyStatement; import net.sourceforge.pmd.lang.java.ast.ASTFormalParameters; import net.sourceforge.pmd.lang.java.ast.ASTIfStatement; import net.sourceforge.pmd.lang.java.ast.ASTLocalVariableDeclaration; @@ -362,9 +363,10 @@ public class CloseResourceRule extends AbstractJavaRule { } } - if (t.getBeginLine() > id.getBeginLine() && t.hasFinally()) { - ASTBlock f = (ASTBlock) t.getFinally().jjtGetChild(0); - List names = f.findDescendantsOfType(ASTName.class); + ASTFinallyStatement finallyClause = t.getFinallyClause(); + if (t.getBeginLine() > id.getBeginLine() && finallyClause != null) { + ASTBlock finallyBody = finallyClause.getBody(); + List names = finallyBody.findDescendantsOfType(ASTName.class); for (ASTName oName : names) { String name = oName.getImage(); if (name != null && name.contains(".")) { @@ -373,7 +375,7 @@ public class CloseResourceRule extends AbstractJavaRule { String methodName = parts[1]; String varName = parts[0]; if (varName.equals(variableToClose) && closeTargets.contains(methodName) - && nullCheckIfCondition(f, oName, varName)) { + && nullCheckIfCondition(finallyBody, oName, varName)) { closed = true; break; } @@ -386,7 +388,7 @@ public class CloseResourceRule extends AbstractJavaRule { } List exprs = new ArrayList<>(); - f.findDescendantsOfType(ASTStatementExpression.class, exprs, true); + finallyBody.findDescendantsOfType(ASTStatementExpression.class, exprs, true); for (ASTStatementExpression stmt : exprs) { ASTPrimaryExpression expr = stmt.getFirstChildOfType(ASTPrimaryExpression.class); if (expr != null) { diff --git a/pmd-java/src/main/resources/category/java/documentation.xml b/pmd-java/src/main/resources/category/java/documentation.xml index 44ca738003..db378336b3 100644 --- a/pmd-java/src/main/resources/category/java/documentation.xml +++ b/pmd-java/src/main/resources/category/java/documentation.xml @@ -31,7 +31,7 @@ A rule for the politically correct... we don't want to offend anyone. class="net.sourceforge.pmd.lang.java.rule.documentation.CommentRequiredRule" externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_documentation.html#commentrequired"> -Denotes whether comments are required (or unwanted) for specific language elements. +Denotes whether javadoc (formal) comments are required (or unwanted) for specific language elements. 3 diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/ExcludeLinesTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/ExcludeLinesTest.java index 4042da9c20..5704ef0951 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/ExcludeLinesTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/ExcludeLinesTest.java @@ -41,7 +41,7 @@ public class ExcludeLinesTest extends RuleTst { ctx.setReport(r); ctx.setSourceCodeFile(new File("n/a")); ctx.setLanguageVersion(LanguageRegistry.getLanguage(JavaLanguageModule.NAME).getDefaultVersion()); - RuleSet rules = new RuleSetFactory().createSingleRuleRuleSet(rule); + RuleSet rules = RulesetsFactoryUtils.defaultFactory().createSingleRuleRuleSet(rule); p.getSourceCodeProcessor().processSourceCode(new StringReader(TEST3), new RuleSets(rules), ctx); assertTrue(r.isEmpty()); assertEquals(r.getSuppressedRuleViolations().size(), 1); diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/RuleSetFactoryTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/RuleSetFactoryTest.java index cd0d8e16f2..c3ba3904f0 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/RuleSetFactoryTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/RuleSetFactoryTest.java @@ -23,7 +23,7 @@ public class RuleSetFactoryTest extends AbstractRuleSetFactoryTest { + " Custom ruleset for tests\n" + " \n" + " \n" + " \n" + "\n"); - RuleSetFactory ruleSetFactory = new RuleSetFactory(); + RuleSetFactory ruleSetFactory = RulesetsFactoryUtils.defaultFactory(); RuleSet ruleset = ruleSetFactory.createRuleSet(ref); Rule rule = ruleset.getRuleByName("UselessParentheses"); assertNull(rule); diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/metrics/xpath/XPathMetricFunctionTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/metrics/xpath/XPathMetricFunctionTest.java index d64d57fde7..48d4c672ec 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/metrics/xpath/XPathMetricFunctionTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/metrics/xpath/XPathMetricFunctionTest.java @@ -19,9 +19,9 @@ import net.sourceforge.pmd.Report; import net.sourceforge.pmd.Rule; import net.sourceforge.pmd.RuleContext; import net.sourceforge.pmd.RuleSet; -import net.sourceforge.pmd.RuleSetFactory; import net.sourceforge.pmd.RuleSets; import net.sourceforge.pmd.RuleViolation; +import net.sourceforge.pmd.RulesetsFactoryUtils; import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.lang.java.JavaLanguageModule; import net.sourceforge.pmd.lang.java.xpath.MetricFunction; @@ -55,7 +55,7 @@ public class XPathMetricFunctionTest { ctx.setReport(report); ctx.setSourceCodeFile(new File("n/a")); ctx.setIgnoreExceptions(false); // for test, we want immediate exceptions thrown and not collect them - RuleSet rules = new RuleSetFactory().createSingleRuleRuleSet(rule); + RuleSet rules = RulesetsFactoryUtils.defaultFactory().createSingleRuleRuleSet(rule); p.getSourceCodeProcessor().processSourceCode(new StringReader(code), new RuleSets(rules), ctx); return report.iterator(); } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/XPathRuleTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/XPathRuleTest.java index 61ed6bd219..535d09d555 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/XPathRuleTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/XPathRuleTest.java @@ -22,9 +22,9 @@ import net.sourceforge.pmd.Report; import net.sourceforge.pmd.Rule; import net.sourceforge.pmd.RuleContext; import net.sourceforge.pmd.RuleSet; -import net.sourceforge.pmd.RuleSetFactory; import net.sourceforge.pmd.RuleSets; import net.sourceforge.pmd.RuleViolation; +import net.sourceforge.pmd.RulesetsFactoryUtils; import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.lang.LanguageVersion; import net.sourceforge.pmd.lang.Parser; @@ -213,7 +213,7 @@ public class XPathRuleTest extends RuleTst { Report report = new Report(); ctx.setReport(report); ctx.setSourceCodeFile(new File("n/a")); - RuleSet rules = new RuleSetFactory().createSingleRuleRuleSet(r); + RuleSet rules = RulesetsFactoryUtils.defaultFactory().createSingleRuleRuleSet(r); p.getSourceCodeProcessor().processSourceCode(new StringReader(test), new RuleSets(rules), ctx); return report; } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/documentation/CommentRequiredTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/documentation/CommentRequiredTest.java index 6b31f2edfe..7b6d9a6eca 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/documentation/CommentRequiredTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/documentation/CommentRequiredTest.java @@ -8,6 +8,7 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import java.util.ArrayList; +import java.util.Iterator; import java.util.List; import org.junit.Test; @@ -23,6 +24,14 @@ public class CommentRequiredTest extends PmdRuleTst { assertNull("By default, the rule should be functional", rule.dysfunctionReason()); List> propertyDescriptors = getProperties(rule); + // remove deprecated properties + for (Iterator> it = propertyDescriptors.iterator(); it.hasNext();) { + PropertyDescriptor property = it.next(); + if (property.description().startsWith("Deprecated!")) { + it.remove(); + } + } + for (PropertyDescriptor property : propertyDescriptors) { setPropertyValue(rule, property, "Ignored"); } diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/documentation/xml/CommentRequired.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/documentation/xml/CommentRequired.xml index cab2f7cdc9..3c372bdd0a 100755 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/documentation/xml/CommentRequired.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/documentation/xml/CommentRequired.xml @@ -50,7 +50,7 @@ public class Foo { Missing comments - only class level required. - Required + Required Ignored Ignored Ignored @@ -61,7 +61,7 @@ public class Foo { Too many comments - all comments are unwanted. - Unwanted + Unwanted Unwanted Unwanted Unwanted @@ -505,5 +505,23 @@ public class CommentRequired { ]]> + + #1683 [java] CommentRequired property names are inconsistent - use deprecated property + Unwanted + 1 + + + + #1683 [java] CommentRequired property names are inconsistent - use new property + Unwanted + 1 + + diff --git a/pmd-javascript/pom.xml b/pmd-javascript/pom.xml index 11fb5fd85e..7d7011d3da 100644 --- a/pmd-javascript/pom.xml +++ b/pmd-javascript/pom.xml @@ -39,7 +39,7 @@ - + diff --git a/pmd-jsp/pom.xml b/pmd-jsp/pom.xml index ecb2101690..d1499f1c77 100644 --- a/pmd-jsp/pom.xml +++ b/pmd-jsp/pom.xml @@ -40,7 +40,7 @@ - + diff --git a/pmd-jsp/src/test/java/net/sourceforge/pmd/lang/jsp/ast/XPathJspRuleTest.java b/pmd-jsp/src/test/java/net/sourceforge/pmd/lang/jsp/ast/XPathJspRuleTest.java index 11f3bc77e9..3597fa8496 100644 --- a/pmd-jsp/src/test/java/net/sourceforge/pmd/lang/jsp/ast/XPathJspRuleTest.java +++ b/pmd-jsp/src/test/java/net/sourceforge/pmd/lang/jsp/ast/XPathJspRuleTest.java @@ -16,9 +16,9 @@ import net.sourceforge.pmd.Report; import net.sourceforge.pmd.Rule; import net.sourceforge.pmd.RuleContext; import net.sourceforge.pmd.RuleSet; -import net.sourceforge.pmd.RuleSetFactory; import net.sourceforge.pmd.RuleSets; import net.sourceforge.pmd.RuleViolation; +import net.sourceforge.pmd.RulesetsFactoryUtils; import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.lang.jsp.JspLanguageModule; import net.sourceforge.pmd.lang.rule.XPathRule; @@ -28,14 +28,14 @@ public class XPathJspRuleTest extends RuleTst { /** * Test matching a XPath expression against a JSP source. - * @throws PMDException + * @throws PMDException */ @Test public void testExpressionMatching() throws PMDException { Rule rule = new XPathRule(XPATH_EXPRESSION); rule.setMessage("Test"); rule.setLanguage(LanguageRegistry.getLanguage(JspLanguageModule.NAME)); - RuleSet rules = new RuleSetFactory().createSingleRuleRuleSet(rule); + RuleSet rules = RulesetsFactoryUtils.defaultFactory().createSingleRuleRuleSet(rule); RuleContext ctx = new RuleContext(); Report report = new Report(); diff --git a/pmd-matlab/pom.xml b/pmd-matlab/pom.xml index 73b9d0f7eb..d49731c5b8 100644 --- a/pmd-matlab/pom.xml +++ b/pmd-matlab/pom.xml @@ -34,7 +34,7 @@ - + diff --git a/pmd-objectivec/pom.xml b/pmd-objectivec/pom.xml index e43873e5dd..70732c4667 100644 --- a/pmd-objectivec/pom.xml +++ b/pmd-objectivec/pom.xml @@ -34,7 +34,7 @@ - + diff --git a/pmd-plsql/etc/grammar/PldocAST.jjt b/pmd-plsql/etc/grammar/PldocAST.jjt index f01524dc45..da37ca0f3d 100644 --- a/pmd-plsql/etc/grammar/PldocAST.jjt +++ b/pmd-plsql/etc/grammar/PldocAST.jjt @@ -227,6 +227,9 @@ public class PLSQLParser { /** * Semantic lookahead to check if the next identifier is a * specific keyword. + * + *

+ * Usage: LOOKAHEAD({isKeyword("WAIT")}) KEYWORD("WAIT") */ private boolean isKeyword(String keyword) { return getToken(1).kind == IDENTIFIER && getToken(1).image.equalsIgnoreCase(keyword); @@ -259,13 +262,13 @@ ASTInput Input(String sourcecode) : {} | LOOKAHEAD(6) Directory() | LOOKAHEAD(6) DatabaseLink() | LOOKAHEAD(6) Global() - | LOOKAHEAD(6) DDLCommand() //Ignore any other DDL Event + | (LOOKAHEAD(6) DDLCommand())+ | LOOKAHEAD(2) SqlPlusCommand() | UpdateStatement() | DeleteStatement() | InsertStatement() - | SelectStatement() (";")? - |(|||||) SkipPastNextTokenOccurrence(SQLPLUS_TERMINATOR) //Ignore SQL statements in scripts + | SelectStatement() [";"] + |(|||
||) ReadPastNextOccurrence(";") //Ignore SQL statements in scripts ) ("/")* )* @@ -279,8 +282,15 @@ ASTDDLCommand DDLCommand() : } { ( - simpleNode = DDLEvent() - SkipPastNextTokenOccurrence(SQLPLUS_TERMINATOR) + ( + LOOKAHEAD({isKeyword("COMMENT")}) + simpleNode = Comment() + ) + | + ( + simpleNode = DDLEvent() + ReadPastNextOccurrence(";") + ) ) { jjtThis.setImage(simpleNode.getImage()) ; return jjtThis ; } } @@ -315,7 +325,7 @@ ASTSqlPlusCommand SqlPlusCommand() : | | // DDL that might be encountered - | + | LOOKAHEAD({isKeyword("COMMENT")}) KEYWORD("COMMENT") | | | @@ -1121,6 +1131,7 @@ ASTRead2NextOccurrence Read2NextOccurrence(String target) : { nextToken = getNextToken(); sb.append(nextToken.image); + sb.append(' '); nextToken = getToken(1); } } @@ -1133,10 +1144,11 @@ ASTRead2NextOccurrence Read2NextOccurrence(String target) : */ ASTReadPastNextOccurrence ReadPastNextOccurrence(String target) : { + ASTRead2NextOccurrence skipped = Read2NextOccurrence(target); + StringBuilder sb = new StringBuilder(); - Token t = null; - sb.append(Read2NextOccurrence(target)) ; - t = getNextToken(); // Chomp this one + sb.append(skipped.getImage()) ; + Token t = getNextToken(); // Chomp this one sb.append(t.image); } { @@ -3425,54 +3437,10 @@ ASTLiteral Literal() : } ASTStringLiteral StringLiteral() : +{} { - Token thisToken = null; - StringBuilder literal = new StringBuilder() ; - char startDelimiter ; - char endDelimiter ; - String terminator = null; -} -{ - //Essentially unchanged - ( - thisToken = - { - literal.append(thisToken.image); - /* - This might be Q-Quoted string and this might be only a partial string - The token will only match up to the first single quote. - The code below appends any remaining part, theh returns the complete string - */ - if (thisToken.image.toUpperCase().startsWith("Q'") - && thisToken.image.length() > 2 - ) - { - // Get the first token of the string so that the delimiter can be identified - - startDelimiter= thisToken.image.charAt(2) ; - /* - if the start delimiter is one of [, {, <, or (, the end delimiter - is the corresponding closing character - */ - switch (startDelimiter) - { - case '<' : endDelimiter = '>' ; break ; - case '{' : endDelimiter = '}' ; break ; - case '(' : endDelimiter = ')' ; break ; - case '[' : endDelimiter = ']' ; break ; - default: endDelimiter = startDelimiter ; - } - - terminator = new String(endDelimiter + "'"); - if (!thisToken.image.endsWith(terminator)) - { - //Loop until we find atoken that ends with a single-quote precede by the terminator - literal.append(ReadPastNextOccurrence(terminator)); - } - } - } - ) - { jjtThis.setImage(literal.toString()) ; jjtThis.value = literal.toString() ; return jjtThis ; } + + { jjtThis.setImage(token.image); return jjtThis ; } } @@ -3904,19 +3872,22 @@ ASTViewColumn ViewColumn() : { return jjtThis ; } } +/** + * https://docs.oracle.com/en/database/oracle/oracle-database/18/sqlrf/COMMENT.html#GUID-65F447C4-6914-4823-9691-F15D52DB74D7 + */ ASTComment Comment() : { } { - ( + LOOKAHEAD({isKeyword("COMMENT")}) KEYWORD("COMMENT") { jjtThis.setImage(token.image); } + ( ((
| | ) [LOOKAHEAD(2) ID()"."] ID()) | ( [LOOKAHEAD(ID()"."ID()"."ID()) ID()"."] ID() "." ID()) ) - - [";"] - { return jjtThis ; } + StringLiteral() + ";" + { return jjtThis ; } } -// SRT * / @@ -4489,23 +4460,26 @@ ASTNonDMLTrigger NonDMLTrigger() : { return jjtThis ; } } - +/** + * https://docs.oracle.com/en/database/oracle/oracle-database/18/sqlrf/Types-of-SQL-Statements.html#GUID-FD9A8CB4-6B9A-44E5-B114-EFB8DA76FC88 + */ ASTDDLEvent DDLEvent(): {} { ( | | | - | + | LOOKAHEAD({isKeyword("COMMENT")}) KEYWORD("COMMENT") | | | + // | | | + // | | | | - | ) { jjtThis.setImage(token.toString()) ; jjtThis.value = token ; return jjtThis ; } } @@ -4741,7 +4715,6 @@ TOKEN [IGNORE_CASE]: | | | - | | | | @@ -4997,7 +4970,6 @@ TOKEN [IGNORE_CASE]: | //11G Trigger Syntax | //11G Trigger Syntax | //11G Trigger Syntax -| //11G Trigger Syntax | //11G Trigger Syntax | //11G Trigger Syntax | //11G Trigger Syntax @@ -5109,26 +5081,9 @@ TOKEN : | < #INTEGER_LITERAL: ( )+ > | - -< #_WHATEVER_CHARACTER_WO_ASTERISK: (~["'"]) > +< #_WHATEVER_CHARACTER_WO_APOSTROPHE: (~["'"]) > | -< CHARACTER_LITERAL: "'" (<_WHATEVER_CHARACTER_WO_ASTERISK> | )? "'" > -//|< STRING_LITERAL: -// (["q","Q"])* "'" (<_WHATEVER_CHARACTER_WO_ASTERISK> | | "''")* "'" -//> //Cope with Q-quoted stringswithout single quotes in them -|< STRING_LITERAL: -// Hard-code the most likely q-quote strings - "'" (<_WHATEVER_CHARACTER_WO_ASTERISK> | | "''")* "'" -|(["n","N"]) "'" (<_WHATEVER_CHARACTER_WO_ASTERISK> | | "''")* "'" //National Character Set String -|(["q","Q"]) "'" (<_WHATEVER_CHARACTER_WO_ASTERISK> | | "''")* "'" // Bug 160632 -|(["q","Q"]) "'[" (~["[","]"])* "]'" -|(["q","Q"]) "'{" (~["{","}"])* "}'" -|(["q","Q"]) "'<" (~["<",">"])* ">'" -|(["q","Q"]) "'(" (~["(",")"])* ")'" -|(["q","Q"]) "'/" (~["/"])* "/'" -|(["q","Q"]) "'!" (~["!"])* "!'" -|(["q","Q"]) "'#" (~["#"])* "#'" -> //Cope with Q-quoted stringswithout single quotes in them +< CHARACTER_LITERAL: "'" (<_WHATEVER_CHARACTER_WO_APOSTROPHE> | )? "'" > | < #_WHATEVER_CHARACTER_WO_QUOTE: (~["\""]) > | @@ -5143,10 +5098,43 @@ TOKEN : < JAVA_INTERFACE_CLASS: ( "SQLData" | "CustomDatum" | "OraData" ) > //| //< #BOOLEAN_LITERAL: "TRUE" | "FALSE" > -| - } +/** + * https://docs.oracle.com/en/database/oracle/oracle-database/18/sqlrf/Literals.html#GUID-1824CBAA-6E16-4921-B2A6-112FB02248DA + */ + MORE : +{ + < #_ALTERNATIVE_QUOTING_STRING_LITERAL: + (["q","Q"]) "'[" (~["]"] | "]" ~["'"] )* "]" + | (["q","Q"]) "'{" (~["}"] | "}" ~["'"] )* "}" + | (["q","Q"]) "'<" (~[">"] | ">" ~["'"] )* ">" + | (["q","Q"]) "'(" (~[")"] | ")" ~["'"] )* ")" + > + | <(["n","N"])? "'" (<_WHATEVER_CHARACTER_WO_APOSTROPHE> | | "''")*> : IN_STRING_LITERAL_TOKENIZE + | <(["n","N"])? <_ALTERNATIVE_QUOTING_STRING_LITERAL>> : IN_STRING_LITERAL_TOKENIZE + + // special handling for custom quote delimiters + | <(["n","N"])? (["q","Q"]) "'" (~[" ", "\t", "\r", "\n", "[", "{", "<", "("])> : IN_STRING_LITERAL +} + MORE : { + <~["'"]> + | <"'"> { + int quoteDelimiter = image.charAt(2); + if (image.charAt(0) == 'n' || image.charAt(0) == 'N') { + quoteDelimiter = image.charAt(3); + } + int beforeQuote = image.charAt(image.length() - 2); + if (quoteDelimiter == beforeQuote) { + input_stream.backup(1); + image.setLength(image.length() - 1); + SwitchTo(IN_STRING_LITERAL_TOKENIZE); + } + } +} + TOKEN : { : DEFAULT } + + /** * PL/SQL Reserved words * @@ -5386,7 +5374,6 @@ ASTKEYWORD_UNRESERVED KEYWORD_UNRESERVED (): {} //| //| //| -| | //| //| @@ -5447,7 +5434,6 @@ ASTKEYWORD_UNRESERVED KEYWORD_UNRESERVED (): {} //| //| | -| //| //| | @@ -6352,7 +6338,7 @@ ASTID ID(): {} | KEYWORD_UNRESERVED() //SRT 2011-04-17 /*KEYWORDS_UNRESERVED | | | | | | | | | - | | | | + | | | */ //20120501 | | | @@ -6615,7 +6601,6 @@ ASTQualifiedID QualifiedID(): {} // //| //20120501 | - //| //| // // diff --git a/pmd-plsql/pom.xml b/pmd-plsql/pom.xml index 56abfd1d71..76401058aa 100644 --- a/pmd-plsql/pom.xml +++ b/pmd-plsql/pom.xml @@ -40,7 +40,7 @@ - + diff --git a/pmd-plsql/src/main/ant/alljavacc.xml b/pmd-plsql/src/main/ant/alljavacc.xml index d22c66587a..c161ad16ca 100644 --- a/pmd-plsql/src/main/ant/alljavacc.xml +++ b/pmd-plsql/src/main/ant/alljavacc.xml @@ -67,6 +67,7 @@ + diff --git a/pmd-plsql/src/main/java/net/sourceforge/pmd/lang/plsql/ast/ASTStringLiteral.java b/pmd-plsql/src/main/java/net/sourceforge/pmd/lang/plsql/ast/ASTStringLiteral.java new file mode 100644 index 0000000000..d66683fafc --- /dev/null +++ b/pmd-plsql/src/main/java/net/sourceforge/pmd/lang/plsql/ast/ASTStringLiteral.java @@ -0,0 +1,43 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + + +package net.sourceforge.pmd.lang.plsql.ast; + +public final class ASTStringLiteral extends net.sourceforge.pmd.lang.plsql.ast.AbstractPLSQLNode { + + + ASTStringLiteral(int id) { + super(id); + } + + + ASTStringLiteral(PLSQLParser p, int id) { + super(p, id); + } + + + @Override + public Object jjtAccept(PLSQLParserVisitor visitor, Object data) { + return visitor.visit(this, data); + } + + /** + * Gets the plain string from the string literal. + * @return the plain string value from the string literal. + */ + public String getString() { + String image = getImage(); + if (image.charAt(0) == 'N' || image.charAt(0) == 'n') { + image = image.substring(1); + } + + if (image.charAt(0) == '\'') { + image = image.substring(1, image.length() - 1); + } else if (image.charAt(0) == 'Q' || image.charAt(0) == 'q') { + image = image.substring("q'x".length(), image.length() - 2); + } + return image; + } +} diff --git a/pmd-plsql/src/test/java/net/sourceforge/pmd/lang/plsql/ast/MultipleDDLStatementsTest.java b/pmd-plsql/src/test/java/net/sourceforge/pmd/lang/plsql/ast/MultipleDDLStatementsTest.java new file mode 100644 index 0000000000..cbd5a74497 --- /dev/null +++ b/pmd-plsql/src/test/java/net/sourceforge/pmd/lang/plsql/ast/MultipleDDLStatementsTest.java @@ -0,0 +1,29 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.plsql.ast; + +import java.nio.charset.StandardCharsets; +import java.util.List; + +import org.apache.commons.io.IOUtils; +import org.junit.Assert; +import org.junit.Test; + +import net.sourceforge.pmd.lang.plsql.AbstractPLSQLParserTst; + +public class MultipleDDLStatementsTest extends AbstractPLSQLParserTst { + + @Test + public void parseDDLCommands() throws Exception { + String code = IOUtils.toString(this.getClass().getResourceAsStream("DDLCommands.sql"), + StandardCharsets.UTF_8); + ASTInput input = parsePLSQL(code); + List ddlcommands = input.findDescendantsOfType(ASTDDLCommand.class); + Assert.assertEquals(4, ddlcommands.size()); + List comments = input.findDescendantsOfType(ASTComment.class); + Assert.assertEquals(3, comments.size()); + Assert.assertEquals("'abbreviated job title'", comments.get(0).getFirstChildOfType(ASTStringLiteral.class).getImage()); + } +} diff --git a/pmd-plsql/src/test/java/net/sourceforge/pmd/lang/plsql/ast/StringLiteralsTest.java b/pmd-plsql/src/test/java/net/sourceforge/pmd/lang/plsql/ast/StringLiteralsTest.java new file mode 100644 index 0000000000..fcc7777e03 --- /dev/null +++ b/pmd-plsql/src/test/java/net/sourceforge/pmd/lang/plsql/ast/StringLiteralsTest.java @@ -0,0 +1,54 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.plsql.ast; + +import java.nio.charset.StandardCharsets; +import java.util.List; + +import org.apache.commons.io.IOUtils; +import org.junit.Assert; +import org.junit.Test; + +import net.sourceforge.pmd.lang.plsql.AbstractPLSQLParserTst; + +public class StringLiteralsTest extends AbstractPLSQLParserTst { + + + @Test + public void parseStringLiterals() throws Exception { + String code = IOUtils.toString(this.getClass().getResourceAsStream("StringLiterals.pls"), + StandardCharsets.UTF_8); + ASTInput input = parsePLSQL(code); + List strings = input.findDescendantsOfType(ASTStringLiteral.class); + Assert.assertEquals(20, strings.size()); + + assertString("'Hello'", "Hello", 0, strings); + assertString("N'nchar literal'", "nchar literal", 4, strings); + assertString("nQ'[ab']cd]'", "ab']cd", 11, strings); + assertString("Q'{SELECT * FROM employees WHERE last_name = 'Smith';}'", + "SELECT * FROM employees WHERE last_name = 'Smith';", 13, strings); + assertString("q'{\n" + " also multiple\n" + " lines\n" + " }'", + "\n" + " also multiple\n" + " lines\n" + " ", 15, strings); + } + + @Test + public void parseMultilineVarchar() throws Exception { + String code = IOUtils.toString(this.getClass().getResourceAsStream("MultilineVarchar.pls"), + StandardCharsets.UTF_8); + ASTInput input = parsePLSQL(code); + List strings = input.findDescendantsOfType(ASTStringLiteral.class); + Assert.assertEquals(1, strings.size()); + Assert.assertTrue(normalizeEol(strings.get(0).getString()).startsWith("\ncreate or replace and")); + } + + private static void assertString(String quoted, String plain, int index, List strings) { + Assert.assertEquals(quoted, normalizeEol(strings.get(index).getImage())); + Assert.assertEquals(plain, normalizeEol(strings.get(index).getString())); + } + + private static String normalizeEol(String s) { + return s.replaceAll("\r\n|\n\r|\n|\r", "\n"); + } +} diff --git a/pmd-plsql/src/test/resources/net/sourceforge/pmd/lang/plsql/ast/DDLCommands.sql b/pmd-plsql/src/test/resources/net/sourceforge/pmd/lang/plsql/ast/DDLCommands.sql new file mode 100644 index 0000000000..5833689339 --- /dev/null +++ b/pmd-plsql/src/test/resources/net/sourceforge/pmd/lang/plsql/ast/DDLCommands.sql @@ -0,0 +1,7 @@ +COMMENT ON COLUMN employees.job_id IS 'abbreviated job title'; + +COMMENT ON COLUMN employees.job_id IS 'abbreviated job title'; + +DROP TABLE employees; + +COMMENT ON COLUMN employees.job_id IS 'abbreviated job title'; diff --git a/pmd-plsql/src/test/resources/net/sourceforge/pmd/lang/plsql/ast/MultilineVarchar.pls b/pmd-plsql/src/test/resources/net/sourceforge/pmd/lang/plsql/ast/MultilineVarchar.pls new file mode 100644 index 0000000000..dad51aed4f --- /dev/null +++ b/pmd-plsql/src/test/resources/net/sourceforge/pmd/lang/plsql/ast/MultilineVarchar.pls @@ -0,0 +1,37 @@ +-- +-- From https://github.com/pmd/pmd/pull/1988 +-- +declare + + w_java_source clob := q'% +create or replace and compile java source named PdfUtils as + /** BLOB encryption class using AES 128bit + * Load class to oracle db. Class file is on the oracle serever: + * sql script: + * exec sys.dbms_java.loadjava('-v -r /tsfa/tia7400/PdfPassword.class'); + * command line: + * loadjava -user tia -resolve -verbose -r /tsfa/tia7400/PdfPassword.java + * dropjava -user tia -resolve -verbose -r /tsfa/tia7400/PdfPassword.java + */ + /** + * Function reads input blob, encrypts and returns it to the caller + * @author Karol Wozniak + * @param secret password key + * @param fortuneBLOB blob var + * @return oracle.sql.BLOB + */ + public static BLOB encryptPdf(String secret, Blob blobVar) throws Exception { + System.out.println("Start readBlob"); + blobfile = writeToBlob(boas.toByteArray()); + is.close(); + reader.close(); + System.out.println("encrypted using password " + secret); + System.out.println("End readBlob"); + return blobfile; + } +}; +%'; + +begin + null; +end; \ No newline at end of file diff --git a/pmd-plsql/src/test/resources/net/sourceforge/pmd/lang/plsql/ast/StringLiterals.pls b/pmd-plsql/src/test/resources/net/sourceforge/pmd/lang/plsql/ast/StringLiterals.pls new file mode 100644 index 0000000000..66118495ad --- /dev/null +++ b/pmd-plsql/src/test/resources/net/sourceforge/pmd/lang/plsql/ast/StringLiterals.pls @@ -0,0 +1,45 @@ +-- +-- See https://github.com/pmd/pmd/issues/2008 +-- [plsql] In StringLiteral using alternative quoting mechanism single quotes cause parsing errors +-- +-- https://docs.oracle.com/en/database/oracle/oracle-database/18/sqlrf/Literals.html#GUID-1824CBAA-6E16-4921-B2A6-112FB02248DA +-- + +declare + + literal1 clob := 'Hello'; + literal2 clob := 'ORACLE.dbs'; + literal3 clob := 'Jackie''s raincoat'; + literal4 clob := '09-MAR-98'; + literal5 clob := N'nchar literal'; + + -- alternative quoting mechanism + qliteral1a clob := q'[abc]'; + qliteral1b clob := q'[ab']cd]'; + qliteral1c clob := q'[ab[cd]'; + qliteral1d clob := q'[ab]cd]'; + qliteral1e clob := q'[ab + cd]'; + qliteral1f clob := Nq'[a"b"c]'; + qliteral1g clob := nQ'[ab']cd]'; + + qliteral1 clob := q'!name LIKE '%DBMS_%%'!'; + qliteral2 clob := Q'{SELECT * FROM employees WHERE last_name = 'Smith';}'; + qliteral1a clob := q'! test !'; + qliteral2a clob := q'{ + also multiple + lines + }'; + qliteral3a clob := q'% test abc %'; + qliteral3b clob := q'% test % abc %'; + + + qliteral3c clob := q'% test'test %'; + qliteral4 clob := nq'!name LIKE '%DBMS_%%'!'; + + + +begin + null; +end; +/ diff --git a/pmd-python/pom.xml b/pmd-python/pom.xml index 37cecb5d6b..92b9798e44 100644 --- a/pmd-python/pom.xml +++ b/pmd-python/pom.xml @@ -34,7 +34,7 @@ - + diff --git a/pmd-scala/src/test/java/net/sourceforge/pmd/lang/scala/rule/ScalaRuleTest.java b/pmd-scala/src/test/java/net/sourceforge/pmd/lang/scala/rule/ScalaRuleTest.java index e3d0b2fd37..8c7cc39599 100644 --- a/pmd-scala/src/test/java/net/sourceforge/pmd/lang/scala/rule/ScalaRuleTest.java +++ b/pmd-scala/src/test/java/net/sourceforge/pmd/lang/scala/rule/ScalaRuleTest.java @@ -19,8 +19,8 @@ import net.sourceforge.pmd.Report; import net.sourceforge.pmd.Rule; import net.sourceforge.pmd.RuleContext; import net.sourceforge.pmd.RuleSet; -import net.sourceforge.pmd.RuleSetFactory; import net.sourceforge.pmd.RuleSets; +import net.sourceforge.pmd.RulesetsFactoryUtils; import net.sourceforge.pmd.internal.util.IteratorUtil; import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.lang.LanguageVersionHandler; @@ -77,7 +77,7 @@ public class ScalaRuleTest { Report report = new Report(); ctx.setReport(report); ctx.setSourceCodeFile(new File("test.scala")); - RuleSet rules = new RuleSetFactory().createSingleRuleRuleSet(r); + RuleSet rules = RulesetsFactoryUtils.defaultFactory().createSingleRuleRuleSet(r); p.getSourceCodeProcessor().processSourceCode(new StringReader(test), new RuleSets(rules), ctx); return report; } diff --git a/pmd-scala/src/test/java/net/sourceforge/pmd/lang/scala/rule/XPathRuleTest.java b/pmd-scala/src/test/java/net/sourceforge/pmd/lang/scala/rule/XPathRuleTest.java index 31de1124f4..5c64d291df 100644 --- a/pmd-scala/src/test/java/net/sourceforge/pmd/lang/scala/rule/XPathRuleTest.java +++ b/pmd-scala/src/test/java/net/sourceforge/pmd/lang/scala/rule/XPathRuleTest.java @@ -19,9 +19,9 @@ import net.sourceforge.pmd.Report; import net.sourceforge.pmd.Rule; import net.sourceforge.pmd.RuleContext; import net.sourceforge.pmd.RuleSet; -import net.sourceforge.pmd.RuleSetFactory; import net.sourceforge.pmd.RuleSets; import net.sourceforge.pmd.RuleViolation; +import net.sourceforge.pmd.RulesetsFactoryUtils; import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.lang.rule.XPathRule; import net.sourceforge.pmd.lang.rule.xpath.XPathRuleQuery; @@ -57,7 +57,7 @@ public class XPathRuleTest extends RuleTst { Report report = new Report(); ctx.setReport(report); ctx.setSourceCodeFile(new File("test.scala")); - RuleSet rules = new RuleSetFactory().createSingleRuleRuleSet(r); + RuleSet rules = RulesetsFactoryUtils.defaultFactory().createSingleRuleRuleSet(r); p.getSourceCodeProcessor().processSourceCode(new StringReader(test), new RuleSets(rules), ctx); return report; } diff --git a/pmd-test/src/main/java/net/sourceforge/pmd/AbstractLanguageVersionTest.java b/pmd-test/src/main/java/net/sourceforge/pmd/AbstractLanguageVersionTest.java index ff5343edb3..c9984f1b0b 100644 --- a/pmd-test/src/main/java/net/sourceforge/pmd/AbstractLanguageVersionTest.java +++ b/pmd-test/src/main/java/net/sourceforge/pmd/AbstractLanguageVersionTest.java @@ -141,7 +141,7 @@ public class AbstractLanguageVersionTest { String rulesetFilenames = props.getProperty("rulesets.filenames"); assertNotNull(rulesetFilenames); - RuleSetFactory factory = new RuleSetFactory(); + RuleSetFactory factory = RulesetsFactoryUtils.defaultFactory(); if (rulesetFilenames.trim().isEmpty()) { return; diff --git a/pmd-test/src/main/java/net/sourceforge/pmd/AbstractRuleSetFactoryTest.java b/pmd-test/src/main/java/net/sourceforge/pmd/AbstractRuleSetFactoryTest.java index 16f9f85c25..752fe19afd 100644 --- a/pmd-test/src/main/java/net/sourceforge/pmd/AbstractRuleSetFactoryTest.java +++ b/pmd-test/src/main/java/net/sourceforge/pmd/AbstractRuleSetFactoryTest.java @@ -261,7 +261,7 @@ public abstract class AbstractRuleSetFactoryTest { } private RuleSet loadRuleSetByFileName(String ruleSetFileName) throws RuleSetNotFoundException { - RuleSetFactory rsf = new RuleSetFactory(); + RuleSetFactory rsf = RulesetsFactoryUtils.defaultFactory(); return rsf.createRuleSet(ruleSetFileName); } @@ -360,7 +360,7 @@ public abstract class AbstractRuleSetFactoryTest { // System.out.println("xml2: " + xml2); // Read RuleSet from XML, first time - RuleSetFactory ruleSetFactory = new RuleSetFactory(); + RuleSetFactory ruleSetFactory = RulesetsFactoryUtils.defaultFactory(); RuleSet ruleSet2 = ruleSetFactory.createRuleSet(createRuleSetReferenceId(xml2)); // Do write/read a 2nd time, just to be sure diff --git a/pmd-test/src/main/java/net/sourceforge/pmd/testframework/PMDTestRunner.java b/pmd-test/src/main/java/net/sourceforge/pmd/testframework/PMDTestRunner.java index 0f9b6d310f..a0e05007bf 100644 --- a/pmd-test/src/main/java/net/sourceforge/pmd/testframework/PMDTestRunner.java +++ b/pmd-test/src/main/java/net/sourceforge/pmd/testframework/PMDTestRunner.java @@ -66,7 +66,7 @@ public class PMDTestRunner extends Runner implements Filterable, Sortable { try { unitTests.filter(filter); } catch (NoTestsRemainException e) { - noUnitTests = false; + noUnitTests = true; } if (noRuleTests && noUnitTests) { diff --git a/pmd-test/src/main/java/net/sourceforge/pmd/testframework/RuleTst.java b/pmd-test/src/main/java/net/sourceforge/pmd/testframework/RuleTst.java index 0522d5238b..d134ebe6c1 100644 --- a/pmd-test/src/main/java/net/sourceforge/pmd/testframework/RuleTst.java +++ b/pmd-test/src/main/java/net/sourceforge/pmd/testframework/RuleTst.java @@ -41,10 +41,10 @@ import net.sourceforge.pmd.Report; import net.sourceforge.pmd.Rule; import net.sourceforge.pmd.RuleContext; import net.sourceforge.pmd.RuleSet; -import net.sourceforge.pmd.RuleSetFactory; import net.sourceforge.pmd.RuleSetNotFoundException; import net.sourceforge.pmd.RuleSets; import net.sourceforge.pmd.RuleViolation; +import net.sourceforge.pmd.RulesetsFactoryUtils; import net.sourceforge.pmd.lang.Language; import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.lang.LanguageVersion; @@ -101,7 +101,7 @@ public abstract class RuleTst { */ public Rule findRule(String ruleSet, String ruleName) { try { - Rule rule = new RuleSetFactory().createRuleSets(ruleSet).getRuleByName(ruleName); + Rule rule = RulesetsFactoryUtils.defaultFactory().createRuleSets(ruleSet).getRuleByName(ruleName); if (rule == null) { fail("Rule " + ruleName + " not found in ruleset " + ruleSet); } else { @@ -282,7 +282,7 @@ public abstract class RuleTst { ctx.setSourceCodeFile(new File("n/a")); ctx.setLanguageVersion(languageVersion); ctx.setIgnoreExceptions(false); - RuleSet rules = new RuleSetFactory().createSingleRuleRuleSet(rule); + RuleSet rules = RulesetsFactoryUtils.defaultFactory().createSingleRuleRuleSet(rule); p.getSourceCodeProcessor().processSourceCode(new StringReader(code), new RuleSets(rules), ctx); } catch (Exception e) { throw new RuntimeException(e); diff --git a/pmd-visualforce/pom.xml b/pmd-visualforce/pom.xml index 2df2b60785..3a00d97ae3 100644 --- a/pmd-visualforce/pom.xml +++ b/pmd-visualforce/pom.xml @@ -40,7 +40,7 @@ - + diff --git a/pmd-vm/pom.xml b/pmd-vm/pom.xml index 2ec5f0b9ac..f8effb5cf7 100644 --- a/pmd-vm/pom.xml +++ b/pmd-vm/pom.xml @@ -40,7 +40,7 @@ - + diff --git a/pom.xml b/pom.xml index f422e593ae..75144d261d 100644 --- a/pom.xml +++ b/pom.xml @@ -107,6 +107,7 @@ Objective-C, Perl, PHP, PLSQL, Python, Ruby, Salesforce.com Apex, Scala, Swift a 5 6.19.0 + ${settings.localRepository}/net/java/dev/javacc/javacc/${javacc.version}/javacc-${javacc.version}.jar