diff --git a/pmd-doc/src/main/java/net/sourceforge/pmd/docs/EscapeUtils.java b/pmd-doc/src/main/java/net/sourceforge/pmd/docs/EscapeUtils.java index a1aa42148b..78e5a4fd57 100644 --- a/pmd-doc/src/main/java/net/sourceforge/pmd/docs/EscapeUtils.java +++ b/pmd-doc/src/main/java/net/sourceforge/pmd/docs/EscapeUtils.java @@ -5,6 +5,8 @@ package net.sourceforge.pmd.docs; import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import org.apache.commons.text.StringEscapeUtils; @@ -12,6 +14,7 @@ public final class EscapeUtils { private static final String BACKTICK = "`"; private static final String URL_START = " toLines(String s) { diff --git a/pmd-doc/src/main/java/net/sourceforge/pmd/docs/RuleTagChecker.java b/pmd-doc/src/main/java/net/sourceforge/pmd/docs/RuleTagChecker.java index bf4ab3a91d..11f2d2a09c 100644 --- a/pmd-doc/src/main/java/net/sourceforge/pmd/docs/RuleTagChecker.java +++ b/pmd-doc/src/main/java/net/sourceforge/pmd/docs/RuleTagChecker.java @@ -28,8 +28,10 @@ import java.util.regex.Pattern; public class RuleTagChecker { private static final Logger LOG = Logger.getLogger(DeadLinksChecker.class.getName()); - private static final Pattern RULE_TAG = Pattern.compile("\\{%\\s*rule\\s+\"(.*?)\"\\s*"); - private static final Pattern RULE_REFERENCE = Pattern.compile("(\\w+)\\/(\\w+)\\/(\\w+)"); + private static final String QUOTE = "\""; + private static final Pattern RULE_TAG = Pattern.compile("\\{%\\s*rule\\s+(\"?[^\"%\\}\\s]+\"?)\\s*"); + private static final Pattern RULE_REFERENCE = Pattern.compile("\"?(\\w+)\\/(\\w+)\\/(\\w+)\"?"); + private static final Pattern RULE_SIMPLE_REFERENCE = Pattern.compile("\"?(\\w+)\"?"); private final Path pagesDirectory; private final List issues = new ArrayList<>(); @@ -72,6 +74,9 @@ public class RuleTagChecker { int pos = ruleTagMatcher.end(); if (line.charAt(pos) != '%' || line.charAt(pos + 1) != '}') { addIssue(file, lineNo, "Rule tag for " + ruleReference + " is not closed properly"); + } else if (ruleReference.startsWith(QUOTE) && !ruleReference.endsWith(QUOTE) + || !ruleReference.startsWith(QUOTE) && ruleReference.endsWith(QUOTE)) { + addIssue(file, lineNo, "Rule tag for " + ruleReference + " has a missing quote"); } else if (!ruleReferenceTargetExists(ruleReference)) { addIssue(file, lineNo, "Rule " + ruleReference + " is not found"); } @@ -81,6 +86,7 @@ public class RuleTagChecker { private boolean ruleReferenceTargetExists(String ruleReference) { Matcher ruleRefMatcher = RULE_REFERENCE.matcher(ruleReference); + Matcher simpleRefMatcher = RULE_SIMPLE_REFERENCE.matcher(ruleReference); if (ruleRefMatcher.matches()) { String language = ruleRefMatcher.group(1); String category = ruleRefMatcher.group(2); @@ -89,6 +95,9 @@ public class RuleTagChecker { Path ruleDocPage = pagesDirectory.resolve("pmd/rules/" + language + "/" + category.toLowerCase(Locale.ROOT) + ".md"); Set rules = getRules(ruleDocPage); return rules.contains(rule); + } else if (simpleRefMatcher.matches()) { + // can't check - would need to know the current language + category + return true; } return false; } diff --git a/pmd-doc/src/test/java/net/sourceforge/pmd/docs/RuleTagCheckerTest.java b/pmd-doc/src/test/java/net/sourceforge/pmd/docs/RuleTagCheckerTest.java index 3b9a6fc777..ccc82a54d8 100644 --- a/pmd-doc/src/test/java/net/sourceforge/pmd/docs/RuleTagCheckerTest.java +++ b/pmd-doc/src/test/java/net/sourceforge/pmd/docs/RuleTagCheckerTest.java @@ -18,12 +18,18 @@ public class RuleTagCheckerTest { RuleTagChecker checker = new RuleTagChecker(FileSystems.getDefault().getPath("src/test/resources/ruletagchecker")); List issues = checker.check(); - Assert.assertEquals(3, issues.size()); - Assert.assertEquals("ruletag-examples.md: 8: Rule tag for java/bestpractices/AvoidPrintStackTrace is not closed properly", + Assert.assertEquals(7, issues.size()); + Assert.assertEquals("ruletag-examples.md: 9: Rule tag for \"java/bestpractices/AvoidPrintStackTrace\" is not closed properly", issues.get(0)); - Assert.assertEquals("ruletag-examples.md:10: Rule java/notexistingcategory/AvoidPrintStackTrace is not found", + Assert.assertEquals("ruletag-examples.md:12: Rule \"java/notexistingcategory/AvoidPrintStackTrace\" is not found", issues.get(1)); - Assert.assertEquals("ruletag-examples.md:12: Rule java/bestpractices/NotExistingRule is not found", + Assert.assertEquals("ruletag-examples.md:14: Rule \"java/bestpractices/NotExistingRule\" is not found", issues.get(2)); + Assert.assertEquals("ruletag-examples.md:16: Rule tag for \"java/bestpractices/OtherRule has a missing quote", + issues.get(3)); + Assert.assertEquals("ruletag-examples.md:17: Rule tag for java/bestpractices/OtherRule\" has a missing quote", + issues.get(4)); + Assert.assertEquals("ruletag-examples.md:21: Rule tag for \"OtherRule has a missing quote", issues.get(5)); + Assert.assertEquals("ruletag-examples.md:22: Rule tag for OtherRule\" has a missing quote", issues.get(6)); } } diff --git a/pmd-doc/src/test/resources/expected/java.md b/pmd-doc/src/test/resources/expected/java.md index 9003990f25..a20c0aa803 100644 --- a/pmd-doc/src/test/resources/expected/java.md +++ b/pmd-doc/src/test/resources/expected/java.md @@ -11,7 +11,7 @@ folder: pmd/rules {% include callout.html content="Sample ruleset to test rule doc generation. Here might be <script>alert('XSS');</script> as well. And "quotes". Inside CDATA <script>alert('XSS');</script>." %} -* [DeprecatedSample](pmd_rules_java_sample.html#deprecatedsample): Deprecated Just some description of a deprecated rule. +* [DeprecatedSample](pmd_rules_java_sample.html#deprecatedsample): Deprecated Just some description of a deprecated rule. RuleTag with quotes: Use {% rule "RenamedRule" %} ins... * [JumbledIncrementer](pmd_rules_java_sample.html#jumbledincrementer): Avoid jumbled loop incrementers - its usually a mistake, and is confusing even if intentional. * [MovedRule](pmd_rules_java_sample.html#movedrule): Deprecated The rule has been moved to another ruleset. Use instead [JumbledIncrementer](pmd_rules_java_sample2.html#jumbledincrementer). * [OverrideBothEqualsAndHashcode](pmd_rules_java_sample.html#overridebothequalsandhashcode): Override both 'public boolean Object.equals(Object other)', and 'public int Object.hashCode()', o... diff --git a/pmd-doc/src/test/resources/expected/sample.md b/pmd-doc/src/test/resources/expected/sample.md index 79b91c61a8..ff3c6fc81d 100644 --- a/pmd-doc/src/test/resources/expected/sample.md +++ b/pmd-doc/src/test/resources/expected/sample.md @@ -19,6 +19,14 @@ language: Java Just some description of a deprecated rule. +RuleTag with quotes: Use {% rule "RenamedRule" %} instead. + +RuleTag without quotes: Use {% rule RenamedRule %} instead. + +RuleTag with full category and quotes: Use {% rule "java/sample/RenamedRule" %} instead. + +RuleTag with full category and without quotes: Use {% rule java/sample/RenamedRule %} instead. + **This rule is defined by the following XPath expression:** ``` xpath //ForStatement diff --git a/pmd-doc/src/test/resources/rulesets/ruledoctest/sample.xml b/pmd-doc/src/test/resources/rulesets/ruledoctest/sample.xml index 78591da860..da81d702d1 100644 --- a/pmd-doc/src/test/resources/rulesets/ruledoctest/sample.xml +++ b/pmd-doc/src/test/resources/rulesets/ruledoctest/sample.xml @@ -194,6 +194,14 @@ public class JumbledIncrementerRule1 { deprecated="true"> Just some description of a deprecated rule. + +RuleTag with quotes: Use {% rule "RenamedRule" %} instead. + +RuleTag without quotes: Use {% rule RenamedRule %} instead. + +RuleTag with full category and quotes: Use {% rule "java/sample/RenamedRule" %} instead. + +RuleTag with full category and without quotes: Use {% rule java/sample/RenamedRule %} instead. 3 diff --git a/pmd-doc/src/test/resources/ruletagchecker/docs/pages/ruletag-examples.md b/pmd-doc/src/test/resources/ruletagchecker/docs/pages/ruletag-examples.md index 89bdeb6a01..ca33c366c3 100644 --- a/pmd-doc/src/test/resources/ruletagchecker/docs/pages/ruletag-examples.md +++ b/pmd-doc/src/test/resources/ruletagchecker/docs/pages/ruletag-examples.md @@ -4,9 +4,19 @@ permalink: rule_tag_samples.html --- This is a link to the rule AvoidPrintStackTrace: {% rule "java/bestpractices/AvoidPrintStackTrace" %}. +This is a link to the rule AvoidPrintStackTrace without quotes: {% rule java/bestpractices/AvoidPrintStackTrace %}. This is the same link, but the rule tag is not closed properly: {% rule "java/bestpractices/AvoidPrintStackTrace" %). -Now this is link to a rule inside a category, which doesn't exist: {% rule "java/notexistingcategory/AvoidPrintStackTrace" %}. +Now this is link to a rule inside a category, which doesn't exist: +{% rule "java/notexistingcategory/AvoidPrintStackTrace" %}. This is link to a rule, which doesn't exist: {% rule "java/bestpractices/NotExistingRule" %}. + +This is a rule tag, which has only one quote: {% rule "java/bestpractices/OtherRule %}. +This is a rule tag, which has only one quote: {% rule java/bestpractices/OtherRule" %}. + +This is a rule tag, that references a rule in the same category: {% rule OtherRule %} (correct). +This is a rule tag, that references a rule in the same category: {% rule "OtherRule" %} (correct). +This is a rule tag, that references a rule in the same category: {% rule "OtherRule %} (missing quote). +This is a rule tag, that references a rule in the same category: {% rule OtherRule" %} (missing quote). diff --git a/pmd-java/src/main/resources/category/java/bestpractices.xml b/pmd-java/src/main/resources/category/java/bestpractices.xml index 272db05754..9c2dbf7138 100644 --- a/pmd-java/src/main/resources/category/java/bestpractices.xml +++ b/pmd-java/src/main/resources/category/java/bestpractices.xml @@ -1059,7 +1059,7 @@ String name, Position literals first in comparisons, if the second argument is null then NullPointerExceptions can be avoided, they will just return false. -This rule is replaced by the more general rule {% rule "LiteralsFirstInComparisons %}. +This rule is replaced by the more general rule {% rule "LiteralsFirstInComparisons" %}. 3 @@ -1084,7 +1084,7 @@ class Foo { Position literals first in comparisons, if the second argument is null then NullPointerExceptions can be avoided, they will just return false. -This rule is replaced by the more general rule {% rule "LiteralsFirstInComparisons %}. +This rule is replaced by the more general rule {% rule "LiteralsFirstInComparisons" %}. 3