diff --git a/docs/pages/release_notes.md b/docs/pages/release_notes.md index b0b392968a..ace594edfe 100644 --- a/docs/pages/release_notes.md +++ b/docs/pages/release_notes.md @@ -306,6 +306,14 @@ package or made (package) private and are _not accessible_ anymore. * `net.sourceforge.pmd.RuleSets` * `net.sourceforge.pmd.lang.rule.ParametricRuleViolation` is now package private and moved to `net.sourceforge.pmd.reporting.ParametricRuleViolation`. The only public API is {%jdoc core::reporting.RuleViolation %}. + * {%jdoc !!core::lang.rule.RuleSet %} + * Method `applies(Rule,LanguageVersion)` is now package private. + * Method `applies(TextFile)` has been removed. + * Method `applies(FileId)` is now package private. + * {%jdoc !!core::lang.rule.RuleSetLoader %} + * Method `loadRuleSetsWithoutException(java.util.List)` is now package private. + * {%jdoc !!core::lang.rule.RuleSetLoadException %} + * All constructors are package private now. * pmd-ant * {%jdoc !!ant::ant.Formatter %} * Method `getRenderer()` has been removed. diff --git a/pmd-ant/src/main/java/net/sourceforge/pmd/ant/internal/PMDTaskImpl.java b/pmd-ant/src/main/java/net/sourceforge/pmd/ant/internal/PMDTaskImpl.java index e35a8ff962..a4be75fb15 100644 --- a/pmd-ant/src/main/java/net/sourceforge/pmd/ant/internal/PMDTaskImpl.java +++ b/pmd-ant/src/main/java/net/sourceforge/pmd/ant/internal/PMDTaskImpl.java @@ -4,6 +4,8 @@ package net.sourceforge.pmd.ant.internal; +import static net.sourceforge.pmd.lang.rule.InternalApiBridge.loadRuleSetsWithoutException; + import java.nio.charset.Charset; import java.nio.file.Paths; import java.util.ArrayList; @@ -125,7 +127,7 @@ public class PMDTaskImpl { try (PmdAnalysis pmd = PmdAnalysis.create(configuration)) { RuleSetLoader rulesetLoader = pmd.newRuleSetLoader().loadResourcesWith(setupResourceLoader()); - pmd.addRuleSets(rulesetLoader.loadRuleSetsWithoutException(ruleSetPaths)); + pmd.addRuleSets(loadRuleSetsWithoutException(rulesetLoader, ruleSetPaths)); for (FileSet fileset : filesets) { DirectoryScanner ds = fileset.getDirectoryScanner(project); diff --git a/pmd-compat6/src/main/java/net/sourceforge/pmd/PmdAnalysis.java b/pmd-compat6/src/main/java/net/sourceforge/pmd/PmdAnalysis.java index de0b41595e..3a84752c39 100644 --- a/pmd-compat6/src/main/java/net/sourceforge/pmd/PmdAnalysis.java +++ b/pmd-compat6/src/main/java/net/sourceforge/pmd/PmdAnalysis.java @@ -193,7 +193,7 @@ public final class PmdAnalysis implements AutoCloseable { if (!config.getRuleSetPaths().isEmpty()) { final net.sourceforge.pmd.lang.rule.RuleSetLoader ruleSetLoader = pmd.newRuleSetLoader(); - final List ruleSets = ruleSetLoader.loadRuleSetsWithoutException(config.getRuleSetPaths()); + final List ruleSets = net.sourceforge.pmd.lang.rule.InternalApiBridge.loadRuleSetsWithoutException(ruleSetLoader, config.getRuleSetPaths()); pmd.addRuleSets(ruleSets); } @@ -490,7 +490,7 @@ public final class PmdAnalysis implements AutoCloseable { Objects.requireNonNull(ruleLanguage, "Rule has no language " + rule); if (!languages.contains(ruleLanguage)) { LanguageVersion version = discoverer.getDefaultLanguageVersion(ruleLanguage); - if (RuleSet.applies((net.sourceforge.pmd.lang.rule.Rule) rule, version)) { + if (net.sourceforge.pmd.lang.rule.InternalApiBridge.ruleSetApplies((net.sourceforge.pmd.lang.rule.Rule) rule, version)) { configuration.checkLanguageIsRegistered(ruleLanguage); languages.add(ruleLanguage); if (!quiet) { diff --git a/pmd-compat6/src/main/java/net/sourceforge/pmd/RuleSets.java b/pmd-compat6/src/main/java/net/sourceforge/pmd/RuleSets.java index 601dffd5a1..7ee5ec4daa 100644 --- a/pmd-compat6/src/main/java/net/sourceforge/pmd/RuleSets.java +++ b/pmd-compat6/src/main/java/net/sourceforge/pmd/RuleSets.java @@ -22,6 +22,7 @@ import net.sourceforge.pmd.benchmark.TimedOperationCategory; import net.sourceforge.pmd.lang.LanguageProcessorRegistry; import net.sourceforge.pmd.lang.ast.RootNode; import net.sourceforge.pmd.lang.document.TextFile; +import net.sourceforge.pmd.lang.rule.InternalApiBridge; import net.sourceforge.pmd.lang.rule.Rule; import net.sourceforge.pmd.lang.rule.RuleSet; import net.sourceforge.pmd.lang.rule.internal.RuleApplicator; @@ -126,7 +127,7 @@ public class RuleSets { */ public boolean applies(TextFile file) { for (RuleSet ruleSet : ruleSets) { - if (ruleSet.applies(file)) { + if (InternalApiBridge.ruleSetApplies(ruleSet, file.getFileId())) { return true; } } @@ -155,7 +156,7 @@ public class RuleSets { } for (RuleSet ruleSet : ruleSets) { - if (ruleSet.applies(root.getTextDocument().getFileId())) { + if (InternalApiBridge.ruleSetApplies(ruleSet, root.getTextDocument().getFileId())) { ruleApplicator.apply(ruleSet.getRules(), listener); } } diff --git a/pmd-compat6/src/main/java/net/sourceforge/pmd/lang/rule/RuleSetLoadException.java b/pmd-compat6/src/main/java/net/sourceforge/pmd/lang/rule/RuleSetLoadException.java new file mode 100644 index 0000000000..5cc43b7131 --- /dev/null +++ b/pmd-compat6/src/main/java/net/sourceforge/pmd/lang/rule/RuleSetLoadException.java @@ -0,0 +1,36 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +// Copied from 7.0.0-SNAPSHOT +// Changes: constructors are public again + +package net.sourceforge.pmd.lang.rule; + +import org.checkerframework.checker.nullness.qual.NonNull; + +import net.sourceforge.pmd.lang.rule.internal.RuleSetReferenceId; + +/** + * An exception that is thrown when something wrong occurs while + * {@linkplain RuleSetLoader loading rulesets}. This may be because the + * XML is not well-formed, does not respect the ruleset schema, is + * not a valid ruleset or is otherwise unparsable. + */ +public class RuleSetLoadException extends RuntimeException { + + /** + * @apiNote Internal API. + */ + public RuleSetLoadException(RuleSetReferenceId rsetId, @NonNull Throwable cause) { + super("Cannot load ruleset " + rsetId + ": " + cause.getMessage(), cause); + } + + /** + * @apiNote Internal API. + */ + public RuleSetLoadException(RuleSetReferenceId rsetId, String message) { + super("Cannot load ruleset " + rsetId + ": " + message); + } + +} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/PmdAnalysis.java b/pmd-core/src/main/java/net/sourceforge/pmd/PmdAnalysis.java index e3a211cefa..b894b46c39 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/PmdAnalysis.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/PmdAnalysis.java @@ -4,6 +4,8 @@ package net.sourceforge.pmd; +import static net.sourceforge.pmd.lang.rule.InternalApiBridge.loadRuleSetsWithoutException; +import static net.sourceforge.pmd.lang.rule.InternalApiBridge.ruleSetApplies; import static net.sourceforge.pmd.util.CollectionUtil.listOf; import java.nio.file.Path; @@ -193,7 +195,7 @@ public final class PmdAnalysis implements AutoCloseable { if (!config.getRuleSetPaths().isEmpty()) { final RuleSetLoader ruleSetLoader = pmd.newRuleSetLoader(); - final List ruleSets = ruleSetLoader.loadRuleSetsWithoutException(config.getRuleSetPaths()); + final List ruleSets = loadRuleSetsWithoutException(ruleSetLoader, config.getRuleSetPaths()); pmd.addRuleSets(ruleSets); } @@ -490,7 +492,7 @@ public final class PmdAnalysis implements AutoCloseable { Objects.requireNonNull(ruleLanguage, "Rule has no language " + rule); if (!languages.contains(ruleLanguage)) { LanguageVersion version = discoverer.getDefaultLanguageVersion(ruleLanguage); - if (RuleSet.applies(rule, version)) { + if (ruleSetApplies(rule, version)) { configuration.checkLanguageIsRegistered(ruleLanguage); languages.add(ruleLanguage); if (!quiet) { diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/InternalApiBridge.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/InternalApiBridge.java new file mode 100644 index 0000000000..b532eef955 --- /dev/null +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/InternalApiBridge.java @@ -0,0 +1,39 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.rule; + +import java.util.List; + +import net.sourceforge.pmd.annotation.InternalApi; +import net.sourceforge.pmd.lang.LanguageVersion; +import net.sourceforge.pmd.lang.document.FileId; + +/** + * Internal API. + * + *

Acts as a bridge between outer parts of PMD and the restricted access + * internal API of this package. + * + *

None of this is published API, and compatibility can be broken anytime! + * Use this only at your own risk. + * + * @apiNote Internal API + */ +@InternalApi +public final class InternalApiBridge { + private InternalApiBridge() {} + + public static boolean ruleSetApplies(Rule rule, LanguageVersion languageVersion) { + return RuleSet.applies(rule, languageVersion); + } + + public static boolean ruleSetApplies(RuleSet ruleSet, FileId fileId) { + return ruleSet.applies(fileId); + } + + public static List loadRuleSetsWithoutException(RuleSetLoader ruleSetLoader, List rulesetPaths) { + return ruleSetLoader.loadRuleSetsWithoutException(rulesetPaths); + } +} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/RuleSet.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/RuleSet.java index 47cd37243c..c4a5d5b60a 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/RuleSet.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/RuleSet.java @@ -20,12 +20,10 @@ import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import net.sourceforge.pmd.annotation.InternalApi; import net.sourceforge.pmd.cache.internal.ChecksumAware; import net.sourceforge.pmd.internal.util.PredicateUtil; import net.sourceforge.pmd.lang.LanguageVersion; import net.sourceforge.pmd.lang.document.FileId; -import net.sourceforge.pmd.lang.document.TextFile; import net.sourceforge.pmd.lang.rule.internal.RuleSetReference; import net.sourceforge.pmd.lang.rule.xpath.XPathRule; @@ -607,30 +605,13 @@ public class RuleSet implements ChecksumAware { * * @return true if the file should be checked, * false otherwise + * + * @apiNote Internal API. */ - // TODO get rid of this overload - @InternalApi - public boolean applies(FileId qualFileName) { + boolean applies(FileId qualFileName) { return filter.test(qualFileName.getAbsolutePath()); } - /** - * Check if a given source file should be checked by rules in this RuleSet. - * A file should not be checked if there is an exclude pattern - * which matches the file, unless there is an include pattern - * which also matches the file. In other words, include - * patterns override exclude patterns. - * - * @param file a text file - * - * @return true if the file should be checked, - * false otherwise - */ - @InternalApi - public boolean applies(TextFile file) { - return applies(file.getFileId()); - } - /** * Does the given Rule apply to the given LanguageVersion? If so, the * Language must be the same and be between the minimum and maximums @@ -644,12 +625,9 @@ public class RuleSet implements ChecksumAware { * @return true if the given rule matches the given language, * which means, that the rule would be executed. * - * @deprecated This is internal API, removed in PMD 7. You should - * not use a ruleset directly. + * @apiNote This is internal API. */ - @Deprecated - @InternalApi - public static boolean applies(Rule rule, LanguageVersion languageVersion) { + static boolean applies(Rule rule, LanguageVersion languageVersion) { final LanguageVersion min = rule.getMinimumLanguageVersion(); final LanguageVersion max = rule.getMaximumLanguageVersion(); assert rule.getLanguage() != null : "Rule has no language " + rule; @@ -715,17 +693,13 @@ public class RuleSet implements ChecksumAware { /** * Remove and collect any misconfigured rules. - * TODO remove this method. This mutates rulesets for nothing. Whether - * a rule is dysfunctional or not should be checked when it is initialized. * * @param collector * the removed rules will be added to this collection - * - * @deprecated This is internal API, removed in PMD 7. You should - * not use a ruleset directly. */ - @Deprecated - @InternalApi + // TODO remove this method. This mutates rulesets for nothing. Whether a rule + // is dysfunctional or not should be checked when it is initialized. + // See also https://github.com/pmd/pmd/issues/3868 and https://github.com/pmd/pmd/issues/3901 public void removeDysfunctionalRules(Collection collector) { Iterator iter = rules.iterator(); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/RuleSetLoadException.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/RuleSetLoadException.java index c87ba35118..ca53380384 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/RuleSetLoadException.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/RuleSetLoadException.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.rule; import org.checkerframework.checker.nullness.qual.NonNull; -import net.sourceforge.pmd.annotation.InternalApi; import net.sourceforge.pmd.lang.rule.internal.RuleSetReferenceId; /** @@ -17,15 +16,17 @@ import net.sourceforge.pmd.lang.rule.internal.RuleSetReferenceId; */ public class RuleSetLoadException extends RuntimeException { - /** Constructors are internal. */ - @InternalApi - public RuleSetLoadException(RuleSetReferenceId rsetId, @NonNull Throwable cause) { + /** + * @apiNote Internal API. + */ + RuleSetLoadException(RuleSetReferenceId rsetId, @NonNull Throwable cause) { super("Cannot load ruleset " + rsetId + ": " + cause.getMessage(), cause); } - /** Constructors are internal. */ - @InternalApi - public RuleSetLoadException(RuleSetReferenceId rsetId, String message) { + /** + * @apiNote Internal API. + */ + RuleSetLoadException(RuleSetReferenceId rsetId, String message) { super("Cannot load ruleset " + rsetId + ": " + message); } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/RuleSetLoader.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/RuleSetLoader.java index 0fa046c824..e1bb83cdc2 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/RuleSetLoader.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/RuleSetLoader.java @@ -21,7 +21,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import net.sourceforge.pmd.PMDConfiguration; -import net.sourceforge.pmd.annotation.InternalApi; import net.sourceforge.pmd.lang.Language; import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.lang.rule.internal.RuleSetReferenceId; @@ -195,11 +194,10 @@ public final class RuleSetLoader { * Loads a list of rulesets, if any has an error, report it on the contextual * error reporter instead of aborting, and continue loading the rest. * - *

Internal API: might be published later, or maybe in PMD 7 this + * @apiNote Internal API: might be published later, or maybe this * will be the default behaviour of every method of this class. */ - @InternalApi - public List loadRuleSetsWithoutException(List rulesetPaths) { + List loadRuleSetsWithoutException(List rulesetPaths) { List ruleSets = new ArrayList<>(rulesetPaths.size()); boolean anyRules = false; boolean error = false; diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/internal/RuleApplicator.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/internal/RuleApplicator.java index e61d28ebff..52e5378369 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/internal/RuleApplicator.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/internal/RuleApplicator.java @@ -4,6 +4,8 @@ package net.sourceforge.pmd.lang.rule.internal; +import static net.sourceforge.pmd.lang.rule.InternalApiBridge.ruleSetApplies; + import java.util.Collection; import java.util.Iterator; @@ -19,7 +21,6 @@ import net.sourceforge.pmd.lang.LanguageVersion; import net.sourceforge.pmd.lang.ast.Node; import net.sourceforge.pmd.lang.ast.RootNode; import net.sourceforge.pmd.lang.rule.Rule; -import net.sourceforge.pmd.lang.rule.RuleSet; import net.sourceforge.pmd.reporting.FileAnalysisListener; import net.sourceforge.pmd.reporting.InternalApiBridge; import net.sourceforge.pmd.reporting.Report.ProcessingError; @@ -58,7 +59,7 @@ public class RuleApplicator { private void applyOnIndex(TreeIndex idx, Collection rules, FileAnalysisListener listener) { for (Rule rule : rules) { - if (!RuleSet.applies(rule, currentLangVer)) { + if (!ruleSetApplies(rule, currentLangVer)) { continue; // No point in even trying to apply the rule } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/internal/RuleSets.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/internal/RuleSets.java index 7d05afd410..829b06116e 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/internal/RuleSets.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/internal/RuleSets.java @@ -18,6 +18,7 @@ import net.sourceforge.pmd.benchmark.TimedOperationCategory; import net.sourceforge.pmd.lang.LanguageProcessorRegistry; import net.sourceforge.pmd.lang.ast.RootNode; import net.sourceforge.pmd.lang.document.TextFile; +import net.sourceforge.pmd.lang.rule.InternalApiBridge; import net.sourceforge.pmd.lang.rule.Rule; import net.sourceforge.pmd.lang.rule.RuleSet; import net.sourceforge.pmd.reporting.FileAnalysisListener; @@ -121,7 +122,7 @@ public class RuleSets { */ public boolean applies(TextFile file) { for (RuleSet ruleSet : ruleSets) { - if (ruleSet.applies(file)) { + if (InternalApiBridge.ruleSetApplies(ruleSet, file.getFileId())) { return true; } } @@ -150,7 +151,7 @@ public class RuleSets { } for (RuleSet ruleSet : ruleSets) { - if (ruleSet.applies(root.getTextDocument().getFileId())) { + if (InternalApiBridge.ruleSetApplies(ruleSet, root.getTextDocument().getFileId())) { ruleApplicator.apply(ruleSet.getRules(), listener); } } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/lang/rule/RuleSetTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/lang/rule/RuleSetTest.java index 470b6c4c64..32387b4a73 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/lang/rule/RuleSetTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/lang/rule/RuleSetTest.java @@ -45,6 +45,7 @@ import net.sourceforge.pmd.FooRule; import net.sourceforge.pmd.lang.ast.DummyNode.DummyRootNode; import net.sourceforge.pmd.lang.ast.Node; import net.sourceforge.pmd.lang.ast.RootNode; +import net.sourceforge.pmd.lang.document.FileId; import net.sourceforge.pmd.lang.document.TextFile; import net.sourceforge.pmd.lang.rule.RuleSet.RuleSetBuilder; import net.sourceforge.pmd.lang.rule.internal.RuleSets; @@ -389,27 +390,28 @@ class RuleSetTest { @Test void testIncludeExcludeApplies() { - TextFile file = TextFile.forPath(Paths.get("C:\\myworkspace\\project\\some\\random\\package\\RandomClass.java"), Charset.defaultCharset(), dummyVersion()); + FileId fileId = TextFile.forPath(Paths.get("C:\\myworkspace\\project\\some\\random\\package\\RandomClass.java"), Charset.defaultCharset(), dummyVersion()) + .getFileId(); RuleSet ruleSet = createRuleSetBuilder("ruleset").build(); - assertTrue(ruleSet.applies(file), "No patterns"); + assertTrue(ruleSet.applies(fileId), "No patterns"); ruleSet = createRuleSetBuilder("ruleset") .withFileExclusions(Pattern.compile("nomatch")) .build(); - assertTrue(ruleSet.applies(file), "Non-matching exclude"); + assertTrue(ruleSet.applies(fileId), "Non-matching exclude"); ruleSet = createRuleSetBuilder("ruleset") .withFileExclusions(Pattern.compile("nomatch"), Pattern.compile(".*/package/.*")) .build(); - assertFalse(ruleSet.applies(file), "Matching exclude"); + assertFalse(ruleSet.applies(fileId), "Matching exclude"); ruleSet = createRuleSetBuilder("ruleset") .withFileExclusions(Pattern.compile("nomatch")) .withFileExclusions(Pattern.compile(".*/package/.*")) .withFileInclusions(Pattern.compile(".*/randomX/.*")) .build(); - assertFalse(ruleSet.applies(file), "Non-matching include"); + assertFalse(ruleSet.applies(fileId), "Non-matching include"); ruleSet = createRuleSetBuilder("ruleset") .withFileExclusions(Pattern.compile("nomatch")) @@ -417,7 +419,7 @@ class RuleSetTest { .withFileInclusions(Pattern.compile(".*/randomX/.*")) .withFileInclusions(Pattern.compile(".*/random/.*")) .build(); - assertTrue(ruleSet.applies(file), "Matching include"); + assertTrue(ruleSet.applies(fileId), "Matching include"); } @Test