diff --git a/.travis/build-deploy.sh b/.travis/build-deploy.sh index f39a5e20b..4f15d505c 100755 --- a/.travis/build-deploy.sh +++ b/.travis/build-deploy.sh @@ -3,6 +3,25 @@ set -e source .travis/common-functions.sh +function push_docs() { + if git diff --quiet docs; then + echo "No changes in docs..." + else + echo "Found changes in docs..." + + if [ "$TRAVIS_BRANCH" == "master" ]; then + git config user.name "Travis CI (pmd-bot)" + git config user.email "adangel+pmd-bot@users.sourceforge.net" + git add -A docs + git commit -m "Update documentation" + git push git@github.com:pmd/pmd.git HEAD:master + else + echo "Not on master branch, won't commit+push" + fi + fi +} + + VERSION=$(./mvnw -q -Dexec.executable="echo" -Dexec.args='${project.version}' --non-recursive org.codehaus.mojo:exec-maven-plugin:1.5.0:exec | tail -1) echo "Building PMD ${VERSION} on branch ${TRAVIS_BRANCH}" @@ -19,6 +38,7 @@ elif travis_isPush; then elif [[ "$VERSION" == *-SNAPSHOT ]]; then echo "This is a snapshot build" ./mvnw deploy -Possrh -B -V + push_docs else # other build. Can happen during release: the commit with a non snapshot version is built, but not from the tag. echo "This is some other build, probably during release: commit with a non-snapshot version on branch master..." diff --git a/docs/_data/sidebars/pmd_sidebar.yml b/docs/_data/sidebars/pmd_sidebar.yml index 7ea20c072..efb51ff2b 100644 --- a/docs/_data/sidebars/pmd_sidebar.yml +++ b/docs/_data/sidebars/pmd_sidebar.yml @@ -3,7 +3,7 @@ entries: - title: sidebar - product: PMD Project + product: PMD version: "!PMD_VERSION!" folders: @@ -79,7 +79,7 @@ entries: url: /pmd_rules_java.html output: web, pdf - title: JavaScript Rules - url: /pmd_rules_javascript.html + url: /pmd_rules_ecmascript.html output: web, pdf - title: JSP Rules url: /pmd_rules_jsp.html @@ -87,18 +87,27 @@ entries: - title: PLSQL Rules url: /pmd_rules_plsql.html output: web, pdf + - title: VisualForce Rules + url: /pmd_rules_vf.html + output: web, pdf - title: Apache Velocity Rules url: /pmd_rules_vm.html output: web, pdf - - title: XML and XSL Rules + - title: XML Rules url: /pmd_rules_xml.html output: web, pdf + - title: XSL Rules + url: /pmd_rules_xsl.html + output: web, pdf - title: Language Specific Documentation output: web, pdf folderitems: - title: JSP Support url: /pmd_languages_jsp.html output: web, pdf + - title: Java code metrics + url: /pmd_java_metrics_index.html + output: web, pdf - title: Developer Documentation output: web, pdf folderitems: @@ -138,9 +147,6 @@ entries: - title: Using code metrics in custom rules url: /pmd_devdocs_metrics_howto.html output: web, pdf - - title: Index of code metrics - url: /pmd_devdocs_metrics_index.html - output: web, pdf - title: Making Rulesets url: /pmd_devdocs_making_rulesets.html output: web, pdf diff --git a/docs/_includes/sidebar.html b/docs/_includes/sidebar.html index 235ce7357..30adf8722 100644 --- a/docs/_includes/sidebar.html +++ b/docs/_includes/sidebar.html @@ -12,7 +12,7 @@ {% if folderitem.output contains "web" %} {% if folderitem.external_url %}
  • {{folderitem.title}}
  • - {% elsif page.url == folderitem.url %} + {% elsif page.url == folderitem.url or page.sidebaractiveurl == folderitem.url %}
  • {{folderitem.title}}
  • {% else %}
  • {{folderitem.title}}
  • diff --git a/docs/_layouts/page.html b/docs/_layouts/page.html index 78a082ab1..8e65184b0 100644 --- a/docs/_layouts/page.html +++ b/docs/_layouts/page.html @@ -41,7 +41,12 @@

    {{ page.title }}

    {% if site.github_editme_path %} - Edit me + {% assign editmepath = page.path %} + {% if page.editmepath %} + {% assign editmepath = page.editmepath %} + {% endif %} + + Edit me {% endif %} diff --git a/docs/pages/pmd/devdocs/metrics_howto.md b/docs/pages/pmd/devdocs/metrics_howto.md index 9eb3294a0..71eedd01a 100644 --- a/docs/pages/pmd/devdocs/metrics_howto.md +++ b/docs/pages/pmd/devdocs/metrics_howto.md @@ -1,9 +1,9 @@ --- title: Using code metrics in custom rules tags: [customizing] -summary: "PMD was recently enhanced with the ability to compute code metrics on Java code (the so-called Metrics -Framework). This framework provides developers with a straightforward interface to use code metrics in their rules, and -to extend the framework with their own custom metrics." +summary: "PMD was recently enhanced with the ability to compute code metrics on Java and Apex source (the so-called +Metrics Framework). This framework provides developers with a straightforward interface to use code metrics in their +rules, and to extend the framework with their own custom metrics." last_updated: July 20, 2017 sidebar: pmd_sidebar permalink: pmd_devdocs_metrics_howto.html @@ -13,4 +13,73 @@ folder: pmd/devdocs ## Using the metrics framework -## Writing custom metrics \ No newline at end of file +{%include note.html content="Using the metrics framework is for now restricted to Java rules (with plans to support +XPath rules later)." %} + +To use the metrics framework in a custom rule, the first thing to do would be to enable metrics by adding the +`metrics="true"` attribute to your rule's XML element. + +{%include note.html content="The following explains how to use the Java metrics framework. The Apex framework +differs only by the name of its classes." %} + +In PMD's Metrics framework, a metric is an operation that can be carried out on nodes of a certain type and produces +a numeric result. In the Java framework, metrics can be computed on operation declaration nodes (constructor and +method declaration), and type declaration nodes (class, interface, enum, and annotation declarations). A metric +object in the framework can only handle either types or operations, but not both. + +The framework provides a library of already implemented metrics. These metrics are referenced by `MetricKey` objects, +which are listed in two public enums: `JavaClassMetricKey` and `JavaOperationMetricKey`. Metric keys wrap a metric, and +know which type of node their metric can be computed on. That way, you cannot compute an operation metric on a class +declaration node. Metrics that can be computed on operation and type declarations (e.g. NCSS) have one metric key in +each enum. + +The static façade class `JavaMetrics` is the only entry point to compute metrics in the Java framework. +This class provides the method `get` and its overloads. The following sections describes the interface of this class. + +### Basic usage + +The simplest overloads of `JavaMetrics.get` take two parameters: **a `MetricKey` and a node of the corresponding type.** +Say you want to write a rule to report methods that have a high cyclomatic complexity. In your rule's visitor, you +can get the value of Cyclo for a method node like so: +```java +public Object visit(ASTMethodDeclaration node, Object data) { + int cyclo = (int) JavaMetrics.get(JavaOperationMetricKey.CYCLO, node); + if (cyclo > 10) { + // add violation + } + return data; +} +``` + +The same goes for class metrics: you select one among `JavaClassMetricKey`'s constants and pass it along with the node + to `JavaMetrics.get`. + +### Capability checking + +Metrics are not necessarily computable on any node of the type they handle. For example, Cyclo cannot be computed on +abstract methods. Metric keys provides a `supports(Node)` boolean method to find out if the metric can be computed on +the specified node. **If the metric cannot be computed on the given node, `JavaMetrics.get` will return `Double.NaN` .** +If you're concerned about that, you can condition your call on whether the node is supported or not: +```java +public Object visit(ASTMethodDeclaration node, Object data) { + if (JavaOperationMetricKey.CYCLO.supports(node)) { + int cyclo = (int) JavaMetrics.get(JavaOperationMetricKey.CYCLO, node); + if (cyclo > 10) { + // add violation + } + return data; + } +} +``` + +### Metric versions + +{%include important.html content="Metric versions are about to be revamped into options that can be combined +together." %} + + +### Result options + +## Writing custom metrics + +{%include warning.html content="WIP" %} diff --git a/docs/pages/pmd/devdocs/metrics_index.md b/docs/pages/pmd/devdocs/metrics_index.md deleted file mode 100644 index 4f558f2a6..000000000 --- a/docs/pages/pmd/devdocs/metrics_index.md +++ /dev/null @@ -1,111 +0,0 @@ ---- -title: Index of code metrics -tags: [customizing] -summary: "Index of the code metrics available out of the box to Java rule developers." -last_updated: July 20, 2017 -sidebar: pmd_sidebar -permalink: pmd_devdocs_metrics_index.html -folder: pmd/devdocs -toc: - minimumHeaders: 8 ---- -# Index of code metrics - -## Access to Foreign Data (ATFD) - -*Operation metric, class metric.* - -### Description - - -## Cyclomatic Complexity (CYCLO) - -*Operation metric.* Can be calculated on any non-abstract method. - -### Description - -Number of independent paths through a block of code \[[Lanza05](#Lanza05)\]. -Formally, given that the control flow graph of the block has n vertices, e edges and p connected components, the -Cyclomatic complexity of the block is given by `CYCLO = e - n + 2p` \[[McCabe76](#McCabe76)\]. In practice it can be -calculated by counting control flow statements following the standard rules given below. - -The standard version of the metric complies with McCabe's original definition: - -* Methods have a base complexity of 1. -* +1 for every control flow statement (`if`, `case`, `catch`, `throw`, `do`, `while`, `for`, `break`, `continue`) and -conditional expression (`?:`) \[[Sonarqube](#Sonarqube)\]. Notice switch cases count as one, but not the switch itself: the point is that - a switch should have the same complexity value as the equivalent series of `if` statements. -* `else`, `finally` and `default` don't count; -* +1 for every boolean operator (`&&`, `||`) in the guard condition of a control flow statement. That's because -Java has short-circuit evaluation semantics for boolean operators, which makes every boolean operator kind of a -control flow statement in itself. - -### Versions - -* Version `CycloVersion#IGNORE_BOOLEAN_PATHS`: Boolean operators are not counted, nor are empty - fall-through cases in `switch` statements. You can use this version to get results - similar to those of the old `StdCyclomaticComplexityRule`, which is to be replaced. - - -## Lines of Code (LoC) - -*Operation metric, class metric.* Can be calculated on any of those nodes. - -### Description - -Simply counts the number of lines of code the operation or class takes up in the source. This metric doesn't discount - comments or blank lines. - - -## Non-commenting source statements (NCSS) - -*Operation metric, class metric.* Can be calculated on any of those nodes. - -### Description - -Number of statements in a class or operation. That's roughly equivalent to counting the number of semicolons and -opening braces in the program. Comments and blank lines are ignored, and statements spread on multiple lines count as - only one (e.g. `int\n a;` counts a single statement). - -The standard version of the metric is based off JavaNCSS's version \[[JavaNcss](#JavaNcss)\]: - -* +1 for any of the following statements: `if`, `else`, `while`, `do`, `for`, `switch`, `break`, `continue`, `return`, -`throw`, `synchronized`, `catch`, `finally`. -* +1 for each assignment, variable declaration (except `for` loop initializers) or statement expression. We count -variables declared on the same line (e.g. `int a, b, c;`) as a single statement. -* Contrary to Sonarqube, but as JavaNCSS, we count type declarations (class, interface, enum, annotation), -and method and field declarations \[[Sonarqube](#Sonarqube)\]. -* Contrary to JavaNCSS, but as Sonarqube, we do not count package declaration and import declarations as statements. -This makes it easier to compare nested classes to outer classes. Besides, it makes for class metric results that -actually represent the size of the class and not of the file. If you don't like that behaviour, use the `JAVANCSS` -version. - - -### Versions - -* Version `NcssVersion#JAVANCSS`: Import and package statements are counted as well. This version fully complies with - JavaNCSS. - - -## Weighted Method Count (WMC) - -*Class metric.* Can be computed on classes and enums - -### Description - -Sum of the statistical complexity of the operations in the class. We use [CYCLO](#cyclomatic-complexity-cyclo) to -quantify the complexity of an operation \[[Lanza05](#Lanza05)\]. - -### Versions - -WMC uses the same versions as CYCLO, which have the effect of summing that version of CYCLO to calculate the metric. - -# References - -Lanza05: Lanza, Marinescu; Object-Oriented Metrics in Practice, 2005. - -McCabe76: McCabe, A Complexity Measure, in Proceedings of the 2nd ICSE (1976). - -Sonarqube: [Sonarqube online documentation.](https://docs.sonarqube.org/display/SONAR/Metric+Definitions) - -JavaNcss: [JavaNCSS online documentation.](http://www.kclee.de/clemens/java/javancss/) \ No newline at end of file diff --git a/docs/pages/pmd/languages/java_metrics_index.md b/docs/pages/pmd/languages/java_metrics_index.md new file mode 100644 index 000000000..f73b31b4e --- /dev/null +++ b/docs/pages/pmd/languages/java_metrics_index.md @@ -0,0 +1,228 @@ +--- +title: Index of code metrics +tags: [customizing] +summary: "Index of the code metrics available out of the box to Java rule developers." +last_updated: July 20, 2017 +sidebar: pmd_sidebar +permalink: pmd_java_metrics_index.html +folder: pmd/languages +toc: + minimumHeaders: 8 +--- +# Index of code metrics + +## Access to Foreign Data (ATFD) + +*Operation metric, class metric.* + +### Description + + +## Cyclomatic Complexity (CYCLO) + +*Operation metric.* Can be calculated on any non-abstract operation. + +### Description + +Number of independent paths through a block of code \[[Lanza05](#Lanza05)\]. +Formally, given that the control flow graph of the block has n vertices, e edges and p connected components, the +cyclomatic complexity of the block is given by `CYCLO = e - n + 2p` \[[McCabe76](#McCabe76)\]. In practice it can be +calculated by counting control flow statements following the standard rules given below. + +The standard version of the metric complies with McCabe's original definition: + +* Methods have a base complexity of 1. +* +1 for every control flow statement (`if`, `case`, `catch`, `throw`, `do`, `while`, `for`, `break`, `continue`) and + conditional expression (`?:`) \[[Sonarqube](#Sonarqube)\]. Notice switch cases count as one, but not the switch + itself: the point is that a switch should have the same complexity value as the equivalent series of `if` statements. +* `else`, `finally` and `default` don't count; +* +1 for every boolean operator (`&&`, `||`) in the guard condition of a control flow statement. That's because + Java has short-circuit evaluation semantics for boolean operators, which makes every boolean operator kind of a + control flow statement in itself. + +### Code examples + +```java +class Foo { + void baseCyclo() { // Cyclo = 1 + highCyclo(); + } + + void highCyclo() { // Cyclo = 10 + int x = 0, y = 2; + boolean a = false, b = true; + + if (a && (y == 1 ? b : true)) { // +3 + if (y == x) { // +1 + while (true) { // +1 + if (x++ < 20) { // +1 + break; // +1 + } + } + } else if (y == t && !d) { // +2 + x = a ? y : x; // +1 + } else { + x = 2; + } + } + } +} +``` +### Versions + +* Version `CycloVersion#IGNORE_BOOLEAN_PATHS`: Boolean operators are not counted, nor are empty + fall-through cases in `switch` statements. You can use this version to get results + similar to those of the old `StdCyclomaticComplexityRule`, which is to be replaced. + + +## Lines of Code (LoC) + +*Operation metric, class metric.* Can be calculated on any of those nodes. + +### Description + +Simply counts the number of lines of code the operation or class takes up in the source. This metric doesn't discount +comments or blank lines. See also [NCSS](#non-commenting-source-statements-ncss). + + +## Non-commenting source statements (NCSS) + +*Operation metric, class metric.* Can be calculated on any of those nodes. + +### Description + +Number of statements in a class or operation. That's roughly equivalent to counting the number of semicolons and +opening braces in the program. Comments and blank lines are ignored, and statements spread on multiple lines count as + only one (e.g. `int\n a;` counts a single statement). + +The standard version of the metric is based off JavaNCSS's version \[[JavaNcss](#JavaNcss)\]: + +* +1 for any of the following statements: `if`, `else`, `while`, `do`, `for`, `switch`, `break`, `continue`, `return`, + `throw`, `synchronized`, `catch`, `finally`. +* +1 for each assignment, variable declaration (except `for` loop initializers) or statement expression. We count + variables declared on the same line (e.g. `int a, b, c;`) as a single statement. +* Contrary to Sonarqube, but as JavaNCSS, we count type declarations (class, interface, enum, annotation), + and method and field declarations \[[Sonarqube](#Sonarqube)\]. +* Contrary to JavaNCSS, but as Sonarqube, we do not count package declaration and import declarations as statements. + This makes it easier to compare nested classes to outer classes. Besides, it makes for class metric results that + actually represent the size of the class and not of the file. If you don't like that behaviour, use the `JAVANCSS` + version. + +### Code example +```java +import java.util.Collections; // +0 +import java.io.IOException; // +0 + +class Foo { // +1, total Ncss = 12 + + public void bigMethod() // +1 + throws IOException { + int x = 0, y = 2; // +1 + boolean a = false, b = true; // +1 + + if (a || b) { // +1 + try { // +1 + do { // +1 + x += 2; // +1 + } while (x < 12); + + System.exit(0); // +1 + } catch (IOException ioe) { // +1 + throw new PatheticFailException(ioe); // +1 + } + } else { + assert false; // +1 + } + } +} +``` + + +### Versions + +* Version `NcssVersion#JAVANCSS`: Import and package statements are counted as well. This version fully complies with + JavaNCSS. + +## NPath complexity (NPath) + +*Operation metric.* Can be computed on any non-abstract operation. + +### Description + +Number of acyclic execution paths through a piece of code. This is related to cyclomatic complexity, but the two +metrics don't count the same thing: NPath counts the number of distinct *full* paths from the beginning to the end of +the method, while Cyclo only counts the number of decision points. NPath is not computed as simply as Cyclo. With +NPath, two decision points appearing sequentially have their complexity multiplied. + +The fact that NPath multiplies the complexity of statements makes it grow exponentially: 10 `if` - `else` statements in +a row would give an NPath of 1024, while Cyclo would evaluate to 20. Methods with an NPath complexity over 200 are +generally considered too complex. + +We compute NPath recursively, with the following set of rules: +* An empty block has a complexity of 1. +* The complexity of a block is the product of the NPath complexity of its statements, calculated as follows: + * The complexity of `for`, `do` and `while` statements is 1, plus the complexity of the block, plus the complexity of + the guard condition. + * The complexity of a cascading `if` statement (`if .. else if ..`) is the number of `if` statements in the chain, + plus the complexity of their guard condition, plus the complexity of the unguarded `else` block (or 1 if there is + none). + * The complexity of a `switch` statement is the number of cases, plus the complexity of each `case` block. It's + equivalent to the complexity of the equivalent cascade of `if` statements. + * The complexity of a ternary expression (`?:`) is the complexity of the guard condition, plus the + complexity of both expressions. It's equivalent to the complexity of the equivalent `if .. else` construct. + * The complexity of a `try .. catch` statement is the complexity of the `try` block, plus the complexity of each + catch block. + * The complexity of a `return` statement is the complexity of the expression (or 1 if there is none). + * All other statements have a complexity of 1 and are discarded from the product. + +### Code example + +```java +void fun(boolean a, boolean b, boolean c) { // NPath = 6 + + // block #0 + + if (a) { + // block #1 + } else { + // block #2 + } + + // block #3 + + if (b) { + // block #4 + } else if (c) { + // block #5 + } + + // block #6 +} +``` +After block 0, the control flow can either execute block 1 or 2 before jumping to block 3. From block three, the +control flow will again have the choice between blocks 4 and 5 before jumping to block 6. The first `if` offers 2 +choices, the second offers 3, so the cyclomatic complexity of this method is 2 + 3 = 5. NPath, however, sees 2 * 3 = +6 full paths from the beginning to the end. + +## Weighted Method Count (WMC) + +*Class metric.* Can be computed on classes and enums + +### Description + +Sum of the statistical complexity of the operations in the class. We use [CYCLO](#cyclomatic-complexity-cyclo) to +quantify the complexity of an operation \[[Lanza05](#Lanza05)\]. + +### Versions + +WMC uses the same versions as CYCLO, which have the effect of summing that version of CYCLO to calculate the metric. + +# References + +Lanza05: Lanza, Marinescu; Object-Oriented Metrics in Practice, 2005. + +McCabe76: McCabe, A Complexity Measure, in Proceedings of the 2nd ICSE (1976). + +Sonarqube: [Sonarqube online documentation.](https://docs.sonarqube.org/display/SONAR/Metric+Definitions) + +JavaNcss: [JavaNCSS online documentation.](http://www.kclee.de/clemens/java/javancss/) \ No newline at end of file diff --git a/docs/pages/pmd/rules/apex.md b/docs/pages/pmd/rules/apex.md index a3210e3c0..6c4a2ae36 100644 --- a/docs/pages/pmd/rules/apex.md +++ b/docs/pages/pmd/rules/apex.md @@ -1,43 +1,60 @@ --- title: Apex Rules -tags: [languages] -summary: PMD Apex Rules -sidebar: pmd_sidebar permalink: pmd_rules_apex.html -last_updated: July 3, 2016 folder: pmd/rules --- +List of rulesets and rules contained in each ruleset. + +* [ApexUnit](pmd_rules_apex_apexunit.html): These rules deal with different problems that can occur with Apex unit tests. +* [Braces](pmd_rules_apex_braces.html): The Braces ruleset contains rules regarding the use and placement of braces. +* [Complexity](pmd_rules_apex_complexity.html): The Complexity ruleset contains rules that find problems related to code size or complexity. +* [Performance](pmd_rules_apex_performance.html): The Performance ruleset contains a collection of good practices which should be followed. +* [Security](pmd_rules_apex_security.html): These rules deal with different security problems that can occur within Apex. +* [Style](pmd_rules_apex_style.html): The Style Ruleset contains rules regarding preferred usage of names and identifiers. + +## ApexUnit +* [ApexUnitTestClassShouldHaveAsserts](pmd_rules_apex_apexunit.html#apexunittestclassshouldhaveasserts): Apex unit tests should include at least one assertion. This makes the tests more robust, and usi... +* [ApexUnitTestShouldNotUseSeeAllDataTrue](pmd_rules_apex_apexunit.html#apexunittestshouldnotuseseealldatatrue): Apex unit tests should not use @isTest(seeAllData=true) because it opens up the existing database... + +## Braces +* [ForLoopsMustUseBraces](pmd_rules_apex_braces.html#forloopsmustusebraces): Avoid using 'for' statements without using surrounding braces. If the code formatting orindentati... +* [IfElseStmtsMustUseBraces](pmd_rules_apex_braces.html#ifelsestmtsmustusebraces): Avoid using if..else statements without using surrounding braces. If the code formattingor indent... +* [IfStmtsMustUseBraces](pmd_rules_apex_braces.html#ifstmtsmustusebraces): Avoid using if statements without using braces to surround the code block. If the codeformatting ... +* [WhileLoopsMustUseBraces](pmd_rules_apex_braces.html#whileloopsmustusebraces): Avoid using 'while' statements without using braces to surround the code block. If the codeformat... + +## Complexity +* [AvoidDeeplyNestedIfStmts](pmd_rules_apex_complexity.html#avoiddeeplynestedifstmts): Avoid creating deeply nested if-then statements since they are harder to read and error-prone to ... +* [ExcessiveClassLength](pmd_rules_apex_complexity.html#excessiveclasslength): Excessive class file lengths are usually indications that the class may be burdened with excessiv... +* [ExcessiveParameterList](pmd_rules_apex_complexity.html#excessiveparameterlist): Methods with numerous parameters are a challenge to maintain, especially if most of them share th... +* [ExcessivePublicCount](pmd_rules_apex_complexity.html#excessivepubliccount): Classes with large numbers of public methods and attributes require disproportionate testing effo... +* [NcssConstructorCount](pmd_rules_apex_complexity.html#ncssconstructorcount): This rule uses the NCSS (Non-Commenting Source Statements) algorithm to determine the number of l... +* [NcssMethodCount](pmd_rules_apex_complexity.html#ncssmethodcount): This rule uses the NCSS (Non-Commenting Source Statements) algorithm to determine the number of l... +* [NcssTypeCount](pmd_rules_apex_complexity.html#ncsstypecount): This rule uses the NCSS (Non-Commenting Source Statements) algorithm to determine the number of l... +* [StdCyclomaticComplexity](pmd_rules_apex_complexity.html#stdcyclomaticcomplexity): Complexity directly affects maintenance costs is determined by the number of decision points in a... +* [TooManyFields](pmd_rules_apex_complexity.html#toomanyfields): Classes that have too many fields can become unwieldy and could be redesigned to have fewer field... + +## Performance +* [AvoidDmlStatementsInLoops](pmd_rules_apex_performance.html#avoiddmlstatementsinloops): Avoid DML statements inside loops to avoid hitting the DML governor limit. Instead, try to batch ... +* [AvoidSoqlInLoops](pmd_rules_apex_performance.html#avoidsoqlinloops): New objects created within loops should be checked to see if they can created outside them and re... + +## Security +* [ApexBadCrypto](pmd_rules_apex_security.html#apexbadcrypto): The rule makes sure you are using randomly generated IVs and keys for 'Crypto' calls.Hard-wiring ... +* [ApexCRUDViolation](pmd_rules_apex_security.html#apexcrudviolation): The rule validates you are checking for access permissions before a SOQL/SOSL/DML operation.Since... +* [ApexCSRF](pmd_rules_apex_security.html#apexcsrf): Check to avoid making DML operations in Apex class constructor/init method. This preventsmodifica... +* [ApexDangerousMethods](pmd_rules_apex_security.html#apexdangerousmethods): Checks against calling dangerous methods.For the time being, it reports: Against 'FinancialForce'... +* [ApexInsecureEndpoint](pmd_rules_apex_security.html#apexinsecureendpoint): Checks against accessing endpoints under plain http. You should always usehttps for security. +* [ApexOpenRedirect](pmd_rules_apex_security.html#apexopenredirect): Checks against redirects to user-controlled locations. This prevents attackers fromredirecting us... +* [ApexSharingViolations](pmd_rules_apex_security.html#apexsharingviolations): Detect classes declared without explicit sharing mode if DML methods are used. Thisforces the dev... +* [ApexSOQLInjection](pmd_rules_apex_security.html#apexsoqlinjection): Detects the usage of untrusted / unescaped variables in DML queries. +* [ApexSuggestUsingNamedCred](pmd_rules_apex_security.html#apexsuggestusingnamedcred): Detects hardcoded credentials used in requests to an endpoint.You should refrain from hardcoding ... +* [ApexXSSFromEscapeFalse](pmd_rules_apex_security.html#apexxssfromescapefalse): Reports on calls to 'addError' with disabled escaping. The message passed to 'addError'will be di... +* [ApexXSSFromURLParam](pmd_rules_apex_security.html#apexxssfromurlparam): Makes sure that all values obtained from URL parameters are properly escaped / sanitizedto avoid ... + +## Style +* [AvoidGlobalModifier](pmd_rules_apex_style.html#avoidglobalmodifier): Global classes should be avoided (especially in managed packages) as they can never be deleted or... +* [AvoidLogicInTrigger](pmd_rules_apex_style.html#avoidlogicintrigger): As triggers do not allow methods like regular classes they are less flexible and suited to apply ... +* [ClassNamingConventions](pmd_rules_apex_style.html#classnamingconventions): Class names should always begin with an upper case character. +* [MethodNamingConventions](pmd_rules_apex_style.html#methodnamingconventions): Method names should always begin with a lower case character, and should not contain underscores. +* [MethodWithSameNameAsEnclosingClass](pmd_rules_apex_style.html#methodwithsamenameasenclosingclass): Non-constructor methods should not have the same name as the enclosing class. +* [VariableNamingConventions](pmd_rules_apex_style.html#variablenamingconventions): A variable naming conventions rule - customize this to your liking. Currently, itchecks for fina... -## PMD Has the Following Rules for Apex *(Salesforce)* - -### Performance - -| Rule | Description | -| --- | --- | -| AvoidSoqlInLoops | New objects created within loops should be checked to see if they can created outside them and reused. | -| AvoidDmlStatementsInLoops | Avoid DML statements inside loops to avoid hitting the DML governor limit. Instead, try to batch up the data into a list and invoke your DML once on that list of data outside the loop. | - -### Complexity - -| Rule | Description | -| --- | --- | -| AvoidDeeplyNestedIfStmts | Avoid creating deeply nested if-then statements since they are harder to read and error-prone to maintain. | -| ExcessiveParameterList | Methods with numerous parameters are a challenge to maintain, especially if most of them share the same datatype. These situations usually denote the need for new objects to wrap the numerous parameters. | -| ExcessiveClassLength | Excessive class file lengths are usually indications that the class may be burdened with excessive responsibilities that could be provided by external classes or functions. In breaking these methods apart the code becomes more managable and ripe for reuse. | -| NcssMethodCount | This rule uses the NCSS (Non-Commenting Source Statements) algorithm to determine the number of lines of code for a given method. NCSS ignores comments, and counts actual statements. Using this algorithm, lines of code that are split are counted as one. | -| NcssTypeCount | This rule uses the NCSS (Non-Commenting Source Statements) algorithm to determine the number of lines of code for a given type. NCSS ignores comments, and counts actual statements. Using this algorithm, lines of code that are split are counted as one. | -| NcssConstructorCount | This rule uses the NCSS (Non-Commenting Source Statements) algorithm to determine the number of lines of code for a given constructor. NCSS ignores comments, and counts actual statements. Using this algorithm, lines of code that are split are counted as one. | -| StdCyclomaticComplexity | Complexity directly affects maintenance costs is determined by the number of decision points in a method plus one for the method entry. The decision points include ‘if’, ‘while’, ‘for’, and ‘case labels’ calls. Generally, numbers ranging from 1-4 denote low complexity, 5-7 denote moderate complexity, 8-10 denote high complexity, and 11+ is very high complexity. | -| TooManyFields | Classes that have too many fields can become unwieldy and could be redesigned to have fewer fields, possibly through grouping related fields in new objects. For example, a class with individual city/state/zip fields could park them within a single Address field. | -| ExcessivePublicCount | Classes with large numbers of public methods and attributes require disproportionate testing efforts since combinational side effects grow rapidly and increase risk. Refactoring these classes into smaller ones not only increases testability and reliability but also allows new variations to be developed easily. | - -### Style - -| Rule | Description | -| --- | --- | -| VariableNamingConventions | A variable naming conventions rule - customize this to your liking. Currently, it checks for final variables that should be fully capitalized and non-final variables that should not include underscores. | -| MethodNamingConventions | Method names should always begin with a lower case character, and should not contain underscores. | -| ClassNamingConventions | Class names should always begin with an upper case character. | -| MethodWithSameNameAsEnclosingClass | Non-constructor methods should not have the same name as the enclosing class. | -| AvoidLogicInTrigger | As triggers do not allow methods like regular classes they are less flexible and suited to apply good encapsulation style. Therefore delegate the triggers work to a regular class (often called Trigger handler class). See more here: Trigger Frameworks and Apex Trigger Best Practices | -| AvoidGlobalModifier | Global classes should be avoided (especially in managed packages) as they can never be deleted or changed in signature. Always check twice if something needs to be global. Many interfaces (e.g. Batch) required global modifiers in the past but don’t require this anymore. Don’t look yourself in. | diff --git a/docs/pages/pmd/rules/apex/apexunit.md b/docs/pages/pmd/rules/apex/apexunit.md new file mode 100644 index 000000000..ac9c8510c --- /dev/null +++ b/docs/pages/pmd/rules/apex/apexunit.md @@ -0,0 +1,73 @@ +--- +title: ApexUnit +summary: These rules deal with different problems that can occur with Apex unit tests. +permalink: pmd_rules_apex_apexunit.html +folder: pmd/rules/apex +sidebaractiveurl: /pmd_rules_apex.html +editmepath: ../pmd-apex/src/main/resources/rulesets/apex/apexunit.xml +--- +## ApexUnitTestClassShouldHaveAsserts + +**Since:** PMD 5.5.1 + +**Priority:** Medium (3) + +Apex unit tests should include at least one assertion. This makes the tests more robust, and using assert + with messages provide the developer a clearer idea of what the test does. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.apex.rule.apexunit.ApexUnitTestClassShouldHaveAsserts](https://github.com/pmd/pmd/blob/master/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/apexunit/ApexUnitTestClassShouldHaveAsserts.java) + +**Example(s):** + +``` +@isTest +public class Foo { + public static testMethod void testSomething() { + Account a = null; + // This is better than having a NullPointerException + // System.assertNotEquals(a, null, 'account not found'); + a.toString(); + } +} +``` + +**This rule has the following properties:** + +|Name|Default Value|Description| +|----|-------------|-----------| +|cc_categories|[Style]|Code Climate Categories| +|cc_remediation_points_multiplier|1|Code Climate Remediation Points multiplier| +|cc_block_highlighting|false|Code Climate Block Highlighting| + +## ApexUnitTestShouldNotUseSeeAllDataTrue + +**Since:** PMD 5.5.1 + +**Priority:** Medium (3) + +Apex unit tests should not use @isTest(seeAllData=true) because it opens up the existing database data for unexpected modification by tests. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.apex.rule.apexunit.ApexUnitTestShouldNotUseSeeAllDataTrue](https://github.com/pmd/pmd/blob/master/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/apexunit/ApexUnitTestShouldNotUseSeeAllDataTrue.java) + +**Example(s):** + +``` +@isTest(seeAllData = true) +public class Foo { + public static testMethod void testSomething() { + Account a = null; + // This is better than having a NullPointerException + // System.assertNotEquals(a, null, 'account not found'); + a.toString(); + } +} +``` + +**This rule has the following properties:** + +|Name|Default Value|Description| +|----|-------------|-----------| +|cc_categories|[Style]|Code Climate Categories| +|cc_remediation_points_multiplier|1|Code Climate Remediation Points multiplier| +|cc_block_highlighting|false|Code Climate Block Highlighting| + diff --git a/docs/pages/pmd/rules/apex/braces.md b/docs/pages/pmd/rules/apex/braces.md new file mode 100644 index 000000000..7ecd2d09a --- /dev/null +++ b/docs/pages/pmd/rules/apex/braces.md @@ -0,0 +1,146 @@ +--- +title: Braces +summary: The Braces ruleset contains rules regarding the use and placement of braces. +permalink: pmd_rules_apex_braces.html +folder: pmd/rules/apex +sidebaractiveurl: /pmd_rules_apex.html +editmepath: ../pmd-apex/src/main/resources/rulesets/apex/braces.xml +--- +## ForLoopsMustUseBraces + +**Since:** PMD 5.6.0 + +**Priority:** Medium (3) + +Avoid using 'for' statements without using surrounding braces. If the code formatting or +indentation is lost then it becomes difficult to separate the code being controlled +from the rest. + +``` +//ForLoopStatement/BlockStatement[@CurlyBrace='false'] +| +//ForEachStatement/BlockStatement[@CurlyBrace='false'] +``` + +**Example(s):** + +``` +for (int i = 0; i < 42; i++) // not recommended + foo(); + +for (int i = 0; i < 42; i++) { // preferred approach + foo(); +} +``` + +**This rule has the following properties:** + +|Name|Default Value|Description| +|----|-------------|-----------| +|cc_categories|[Style]|Code Climate Categories| +|cc_remediation_points_multiplier|1|Code Climate Remediation Points multiplier| +|cc_block_highlighting|false|Code Climate Block Highlighting| + +## IfElseStmtsMustUseBraces + +**Since:** PMD 5.6.0 + +**Priority:** Medium (3) + +Avoid using if..else statements without using surrounding braces. If the code formatting +or indentation is lost then it becomes difficult to separate the code being controlled +from the rest. + +``` +//IfBlockStatement/BlockStatement[@CurlyBrace='false'][count(child::*) > 0] +| +//IfElseBlockStatement/BlockStatement[@CurlyBrace='false'][count(child::*) > 0] +``` + +**Example(s):** + +``` +// this is OK +if (foo) x++; + + // but this is not +if (foo) + x = x+1; + else + x = x-1; +``` + +**This rule has the following properties:** + +|Name|Default Value|Description| +|----|-------------|-----------| +|cc_categories|[Style]|Code Climate Categories| +|cc_remediation_points_multiplier|1|Code Climate Remediation Points multiplier| +|cc_block_highlighting|false|Code Climate Block Highlighting| + +## IfStmtsMustUseBraces + +**Since:** PMD 5.6.0 + +**Priority:** Medium (3) + +Avoid using if statements without using braces to surround the code block. If the code +formatting or indentation is lost then it becomes difficult to separate the code being +controlled from the rest. + +``` +//IfBlockStatement/BlockStatement[@CurlyBrace='false'] +``` + +**Example(s):** + +``` +if (foo) // not recommended + x++; + +if (foo) { // preferred approach + x++; +} +``` + +**This rule has the following properties:** + +|Name|Default Value|Description| +|----|-------------|-----------| +|cc_categories|[Style]|Code Climate Categories| +|cc_remediation_points_multiplier|1|Code Climate Remediation Points multiplier| +|cc_block_highlighting|false|Code Climate Block Highlighting| + +## WhileLoopsMustUseBraces + +**Since:** PMD 5.6.0 + +**Priority:** Medium (3) + +Avoid using 'while' statements without using braces to surround the code block. If the code +formatting or indentation is lost then it becomes difficult to separate the code being +controlled from the rest. + +``` +//WhileLoopStatement/BlockStatement[@CurlyBrace='false'] +``` + +**Example(s):** + +``` +while (true) // not recommended + x++; + +while (true) { // preferred approach + x++; +} +``` + +**This rule has the following properties:** + +|Name|Default Value|Description| +|----|-------------|-----------| +|cc_categories|[Style]|Code Climate Categories| +|cc_remediation_points_multiplier|1|Code Climate Remediation Points multiplier| +|cc_block_highlighting|false|Code Climate Block Highlighting| + diff --git a/docs/pages/pmd/rules/apex/complexity.md b/docs/pages/pmd/rules/apex/complexity.md new file mode 100644 index 000000000..7fb9358c9 --- /dev/null +++ b/docs/pages/pmd/rules/apex/complexity.md @@ -0,0 +1,383 @@ +--- +title: Complexity +summary: The Complexity ruleset contains rules that find problems related to code size or complexity. +permalink: pmd_rules_apex_complexity.html +folder: pmd/rules/apex +sidebaractiveurl: /pmd_rules_apex.html +editmepath: ../pmd-apex/src/main/resources/rulesets/apex/complexity.xml +--- +## AvoidDeeplyNestedIfStmts + +**Since:** PMD 5.5.0 + +**Priority:** Medium (3) + +Avoid creating deeply nested if-then statements since they are harder to read and error-prone to maintain. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.apex.rule.complexity.AvoidDeeplyNestedIfStmtsRule](https://github.com/pmd/pmd/blob/master/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/complexity/AvoidDeeplyNestedIfStmtsRule.java) + +**Example(s):** + +``` +public class Foo { + public void bar(Integer x, Integer y, Integer z) { + if (x>y) { + if (y>z) { + if (z==x) { + // !! too deep + } + } + } + } +} +``` + +**This rule has the following properties:** + +|Name|Default Value|Description| +|----|-------------|-----------| +|cc_categories|[Style]|Code Climate Categories| +|cc_remediation_points_multiplier|1|Code Climate Remediation Points multiplier| +|cc_block_highlighting|false|Code Climate Block Highlighting| +|problemDepth|3|The if statement depth reporting threshold| + +## ExcessiveClassLength + +**Since:** PMD 5.5.0 + +**Priority:** Medium (3) + +Excessive class file lengths are usually indications that the class may be burdened with excessive +responsibilities that could be provided by external classes or functions. In breaking these methods +apart the code becomes more managable and ripe for reuse. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.apex.rule.complexity.ExcessiveClassLengthRule](https://github.com/pmd/pmd/blob/master/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/complexity/ExcessiveClassLengthRule.java) + +**Example(s):** + +``` +public class Foo { + public void bar1() { + // 1000 lines of code + } + public void bar2() { + // 1000 lines of code + } + public void bar3() { + // 1000 lines of code + } + public void barN() { + // 1000 lines of code + } +} +``` + +**This rule has the following properties:** + +|Name|Default Value|Description| +|----|-------------|-----------| +|topscore||Top score value| +|minimum||Minimum reporting threshold| +|cc_categories|[Style]|Code Climate Categories| +|cc_remediation_points_multiplier|1|Code Climate Remediation Points multiplier| +|cc_block_highlighting|false|Code Climate Block Highlighting| +|sigma||Sigma value| + +## ExcessiveParameterList + +**Since:** PMD 5.5.0 + +**Priority:** Medium (3) + +Methods with numerous parameters are a challenge to maintain, especially if most of them share the +same datatype. These situations usually denote the need for new objects to wrap the numerous parameters. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.apex.rule.complexity.ExcessiveParameterListRule](https://github.com/pmd/pmd/blob/master/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/complexity/ExcessiveParameterListRule.java) + +**Example(s):** + +``` +// too many arguments liable to be mixed up +public void addPerson(int birthYear, int birthMonth, int birthDate, int height, int weight, int ssn) { + ... +} +// preferred approach +public void addPerson(Date birthdate, BodyMeasurements measurements, int ssn) { + ... +} +``` + +**This rule has the following properties:** + +|Name|Default Value|Description| +|----|-------------|-----------| +|topscore||Top score value| +|minimum||Minimum reporting threshold| +|cc_categories|[Style]|Code Climate Categories| +|cc_remediation_points_multiplier|1|Code Climate Remediation Points multiplier| +|cc_block_highlighting|false|Code Climate Block Highlighting| +|sigma||Sigma value| + +## ExcessivePublicCount + +**Since:** PMD 5.5.0 + +**Priority:** Medium (3) + +Classes with large numbers of public methods and attributes require disproportionate testing efforts +since combinational side effects grow rapidly and increase risk. Refactoring these classes into +smaller ones not only increases testability and reliability but also allows new variations to be +developed easily. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.apex.rule.complexity.ExcessivePublicCountRule](https://github.com/pmd/pmd/blob/master/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/complexity/ExcessivePublicCountRule.java) + +**Example(s):** + +``` +public class Foo { + public String value; + public Bar something; + public Variable var; + // [... more more public attributes ...] + + public void doWork() {} + public void doMoreWork() {} + public void doWorkAgain() {} + // [... more more public methods ...] +} +``` + +**This rule has the following properties:** + +|Name|Default Value|Description| +|----|-------------|-----------| +|topscore||Top score value| +|minimum||Minimum reporting threshold| +|cc_categories|[Style]|Code Climate Categories| +|cc_remediation_points_multiplier|1|Code Climate Remediation Points multiplier| +|cc_block_highlighting|false|Code Climate Block Highlighting| +|sigma||Sigma value| + +## NcssConstructorCount + +**Since:** PMD 5.5.0 + +**Priority:** Medium (3) + +This rule uses the NCSS (Non-Commenting Source Statements) algorithm to determine the number of lines +of code for a given constructor. NCSS ignores comments, and counts actual statements. Using this algorithm, +lines of code that are split are counted as one. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.apex.rule.complexity.NcssConstructorCountRule](https://github.com/pmd/pmd/blob/master/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/complexity/NcssConstructorCountRule.java) + +**Example(s):** + +``` +public class Foo extends Bar { + //this constructor only has 1 NCSS lines + public Foo() { + super(); + + + + + super.foo(); + } +} +``` + +**This rule has the following properties:** + +|Name|Default Value|Description| +|----|-------------|-----------| +|topscore||Top score value| +|minimum||Minimum reporting threshold| +|cc_categories|[Style]|Code Climate Categories| +|cc_remediation_points_multiplier|1|Code Climate Remediation Points multiplier| +|cc_block_highlighting|false|Code Climate Block Highlighting| +|sigma||Sigma value| + +## NcssMethodCount + +**Since:** PMD 5.5.0 + +**Priority:** Medium (3) + +This rule uses the NCSS (Non-Commenting Source Statements) algorithm to determine the number of lines +of code for a given method. NCSS ignores comments, and counts actual statements. Using this algorithm, +lines of code that are split are counted as one. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.apex.rule.complexity.NcssMethodCountRule](https://github.com/pmd/pmd/blob/master/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/complexity/NcssMethodCountRule.java) + +**Example(s):** + +``` +public class Foo extends Bar { + //this method only has 1 NCSS lines + public Integer methd() { + super.methd(); + + + + return 1; + } +} +``` + +**This rule has the following properties:** + +|Name|Default Value|Description| +|----|-------------|-----------| +|topscore||Top score value| +|minimum||Minimum reporting threshold| +|cc_categories|[Style]|Code Climate Categories| +|cc_remediation_points_multiplier|1|Code Climate Remediation Points multiplier| +|cc_block_highlighting|false|Code Climate Block Highlighting| +|sigma||Sigma value| + +## NcssTypeCount + +**Since:** PMD 5.5.0 + +**Priority:** Medium (3) + +This rule uses the NCSS (Non-Commenting Source Statements) algorithm to determine the number of lines +of code for a given type. NCSS ignores comments, and counts actual statements. Using this algorithm, +lines of code that are split are counted as one. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.apex.rule.complexity.NcssTypeCountRule](https://github.com/pmd/pmd/blob/master/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/complexity/NcssTypeCountRule.java) + +**Example(s):** + +``` +//this class only has 6 NCSS lines +public class Foo extends Bar { + public Foo() { + super(); + + + + + + super.foo(); + } +} +``` + +**This rule has the following properties:** + +|Name|Default Value|Description| +|----|-------------|-----------| +|topscore||Top score value| +|minimum||Minimum reporting threshold| +|cc_categories|[Style]|Code Climate Categories| +|cc_remediation_points_multiplier|1|Code Climate Remediation Points multiplier| +|cc_block_highlighting|false|Code Climate Block Highlighting| +|sigma||Sigma value| + +## StdCyclomaticComplexity + +**Since:** PMD 5.5.0 + +**Priority:** Medium (3) + +Complexity directly affects maintenance costs is determined by the number of decision points in a method +plus one for the method entry. The decision points include 'if', 'while', 'for', and 'case labels' calls. +Generally, numbers ranging from 1-4 denote low complexity, 5-7 denote moderate complexity, 8-10 denote +high complexity, and 11+ is very high complexity. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.apex.rule.complexity.StdCyclomaticComplexityRule](https://github.com/pmd/pmd/blob/master/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/complexity/StdCyclomaticComplexityRule.java) + +**Example(s):** + +``` +// This has a Cyclomatic Complexity = 12 + public class Foo { +1 public void example() { +2 if (a == b || (c == d && e == f)) { +3 if (a1 == b1) { + fiddle(); +4 } else if a2 == b2) { + fiddle(); + } else { + fiddle(); + } +5 } else if (c == d) { +6 while (c == d) { + fiddle(); + } +7 } else if (e == f) { +8 for (int n = 0; n < h; n++) { + fiddle(); + } + } else { + switch (z) { +9 case 1: + fiddle(); + break; +10 case 2: + fiddle(); + break; +11 case 3: + fiddle(); + break; +12 default: + fiddle(); + break; + } + } + } + } +``` + +**This rule has the following properties:** + +|Name|Default Value|Description| +|----|-------------|-----------| +|showMethodsComplexity|true|Add method average violations to the report| +|showClassesComplexity|true|Add class average violations to the report| +|cc_categories|[Style]|Code Climate Categories| +|cc_remediation_points_multiplier|1|Code Climate Remediation Points multiplier| +|cc_block_highlighting|false|Code Climate Block Highlighting| +|reportLevel|10|Cyclomatic Complexity reporting threshold| + +## TooManyFields + +**Since:** PMD 5.5.0 + +**Priority:** Medium (3) + +Classes that have too many fields can become unwieldy and could be redesigned to have fewer fields, +possibly through grouping related fields in new objects. For example, a class with individual +city/state/zip fields could park them within a single Address field. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.apex.rule.complexity.TooManyFieldsRule](https://github.com/pmd/pmd/blob/master/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/complexity/TooManyFieldsRule.java) + +**Example(s):** + +``` +public class Person { + // too many separate fields + int birthYear; + int birthMonth; + int birthDate; + float height; + float weight; +} + +public class Person { + // this is more manageable + Date birthDate; + BodyMeasurements measurements; +} +``` + +**This rule has the following properties:** + +|Name|Default Value|Description| +|----|-------------|-----------| +|cc_categories|[Style]|Code Climate Categories| +|cc_remediation_points_multiplier|1|Code Climate Remediation Points multiplier| +|cc_block_highlighting|false|Code Climate Block Highlighting| +|maxfields|15|Max allowable fields| + diff --git a/docs/pages/pmd/rules/apex/performance.md b/docs/pages/pmd/rules/apex/performance.md new file mode 100644 index 000000000..6779c939b --- /dev/null +++ b/docs/pages/pmd/rules/apex/performance.md @@ -0,0 +1,70 @@ +--- +title: Performance +summary: The Performance ruleset contains a collection of good practices which should be followed. +permalink: pmd_rules_apex_performance.html +folder: pmd/rules/apex +sidebaractiveurl: /pmd_rules_apex.html +editmepath: ../pmd-apex/src/main/resources/rulesets/apex/performance.xml +--- +## AvoidDmlStatementsInLoops + +**Since:** PMD 5.5.0 + +**Priority:** Medium (3) + +Avoid DML statements inside loops to avoid hitting the DML governor limit. Instead, try to batch up the data into a list and invoke your DML once on that list of data outside the loop. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.apex.rule.performance.AvoidDmlStatementsInLoopsRule](https://github.com/pmd/pmd/blob/master/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/performance/AvoidDmlStatementsInLoopsRule.java) + +**Example(s):** + +``` +public class Something { + public void foo() { + for (Integer i = 0; i < 151; i++) { + Account account; + ... + insert account; + } + } +} +``` + +**This rule has the following properties:** + +|Name|Default Value|Description| +|----|-------------|-----------| +|cc_categories|[Style]|Code Climate Categories| +|cc_remediation_points_multiplier|1|Code Climate Remediation Points multiplier| +|cc_block_highlighting|false|Code Climate Block Highlighting| + +## AvoidSoqlInLoops + +**Since:** PMD 5.5.0 + +**Priority:** Medium (3) + +New objects created within loops should be checked to see if they can created outside them and reused. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.apex.rule.performance.AvoidSoqlInLoopsRule](https://github.com/pmd/pmd/blob/master/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/performance/AvoidSoqlInLoopsRule.java) + +**Example(s):** + +``` +public class Something { + public static void main( String as[] ) { + for (Integer i = 0; i < 10; i++) { + List accounts = [SELECT Id FROM Account]; + } + } +} +``` + +**This rule has the following properties:** + +|Name|Default Value|Description| +|----|-------------|-----------| +|cc_categories|[Style]|Code Climate Categories| +|cc_remediation_points_multiplier|1|Code Climate Remediation Points multiplier| +|cc_block_highlighting|false|Code Climate Block Highlighting| + diff --git a/docs/pages/pmd/rules/apex/security.md b/docs/pages/pmd/rules/apex/security.md new file mode 100644 index 000000000..c27fdac3e --- /dev/null +++ b/docs/pages/pmd/rules/apex/security.md @@ -0,0 +1,358 @@ +--- +title: Security +summary: These rules deal with different security problems that can occur within Apex. +permalink: pmd_rules_apex_security.html +folder: pmd/rules/apex +sidebaractiveurl: /pmd_rules_apex.html +editmepath: ../pmd-apex/src/main/resources/rulesets/apex/security.xml +--- +## ApexBadCrypto + +**Since:** PMD 5.5.3 + +**Priority:** Medium (3) + +The rule makes sure you are using randomly generated IVs and keys for `Crypto` calls. +Hard-wiring these values greatly compromises the security of encrypted data. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.apex.rule.security.ApexBadCryptoRule](https://github.com/pmd/pmd/blob/master/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/security/ApexBadCryptoRule.java) + +**Example(s):** + +``` +public without sharing class Foo { + Blob hardCodedIV = Blob.valueOf('Hardcoded IV 123'); + Blob hardCodedKey = Blob.valueOf('0000000000000000'); + Blob data = Blob.valueOf('Data to be encrypted'); + Blob encrypted = Crypto.encrypt('AES128', hardCodedKey, hardCodedIV, data); +} +``` + +**This rule has the following properties:** + +|Name|Default Value|Description| +|----|-------------|-----------| +|cc_categories|[Style]|Code Climate Categories| +|cc_remediation_points_multiplier|1|Code Climate Remediation Points multiplier| +|cc_block_highlighting|false|Code Climate Block Highlighting| + +## ApexCRUDViolation + +**Since:** PMD 5.5.3 + +**Priority:** Medium (3) + +The rule validates you are checking for access permissions before a SOQL/SOSL/DML operation. +Since Apex runs in system mode not having proper permissions checks results in escalation of +privilege and may produce runtime errors. This check forces you to handle such scenarios. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.apex.rule.security.ApexCRUDViolationRule](https://github.com/pmd/pmd/blob/master/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/security/ApexCRUDViolationRule.java) + +**Example(s):** + +``` +public class Foo { + public Contact foo(String status, String ID) { + Contact c = [SELECT Status__c FROM Contact WHERE Id=:ID]; + + // Make sure we can update the database before even trying + if (!Schema.sObjectType.Contact.fields.Name.isUpdateable()) { + return null; + } + + c.Status__c = status; + update c; + return c; + } +} +``` + +**This rule has the following properties:** + +|Name|Default Value|Description| +|----|-------------|-----------| +|cc_categories|[Style]|Code Climate Categories| +|cc_remediation_points_multiplier|1|Code Climate Remediation Points multiplier| +|cc_block_highlighting|false|Code Climate Block Highlighting| + +## ApexCSRF + +**Since:** PMD 5.5.3 + +**Priority:** Medium (3) + +Check to avoid making DML operations in Apex class constructor/init method. This prevents +modification of the database just by accessing a page. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.apex.rule.security.ApexCSRFRule](https://github.com/pmd/pmd/blob/master/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/security/ApexCSRFRule.java) + +**Example(s):** + +``` +public class Foo { + public init() { + insert data; + } + + public Foo() { + insert data; + } +} +``` + +**This rule has the following properties:** + +|Name|Default Value|Description| +|----|-------------|-----------| +|cc_categories|[Style]|Code Climate Categories| +|cc_remediation_points_multiplier|1|Code Climate Remediation Points multiplier| +|cc_block_highlighting|false|Code Climate Block Highlighting| + +## ApexDangerousMethods + +**Since:** PMD 5.5.3 + +**Priority:** Medium (3) + +Checks against calling dangerous methods. + +For the time being, it reports: + +* Against `FinancialForce`'s `Configuration.disableTriggerCRUDSecurity()`. Disabling CRUD security +opens the door to several attacks and requires manual validation, which is unreliable. +* Calling `System.debug` passing sensitive data as parameter, which could lead to exposure +of private data. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.apex.rule.security.ApexDangerousMethodsRule](https://github.com/pmd/pmd/blob/master/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/security/ApexDangerousMethodsRule.java) + +**Example(s):** + +``` +public class Foo { + public Foo() { + Configuration.disableTriggerCRUDSecurity(); + } +} +``` + +**This rule has the following properties:** + +|Name|Default Value|Description| +|----|-------------|-----------| +|cc_categories|[Style]|Code Climate Categories| +|cc_remediation_points_multiplier|1|Code Climate Remediation Points multiplier| +|cc_block_highlighting|false|Code Climate Block Highlighting| + +## ApexInsecureEndpoint + +**Since:** PMD 5.5.3 + +**Priority:** Medium (3) + +Checks against accessing endpoints under plain **http**. You should always use +**https** for security. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.apex.rule.security.ApexInsecureEndpointRule](https://github.com/pmd/pmd/blob/master/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/security/ApexInsecureEndpointRule.java) + +**Example(s):** + +``` +public without sharing class Foo { + void foo() { + HttpRequest req = new HttpRequest(); + req.setEndpoint('http://localhost:com'); + } +} +``` + +**This rule has the following properties:** + +|Name|Default Value|Description| +|----|-------------|-----------| +|cc_categories|[Style]|Code Climate Categories| +|cc_remediation_points_multiplier|1|Code Climate Remediation Points multiplier| +|cc_block_highlighting|false|Code Climate Block Highlighting| + +## ApexOpenRedirect + +**Since:** PMD 5.5.3 + +**Priority:** Medium (3) + +Checks against redirects to user-controlled locations. This prevents attackers from +redirecting users to phishing sites. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.apex.rule.security.ApexOpenRedirectRule](https://github.com/pmd/pmd/blob/master/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/security/ApexOpenRedirectRule.java) + +**Example(s):** + +``` +public without sharing class Foo { + String unsafeLocation = ApexPage.getCurrentPage().getParameters.get('url_param'); + PageReference page() { + return new PageReference(unsafeLocation); + } +} +``` + +**This rule has the following properties:** + +|Name|Default Value|Description| +|----|-------------|-----------| +|cc_categories|[Style]|Code Climate Categories| +|cc_remediation_points_multiplier|1|Code Climate Remediation Points multiplier| +|cc_block_highlighting|false|Code Climate Block Highlighting| + +## ApexSharingViolations + +**Since:** PMD 5.5.3 + +**Priority:** Medium (3) + +Detect classes declared without explicit sharing mode if DML methods are used. This +forces the developer to take access restrictions into account before modifying objects. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.apex.rule.security.ApexSharingViolationsRule](https://github.com/pmd/pmd/blob/master/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/security/ApexSharingViolationsRule.java) + +**Example(s):** + +``` +public without sharing class Foo { +// DML operation here +} +``` + +**This rule has the following properties:** + +|Name|Default Value|Description| +|----|-------------|-----------| +|cc_categories|[Style]|Code Climate Categories| +|cc_remediation_points_multiplier|1|Code Climate Remediation Points multiplier| +|cc_block_highlighting|false|Code Climate Block Highlighting| + +## ApexSOQLInjection + +**Since:** PMD 5.5.3 + +**Priority:** Medium (3) + +Detects the usage of untrusted / unescaped variables in DML queries. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.apex.rule.security.ApexSOQLInjectionRule](https://github.com/pmd/pmd/blob/master/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/security/ApexSOQLInjectionRule.java) + +**Example(s):** + +``` +public class Foo { + public void test1(String t1) { + Database.query('SELECT Id FROM Account' + t1); + } +} +``` + +**This rule has the following properties:** + +|Name|Default Value|Description| +|----|-------------|-----------| +|cc_categories|[Style]|Code Climate Categories| +|cc_remediation_points_multiplier|1|Code Climate Remediation Points multiplier| +|cc_block_highlighting|false|Code Climate Block Highlighting| + +## ApexSuggestUsingNamedCred + +**Since:** PMD 5.5.3 + +**Priority:** Medium (3) + +Detects hardcoded credentials used in requests to an endpoint. + +You should refrain from hardcoding credentials: + * They are hard to mantain by being mixed in application code + * Particularly hard to update them when used from different classes + * Granting a developer access to the codebase means granting knowledge + of credentials, keeping a two-level access is not possible. + * Using different credentials for different environments is troublesome + and error-prone. + +Instead, you should use *Named Credentials* and a callout endpoint. + +For more information, you can check [this](https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_callouts_named_credentials.htm) + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.apex.rule.security.ApexSuggestUsingNamedCredRule](https://github.com/pmd/pmd/blob/master/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/security/ApexSuggestUsingNamedCredRule.java) + +**Example(s):** + +``` +public class Foo { + public void foo(String username, String password) { + Blob headerValue = Blob.valueOf(username + ':' + password); + String authorizationHeader = 'BASIC ' + EncodingUtil.base64Encode(headerValue); + req.setHeader('Authorization', authorizationHeader); + } +} +``` + +**This rule has the following properties:** + +|Name|Default Value|Description| +|----|-------------|-----------| +|cc_categories|[Style]|Code Climate Categories| +|cc_remediation_points_multiplier|1|Code Climate Remediation Points multiplier| +|cc_block_highlighting|false|Code Climate Block Highlighting| + +## ApexXSSFromEscapeFalse + +**Since:** PMD 5.5.3 + +**Priority:** Medium (3) + +Reports on calls to `addError` with disabled escaping. The message passed to `addError` +will be displayed directly to the user in the UI, making it prime ground for XSS +attacks if unescaped. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.apex.rule.security.ApexXSSFromEscapeFalseRule](https://github.com/pmd/pmd/blob/master/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/security/ApexXSSFromEscapeFalseRule.java) + +**Example(s):** + +``` +public without sharing class Foo { + Trigger.new[0].addError(vulnerableHTMLGoesHere, false); +} +``` + +**This rule has the following properties:** + +|Name|Default Value|Description| +|----|-------------|-----------| +|cc_categories|[Style]|Code Climate Categories| +|cc_remediation_points_multiplier|1|Code Climate Remediation Points multiplier| +|cc_block_highlighting|false|Code Climate Block Highlighting| + +## ApexXSSFromURLParam + +**Since:** PMD 5.5.3 + +**Priority:** Medium (3) + +Makes sure that all values obtained from URL parameters are properly escaped / sanitized +to avoid XSS attacks. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.apex.rule.security.ApexXSSFromURLParamRule](https://github.com/pmd/pmd/blob/master/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/security/ApexXSSFromURLParamRule.java) + +**Example(s):** + +``` +public without sharing class Foo { + String unescapedstring = ApexPage.getCurrentPage().getParameters.get('url_param'); + String usedLater = unescapedstring; +} +``` + +**This rule has the following properties:** + +|Name|Default Value|Description| +|----|-------------|-----------| +|cc_categories|[Style]|Code Climate Categories| +|cc_remediation_points_multiplier|1|Code Climate Remediation Points multiplier| +|cc_block_highlighting|false|Code Climate Block Highlighting| + diff --git a/docs/pages/pmd/rules/apex/style.md b/docs/pages/pmd/rules/apex/style.md new file mode 100644 index 000000000..067dd9b1a --- /dev/null +++ b/docs/pages/pmd/rules/apex/style.md @@ -0,0 +1,197 @@ +--- +title: Style +summary: The Style Ruleset contains rules regarding preferred usage of names and identifiers. +permalink: pmd_rules_apex_style.html +folder: pmd/rules/apex +sidebaractiveurl: /pmd_rules_apex.html +editmepath: ../pmd-apex/src/main/resources/rulesets/apex/style.xml +--- +## AvoidGlobalModifier + +**Since:** PMD 5.5.0 + +**Priority:** Medium (3) + +Global classes should be avoided (especially in managed packages) as they can never be deleted or changed in signature. Always check twice if something needs to be global. +Many interfaces (e.g. Batch) required global modifiers in the past but don't require this anymore. Don't lock yourself in. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.apex.rule.style.AvoidGlobalModifierRule](https://github.com/pmd/pmd/blob/master/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/style/AvoidGlobalModifierRule.java) + +**Example(s):** + +``` +global class Unchangeable { + global UndeletableType unchangable(UndeletableType param) { + // ... + } +} +``` + +**This rule has the following properties:** + +|Name|Default Value|Description| +|----|-------------|-----------| +|cc_categories|[Style]|Code Climate Categories| +|cc_remediation_points_multiplier|1|Code Climate Remediation Points multiplier| +|cc_block_highlighting|false|Code Climate Block Highlighting| + +## AvoidLogicInTrigger + +**Since:** PMD 5.5.0 + +**Priority:** Medium (3) + +As triggers do not allow methods like regular classes they are less flexible and suited to apply good encapsulation style. +Therefore delegate the triggers work to a regular class (often called Trigger handler class). + +See more here: https://developer.salesforce.com/page/Trigger_Frameworks_and_Apex_Trigger_Best_Practices + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.apex.rule.style.AvoidLogicInTriggerRule](https://github.com/pmd/pmd/blob/master/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/style/AvoidLogicInTriggerRule.java) + +**Example(s):** + +``` +trigger Accounts on Account (before insert, before update, before delete, after insert, after update, after delete, after undelete) { + for(Account acc : Trigger.new) { + if(Trigger.isInsert) { + ... + } + + ... + + if(Trigger.isDelete) { + ... + } + } +} +``` + +**This rule has the following properties:** + +|Name|Default Value|Description| +|----|-------------|-----------| +|cc_categories|[Style]|Code Climate Categories| +|cc_remediation_points_multiplier|1|Code Climate Remediation Points multiplier| +|cc_block_highlighting|false|Code Climate Block Highlighting| + +## ClassNamingConventions + +**Since:** PMD 5.5.0 + +**Priority:** High (1) + +Class names should always begin with an upper case character. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.apex.rule.style.ClassNamingConventionsRule](https://github.com/pmd/pmd/blob/master/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/style/ClassNamingConventionsRule.java) + +**Example(s):** + +``` +public class Foo {} +``` + +**This rule has the following properties:** + +|Name|Default Value|Description| +|----|-------------|-----------| +|cc_categories|[Style]|Code Climate Categories| +|cc_remediation_points_multiplier|1|Code Climate Remediation Points multiplier| +|cc_block_highlighting|false|Code Climate Block Highlighting| + +## MethodNamingConventions + +**Since:** PMD 5.5.0 + +**Priority:** High (1) + +Method names should always begin with a lower case character, and should not contain underscores. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.apex.rule.style.MethodNamingConventionsRule](https://github.com/pmd/pmd/blob/master/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/style/MethodNamingConventionsRule.java) + +**Example(s):** + +``` +public class Foo { + public void fooStuff() { + } +} +``` + +**This rule has the following properties:** + +|Name|Default Value|Description| +|----|-------------|-----------| +|cc_categories|[Style]|Code Climate Categories| +|cc_remediation_points_multiplier|1|Code Climate Remediation Points multiplier| +|cc_block_highlighting|false|Code Climate Block Highlighting| + +## MethodWithSameNameAsEnclosingClass + +**Since:** PMD 5.5.0 + +**Priority:** Medium (3) + +Non-constructor methods should not have the same name as the enclosing class. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.apex.rule.style.MethodWithSameNameAsEnclosingClassRule](https://github.com/pmd/pmd/blob/master/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/style/MethodWithSameNameAsEnclosingClassRule.java) + +**Example(s):** + +``` +public class MyClass { + // this is OK because it is a constructor + public MyClass() {} + // this is bad because it is a method + public void MyClass() {} +} +``` + +**This rule has the following properties:** + +|Name|Default Value|Description| +|----|-------------|-----------| +|cc_categories|[Style]|Code Climate Categories| +|cc_remediation_points_multiplier|1|Code Climate Remediation Points multiplier| +|cc_block_highlighting|false|Code Climate Block Highlighting| + +## VariableNamingConventions + +**Since:** PMD 5.5.0 + +**Priority:** High (1) + +A variable naming conventions rule - customize this to your liking. Currently, it +checks for final variables that should be fully capitalized and non-final variables +that should not include underscores. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.apex.rule.style.VariableNamingConventionsRule](https://github.com/pmd/pmd/blob/master/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/style/VariableNamingConventionsRule.java) + +**Example(s):** + +``` +public class Foo { + public static final Integer MY_NUM = 0; + public String myTest = ''; + DataModule dmTest = new DataModule(); +} +``` + +**This rule has the following properties:** + +|Name|Default Value|Description| +|----|-------------|-----------| +|parameterSuffix|[]|Method parameter variable suffixes| +|parameterPrefix|[]|Method parameter variable prefixes| +|localSuffix|[]|Local variable suffixes| +|localPrefix|[]|Local variable prefixes| +|memberSuffix|[]|Member variable suffixes| +|memberPrefix|[]|Member variable prefixes| +|staticSuffix|[]|Static variable suffixes| +|staticPrefix|[]|Static variable prefixes| +|checkParameters|true|Check constructor and method parameter variables| +|checkLocals|true|Check local variables| +|cc_categories|[Style]|Code Climate Categories| +|cc_remediation_points_multiplier|1|Code Climate Remediation Points multiplier| +|cc_block_highlighting|false|Code Climate Block Highlighting| +|checkMembers|true|Check member variables| + diff --git a/docs/pages/pmd/rules/ecmascript.md b/docs/pages/pmd/rules/ecmascript.md new file mode 100644 index 000000000..2c3d5023b --- /dev/null +++ b/docs/pages/pmd/rules/ecmascript.md @@ -0,0 +1,33 @@ +--- +title: Ecmascript Rules +permalink: pmd_rules_ecmascript.html +folder: pmd/rules +--- +List of rulesets and rules contained in each ruleset. + +* [Basic Ecmascript](pmd_rules_ecmascript_basic.html): Rules concerning basic ECMAScript guidelines. +* [Braces](pmd_rules_ecmascript_braces.html): The Braces Ruleset contains a collection of braces rules. +* [Unnecessary](pmd_rules_ecmascript_unnecessary.html): The Unnecessary Ruleset contains a collection of rules for unnecessary code. + +## Basic Ecmascript +* [AssignmentInOperand](pmd_rules_ecmascript_basic.html#assignmentinoperand): Avoid assignments in operands; this can make code more complicated and harder to read. This is s... +* [AvoidTrailingComma](pmd_rules_ecmascript_basic.html#avoidtrailingcomma): This rule helps improve code portability due to differences in browser treatment of trailing comm... +* [ConsistentReturn](pmd_rules_ecmascript_basic.html#consistentreturn): ECMAScript does provide for return types on functions, and therefore there is no solid rule as to... +* [EqualComparison](pmd_rules_ecmascript_basic.html#equalcomparison): Using == in condition may lead to unexpected results, as the variables are automatically casted t... +* [GlobalVariable](pmd_rules_ecmascript_basic.html#globalvariable): This rule helps to avoid using accidently global variables by simply missing the "var" declaratio... +* [InnaccurateNumericLiteral](pmd_rules_ecmascript_basic.html#innaccuratenumericliteral): The numeric literal will have at different value at runtime, which can happen if you provide too ... +* [ScopeForInVariable](pmd_rules_ecmascript_basic.html#scopeforinvariable): A for-in loop in which the variable name is not explicitly scoped to the enclosing scope with the... +* [UnreachableCode](pmd_rules_ecmascript_basic.html#unreachablecode): A 'return', 'break', 'continue', or 'throw' statement should be the last in a block. Statements a... +* [UseBaseWithParseInt](pmd_rules_ecmascript_basic.html#usebasewithparseint): TODO + +## Braces +* [ForLoopsMustUseBraces](pmd_rules_ecmascript_braces.html#forloopsmustusebraces): Avoid using 'for' statements without using curly braces. +* [IfElseStmtsMustUseBraces](pmd_rules_ecmascript_braces.html#ifelsestmtsmustusebraces): Avoid using if..else statements without using curly braces. +* [IfStmtsMustUseBraces](pmd_rules_ecmascript_braces.html#ifstmtsmustusebraces): Avoid using if statements without using curly braces. +* [WhileLoopsMustUseBraces](pmd_rules_ecmascript_braces.html#whileloopsmustusebraces): Avoid using 'while' statements without using curly braces. + +## Unnecessary +* [NoElseReturn](pmd_rules_ecmascript_unnecessary.html#noelsereturn): The else block in a if-else-construct is unnecessary if the 'if' block contains a return. Then... +* [UnnecessaryBlock](pmd_rules_ecmascript_unnecessary.html#unnecessaryblock): An unnecessary Block is present. Such Blocks are often used in other languages to introduce a... +* [UnnecessaryParentheses](pmd_rules_ecmascript_unnecessary.html#unnecessaryparentheses): Unnecessary parentheses should be removed. + diff --git a/docs/pages/pmd/rules/ecmascript/basic.md b/docs/pages/pmd/rules/ecmascript/basic.md new file mode 100644 index 000000000..bbc859cf5 --- /dev/null +++ b/docs/pages/pmd/rules/ecmascript/basic.md @@ -0,0 +1,326 @@ +--- +title: Basic Ecmascript +summary: Rules concerning basic ECMAScript guidelines. +permalink: pmd_rules_ecmascript_basic.html +folder: pmd/rules/ecmascript +sidebaractiveurl: /pmd_rules_ecmascript.html +editmepath: ../pmd-javascript/src/main/resources/rulesets/ecmascript/basic.xml +--- +## AssignmentInOperand + +**Since:** PMD 5.0 + +**Priority:** Medium High (2) + +Avoid assignments in operands; this can make code more complicated and harder to read. This is sometime +indicative of the bug where the assignment operator '=' was used instead of the equality operator '=='. + +``` +//IfStatement[$allowIf = "false"]/child::node()[1]/descendant-or-self::node()[self::Assignment or self::UnaryExpression[$allowIncrementDecrement = "false" and (@Image = "--" or @Image = "++")]] +| + //WhileLoop[$allowWhile = "false"]/child::node()[1]/descendant-or-self::node()[self::Assignment or self::UnaryExpression[$allowIncrementDecrement = "false" and (@Image = "--" or @Image = "++")]] +| + //DoLoop[$allowWhile = "false"]/child::node()[2]/descendant-or-self::node()[self::Assignment or self::UnaryExpression[$allowIncrementDecrement = "false" and (@Image = "--" or @Image = "++")]] +| + //ForLoop[$allowFor = "false"]/child::node()[2]/descendant-or-self::node()[self::Assignment or self::UnaryExpression[$allowIncrementDecrement = "false" and (@Image = "--" or @Image = "++")]] +| + //ConditionalExpression[$allowTernary = "false"]/child::node()[1]/descendant-or-self::node()[self::Assignment or self::UnaryExpression[$allowIncrementDecrement = "false" and (@Image = "--" or @Image = "++")]] +| + //ConditionalExpression[$allowTernaryResults = "false"]/child::node()[position() = 2 or position() = 3]/descendant-or-self::node()[self::Assignment or self::UnaryExpression[$allowIncrementDecrement = "false" and (@Image = "--" or @Image = "++")]] +``` + +**Example(s):** + +``` +var x = 2; +// Bad +if ((x = getX()) == 3) { + alert('3!'); +} + +function getX() { + return 3; +} +``` + +**This rule has the following properties:** + +|Name|Default Value|Description| +|----|-------------|-----------| +|allowIf|false|Allow assignment within the conditional expression of an if statement| +|allowFor|false|Allow assignment within the conditional expression of a for statement| +|allowWhile|false|Allow assignment within the conditional expression of a while statement| +|allowTernary|false|Allow assignment within the conditional expression of a ternary operator| +|allowTernaryResults|false|Allow assignment within the result expressions of a ternary operator| +|allowIncrementDecrement|false|Allow increment or decrement operators within the conditional expression of an if, for, or while statement| + +## AvoidTrailingComma + +**Since:** PMD 5.1 + +**Priority:** High (1) + +This rule helps improve code portability due to differences in browser treatment of trailing commas in object or array literals. + +``` +//ObjectLiteral[$allowObjectLiteral = "false" and @TrailingComma = 'true'] +| + //ArrayLiteral[$allowArrayLiteral = "false" and @TrailingComma = 'true'] +``` + +**Example(s):** + +``` +function(arg) { + var obj1 = { a : 1 }; // Ok + var arr1 = [ 1, 2 ]; // Ok + + var obj2 = { a : 1, }; // Syntax error in some browsers! + var arr2 = [ 1, 2, ]; // Length 2 or 3 depending on the browser! +} +``` + +**This rule has the following properties:** + +|Name|Default Value|Description| +|----|-------------|-----------| +|allowObjectLiteral|false|Allow a trailing comma within an object literal| +|allowArrayLiteral|false|Allow a trailing comma within an array literal| + +## ConsistentReturn + +**Since:** PMD 5.0 + +**Priority:** Medium High (2) + +ECMAScript does provide for return types on functions, and therefore there is no solid rule as to their usage. +However, when a function does use returns they should all have a value, or all with no value. Mixed return +usage is likely a bug, or at best poor style. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.ecmascript.rule.basic.ConsistentReturnRule](https://github.com/pmd/pmd/blob/master/pmd-javascript/src/main/java/net/sourceforge/pmd/lang/ecmascript/rule/basic/ConsistentReturnRule.java) + +**Example(s):** + +``` +// Ok +function foo() { + if (condition1) { + return true; + } + return false; +} + +// Bad +function bar() { + if (condition1) { + return; + } + return false; +} +``` + +**This rule has the following properties:** + +|Name|Default Value|Description| +|----|-------------|-----------| +|rhinoLanguageVersion|VERSION_DEFAULT|Specifies the Rhino Language Version to use for parsing. Defaults to Rhino default.| +|recordingLocalJsDocComments|true|Specifies that JsDoc comments are produced in the AST.| +|recordingComments|true|Specifies that comments are produced in the AST.| + +## EqualComparison + +**Since:** PMD 5.0 + +**Priority:** Medium (3) + +Using == in condition may lead to unexpected results, as the variables are automatically casted to be of the + same type. The === operator avoids the casting. + +``` +//InfixExpression[(@Image = "==" or @Image = "!=") + and + (child::KeywordLiteral[@Image = "true" or @Image = "false"] + or + child::NumberLiteral) +] +``` + +**Example(s):** + +``` +// Ok +if (someVar === true) { + ... +} +// Ok +if (someVar !== 3) { + ... +} +// Bad +if (someVar == true) { + ... +} +// Bad +if (someVar != 3) { + ... +} +``` + +## GlobalVariable + +**Since:** PMD 5.0 + +**Priority:** High (1) + +This rule helps to avoid using accidently global variables by simply missing the "var" declaration. +Global variables can lead to side-effects that are hard to debug. + +``` +//Assignment[Name/@GlobalName = 'true'] +``` + +**Example(s):** + +``` +function(arg) { + notDeclaredVariable = 1; // this will create a global variable and trigger the rule + + var someVar = 1; // this is a local variable, that's ok + + window.otherGlobal = 2; // this will not trigger the rule, although it is a global variable. +} +``` + +## InnaccurateNumericLiteral + +**Since:** PMD 5.0 + +**Priority:** Medium High (2) + +The numeric literal will have at different value at runtime, which can happen if you provide too much +precision in a floating point number. This may result in numeric calculations being in error. + +``` +//NumberLiteral[ + @Image != @Number + and translate(@Image, "e", "E") != @Number + and concat(@Image, ".0") != @Number + and @Image != substring-before(translate(@Number, ".", ""), "E")] +``` + +**Example(s):** + +``` +var a = 9; // Ok +var b = 999999999999999; // Ok +var c = 999999999999999999999; // Not good +var w = 1.12e-4; // Ok +var x = 1.12; // Ok +var y = 1.1234567890123; // Ok +var z = 1.12345678901234567; // Not good +``` + +## ScopeForInVariable + +**Since:** PMD 5.0 + +**Priority:** High (1) + +A for-in loop in which the variable name is not explicitly scoped to the enclosing scope with the 'var' keyword can +refer to a variable in an enclosing scope outside the nearest enclosing scope. This will overwrite the +existing value of the variable in the outer scope when the body of the for-in is evaluated. When the for-in loop +has finished, the variable will contain the last value used in the for-in, and the original value from before +the for-in loop will be gone. Since the for-in variable name is most likely intended to be a temporary name, it +is better to explicitly scope the variable name to the nearest enclosing scope with 'var'. + +``` +//ForInLoop[not(child::VariableDeclaration)]/Name[1] +``` + +**Example(s):** + +``` +// Ok +function foo() { + var p = 'clean'; + function() { + var obj = { dirty: 'dirty' }; + for (var p in obj) { // Use 'var' here. + obj[p] = obj[p]; + } + return x; + }(); + + // 'p' still has value of 'clean'. +} +// Bad +function bar() { + var p = 'clean'; + function() { + var obj = { dirty: 'dirty' }; + for (p in obj) { // Oh no, missing 'var' here! + obj[p] = obj[p]; + } + return x; + }(); + + // 'p' is trashed and has value of 'dirty'! +} +``` + +## UnreachableCode + +**Since:** PMD 5.0 + +**Priority:** High (1) + +A 'return', 'break', 'continue', or 'throw' statement should be the last in a block. Statements after these +will never execute. This is a bug, or extremely poor style. + +``` +//ReturnStatement[following-sibling::node()] +| + //ContinueStatement[following-sibling::node()] +| + //BreakStatement[following-sibling::node()] +| + //ThrowStatement[following-sibling::node()] +``` + +**Example(s):** + +``` +// Ok +function foo() { + return 1; +} +// Bad +function bar() { + var x = 1; + return x; + x = 2; +} +``` + +## UseBaseWithParseInt + +**Since:** PMD 5.0.1 + +**Priority:** High (1) + +TODO + +``` +//FunctionCall/Name[ + @Image = 'parseInt' + and + count(../*) < 3 +] +``` + +**Example(s):** + +``` +parseInt("10",base); +``` + diff --git a/docs/pages/pmd/rules/ecmascript/braces.md b/docs/pages/pmd/rules/ecmascript/braces.md new file mode 100644 index 000000000..47ff893a4 --- /dev/null +++ b/docs/pages/pmd/rules/ecmascript/braces.md @@ -0,0 +1,116 @@ +--- +title: Braces +summary: The Braces Ruleset contains a collection of braces rules. +permalink: pmd_rules_ecmascript_braces.html +folder: pmd/rules/ecmascript +sidebaractiveurl: /pmd_rules_ecmascript.html +editmepath: ../pmd-javascript/src/main/resources/rulesets/ecmascript/braces.xml +--- +## ForLoopsMustUseBraces + +**Since:** PMD 5.0 + +**Priority:** Medium (3) + +Avoid using 'for' statements without using curly braces. + +``` +//ForLoop[not(child::Scope)] +| + //ForInLoop[not(child::Scope)] +``` + +**Example(s):** + +``` +// Ok +for (var i = 0; i < 42; i++) { + foo(); +} + +// Bad +for (var i = 0; i < 42; i++) + foo(); +``` + +## IfElseStmtsMustUseBraces + +**Since:** PMD 5.0 + +**Priority:** Medium (3) + +Avoid using if..else statements without using curly braces. + +``` +//ExpressionStatement[parent::IfStatement[@Else = "true"]] + [not(child::Scope)] + [not(child::IfStatement)] +``` + +**Example(s):** + +``` +// Ok +if (foo) { + x++; +} else { + y++; +} + +// Bad +if (foo) + x++; +else + y++; +``` + +## IfStmtsMustUseBraces + +**Since:** PMD 5.0 + +**Priority:** Medium (3) + +Avoid using if statements without using curly braces. + +``` +//IfStatement[@Else = "false" and not(child::Scope)] +``` + +**Example(s):** + +``` +// Ok +if (foo) { + x++; +} + +// Bad +if (foo) + x++; +``` + +## WhileLoopsMustUseBraces + +**Since:** PMD 5.0 + +**Priority:** Medium (3) + +Avoid using 'while' statements without using curly braces. + +``` +//WhileLoop[not(child::Scope)] +``` + +**Example(s):** + +``` +// Ok +while (true) { + x++; +} + +// Bad +while (true) + x++; +``` + diff --git a/docs/pages/pmd/rules/ecmascript/unnecessary.md b/docs/pages/pmd/rules/ecmascript/unnecessary.md new file mode 100644 index 000000000..a87140ecc --- /dev/null +++ b/docs/pages/pmd/rules/ecmascript/unnecessary.md @@ -0,0 +1,91 @@ +--- +title: Unnecessary +summary: The Unnecessary Ruleset contains a collection of rules for unnecessary code. +permalink: pmd_rules_ecmascript_unnecessary.html +folder: pmd/rules/ecmascript +sidebaractiveurl: /pmd_rules_ecmascript.html +editmepath: ../pmd-javascript/src/main/resources/rulesets/ecmascript/unnecessary.xml +--- +## NoElseReturn + +**Since:** PMD 5.5.0 + +**Priority:** Medium (3) + +The else block in a if-else-construct is unnecessary if the `if` block contains a return. + Then the content of the else block can be put outside. + + See also: http://eslint.org/docs/rules/no-else-return + +``` +//IfStatement[@Else="true"][Scope[1]/ReturnStatement] +``` + +**Example(s):** + +``` +// Bad: +if (x) { + return y; +} else { + return z; +} + +// Good: +if (x) { + return y; +} +return z; +``` + +## UnnecessaryBlock + +**Since:** PMD 5.0 + +**Priority:** Medium (3) + +An unnecessary Block is present. Such Blocks are often used in other languages to + introduce a new variable scope. Blocks do not behave like this in ECMAScipt, and using them can + be misleading. Considering removing this unnecessary Block. + +``` +//Block[not(parent::FunctionNode or parent::IfStatement or parent::ForLoop or parent::ForInLoop + or parent::WhileLoop or parent::DoLoop or parent::TryStatement or parent::CatchClause)] +| + //Scope[not(parent::FunctionNode or parent::IfStatement or parent::ForLoop or parent::ForInLoop + or parent::WhileLoop or parent::DoLoop or parent::TryStatement or parent::CatchClause)] +``` + +**Example(s):** + +``` +if (foo) { + // Ok +} +if (bar) { + { + // Bad + } +} +``` + +## UnnecessaryParentheses + +**Since:** PMD 5.0 + +**Priority:** Medium Low (4) + +Unnecessary parentheses should be removed. + +``` +//ParenthesizedExpression/ParenthesizedExpression +``` + +**Example(s):** + +``` +var x = 1; // Ok +var y = (1 + 1); // Ok +var z = ((1 + 1)); // Bad +``` + diff --git a/docs/pages/pmd/rules/java.md b/docs/pages/pmd/rules/java.md index 8a8e7b489..9f2019db2 100644 --- a/docs/pages/pmd/rules/java.md +++ b/docs/pages/pmd/rules/java.md @@ -1,6 +1,365 @@ --- title: Java Rules -sidebar: pmd_sidebar permalink: pmd_rules_java.html folder: pmd/rules --- +List of rulesets and rules contained in each ruleset. + +* [Android](pmd_rules_java_android.html): These rules deal with the Android SDK, mostly related to best practices. To get better results, make sure that the auxclasspath is defined for type resolution to work. +* [Basic](pmd_rules_java_basic.html): The Basic ruleset contains a collection of good practices which should be followed. +* [Braces](pmd_rules_java_braces.html): The Braces ruleset contains rules regarding the use and placement of braces. +* [Clone Implementation](pmd_rules_java_clone.html): The Clone Implementation ruleset contains a collection of rules that find questionable usages of the clone() method. +* [Code Size](pmd_rules_java_codesize.html): The Code Size ruleset contains rules that find problems related to code size or complexity. +* [Comments](pmd_rules_java_comments.html): Rules intended to catch errors related to code comments +* [Controversial](pmd_rules_java_controversial.html): The Controversial ruleset contains rules that, for whatever reason, are considered controversial. They are held here to allow people to include them as they see fit within their custom rulesets. +* [Coupling](pmd_rules_java_coupling.html): Rules which find instances of high or inappropriate coupling between objects and packages. +* [Design](pmd_rules_java_design.html): The Design ruleset contains rules that flag suboptimal code implementations. Alternate approaches are suggested. +* [Empty Code](pmd_rules_java_empty.html): The Empty Code ruleset contains rules that find empty statements of any kind (empty method, empty block statement, empty try or catch block,...). +* [Finalizer](pmd_rules_java_finalizers.html): These rules deal with different problems that can occur with finalizers. +* [Import Statements](pmd_rules_java_imports.html): These rules deal with different problems that can occur with import statements. +* [J2EE](pmd_rules_java_j2ee.html): Rules specific to the use of J2EE implementations. +* [Jakarta Commons Logging](pmd_rules_java_logging-jakarta-commons.html): The Jakarta Commons Logging ruleset contains a collection of rules that find questionable usages of that framework. +* [Java Logging](pmd_rules_java_logging-java.html): The Java Logging ruleset contains a collection of rules that find questionable usages of the logger. +* [JavaBeans](pmd_rules_java_javabeans.html): The JavaBeans Ruleset catches instances of bean rules not being followed. +* [JUnit](pmd_rules_java_junit.html): These rules deal with different problems that can occur with JUnit tests. +* [Migration](pmd_rules_java_migrating.html): Contains rules about migrating from one JDK version to another. Don't use these rules directly, rather, use a wrapper ruleset such as migrating_to_13.xml. +* [Naming](pmd_rules_java_naming.html): The Naming Ruleset contains rules regarding preferred usage of names and identifiers. +* [Optimization](pmd_rules_java_optimizations.html): These rules deal with different optimizations that generally apply to best practices. +* [Security Code Guidelines](pmd_rules_java_sunsecure.html): These rules check the security guidelines from Sun, published at http://java.sun.com/security/seccodeguide.html#gcg +* [Strict Exceptions](pmd_rules_java_strictexception.html): These rules provide some strict guidelines about throwing and catching exceptions. +* [String and StringBuffer](pmd_rules_java_strings.html): These rules deal with different issues that can arise with manipulation of the String, StringBuffer, or StringBuilder instances. +* [Unnecessary](pmd_rules_java_unnecessary.html): The Unnecessary Ruleset contains a collection of rules for unnecessary code. +* [Unused Code](pmd_rules_java_unusedcode.html): The Unused Code ruleset contains rules that find unused or ineffective code. + +## Android +* [CallSuperFirst](pmd_rules_java_android.html#callsuperfirst): Super should be called at the start of the method +* [CallSuperLast](pmd_rules_java_android.html#callsuperlast): Super should be called at the end of the method +* [DoNotHardCodeSDCard](pmd_rules_java_android.html#donothardcodesdcard): Use Environment.getExternalStorageDirectory() instead of "/sdcard" + +## Basic +* [AvoidBranchingStatementAsLastInLoop](pmd_rules_java_basic.html#avoidbranchingstatementaslastinloop): Using a branching statement as the last part of a loop may be a bug, and/or is confusing.Ensure t... +* [AvoidDecimalLiteralsInBigDecimalConstructor](pmd_rules_java_basic.html#avoiddecimalliteralsinbigdecimalconstructor): One might assume that the result of "new BigDecimal(0.1)" is exactly equal to 0.1, but it is actu... +* [AvoidMultipleUnaryOperators](pmd_rules_java_basic.html#avoidmultipleunaryoperators): The use of multiple unary operators may be problematic, and/or confusing.Ensure that the intended... +* [AvoidThreadGroup](pmd_rules_java_basic.html#avoidthreadgroup): Avoid using java.lang.ThreadGroup; although it is intended to be used in a threaded environmentit... +* [AvoidUsingHardCodedIP](pmd_rules_java_basic.html#avoidusinghardcodedip): Application with hard-coded IP addresses can become impossible to deploy in some cases.Externaliz... +* [AvoidUsingOctalValues](pmd_rules_java_basic.html#avoidusingoctalvalues): Integer literals should not start with zero since this denotes that the rest of literal will bein... +* [BigIntegerInstantiation](pmd_rules_java_basic.html#bigintegerinstantiation): Don't create instances of already existing BigInteger (BigInteger.ZERO, BigInteger.ONE) andfor Ja... +* [BooleanInstantiation](pmd_rules_java_basic.html#booleaninstantiation): Avoid instantiating Boolean objects; you can reference Boolean.TRUE, Boolean.FALSE, or call Boole... +* [BrokenNullCheck](pmd_rules_java_basic.html#brokennullcheck): The null check is broken since it will throw a NullPointerException itself.It is likely that you ... +* [CheckResultSet](pmd_rules_java_basic.html#checkresultset): Always check the return values of navigation methods (next, previous, first, last) of a ResultSet... +* [CheckSkipResult](pmd_rules_java_basic.html#checkskipresult): The skip() method may skip a smaller number of bytes than requested. Check the returned value to ... +* [ClassCastExceptionWithToArray](pmd_rules_java_basic.html#classcastexceptionwithtoarray): When deriving an array of a specific class from your Collection, one should provide an array ofth... +* [CollapsibleIfStatements](pmd_rules_java_basic.html#collapsibleifstatements): Sometimes two consecutive 'if' statements can be consolidated by separating their conditions with... +* [DontCallThreadRun](pmd_rules_java_basic.html#dontcallthreadrun): Explicitly calling Thread.run() method will execute in the caller's thread of control. Instead, ... +* [DontUseFloatTypeForLoopIndices](pmd_rules_java_basic.html#dontusefloattypeforloopindices): Don't use floating point for loop indices. If you must use floating point, use doubleunless you'r... +* [DoubleCheckedLocking](pmd_rules_java_basic.html#doublecheckedlocking): Partially created objects can be returned by the Double Checked Locking pattern when used in Java... +* [ExtendsObject](pmd_rules_java_basic.html#extendsobject): No need to explicitly extend Object. +* [ForLoopShouldBeWhileLoop](pmd_rules_java_basic.html#forloopshouldbewhileloop): Some for loops can be simplified to while loops, this makes them more concise. +* [JumbledIncrementer](pmd_rules_java_basic.html#jumbledincrementer): Avoid jumbled loop incrementers - its usually a mistake, and is confusing even if intentional. +* [MisplacedNullCheck](pmd_rules_java_basic.html#misplacednullcheck): The null check here is misplaced. If the variable is null a NullPointerException will be thrown.E... +* [OverrideBothEqualsAndHashcode](pmd_rules_java_basic.html#overridebothequalsandhashcode): Override both public boolean Object.equals(Object other), and public int Object.hashCode(), or ov... +* [ReturnFromFinallyBlock](pmd_rules_java_basic.html#returnfromfinallyblock): Avoid returning from a finally block, this can discard exceptions. +* [SimplifiedTernary](pmd_rules_java_basic.html#simplifiedternary): Look for ternary operators with the form 'condition ? literalBoolean : foo'or 'condition ? foo : ... +* [UnconditionalIfStatement](pmd_rules_java_basic.html#unconditionalifstatement): Do not use "if" statements whose conditionals are always true or always false. + +## Braces +* [ForLoopsMustUseBraces](pmd_rules_java_braces.html#forloopsmustusebraces): Avoid using 'for' statements without using curly braces. If the code formatting or indentation is... +* [IfElseStmtsMustUseBraces](pmd_rules_java_braces.html#ifelsestmtsmustusebraces): Avoid using if..else statements without using surrounding braces. If the code formatting or inden... +* [IfStmtsMustUseBraces](pmd_rules_java_braces.html#ifstmtsmustusebraces): Avoid using if statements without using braces to surround the code block. If the code formatting... +* [WhileLoopsMustUseBraces](pmd_rules_java_braces.html#whileloopsmustusebraces): Avoid using 'while' statements without using braces to surround the code block. If the code forma... + +## Clone Implementation +* [CloneMethodMustBePublic](pmd_rules_java_clone.html#clonemethodmustbepublic): The java Manual says "By convention, classes that implement this interface should overrideObject.... +* [CloneMethodMustImplementCloneable](pmd_rules_java_clone.html#clonemethodmustimplementcloneable): The method clone() should only be implemented if the class implements the Cloneable interface wit... +* [CloneMethodReturnTypeMustMatchClassName](pmd_rules_java_clone.html#clonemethodreturntypemustmatchclassname): If a class implements cloneable the return type of the method clone() must be the class name. Tha... +* [CloneThrowsCloneNotSupportedException](pmd_rules_java_clone.html#clonethrowsclonenotsupportedexception): The method clone() should throw a CloneNotSupportedException. +* [ProperCloneImplementation](pmd_rules_java_clone.html#propercloneimplementation): Object clone() should be implemented with super.clone(). + +## Code Size +* [CyclomaticComplexity](pmd_rules_java_codesize.html#cyclomaticcomplexity): Deprecated Complexity directly affects maintenance costs is determined by the number of decision points in a... +* [ExcessiveClassLength](pmd_rules_java_codesize.html#excessiveclasslength): Excessive class file lengths are usually indications that the class may be burdened with excessiv... +* [ExcessiveMethodLength](pmd_rules_java_codesize.html#excessivemethodlength): When methods are excessively long this usually indicates that the method is doing more than itsna... +* [ExcessiveParameterList](pmd_rules_java_codesize.html#excessiveparameterlist): Methods with numerous parameters are a challenge to maintain, especially if most of them share th... +* [ExcessivePublicCount](pmd_rules_java_codesize.html#excessivepubliccount): Classes with large numbers of public methods and attributes require disproportionate testing effo... +* [ModifiedCyclomaticComplexity](pmd_rules_java_codesize.html#modifiedcyclomaticcomplexity): Deprecated Complexity directly affects maintenance costs is determined by the number of decision points in a... +* [NcssConstructorCount](pmd_rules_java_codesize.html#ncssconstructorcount): This rule uses the NCSS (Non-Commenting Source Statements) algorithm to determine the number of l... +* [NcssMethodCount](pmd_rules_java_codesize.html#ncssmethodcount): This rule uses the NCSS (Non-Commenting Source Statements) algorithm to determine the number of l... +* [NcssTypeCount](pmd_rules_java_codesize.html#ncsstypecount): This rule uses the NCSS (Non-Commenting Source Statements) algorithm to determine the number of l... +* [NPathComplexity](pmd_rules_java_codesize.html#npathcomplexity): The NPath complexity of a method is the number of acyclic execution paths through that method.A t... +* [StdCyclomaticComplexity](pmd_rules_java_codesize.html#stdcyclomaticcomplexity): Deprecated Complexity directly affects maintenance costs is determined by the number of decision points in a... +* [TooManyFields](pmd_rules_java_codesize.html#toomanyfields): Classes that have too many fields can become unwieldy and could be redesigned to have fewer field... +* [TooManyMethods](pmd_rules_java_codesize.html#toomanymethods): A class with too many methods is probably a good suspect for refactoring, in order to reduce its ... + +## Comments +* [CommentContent](pmd_rules_java_comments.html#commentcontent): A rule for the politically correct... we don't want to offend anyone. +* [CommentDefaultAccessModifier](pmd_rules_java_comments.html#commentdefaultaccessmodifier): To avoid mistakes if we want that a Method, Constructor, Field or Nested class have a default acc... +* [CommentRequired](pmd_rules_java_comments.html#commentrequired): Denotes whether comments are required (or unwanted) for specific language elements. +* [CommentSize](pmd_rules_java_comments.html#commentsize): Determines whether the dimensions of non-header comments found are within the specified limits. + +## Controversial +* [AssignmentInOperand](pmd_rules_java_controversial.html#assignmentinoperand): Avoid assignments in operands; this can make code more complicated and harder to read. +* [AtLeastOneConstructor](pmd_rules_java_controversial.html#atleastoneconstructor): Each class should declare at least one constructor. +* [AvoidAccessibilityAlteration](pmd_rules_java_controversial.html#avoidaccessibilityalteration): Methods such as getDeclaredConstructors(), getDeclaredConstructor(Class[]) and setAccessible(),as... +* [AvoidFinalLocalVariable](pmd_rules_java_controversial.html#avoidfinallocalvariable): Avoid using final local variables, turn them into fields. +* [AvoidLiteralsInIfCondition](pmd_rules_java_controversial.html#avoidliteralsinifcondition): Avoid using hard-coded literals in conditional statements. By declaring them as static variableso... +* [AvoidPrefixingMethodParameters](pmd_rules_java_controversial.html#avoidprefixingmethodparameters): Prefixing parameters by 'in' or 'out' pollutes the name of the parameters and reduces code readab... +* [AvoidUsingNativeCode](pmd_rules_java_controversial.html#avoidusingnativecode): Unnecessary reliance on Java Native Interface (JNI) calls directly reduces application portabilit... +* [AvoidUsingShortType](pmd_rules_java_controversial.html#avoidusingshorttype): Java uses the 'short' type to reduce memory usage, not to optimize calculation. In fact, the JVM ... +* [AvoidUsingVolatile](pmd_rules_java_controversial.html#avoidusingvolatile): Use of the keyword 'volatile' is generally used to fine tune a Java application, and therefore, r... +* [CallSuperInConstructor](pmd_rules_java_controversial.html#callsuperinconstructor): It is a good practice to call super() in a constructor. If super() is not called butanother const... +* [DataflowAnomalyAnalysis](pmd_rules_java_controversial.html#dataflowanomalyanalysis): The dataflow analysis tracks local definitions, undefinitions and references to variables on diff... +* [DefaultPackage](pmd_rules_java_controversial.html#defaultpackage): Use explicit scoping instead of accidental usage of default package private level.The rule allows... +* [DoNotCallGarbageCollectionExplicitly](pmd_rules_java_controversial.html#donotcallgarbagecollectionexplicitly): Calls to System.gc(), Runtime.getRuntime().gc(), and System.runFinalization() are not advised. Co... +* [DontImportSun](pmd_rules_java_controversial.html#dontimportsun): Avoid importing anything from the 'sun.' packages. These packages are not portable and are likel... +* [NullAssignment](pmd_rules_java_controversial.html#nullassignment): Assigning a "null" to a variable (outside of its declaration) is usually bad form. Sometimes, th... +* [OneDeclarationPerLine](pmd_rules_java_controversial.html#onedeclarationperline): Java allows the use of several variables declaration of the same type on one line. However, itcan... +* [OnlyOneReturn](pmd_rules_java_controversial.html#onlyonereturn): A method should have only one exit point, and that should be the last statement in the method. +* [SuspiciousOctalEscape](pmd_rules_java_controversial.html#suspiciousoctalescape): A suspicious octal escape sequence was found inside a String literal.The Java language specificat... +* [UnnecessaryConstructor](pmd_rules_java_controversial.html#unnecessaryconstructor): This rule detects when a constructor is not necessary; i.e., when there is only one constructor,i... +* [UnnecessaryParentheses](pmd_rules_java_controversial.html#unnecessaryparentheses): Sometimes expressions are wrapped in unnecessary parentheses, making them look like function calls. +* [UseConcurrentHashMap](pmd_rules_java_controversial.html#useconcurrenthashmap): Since Java5 brought a new implementation of the Map designed for multi-threaded access, you canpe... +* [UseObjectForClearerAPI](pmd_rules_java_controversial.html#useobjectforclearerapi): When you write a public method, you should be thinking in terms of an API. If your method is publ... + +## Coupling +* [CouplingBetweenObjects](pmd_rules_java_coupling.html#couplingbetweenobjects): This rule counts the number of unique attributes, local variables, and return types within an obj... +* [ExcessiveImports](pmd_rules_java_coupling.html#excessiveimports): A high number of imports can indicate a high degree of coupling within an object. This rule count... +* [LawOfDemeter](pmd_rules_java_coupling.html#lawofdemeter): The Law of Demeter is a simple rule, that says "only talk to friends". It helps to reduce couplin... +* [LooseCoupling](pmd_rules_java_coupling.html#loosecoupling): The use of implementation types as object references limits your ability to use alternateimplemen... +* [LoosePackageCoupling](pmd_rules_java_coupling.html#loosepackagecoupling): Avoid using classes from the configured package hierarchy outside of the package hierarchy, excep... + +## Design +* [AbstractClassWithoutAbstractMethod](pmd_rules_java_design.html#abstractclasswithoutabstractmethod): The abstract class does not contain any abstract methods. An abstract class suggestsan incomplete... +* [AbstractClassWithoutAnyMethod](pmd_rules_java_design.html#abstractclasswithoutanymethod): If an abstract class does not provides any methods, it may be acting as a simple data containerth... +* [AccessorClassGeneration](pmd_rules_java_design.html#accessorclassgeneration): Instantiation by way of private constructors from outside of the constructor's class often causes... +* [AccessorMethodGeneration](pmd_rules_java_design.html#accessormethodgeneration): When accessing a private field / method from another class, the Java compiler will generate a acc... +* [AssignmentToNonFinalStatic](pmd_rules_java_design.html#assignmenttononfinalstatic): Identifies a possible unsafe usage of a static field. +* [AvoidDeeplyNestedIfStmts](pmd_rules_java_design.html#avoiddeeplynestedifstmts): Avoid creating deeply nested if-then statements since they are harder to read and error-prone to ... +* [AvoidInstanceofChecksInCatchClause](pmd_rules_java_design.html#avoidinstanceofchecksincatchclause): Each caught exception type should be handled in its own catch clause. +* [AvoidProtectedFieldInFinalClass](pmd_rules_java_design.html#avoidprotectedfieldinfinalclass): Do not use protected fields in final classes since they cannot be subclassed.Clarify your intent ... +* [AvoidProtectedMethodInFinalClassNotExtending](pmd_rules_java_design.html#avoidprotectedmethodinfinalclassnotextending): Do not use protected methods in most final classes since they cannot be subclassed. This shouldon... +* [AvoidReassigningParameters](pmd_rules_java_design.html#avoidreassigningparameters): Reassigning values to incoming parameters is not recommended. Use temporary local variables inst... +* [AvoidSynchronizedAtMethodLevel](pmd_rules_java_design.html#avoidsynchronizedatmethodlevel): Method-level synchronization can cause problems when new code is added to the method.Block-level ... +* [BadComparison](pmd_rules_java_design.html#badcomparison): Avoid equality comparisons with Double.NaN. Due to the implicit lack of representationprecision w... +* [ClassWithOnlyPrivateConstructorsShouldBeFinal](pmd_rules_java_design.html#classwithonlyprivateconstructorsshouldbefinal): A class with only private constructors should be final, unless the private constructoris invoked ... +* [CloseResource](pmd_rules_java_design.html#closeresource): Ensure that resources (like Connection, Statement, and ResultSet objects) are always closed after... +* [CompareObjectsWithEquals](pmd_rules_java_design.html#compareobjectswithequals): Use equals() to compare object references; avoid comparing them with ==. +* [ConfusingTernary](pmd_rules_java_design.html#confusingternary): Avoid negation within an "if" expression with an "else" clause. For example, rephrase: if (x !=... +* [ConstantsInInterface](pmd_rules_java_design.html#constantsininterface): Avoid constants in interfaces. Interfaces should define types, constants are implementation detai... +* [ConstructorCallsOverridableMethod](pmd_rules_java_design.html#constructorcallsoverridablemethod): Calling overridable methods during construction poses a risk of invoking methods on an incomplete... +* [DefaultLabelNotLastInSwitchStmt](pmd_rules_java_design.html#defaultlabelnotlastinswitchstmt): By convention, the default label should be the last label in a switch statement. +* [EmptyMethodInAbstractClassShouldBeAbstract](pmd_rules_java_design.html#emptymethodinabstractclassshouldbeabstract): Empty or auto-generated methods in an abstract class should be tagged as abstract. This helps to ... +* [EqualsNull](pmd_rules_java_design.html#equalsnull): Tests for null should not use the equals() method. The '==' operator should be used instead. +* [FieldDeclarationsShouldBeAtStartOfClass](pmd_rules_java_design.html#fielddeclarationsshouldbeatstartofclass): Fields should be declared at the top of the class, before any method declarations, constructors, ... +* [FinalFieldCouldBeStatic](pmd_rules_java_design.html#finalfieldcouldbestatic): If a final field is assigned to a compile-time constant, it could be made static, thus saving ove... +* [GodClass](pmd_rules_java_design.html#godclass): The God Class rule detects the God Class design flaw using metrics. God classes do too many thing... +* [IdempotentOperations](pmd_rules_java_design.html#idempotentoperations): Avoid idempotent operations - they have no effect. +* [ImmutableField](pmd_rules_java_design.html#immutablefield): Identifies private fields whose values never change once they are initialized either in the decla... +* [InstantiationToGetClass](pmd_rules_java_design.html#instantiationtogetclass): Avoid instantiating an object just to call getClass() on it; use the .class public member instead. +* [LogicInversion](pmd_rules_java_design.html#logicinversion): Use opposite operator instead of negating the whole expression with a logic complement operator. +* [MissingBreakInSwitch](pmd_rules_java_design.html#missingbreakinswitch): Switch statements without break or return statements for each case optionmay indicate problematic... +* [MissingStaticMethodInNonInstantiatableClass](pmd_rules_java_design.html#missingstaticmethodinnoninstantiatableclass): A class that has private constructors and does not have any static methods or fields cannot be used. +* [NonCaseLabelInSwitchStatement](pmd_rules_java_design.html#noncaselabelinswitchstatement): A non-case label (e.g. a named break/continue label) was present in a switch statement.This legal... +* [NonStaticInitializer](pmd_rules_java_design.html#nonstaticinitializer): A non-static initializer block will be called any time a constructor is invoked (just prior toinv... +* [NonThreadSafeSingleton](pmd_rules_java_design.html#nonthreadsafesingleton): Non-thread safe singletons can result in bad state changes. Eliminatestatic singletons if possibl... +* [OptimizableToArrayCall](pmd_rules_java_design.html#optimizabletoarraycall): Calls to a collection's toArray() method should specify target arrays sized to match the size of ... +* [PositionLiteralsFirstInCaseInsensitiveComparisons](pmd_rules_java_design.html#positionliteralsfirstincaseinsensitivecomparisons): Position literals first in comparisons, if the second argument is null then NullPointerExceptions... +* [PositionLiteralsFirstInComparisons](pmd_rules_java_design.html#positionliteralsfirstincomparisons): Position literals first in comparisons, if the second argument is null then NullPointerExceptions... +* [PreserveStackTrace](pmd_rules_java_design.html#preservestacktrace): Throwing a new exception from a catch block without passing the original exception into thenew ex... +* [ReturnEmptyArrayRatherThanNull](pmd_rules_java_design.html#returnemptyarrayratherthannull): For any method that returns an array, it is a better to return an empty array rather than anull r... +* [SimpleDateFormatNeedsLocale](pmd_rules_java_design.html#simpledateformatneedslocale): Be sure to specify a Locale when creating SimpleDateFormat instances to ensure that locale-approp... +* [SimplifyBooleanExpressions](pmd_rules_java_design.html#simplifybooleanexpressions): Avoid unnecessary comparisons in boolean expressions, they serve no purpose and impacts readability. +* [SimplifyBooleanReturns](pmd_rules_java_design.html#simplifybooleanreturns): Avoid unnecessary if-then-else statements when returning a boolean. The result ofthe conditional ... +* [SimplifyConditional](pmd_rules_java_design.html#simplifyconditional): No need to check for null before an instanceof; the instanceof keyword returns false when given a... +* [SingleMethodSingleton](pmd_rules_java_design.html#singlemethodsingleton): Some classes contain overloaded getInstance. The problem with overloaded getInstance methodsis th... +* [SingletonClassReturningNewInstance](pmd_rules_java_design.html#singletonclassreturningnewinstance): Some classes contain overloaded getInstance. The problem with overloaded getInstance methodsis th... +* [SingularField](pmd_rules_java_design.html#singularfield): Fields whose scopes are limited to just single methods do not rely on the containingobject to pro... +* [SwitchDensity](pmd_rules_java_design.html#switchdensity): A high ratio of statements to labels in a switch statement implies that the switch statementis ov... +* [SwitchStmtsShouldHaveDefault](pmd_rules_java_design.html#switchstmtsshouldhavedefault): All switch statements should include a default option to catch any unspecified values. +* [TooFewBranchesForASwitchStatement](pmd_rules_java_design.html#toofewbranchesforaswitchstatement): Switch statements are indended to be used to support complex branching behaviour. Using a switch ... +* [UncommentedEmptyConstructor](pmd_rules_java_design.html#uncommentedemptyconstructor): Uncommented Empty Constructor finds instances where a constructor does notcontain statements, but... +* [UncommentedEmptyMethodBody](pmd_rules_java_design.html#uncommentedemptymethodbody): Uncommented Empty Method Body finds instances where a method body does not containstatements, but... +* [UnnecessaryLocalBeforeReturn](pmd_rules_java_design.html#unnecessarylocalbeforereturn): Avoid the creation of unnecessary local variables +* [UnsynchronizedStaticDateFormatter](pmd_rules_java_design.html#unsynchronizedstaticdateformatter): SimpleDateFormat instances are not synchronized. Sun recommends using separate format instancesfo... +* [UseCollectionIsEmpty](pmd_rules_java_design.html#usecollectionisempty): The isEmpty() method on java.util.Collection is provided to determine if a collection has any ele... +* [UseLocaleWithCaseConversions](pmd_rules_java_design.html#uselocalewithcaseconversions): When doing String.toLowerCase()/toUpperCase() conversions, use Locales to avoids problems with la... +* [UseNotifyAllInsteadOfNotify](pmd_rules_java_design.html#usenotifyallinsteadofnotify): Thread.notify() awakens a thread monitoring the object. If more than one thread is monitoring, th... +* [UseUtilityClass](pmd_rules_java_design.html#useutilityclass): For classes that only have static methods, consider making them utility classes.Note that this do... +* [UseVarargs](pmd_rules_java_design.html#usevarargs): Java 5 introduced the varargs parameter declaration for methods and constructors. This syntactic... + +## Empty Code +* [EmptyCatchBlock](pmd_rules_java_empty.html#emptycatchblock): Empty Catch Block finds instances where an exception is caught, but nothing is done. In most cir... +* [EmptyFinallyBlock](pmd_rules_java_empty.html#emptyfinallyblock): Empty finally blocks serve no purpose and should be removed. +* [EmptyIfStmt](pmd_rules_java_empty.html#emptyifstmt): Empty If Statement finds instances where a condition is checked but nothing is done about it. +* [EmptyInitializer](pmd_rules_java_empty.html#emptyinitializer): Empty initializers serve no purpose and should be removed. +* [EmptyStatementBlock](pmd_rules_java_empty.html#emptystatementblock): Empty block statements serve no purpose and should be removed. +* [EmptyStatementNotInLoop](pmd_rules_java_empty.html#emptystatementnotinloop): An empty statement (or a semicolon by itself) that is not used as the sole body of a 'for' or 'wh... +* [EmptyStaticInitializer](pmd_rules_java_empty.html#emptystaticinitializer): An empty static initializer serve no purpose and should be removed. +* [EmptySwitchStatements](pmd_rules_java_empty.html#emptyswitchstatements): Empty switch statements serve no purpose and should be removed. +* [EmptySynchronizedBlock](pmd_rules_java_empty.html#emptysynchronizedblock): Empty synchronized blocks serve no purpose and should be removed. +* [EmptyTryBlock](pmd_rules_java_empty.html#emptytryblock): Avoid empty try blocks - what's the point? +* [EmptyWhileStmt](pmd_rules_java_empty.html#emptywhilestmt): Empty While Statement finds all instances where a while statement does nothing. If it is a timin... + +## Finalizer +* [AvoidCallingFinalize](pmd_rules_java_finalizers.html#avoidcallingfinalize): The method Object.finalize() is called by the garbage collector on an object when garbage collect... +* [EmptyFinalizer](pmd_rules_java_finalizers.html#emptyfinalizer): Empty finalize methods serve no purpose and should be removed. +* [FinalizeDoesNotCallSuperFinalize](pmd_rules_java_finalizers.html#finalizedoesnotcallsuperfinalize): If the finalize() is implemented, its last action should be to call super.finalize. +* [FinalizeOnlyCallsSuperFinalize](pmd_rules_java_finalizers.html#finalizeonlycallssuperfinalize): If the finalize() is implemented, it should do something besides just calling super.finalize(). +* [FinalizeOverloaded](pmd_rules_java_finalizers.html#finalizeoverloaded): Methods named finalize() should not have parameters. It is confusing and most likely an attempt ... +* [FinalizeShouldBeProtected](pmd_rules_java_finalizers.html#finalizeshouldbeprotected): When overriding the finalize(), the new method should be set as protected. If made public, other... + +## Import Statements +* [DontImportJavaLang](pmd_rules_java_imports.html#dontimportjavalang): Avoid importing anything from the package 'java.lang'. These classes are automatically imported ... +* [DuplicateImports](pmd_rules_java_imports.html#duplicateimports): Duplicate or overlapping import statements should be avoided. +* [ImportFromSamePackage](pmd_rules_java_imports.html#importfromsamepackage): There is no need to import a type that lives in the same package. +* [TooManyStaticImports](pmd_rules_java_imports.html#toomanystaticimports): If you overuse the static import feature, it can make your program unreadable and unmaintainable,... +* [UnnecessaryFullyQualifiedName](pmd_rules_java_imports.html#unnecessaryfullyqualifiedname): Import statements allow the use of non-fully qualified names. The use of a fully qualified namew... +* [UnusedImports](pmd_rules_java_imports.html#unusedimports): Avoid the use of unused import statements to prevent unwanted dependencies. + +## J2EE +* [DoNotCallSystemExit](pmd_rules_java_j2ee.html#donotcallsystemexit): Web applications should not call System.exit(), since only the web container or theapplication se... +* [DoNotUseThreads](pmd_rules_java_j2ee.html#donotusethreads): The J2EE specification explicitly forbids the use of threads. +* [LocalHomeNamingConvention](pmd_rules_java_j2ee.html#localhomenamingconvention): The Local Home interface of a Session EJB should be suffixed by 'LocalHome'. +* [LocalInterfaceSessionNamingConvention](pmd_rules_java_j2ee.html#localinterfacesessionnamingconvention): The Local Interface of a Session EJB should be suffixed by 'Local'. +* [MDBAndSessionBeanNamingConvention](pmd_rules_java_j2ee.html#mdbandsessionbeannamingconvention): The EJB Specification states that any MessageDrivenBean or SessionBean should be suffixed by 'Bean'. +* [RemoteInterfaceNamingConvention](pmd_rules_java_j2ee.html#remoteinterfacenamingconvention): Remote Interface of a Session EJB should not have a suffix. +* [RemoteSessionInterfaceNamingConvention](pmd_rules_java_j2ee.html#remotesessioninterfacenamingconvention): A Remote Home interface type of a Session EJB should be suffixed by 'Home'. +* [StaticEJBFieldShouldBeFinal](pmd_rules_java_j2ee.html#staticejbfieldshouldbefinal): According to the J2EE specification, an EJB should not have any static fieldswith write access. H... +* [UseProperClassLoader](pmd_rules_java_j2ee.html#useproperclassloader): In J2EE, the getClassLoader() method might not work as expected. Use Thread.currentThread().getCo... + +## Jakarta Commons Logging +* [GuardDebugLogging](pmd_rules_java_logging-jakarta-commons.html#guarddebuglogging): When log messages are composed by concatenating strings, the whole section should be guarded ... +* [GuardLogStatement](pmd_rules_java_logging-jakarta-commons.html#guardlogstatement): Whenever using a log level, one should check if the loglevel is actually enabled, orotherwise ski... +* [ProperLogger](pmd_rules_java_logging-jakarta-commons.html#properlogger): A logger should normally be defined private static final and be associated with the correct class... +* [UseCorrectExceptionLogging](pmd_rules_java_logging-jakarta-commons.html#usecorrectexceptionlogging): To make sure the full stacktrace is printed out, use the logging statement with two arguments: a ... + +## Java Logging +* [AvoidPrintStackTrace](pmd_rules_java_logging-java.html#avoidprintstacktrace): Avoid printStackTrace(); use a logger call instead. +* [GuardLogStatementJavaUtil](pmd_rules_java_logging-java.html#guardlogstatementjavautil): Whenever using a log level, one should check if the loglevel is actually enabled, orotherwise ski... +* [InvalidSlf4jMessageFormat](pmd_rules_java_logging-java.html#invalidslf4jmessageformat): Check for messages in slf4j loggers with non matching number of arguments and placeholders. +* [LoggerIsNotStaticFinal](pmd_rules_java_logging-java.html#loggerisnotstaticfinal): In most cases, the Logger reference can be declared as static and final. +* [MoreThanOneLogger](pmd_rules_java_logging-java.html#morethanonelogger): Normally only one logger is used in each class. +* [SystemPrintln](pmd_rules_java_logging-java.html#systemprintln): References to System.(out\|err).print are usually intended for debugging purposes and can remain ... + +## JavaBeans +* [BeanMembersShouldSerialize](pmd_rules_java_javabeans.html#beanmembersshouldserialize): If a class is a bean, or is referenced by a bean directly or indirectly it needs to be serializab... +* [MissingSerialVersionUID](pmd_rules_java_javabeans.html#missingserialversionuid): Serializable classes should provide a serialVersionUID field. + +## JUnit +* [JUnitAssertionsShouldIncludeMessage](pmd_rules_java_junit.html#junitassertionsshouldincludemessage): JUnit assertions should include an informative message - i.e., use the three-argument version of ... +* [JUnitSpelling](pmd_rules_java_junit.html#junitspelling): Some JUnit framework methods are easy to misspell. +* [JUnitStaticSuite](pmd_rules_java_junit.html#junitstaticsuite): The suite() method in a JUnit test needs to be both public and static. +* [JUnitTestContainsTooManyAsserts](pmd_rules_java_junit.html#junittestcontainstoomanyasserts): JUnit tests should not contain too many asserts. Many asserts are indicative of a complex test, ... +* [JUnitTestsShouldIncludeAssert](pmd_rules_java_junit.html#junittestsshouldincludeassert): JUnit tests should include at least one assertion. This makes the tests more robust, and using a... +* [SimplifyBooleanAssertion](pmd_rules_java_junit.html#simplifybooleanassertion): Avoid negation in an assertTrue or assertFalse test.For example, rephrase: assertTrue(!expr); ... +* [TestClassWithoutTestCases](pmd_rules_java_junit.html#testclasswithouttestcases): Test classes end with the suffix Test. Having a non-test class with that name is not a good pract... +* [UnnecessaryBooleanAssertion](pmd_rules_java_junit.html#unnecessarybooleanassertion): A JUnit test assertion with a boolean literal is unnecessary since it always will evaluate to the... +* [UseAssertEqualsInsteadOfAssertTrue](pmd_rules_java_junit.html#useassertequalsinsteadofasserttrue): This rule detects JUnit assertions in object equality. These assertions should be made by more sp... +* [UseAssertNullInsteadOfAssertTrue](pmd_rules_java_junit.html#useassertnullinsteadofasserttrue): This rule detects JUnit assertions in object references equality. These assertions should be made... +* [UseAssertSameInsteadOfAssertTrue](pmd_rules_java_junit.html#useassertsameinsteadofasserttrue): This rule detects JUnit assertions in object references equality. These assertions should be made... +* [UseAssertTrueInsteadOfAssertEquals](pmd_rules_java_junit.html#useasserttrueinsteadofassertequals): When asserting a value is the same as a literal or Boxed boolean, use assertTrue/assertFalse, ins... + +## Migration +* [AvoidAssertAsIdentifier](pmd_rules_java_migrating.html#avoidassertasidentifier): Use of the term 'assert' will conflict with newer versions of Java since it is a reserved word. +* [AvoidEnumAsIdentifier](pmd_rules_java_migrating.html#avoidenumasidentifier): Use of the term 'enum' will conflict with newer versions of Java since it is a reserved word. +* [ByteInstantiation](pmd_rules_java_migrating.html#byteinstantiation): Calling new Byte() causes memory allocation that can be avoided by the static Byte.valueOf().It m... +* [IntegerInstantiation](pmd_rules_java_migrating.html#integerinstantiation): Calling new Integer() causes memory allocation that can be avoided by the static Integer.valueOf(... +* [JUnit4SuitesShouldUseSuiteAnnotation](pmd_rules_java_migrating.html#junit4suitesshouldusesuiteannotation): In JUnit 3, test suites are indicated by the suite() method. In JUnit 4, suites are indicatedthro... +* [JUnit4TestShouldUseAfterAnnotation](pmd_rules_java_migrating.html#junit4testshoulduseafterannotation): In JUnit 3, the tearDown method was used to clean up all data entities required in running tests.... +* [JUnit4TestShouldUseBeforeAnnotation](pmd_rules_java_migrating.html#junit4testshouldusebeforeannotation): In JUnit 3, the setUp method was used to set up all data entities required in running tests. JUni... +* [JUnit4TestShouldUseTestAnnotation](pmd_rules_java_migrating.html#junit4testshouldusetestannotation): In JUnit 3, the framework executed all methods which started with the word test as a unit test. I... +* [JUnitUseExpected](pmd_rules_java_migrating.html#junituseexpected): In JUnit4, use the @Test(expected) annotation to denote tests that should throw exceptions. +* [LongInstantiation](pmd_rules_java_migrating.html#longinstantiation): Calling new Long() causes memory allocation that can be avoided by the static Long.valueOf().It m... +* [ReplaceEnumerationWithIterator](pmd_rules_java_migrating.html#replaceenumerationwithiterator): Consider replacing Enumeration usages with the newer java.util.Iterator +* [ReplaceHashtableWithMap](pmd_rules_java_migrating.html#replacehashtablewithmap): Consider replacing Hashtable usage with the newer java.util.Map if thread safety is not required. +* [ReplaceVectorWithList](pmd_rules_java_migrating.html#replacevectorwithlist): Consider replacing Vector usages with the newer java.util.ArrayList if expensive thread-safe oper... +* [ShortInstantiation](pmd_rules_java_migrating.html#shortinstantiation): Calling new Short() causes memory allocation that can be avoided by the static Short.valueOf().It... + +## Naming +* [AbstractNaming](pmd_rules_java_naming.html#abstractnaming): Abstract classes should be named 'AbstractXXX'. +* [AvoidDollarSigns](pmd_rules_java_naming.html#avoiddollarsigns): Avoid using dollar signs in variable/method/class/interface names. +* [AvoidFieldNameMatchingMethodName](pmd_rules_java_naming.html#avoidfieldnamematchingmethodname): It can be confusing to have a field name with the same name as a method. While this is permitted,... +* [AvoidFieldNameMatchingTypeName](pmd_rules_java_naming.html#avoidfieldnamematchingtypename): It is somewhat confusing to have a field name matching the declaring class name.This probably mea... +* [BooleanGetMethodName](pmd_rules_java_naming.html#booleangetmethodname): Methods that return boolean results should be named as predicate statements to denote this.I.e, '... +* [ClassNamingConventions](pmd_rules_java_naming.html#classnamingconventions): Class names should always begin with an upper case character. +* [GenericsNaming](pmd_rules_java_naming.html#genericsnaming): Names for references to generic values should be limited to a single uppercase letter. +* [LongVariable](pmd_rules_java_naming.html#longvariable): Fields, formal arguments, or local variable names that are too long can make the code difficult t... +* [MethodNamingConventions](pmd_rules_java_naming.html#methodnamingconventions): Method names should always begin with a lower case character, and should not contain underscores. +* [MethodWithSameNameAsEnclosingClass](pmd_rules_java_naming.html#methodwithsamenameasenclosingclass): Non-constructor methods should not have the same name as the enclosing class. +* [MisleadingVariableName](pmd_rules_java_naming.html#misleadingvariablename): Detects when a non-field has a name starting with 'm_'. This usually denotes a field and could b... +* [NoPackage](pmd_rules_java_naming.html#nopackage): Detects when a class or interface does not have a package definition. +* [PackageCase](pmd_rules_java_naming.html#packagecase): Detects when a package definition contains uppercase characters. +* [ShortClassName](pmd_rules_java_naming.html#shortclassname): Short Classnames with fewer than e.g. five characters are not recommended. +* [ShortMethodName](pmd_rules_java_naming.html#shortmethodname): Method names that are very short are not helpful to the reader. +* [ShortVariable](pmd_rules_java_naming.html#shortvariable): Fields, local variables, or parameter names that are very short are not helpful to the reader. +* [SuspiciousConstantFieldName](pmd_rules_java_naming.html#suspiciousconstantfieldname): Field names using all uppercase characters - Sun's Java naming conventions indicating constants -... +* [SuspiciousEqualsMethodName](pmd_rules_java_naming.html#suspiciousequalsmethodname): The method name and parameter number are suspiciously close to equals(Object), which can denote a... +* [SuspiciousHashcodeMethodName](pmd_rules_java_naming.html#suspicioushashcodemethodname): The method name and return type are suspiciously close to hashCode(), which may denote an intenti... +* [VariableNamingConventions](pmd_rules_java_naming.html#variablenamingconventions): A variable naming conventions rule - customize this to your liking. Currently, itchecks for fina... + +## Optimization +* [AddEmptyString](pmd_rules_java_optimizations.html#addemptystring): The conversion of literals to strings by concatenating them with empty strings is inefficient.It ... +* [AvoidArrayLoops](pmd_rules_java_optimizations.html#avoidarrayloops): Instead of manually copying data between two arrays, use the efficient System.arraycopy method in... +* [AvoidInstantiatingObjectsInLoops](pmd_rules_java_optimizations.html#avoidinstantiatingobjectsinloops): New objects created within loops should be checked to see if they can created outside them and re... +* [LocalVariableCouldBeFinal](pmd_rules_java_optimizations.html#localvariablecouldbefinal): A local variable assigned only once can be declared final. +* [MethodArgumentCouldBeFinal](pmd_rules_java_optimizations.html#methodargumentcouldbefinal): A method argument that is never re-assigned within the method can be declared final. +* [PrematureDeclaration](pmd_rules_java_optimizations.html#prematuredeclaration): Checks for variables that are defined before they might be used. A reference is deemed to be prem... +* [RedundantFieldInitializer](pmd_rules_java_optimizations.html#redundantfieldinitializer): Java will initialize fields with known default values so any explicit initialization of those sam... +* [SimplifyStartsWith](pmd_rules_java_optimizations.html#simplifystartswith): Since it passes in a literal of length 1, calls to (string).startsWith can be rewritten using (st... +* [UnnecessaryWrapperObjectCreation](pmd_rules_java_optimizations.html#unnecessarywrapperobjectcreation): Most wrapper classes provide static conversion methods that avoid the need to create intermediate... +* [UseArrayListInsteadOfVector](pmd_rules_java_optimizations.html#usearraylistinsteadofvector): ArrayList is a much better Collection implementation than Vector if thread-safe operation is not ... +* [UseArraysAsList](pmd_rules_java_optimizations.html#usearraysaslist): The java.util.Arrays class has a "asList" method that should be used when you want to create a ne... +* [UseStringBufferForStringAppends](pmd_rules_java_optimizations.html#usestringbufferforstringappends): The use of the '+=' operator for appending strings causes the JVM to create and use an internal S... + +## Security Code Guidelines +* [ArrayIsStoredDirectly](pmd_rules_java_sunsecure.html#arrayisstoreddirectly): Constructors and methods receiving arrays should clone objects and store the copy.This prevents f... +* [MethodReturnsInternalArray](pmd_rules_java_sunsecure.html#methodreturnsinternalarray): Exposing internal arrays to the caller violates object encapsulation since elements can be remove... + +## Strict Exceptions +* [AvoidCatchingGenericException](pmd_rules_java_strictexception.html#avoidcatchinggenericexception): Avoid catching generic exceptions such as NullPointerException, RuntimeException, Exception in tr... +* [AvoidCatchingNPE](pmd_rules_java_strictexception.html#avoidcatchingnpe): Code should never throw NullPointerExceptions under normal circumstances. A catch block may hide... +* [AvoidCatchingThrowable](pmd_rules_java_strictexception.html#avoidcatchingthrowable): Catching Throwable errors is not recommended since its scope is very broad. It includes runtime i... +* [AvoidLosingExceptionInformation](pmd_rules_java_strictexception.html#avoidlosingexceptioninformation): Statements in a catch block that invoke accessors on the exception without using the informationo... +* [AvoidRethrowingException](pmd_rules_java_strictexception.html#avoidrethrowingexception): Catch blocks that merely rethrow a caught exception only add to code size and runtime complexity. +* [AvoidThrowingNewInstanceOfSameException](pmd_rules_java_strictexception.html#avoidthrowingnewinstanceofsameexception): Catch blocks that merely rethrow a caught exception wrapped inside a new instance of the same typ... +* [AvoidThrowingNullPointerException](pmd_rules_java_strictexception.html#avoidthrowingnullpointerexception): Avoid throwing NullPointerExceptions. These are confusing because most people will assume that th... +* [AvoidThrowingRawExceptionTypes](pmd_rules_java_strictexception.html#avoidthrowingrawexceptiontypes): Avoid throwing certain exception types. Rather than throw a raw RuntimeException, Throwable,Excep... +* [DoNotExtendJavaLangError](pmd_rules_java_strictexception.html#donotextendjavalangerror): Errors are system exceptions. Do not extend them. +* [DoNotThrowExceptionInFinally](pmd_rules_java_strictexception.html#donotthrowexceptioninfinally): Throwing exceptions within a 'finally' block is confusing since they may mask other exceptions or... +* [ExceptionAsFlowControl](pmd_rules_java_strictexception.html#exceptionasflowcontrol): Using Exceptions as form of flow control is not recommended as they obscure true exceptions when ... +* [SignatureDeclareThrowsException](pmd_rules_java_strictexception.html#signaturedeclarethrowsexception): Methods that declare the generic Exception as a possible throwable are not very helpful since the... + +## String and StringBuffer +* [AppendCharacterWithChar](pmd_rules_java_strings.html#appendcharacterwithchar): Avoid concatenating characters as strings in StringBuffer/StringBuilder.append methods. +* [AvoidDuplicateLiterals](pmd_rules_java_strings.html#avoidduplicateliterals): Code containing duplicate String literals can usually be improved by declaring the String as a co... +* [AvoidStringBufferField](pmd_rules_java_strings.html#avoidstringbufferfield): StringBuffers/StringBuilders can grow considerably, and so may become a source of memory leaksif ... +* [ConsecutiveAppendsShouldReuse](pmd_rules_java_strings.html#consecutiveappendsshouldreuse): Consecutive calls to StringBuffer/StringBuilder .append should be chained, reusing the target obj... +* [ConsecutiveLiteralAppends](pmd_rules_java_strings.html#consecutiveliteralappends): Consecutively calling StringBuffer/StringBuilder.append with String literals +* [InefficientEmptyStringCheck](pmd_rules_java_strings.html#inefficientemptystringcheck): String.trim().length() is an inefficient way to check if a String is really empty, as itcreates a... +* [InefficientStringBuffering](pmd_rules_java_strings.html#inefficientstringbuffering): Avoid concatenating non-literals in a StringBuffer constructor or append() since intermediate buf... +* [InsufficientStringBufferDeclaration](pmd_rules_java_strings.html#insufficientstringbufferdeclaration): Failing to pre-size a StringBuffer or StringBuilder properly could cause it to re-size many times... +* [StringBufferInstantiationWithChar](pmd_rules_java_strings.html#stringbufferinstantiationwithchar): Individual character values provided as initialization arguments will be converted into integers.... +* [StringInstantiation](pmd_rules_java_strings.html#stringinstantiation): Avoid instantiating String objects; this is usually unnecessary since they are immutable and can ... +* [StringToString](pmd_rules_java_strings.html#stringtostring): Avoid calling toString() on objects already known to be string instances; this is unnecessary. +* [UnnecessaryCaseChange](pmd_rules_java_strings.html#unnecessarycasechange): Using equalsIgnoreCase() is faster than using toUpperCase/toLowerCase().equals() +* [UseEqualsToCompareStrings](pmd_rules_java_strings.html#useequalstocomparestrings): Using '==' or '!=' to compare strings only works if intern version is used on both sides.Use the ... +* [UseIndexOfChar](pmd_rules_java_strings.html#useindexofchar): Use String.indexOf(char) when checking for the index of a single character; it executes faster. +* [UselessStringValueOf](pmd_rules_java_strings.html#uselessstringvalueof): No need to call String.valueOf to append to a string; just use the valueOf() argument directly. +* [UseStringBufferLength](pmd_rules_java_strings.html#usestringbufferlength): Use StringBuffer.length() to determine StringBuffer length rather than using StringBuffer.toStrin... + +## Unnecessary +* [UnnecessaryConversionTemporary](pmd_rules_java_unnecessary.html#unnecessaryconversiontemporary): Avoid the use temporary objects when converting primitives to Strings. Use the static conversion ... +* [UnnecessaryFinalModifier](pmd_rules_java_unnecessary.html#unnecessaryfinalmodifier): When a class has the final modifier, all the methods are automatically final and do not need to b... +* [UnnecessaryModifier](pmd_rules_java_unnecessary.html#unnecessarymodifier): Fields in interfaces and annotations are automatically 'public static final', and methods are 'pu... +* [UnnecessaryReturn](pmd_rules_java_unnecessary.html#unnecessaryreturn): Avoid the use of unnecessary return statements. +* [UnusedNullCheckInEquals](pmd_rules_java_unnecessary.html#unusednullcheckinequals): After checking an object reference for null, you should invoke equals() on that object rather tha... +* [UselessOperationOnImmutable](pmd_rules_java_unnecessary.html#uselessoperationonimmutable): An operation on an Immutable object (String, BigDecimal or BigInteger) won't change the object it... +* [UselessOverridingMethod](pmd_rules_java_unnecessary.html#uselessoverridingmethod): The overriding method merely calls the same method defined in a superclass. +* [UselessParentheses](pmd_rules_java_unnecessary.html#uselessparentheses): Useless parentheses should be removed. +* [UselessQualifiedThis](pmd_rules_java_unnecessary.html#uselessqualifiedthis): Look for qualified this usages in the same class. + +## Unused Code +* [UnusedFormalParameter](pmd_rules_java_unusedcode.html#unusedformalparameter): Avoid passing parameters to methods or constructors without actually referencing them in the meth... +* [UnusedLocalVariable](pmd_rules_java_unusedcode.html#unusedlocalvariable): Detects when a local variable is declared and/or assigned, but not used. +* [UnusedPrivateField](pmd_rules_java_unusedcode.html#unusedprivatefield): Detects when a private field is declared and/or assigned a value, but not used. +* [UnusedPrivateMethod](pmd_rules_java_unusedcode.html#unusedprivatemethod): Unused Private Method detects when a private method is declared but is unused. + diff --git a/docs/pages/pmd/rules/java/android.md b/docs/pages/pmd/rules/java/android.md new file mode 100644 index 000000000..2f454543d --- /dev/null +++ b/docs/pages/pmd/rules/java/android.md @@ -0,0 +1,108 @@ +--- +title: Android +summary: These rules deal with the Android SDK, mostly related to best practices. To get better results, make sure that the auxclasspath is defined for type resolution to work. +permalink: pmd_rules_java_android.html +folder: pmd/rules/java +sidebaractiveurl: /pmd_rules_java.html +editmepath: ../pmd-java/src/main/resources/rulesets/java/android.xml +--- +## CallSuperFirst + +**Since:** PMD 4.2.5 + +**Priority:** Medium (3) + +Super should be called at the start of the method + +``` +//MethodDeclaration[MethodDeclarator[ + @Image='onCreate' or + @Image='onConfigurationChanged' or + @Image='onPostCreate' or + @Image='onPostResume' or + @Image='onRestart' or + @Image='onRestoreInstanceState' or + @Image='onResume' or + @Image='onStart' + ]] + /Block[not( + (BlockStatement[1]/Statement/StatementExpression/PrimaryExpression[./PrimaryPrefix[@SuperModifier='true']]/PrimarySuffix[@Image= ancestor::MethodDeclaration/MethodDeclarator/@Image]))] +[ancestor::ClassOrInterfaceDeclaration[ExtendsList/ClassOrInterfaceType[ + typeof(@Image, 'android.app.Activity', 'Activity') or + typeof(@Image, 'android.app.Application', 'Application') or + typeof(@Image, 'android.app.Service', 'Service') +]]] +``` + +**Example(s):** + +``` +public class DummyActivity extends Activity { + public void onCreate(Bundle bundle) { + // missing call to super.onCreate(bundle) + foo(); + } +} +``` + +## CallSuperLast + +**Since:** PMD 4.2.5 + +**Priority:** Medium (3) + +Super should be called at the end of the method + +``` +//MethodDeclaration[MethodDeclarator[ + @Image='finish' or + @Image='onDestroy' or + @Image='onPause' or + @Image='onSaveInstanceState' or + @Image='onStop' or + @Image='onTerminate' + ]] + /Block/BlockStatement[last()] + [not(Statement/StatementExpression/PrimaryExpression[./PrimaryPrefix[@SuperModifier='true']]/PrimarySuffix[@Image= ancestor::MethodDeclaration/MethodDeclarator/@Image])] +[ancestor::ClassOrInterfaceDeclaration[ExtendsList/ClassOrInterfaceType[ + typeof(@Image, 'android.app.Activity', 'Activity') or + typeof(@Image, 'android.app.Application', 'Application') or + typeof(@Image, 'android.app.Service', 'Service') +]]] +``` + +**Example(s):** + +``` +public class DummyActivity extends Activity { + public void onPause() { + foo(); + // missing call to super.onPause() + } +} +``` + +## DoNotHardCodeSDCard + +**Since:** PMD 4.2.6 + +**Priority:** Medium (3) + +Use Environment.getExternalStorageDirectory() instead of "/sdcard" + +``` +//Literal[starts-with(@Image,'"/sdcard')] +``` + +**Example(s):** + +``` +public class MyActivity extends Activity { + protected void foo() { + String storageLocation = "/sdcard/mypackage"; // hard-coded, poor approach + + storageLocation = Environment.getExternalStorageDirectory() + "/mypackage"; // preferred approach + } +} +``` + diff --git a/docs/pages/pmd/rules/java/basic.md b/docs/pages/pmd/rules/java/basic.md new file mode 100644 index 000000000..190861d67 --- /dev/null +++ b/docs/pages/pmd/rules/java/basic.md @@ -0,0 +1,751 @@ +--- +title: Basic +summary: The Basic ruleset contains a collection of good practices which should be followed. +permalink: pmd_rules_java_basic.html +folder: pmd/rules/java +sidebaractiveurl: /pmd_rules_java.html +editmepath: ../pmd-java/src/main/resources/rulesets/java/basic.xml +--- +## AvoidBranchingStatementAsLastInLoop + +**Since:** PMD 5.0 + +**Priority:** Medium High (2) + +Using a branching statement as the last part of a loop may be a bug, and/or is confusing. +Ensure that the usage is not a bug, or consider using another approach. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.basic.AvoidBranchingStatementAsLastInLoopRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/basic/AvoidBranchingStatementAsLastInLoopRule.java) + +**Example(s):** + +``` +// unusual use of branching statement in a loop +for (int i = 0; i < 10; i++) { + if (i*i <= 25) { + continue; + } + break; +} + + // this makes more sense... +for (int i = 0; i < 10; i++) { + if (i*i > 25) { + break; + } +} +``` + +**This rule has the following properties:** + +|Name|Default Value|Description| +|----|-------------|-----------| +|checkReturnLoopTypes|[for, do, while]|Check for return statements in loop types| +|checkContinueLoopTypes|[for, do, while]|Check for continue statements in loop types| +|checkBreakLoopTypes|[for, do, while]|Check for break statements in loop types| + +## AvoidDecimalLiteralsInBigDecimalConstructor + +**Since:** PMD 3.4 + +**Priority:** Medium (3) + +One might assume that the result of "new BigDecimal(0.1)" is exactly equal to 0.1, but it is actually +equal to .1000000000000000055511151231257827021181583404541015625. +This is because 0.1 cannot be represented exactly as a double (or as a binary fraction of any finite +length). Thus, the long value that is being passed in to the constructor is not exactly equal to 0.1, +appearances notwithstanding. + +The (String) constructor, on the other hand, is perfectly predictable: 'new BigDecimal("0.1")' is +exactly equal to 0.1, as one would expect. Therefore, it is generally recommended that the +(String) constructor be used in preference to this one. + +``` +//AllocationExpression +[ClassOrInterfaceType[@Image="BigDecimal"]] +[Arguments/ArgumentList/Expression/PrimaryExpression/PrimaryPrefix + [ + Literal[(not(ends-with(@Image,'"'))) and contains(@Image,".")] + or + Name[ancestor::Block/BlockStatement/LocalVariableDeclaration + [Type[PrimitiveType[@Image='double' or @Image='float'] + or ReferenceType/ClassOrInterfaceType[@Image='Double' or @Image='Float']]] + /VariableDeclarator/VariableDeclaratorId/@Image = @Image + ] + or + Name[ancestor::MethodDeclaration/MethodDeclarator/FormalParameters/FormalParameter + [Type[PrimitiveType[@Image='double' or @Image='float'] + or ReferenceType/ClassOrInterfaceType[@Image='Double' or @Image='Float']]] + /VariableDeclaratorId/@Image = @Image + ] + ] +] +``` + +**Example(s):** + +``` +BigDecimal bd = new BigDecimal(1.123); // loss of precision, this would trigger the rule + +BigDecimal bd = new BigDecimal("1.123"); // preferred approach + +BigDecimal bd = new BigDecimal(12); // preferred approach, ok for integer values +``` + +## AvoidMultipleUnaryOperators + +**Since:** PMD 4.2 + +**Priority:** Medium High (2) + +The use of multiple unary operators may be problematic, and/or confusing. +Ensure that the intended usage is not a bug, or consider simplifying the expression. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.basic.AvoidMultipleUnaryOperatorsRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/basic/AvoidMultipleUnaryOperatorsRule.java) + +**Example(s):** + +``` +// These are typo bugs, or at best needlessly complex and confusing: +int i = - -1; +int j = + - +1; +int z = ~~2; +boolean b = !!true; +boolean c = !!!true; + +// These are better: +int i = 1; +int j = -1; +int z = 2; +boolean b = true; +boolean c = false; + +// And these just make your brain hurt: +int i = ~-2; +int j = -~7; +``` + +## AvoidThreadGroup + +**Since:** PMD 3.6 + +**Priority:** Medium (3) + +Avoid using java.lang.ThreadGroup; although it is intended to be used in a threaded environment +it contains methods that are not thread-safe. + +``` +//AllocationExpression/ClassOrInterfaceType[pmd-java:typeof(@Image, 'java.lang.ThreadGroup')]| +//PrimarySuffix[contains(@Image, 'getThreadGroup')] +``` + +**Example(s):** + +``` +public class Bar { + void buz() { + ThreadGroup tg = new ThreadGroup("My threadgroup") ; + tg = new ThreadGroup(tg, "my thread group"); + tg = Thread.currentThread().getThreadGroup(); + tg = System.getSecurityManager().getThreadGroup(); + } +} +``` + +## AvoidUsingHardCodedIP + +**Since:** PMD 4.1 + +**Priority:** Medium (3) + +Application with hard-coded IP addresses can become impossible to deploy in some cases. +Externalizing IP adresses is preferable. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.basic.AvoidUsingHardCodedIPRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/basic/AvoidUsingHardCodedIPRule.java) + +**Example(s):** + +``` +public class Foo { + private String ip = "127.0.0.1"; // not recommended +} +``` + +**This rule has the following properties:** + +|Name|Default Value|Description| +|----|-------------|-----------| +|checkAddressTypes|[IPv4, IPv6, IPv4 mapped IPv6]|Check for IP address types.| +|pattern|^"[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}"$|Regular Expression| + +## AvoidUsingOctalValues + +**Since:** PMD 3.9 + +**Priority:** Medium (3) + +Integer literals should not start with zero since this denotes that the rest of literal will be +interpreted as an octal value. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.basic.AvoidUsingOctalValuesRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/basic/AvoidUsingOctalValuesRule.java) + +**Example(s):** + +``` +int i = 012; // set i with 10 not 12 +int j = 010; // set j with 8 not 10 +k = i * j; // set k with 80 not 120 +``` + +**This rule has the following properties:** + +|Name|Default Value|Description| +|----|-------------|-----------| +|strict|false|Detect violations between 00 and 07| + +## BigIntegerInstantiation + +**Since:** PMD 3.9 + +**Priority:** Medium (3) + +Don't create instances of already existing BigInteger (BigInteger.ZERO, BigInteger.ONE) and +for Java 1.5 onwards, BigInteger.TEN and BigDecimal (BigDecimal.ZERO, BigDecimal.ONE, BigDecimal.TEN) + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.basic.BigIntegerInstantiationRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/basic/BigIntegerInstantiationRule.java) + +**Example(s):** + +``` +BigInteger bi = new BigInteger(1); // reference BigInteger.ONE instead +BigInteger bi2 = new BigInteger("0"); // reference BigInteger.ZERO instead +BigInteger bi3 = new BigInteger(0.0); // reference BigInteger.ZERO instead +BigInteger bi4; +bi4 = new BigInteger(0); // reference BigInteger.ZERO instead +``` + +## BooleanInstantiation + +**Since:** PMD 1.2 + +**Priority:** Medium High (2) + +Avoid instantiating Boolean objects; you can reference Boolean.TRUE, Boolean.FALSE, or call Boolean.valueOf() instead. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.basic.BooleanInstantiationRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/basic/BooleanInstantiationRule.java) + +**Example(s):** + +``` +Boolean bar = new Boolean("true"); // unnecessary creation, just reference Boolean.TRUE; +Boolean buz = Boolean.valueOf(false); // ...., just reference Boolean.FALSE; +``` + +## BrokenNullCheck + +**Since:** PMD 3.8 + +**Priority:** Medium High (2) + +The null check is broken since it will throw a NullPointerException itself. +It is likely that you used || instead of && or vice versa. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.basic.BrokenNullCheckRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/basic/BrokenNullCheckRule.java) + +**Example(s):** + +``` +public String bar(String string) { + // should be && + if (string!=null || !string.equals("")) + return string; + // should be || + if (string==null && string.equals("")) + return string; +} +``` + +## CheckResultSet + +**Since:** PMD 4.1 + +**Priority:** Medium (3) + +Always check the return values of navigation methods (next, previous, first, last) of a ResultSet. +If the value return is 'false', it should be handled properly. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.basic.CheckResultSetRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/basic/CheckResultSetRule.java) + +**Example(s):** + +``` +Statement stat = conn.createStatement(); +ResultSet rst = stat.executeQuery("SELECT name FROM person"); +rst.next(); // what if it returns false? bad form +String firstName = rst.getString(1); + +Statement stat = conn.createStatement(); +ResultSet rst = stat.executeQuery("SELECT name FROM person"); +if (rst.next()) { // result is properly examined and used + String firstName = rst.getString(1); + } else { + // handle missing data +} +``` + +## CheckSkipResult + +**Since:** PMD 5.0 + +**Priority:** Medium (3) + +The skip() method may skip a smaller number of bytes than requested. Check the returned value to find out if it was the case or not. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.basic.CheckSkipResultRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/basic/CheckSkipResultRule.java) + +**Example(s):** + +``` +public class Foo { + + private FileInputStream _s = new FileInputStream("file"); + + public void skip(int n) throws IOException { + _s.skip(n); // You are not sure that exactly n bytes are skipped + } + + public void skipExactly(int n) throws IOException { + while (n != 0) { + long skipped = _s.skip(n); + if (skipped == 0) + throw new EOFException(); + n -= skipped; + } + } +``` + +## ClassCastExceptionWithToArray + +**Since:** PMD 3.4 + +**Priority:** Medium (3) + +When deriving an array of a specific class from your Collection, one should provide an array of +the same class as the parameter of the toArray() method. Doing otherwise you will will result +in a ClassCastException. + +``` +//CastExpression[Type/ReferenceType/ClassOrInterfaceType[@Image != +"Object"]]/PrimaryExpression +[ + PrimaryPrefix/Name[ends-with(@Image, '.toArray')] + and + PrimarySuffix/Arguments[count(*) = 0] +and +count(PrimarySuffix) = 1 +] +``` + +**Example(s):** + +``` +Collection c = new ArrayList(); +Integer obj = new Integer(1); +c.add(obj); + + // this would trigger the rule (and throw a ClassCastException if executed) +Integer[] a = (Integer [])c.toArray(); + + // this is fine and will not trigger the rule +Integer[] b = (Integer [])c.toArray(new Integer[c.size()]); +``` + +## CollapsibleIfStatements + +**Since:** PMD 3.1 + +**Priority:** Medium (3) + +Sometimes two consecutive 'if' statements can be consolidated by separating their conditions with a boolean short-circuit operator. + +``` +//IfStatement[@Else='false']/Statement + /IfStatement[@Else='false'] + | +//IfStatement[@Else='false']/Statement + /Block[count(BlockStatement)=1]/BlockStatement + /Statement/IfStatement[@Else='false'] +``` + +**Example(s):** + +``` +void bar() { + if (x) { // original implementation + if (y) { + // do stuff + } + } +} + +void bar() { + if (x && y) { // optimized implementation + // do stuff + } +} +``` + +## DontCallThreadRun + +**Since:** PMD 4.3 + +**Priority:** Medium Low (4) + +Explicitly calling Thread.run() method will execute in the caller's thread of control. Instead, call Thread.start() for the intended behavior. + +``` +//StatementExpression/PrimaryExpression +[ + PrimaryPrefix + [ + ./Name[ends-with(@Image, '.run') or @Image = 'run'] + and substring-before(Name/@Image, '.') =//VariableDeclarator/VariableDeclaratorId/@Image + [../../../Type/ReferenceType[ClassOrInterfaceType/@Image = 'Thread']] + or ( + ./AllocationExpression/ClassOrInterfaceType[@Image = 'Thread'] + and ../PrimarySuffix[@Image = 'run']) + ] +] +``` + +**Example(s):** + +``` +Thread t = new Thread(); +t.run(); // use t.start() instead +new Thread().run(); // same violation +``` + +## DontUseFloatTypeForLoopIndices + +**Since:** PMD 4.3 + +**Priority:** Medium (3) + +Don't use floating point for loop indices. If you must use floating point, use double +unless you're certain that float provides enough precision and you have a compelling +performance need (space or time). + +``` +//ForStatement/ForInit/LocalVariableDeclaration +/Type/PrimitiveType[@Image="float"] +``` + +**Example(s):** + +``` +public class Count { + public static void main(String[] args) { + final int START = 2000000000; + int count = 0; + for (float f = START; f < START + 50; f++) + count++; + //Prints 0 because (float) START == (float) (START + 50). + System.out.println(count); + //The termination test misbehaves due to floating point granularity. + } +} +``` + +## DoubleCheckedLocking + +**Since:** PMD 1.04 + +**Priority:** High (1) + +Partially created objects can be returned by the Double Checked Locking pattern when used in Java. +An optimizing JRE may assign a reference to the baz variable before it calls the constructor of the object the +reference points to. + +Note: With Java 5, you can make Double checked locking work, if you declare the variable to be `volatile`. + +For more details refer to: http://www.javaworld.com/javaworld/jw-02-2001/jw-0209-double.html +or http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.basic.DoubleCheckedLockingRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/basic/DoubleCheckedLockingRule.java) + +**Example(s):** + +``` +public class Foo { + /*volatile */ Object baz = null; // fix for Java5 and later: volatile + Object bar() { + if (baz == null) { // baz may be non-null yet not fully created + synchronized(this) { + if (baz == null) { + baz = new Object(); + } + } + } + return baz; + } +} +``` + +## ExtendsObject + +**Since:** PMD 5.0 + +**Priority:** Medium Low (4) + +No need to explicitly extend Object. + +``` +//ExtendsList/ClassOrInterfaceType[@Image='Object' or @Image='java.lang.Object'] +``` + +**Example(s):** + +``` +public class Foo extends Object { // not required +} +``` + +## ForLoopShouldBeWhileLoop + +**Since:** PMD 1.02 + +**Priority:** Medium (3) + +Some for loops can be simplified to while loops, this makes them more concise. + +``` +//ForStatement + [count(*) > 1] + [not(LocalVariableDeclaration)] + [not(ForInit)] + [not(ForUpdate)] + [not(Type and Expression and Statement)] +``` + +**Example(s):** + +``` +public class Foo { + void bar() { + for (;true;) true; // No Init or Update part, may as well be: while (true) + } +} +``` + +## JumbledIncrementer + +**Since:** PMD 1.0 + +**Priority:** Medium (3) + +Avoid jumbled loop incrementers - its usually a mistake, and is confusing even if intentional. + +``` +//ForStatement + [ + ForUpdate/StatementExpressionList/StatementExpression/PostfixExpression/PrimaryExpression/PrimaryPrefix/Name/@Image + = + ancestor::ForStatement/ForInit//VariableDeclaratorId/@Image + ] +``` + +**Example(s):** + +``` +public class JumbledIncrementerRule1 { + public void foo() { + for (int i = 0; i < 10; i++) { // only references 'i' + for (int k = 0; k < 20; i++) { // references both 'i' and 'k' + System.out.println("Hello"); + } + } + } +} +``` + +## MisplacedNullCheck + +**Since:** PMD 3.5 + +**Priority:** Medium (3) + +The null check here is misplaced. If the variable is null a NullPointerException will be thrown. +Either the check is useless (the variable will never be "null") or it is incorrect. + +``` +//Expression + /*[self::ConditionalOrExpression or self::ConditionalAndExpression] + /descendant::PrimaryExpression/PrimaryPrefix + /Name[starts-with(@Image, + concat(ancestor::PrimaryExpression/following-sibling::EqualityExpression + [./PrimaryExpression/PrimaryPrefix/Literal/NullLiteral] + /PrimaryExpression/PrimaryPrefix + /Name[count(../../PrimarySuffix)=0]/@Image,".") + ) + ] + [count(ancestor::ConditionalAndExpression/EqualityExpression + [@Image='!='] + [./PrimaryExpression/PrimaryPrefix/Literal/NullLiteral] + [starts-with(following-sibling::*/PrimaryExpression/PrimaryPrefix/Name/@Image, + concat(./PrimaryExpression/PrimaryPrefix/Name/@Image, '.'))] + ) = 0 + ] +``` + +**Example(s):** + +``` +public class Foo { + void bar() { + if (a.equals(baz) && a != null) {} + } +} +``` + +``` +public class Foo { + void bar() { + if (a.equals(baz) || a == null) {} + } +} +``` + +## OverrideBothEqualsAndHashcode + +**Since:** PMD 0.4 + +**Priority:** Medium (3) + +Override both public boolean Object.equals(Object other), and public int Object.hashCode(), or override neither. Even if you are inheriting a hashCode() from a parent class, consider implementing hashCode and explicitly delegating to your superclass. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.basic.OverrideBothEqualsAndHashcodeRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/basic/OverrideBothEqualsAndHashcodeRule.java) + +**Example(s):** + +``` +public class Bar { // poor, missing a hashcode() method + public boolean equals(Object o) { + // do some comparison + } +} + +public class Baz { // poor, missing an equals() method + public int hashCode() { + // return some hash value + } +} + +public class Foo { // perfect, both methods provided + public boolean equals(Object other) { + // do some comparison + } + public int hashCode() { + // return some hash value + } +} +``` + +## ReturnFromFinallyBlock + +**Since:** PMD 1.05 + +**Priority:** Medium (3) + +Avoid returning from a finally block, this can discard exceptions. + +``` +//FinallyStatement//ReturnStatement +``` + +**Example(s):** + +``` +public class Bar { + public String foo() { + try { + throw new Exception( "My Exception" ); + } catch (Exception e) { + throw e; + } finally { + return "A. O. K."; // return not recommended here + } + } +} +``` + +## SimplifiedTernary + +**Since:** PMD 5.4.0 + +**Priority:** Medium (3) + +Look for ternary operators with the form `condition ? literalBoolean : foo` +or `condition ? foo : literalBoolean`. + +These expressions can be simplified respectively to +`condition || foo` when the literalBoolean is true +`!condition && foo` when the literalBoolean is false +or +`!condition || foo` when the literalBoolean is true +`condition && foo` when the literalBoolean is false + +``` +//ConditionalExpression[@Ternary='true'][not(PrimaryExpression/*/Literal) and (Expression/PrimaryExpression/*/Literal/BooleanLiteral)] +| +//ConditionalExpression[@Ternary='true'][not(Expression/PrimaryExpression/*/Literal) and (PrimaryExpression/*/Literal/BooleanLiteral)] +``` + +**Example(s):** + +``` +public class Foo { + public boolean test() { + return condition ? true : something(); // can be as simple as return condition || something(); + } + + public void test2() { + final boolean value = condition ? false : something(); // can be as simple as value = !condition && something(); + } + + public boolean test3() { + return condition ? something() : true; // can be as simple as return !condition || something(); + } + + public void test4() { + final boolean otherValue = condition ? something() : false; // can be as simple as condition && something(); + } +} +``` + +## UnconditionalIfStatement + +**Since:** PMD 1.5 + +**Priority:** Medium (3) + +Do not use "if" statements whose conditionals are always true or always false. + +``` +//IfStatement/Expression + [count(PrimaryExpression)=1] + /PrimaryExpression/PrimaryPrefix/Literal/BooleanLiteral +``` + +**Example(s):** + +``` +public class Foo { + public void close() { + if (true) { // fixed conditional, not recommended + // ... + } + } +} +``` + diff --git a/docs/pages/pmd/rules/java/braces.md b/docs/pages/pmd/rules/java/braces.md new file mode 100644 index 000000000..66434c31e --- /dev/null +++ b/docs/pages/pmd/rules/java/braces.md @@ -0,0 +1,109 @@ +--- +title: Braces +summary: The Braces ruleset contains rules regarding the use and placement of braces. +permalink: pmd_rules_java_braces.html +folder: pmd/rules/java +sidebaractiveurl: /pmd_rules_java.html +editmepath: ../pmd-java/src/main/resources/rulesets/java/braces.xml +--- +## ForLoopsMustUseBraces + +**Since:** PMD 0.7 + +**Priority:** Medium (3) + +Avoid using 'for' statements without using curly braces. If the code formatting or +indentation is lost then it becomes difficult to separate the code being controlled +from the rest. + +``` +//ForStatement[not(Statement/Block)] +``` + +**Example(s):** + +``` +for (int i = 0; i < 42; i++) + foo(); +``` + +## IfElseStmtsMustUseBraces + +**Since:** PMD 0.2 + +**Priority:** Medium (3) + +Avoid using if..else statements without using surrounding braces. If the code formatting +or indentation is lost then it becomes difficult to separate the code being controlled +from the rest. + +``` +//Statement + [parent::IfStatement[@Else='true']] + [not(child::Block)] + [not(child::IfStatement)] +``` + +**Example(s):** + +``` +// this is OK +if (foo) x++; + + // but this is not +if (foo) + x = x+1; + else + x = x-1; +``` + +## IfStmtsMustUseBraces + +**Since:** PMD 1.0 + +**Priority:** Medium (3) + +Avoid using if statements without using braces to surround the code block. If the code +formatting or indentation is lost then it becomes difficult to separate the code being +controlled from the rest. + +``` +//IfStatement[count(*) < 3][not(Statement/Block)] +``` + +**Example(s):** + +``` +if (foo) // not recommended + x++; + +if (foo) { // preferred approach + x++; +} +``` + +## WhileLoopsMustUseBraces + +**Since:** PMD 0.7 + +**Priority:** Medium (3) + +Avoid using 'while' statements without using braces to surround the code block. If the code +formatting or indentation is lost then it becomes difficult to separate the code being +controlled from the rest. + +``` +//WhileStatement[not(Statement/Block)] +``` + +**Example(s):** + +``` +while (true) // not recommended + x++; + +while (true) { // preferred approach + x++; +} +``` + diff --git a/docs/pages/pmd/rules/java/clone.md b/docs/pages/pmd/rules/java/clone.md new file mode 100644 index 000000000..772bf5d4b --- /dev/null +++ b/docs/pages/pmd/rules/java/clone.md @@ -0,0 +1,179 @@ +--- +title: Clone Implementation +summary: The Clone Implementation ruleset contains a collection of rules that find questionable usages of the clone() method. +permalink: pmd_rules_java_clone.html +folder: pmd/rules/java +sidebaractiveurl: /pmd_rules_java.html +editmepath: ../pmd-java/src/main/resources/rulesets/java/clone.xml +--- +## CloneMethodMustBePublic + +**Since:** PMD 5.4.0 + +**Priority:** Medium (3) + +The java Manual says "By convention, classes that implement this interface should override +Object.clone (which is protected) with a public method." + +``` +//MethodDeclaration[@Public='false'] +[ +MethodDeclarator/@Image = 'clone' +and MethodDeclarator/FormalParameters/@ParameterCount = 0 +] +``` + +**Example(s):** + +``` +public class Foo implements Cloneable { + @Override + protected Object clone() throws CloneNotSupportedException { // Violation, must be public + } +} + +public class Foo implements Cloneable { + @Override + protected Foo clone() { // Violation, must be public + } +} + +public class Foo implements Cloneable { + @Override + public Object clone() // Ok +} +``` + +## CloneMethodMustImplementCloneable + +**Since:** PMD 1.9 + +**Priority:** Medium (3) + +The method clone() should only be implemented if the class implements the Cloneable interface with the exception of a final method that only throws CloneNotSupportedException. + +``` +//ClassOrInterfaceDeclaration +[not(./ExtendsList/ClassOrInterfaceType[@Image='Cloneable'])] +[not(./ImplementsList/ClassOrInterfaceType[@Image='Cloneable'])] +/ClassOrInterfaceBody/ClassOrInterfaceBodyDeclaration +[MethodDeclaration +[MethodDeclarator[@Image += 'clone' and count(FormalParameters/*) = 0]] +[not((../MethodDeclaration[@Final = 'true'] or ancestor::ClassOrInterfaceDeclaration[1][@Final = 'true']) +and Block[count(BlockStatement)=1] +/BlockStatement/Statement/ThrowStatement/Expression +/PrimaryExpression/PrimaryPrefix/AllocationExpression +/ClassOrInterfaceType[@Image = 'CloneNotSupportedException'])]] +``` + +**Example(s):** + +``` +public class MyClass { + public Object clone() throws CloneNotSupportedException { + return foo; + } +} +``` + +## CloneMethodReturnTypeMustMatchClassName + +**Since:** PMD 5.4.0 + +**Priority:** Medium (3) + +**Minimum Language Version:** Java 1.5 + +If a class implements cloneable the return type of the method clone() must be the class name. That way, the caller +of the clone method doesn't need to cast the returned clone to the correct type. + +Note: This is only possible with Java 1.5 or higher. + +``` +//MethodDeclaration +[ +MethodDeclarator/@Image = 'clone' +and MethodDeclarator/FormalParameters/@ParameterCount = 0 +and not (ResultType//ClassOrInterfaceType/@Image = ancestor::ClassOrInterfaceDeclaration[1]/@Image) +] +``` + +**Example(s):** + +``` +public class Foo implements Cloneable { + @Override + protected Object clone() { // Violation, Object must be Foo + } +} + +public class Foo implements Cloneable { + @Override + public Foo clone() { //Ok + } +} +``` + +## CloneThrowsCloneNotSupportedException + +**Since:** PMD 1.9 + +**Priority:** Medium (3) + +The method clone() should throw a CloneNotSupportedException. + +``` +//MethodDeclaration +[ +MethodDeclarator/@Image = 'clone' +and count(MethodDeclarator/FormalParameters/*) = 0 +and count(NameList/Name[contains +(@Image,'CloneNotSupportedException')]) = 0 +] +[ +../../../../ClassOrInterfaceDeclaration[@Final = 'false'] +] +``` + +**Example(s):** + +``` +public class MyClass implements Cloneable{ + public Object clone() { // will cause an error + MyClass clone = (MyClass)super.clone(); + return clone; + } + } +``` + +## ProperCloneImplementation + +**Since:** PMD 1.4 + +**Priority:** Medium High (2) + +Object clone() should be implemented with super.clone(). + +``` +//MethodDeclarator +[@Image = 'clone'] +[count(FormalParameters/*) = 0] +[count(../Block//*[ + (self::AllocationExpression) and + (./ClassOrInterfaceType/@Image = ancestor:: +ClassOrInterfaceDeclaration[1]/@Image) + ])> 0 +] +``` + +**Example(s):** + +``` +class Foo{ + public Object clone(){ + return new Foo(); // This is bad + } +} +``` + diff --git a/docs/pages/pmd/rules/java/codesize.md b/docs/pages/pmd/rules/java/codesize.md new file mode 100644 index 000000000..792e488c3 --- /dev/null +++ b/docs/pages/pmd/rules/java/codesize.md @@ -0,0 +1,574 @@ +--- +title: Code Size +summary: The Code Size ruleset contains rules that find problems related to code size or complexity. +permalink: pmd_rules_java_codesize.html +folder: pmd/rules/java +sidebaractiveurl: /pmd_rules_java.html +editmepath: ../pmd-java/src/main/resources/rulesets/java/codesize.xml +--- +## CyclomaticComplexity + +Deprecated + +**Since:** PMD 1.03 + +**Priority:** Medium (3) + +Complexity directly affects maintenance costs is determined by the number of decision points in a method +plus one for the method entry. The decision points include 'if', 'while', 'for', and 'case labels' calls. +Generally, numbers ranging from 1-4 denote low complexity, 5-7 denote moderate complexity, 8-10 denote +high complexity, and 11+ is very high complexity. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.codesize.CyclomaticComplexityRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codesize/CyclomaticComplexityRule.java) + +**Example(s):** + +``` +public class Foo { // This has a Cyclomatic Complexity = 12 +1 public void example() { +2 if (a == b) { +3 if (a1 == b1) { + fiddle(); +4 } else if a2 == b2) { + fiddle(); + } else { + fiddle(); + } +5 } else if (c == d) { +6 while (c == d) { + fiddle(); + } +7 } else if (e == f) { +8 for (int n = 0; n < h; n++) { + fiddle(); + } + } else{ + switch (z) { +9 case 1: + fiddle(); + break; +10 case 2: + fiddle(); + break; +11 case 3: + fiddle(); + break; +12 default: + fiddle(); + break; + } + } + } +} +``` + +**This rule has the following properties:** + +|Name|Default Value|Description| +|----|-------------|-----------| +|showMethodsComplexity|true|Add method average violations to the report| +|showClassesComplexity|true|Add class average violations to the report| +|reportLevel|10|Cyclomatic Complexity reporting threshold| + +## ExcessiveClassLength + +**Since:** PMD 0.6 + +**Priority:** Medium (3) + +Excessive class file lengths are usually indications that the class may be burdened with excessive +responsibilities that could be provided by external classes or functions. In breaking these methods +apart the code becomes more managable and ripe for reuse. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.codesize.ExcessiveClassLengthRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codesize/ExcessiveClassLengthRule.java) + +**Example(s):** + +``` +public class Foo { + public void bar1() { + // 1000 lines of code + } + public void bar2() { + // 1000 lines of code + } + public void bar3() { + // 1000 lines of code + } + + + public void barN() { + // 1000 lines of code + } +} +``` + +**This rule has the following properties:** + +|Name|Default Value|Description| +|----|-------------|-----------| +|topscore||Top score value| +|minimum||Minimum reporting threshold| +|sigma||Sigma value| + +## ExcessiveMethodLength + +**Since:** PMD 0.6 + +**Priority:** Medium (3) + +When methods are excessively long this usually indicates that the method is doing more than its +name/signature might suggest. They also become challenging for others to digest since excessive +scrolling causes readers to lose focus. +Try to reduce the method length by creating helper methods and removing any copy/pasted code. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.codesize.ExcessiveMethodLengthRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codesize/ExcessiveMethodLengthRule.java) + +**Example(s):** + +``` +public void doSomething() { + System.out.println("Hello world!"); + System.out.println("Hello world!"); + // 98 copies omitted for brevity. +} +``` + +**This rule has the following properties:** + +|Name|Default Value|Description| +|----|-------------|-----------| +|topscore||Top score value| +|minimum||Minimum reporting threshold| +|sigma||Sigma value| + +## ExcessiveParameterList + +**Since:** PMD 0.9 + +**Priority:** Medium (3) + +Methods with numerous parameters are a challenge to maintain, especially if most of them share the +same datatype. These situations usually denote the need for new objects to wrap the numerous parameters. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.codesize.ExcessiveParameterListRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codesize/ExcessiveParameterListRule.java) + +**Example(s):** + +``` +public void addPerson( // too many arguments liable to be mixed up + int birthYear, int birthMonth, int birthDate, int height, int weight, int ssn) { + + . . . . +} + +public void addPerson( // preferred approach + Date birthdate, BodyMeasurements measurements, int ssn) { + + . . . . +} +``` + +**This rule has the following properties:** + +|Name|Default Value|Description| +|----|-------------|-----------| +|topscore||Top score value| +|minimum||Minimum reporting threshold| +|sigma||Sigma value| + +## ExcessivePublicCount + +**Since:** PMD 1.04 + +**Priority:** Medium (3) + +Classes with large numbers of public methods and attributes require disproportionate testing efforts +since combinational side effects grow rapidly and increase risk. Refactoring these classes into +smaller ones not only increases testability and reliability but also allows new variations to be +developed easily. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.codesize.ExcessivePublicCountRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codesize/ExcessivePublicCountRule.java) + +**Example(s):** + +``` +public class Foo { + public String value; + public Bar something; + public Variable var; + // [... more more public attributes ...] + + public void doWork() {} + public void doMoreWork() {} + public void doWorkAgain() {} + // [... more more public methods ...] +} +``` + +**This rule has the following properties:** + +|Name|Default Value|Description| +|----|-------------|-----------| +|topscore||Top score value| +|minimum||Minimum reporting threshold| +|sigma||Sigma value| + +## ModifiedCyclomaticComplexity + +Deprecated + +**Since:** PMD 5.1.2 + +**Priority:** Medium (3) + +Complexity directly affects maintenance costs is determined by the number of decision points in a method +plus one for the method entry. The decision points include 'if', 'while', 'for', and 'case labels' calls. +Generally, numbers ranging from 1-4 denote low complexity, 5-7 denote moderate complexity, 8-10 denote +high complexity, and 11+ is very high complexity. Modified complexity treats switch statements as a single +decision point. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.codesize.ModifiedCyclomaticComplexityRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codesize/ModifiedCyclomaticComplexityRule.java) + +**Example(s):** + +``` +public class Foo { // This has a Cyclomatic Complexity = 9 +1 public void example() { +2 if (a == b) { +3 if (a1 == b1) { + fiddle(); +4 } else if a2 == b2) { + fiddle(); + } else { + fiddle(); + } +5 } else if (c == d) { +6 while (c == d) { + fiddle(); + } +7 } else if (e == f) { +8 for (int n = 0; n < h; n++) { + fiddle(); + } + } else{ +9 switch (z) { + case 1: + fiddle(); + break; + case 2: + fiddle(); + break; + case 3: + fiddle(); + break; + default: + fiddle(); + break; + } + } + } +} +``` + +**This rule has the following properties:** + +|Name|Default Value|Description| +|----|-------------|-----------| +|showMethodsComplexity|true|Add method average violations to the report| +|showClassesComplexity|true|Add class average violations to the report| +|reportLevel|10|Cyclomatic Complexity reporting threshold| + +## NcssConstructorCount + +**Since:** PMD 3.9 + +**Priority:** Medium (3) + +This rule uses the NCSS (Non-Commenting Source Statements) algorithm to determine the number of lines +of code for a given constructor. NCSS ignores comments, and counts actual statements. Using this algorithm, +lines of code that are split are counted as one. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.codesize.NcssConstructorCountRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codesize/NcssConstructorCountRule.java) + +**Example(s):** + +``` +public class Foo extends Bar { + public Foo() { + super(); + + + + + + //this constructor only has 1 NCSS lines + super.foo(); + } +} +``` + +**This rule has the following properties:** + +|Name|Default Value|Description| +|----|-------------|-----------| +|topscore||Top score value| +|minimum||Minimum reporting threshold| +|sigma||Sigma value| + +## NcssMethodCount + +**Since:** PMD 3.9 + +**Priority:** Medium (3) + +This rule uses the NCSS (Non-Commenting Source Statements) algorithm to determine the number of lines +of code for a given method. NCSS ignores comments, and counts actual statements. Using this algorithm, +lines of code that are split are counted as one. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.codesize.NcssMethodCountRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codesize/NcssMethodCountRule.java) + +**Example(s):** + +``` +public class Foo extends Bar { + public int methd() { + super.methd(); + + + + + + //this method only has 1 NCSS lines + return 1; + } +} +``` + +**This rule has the following properties:** + +|Name|Default Value|Description| +|----|-------------|-----------| +|topscore||Top score value| +|minimum||Minimum reporting threshold| +|sigma||Sigma value| + +## NcssTypeCount + +**Since:** PMD 3.9 + +**Priority:** Medium (3) + +This rule uses the NCSS (Non-Commenting Source Statements) algorithm to determine the number of lines +of code for a given type. NCSS ignores comments, and counts actual statements. Using this algorithm, +lines of code that are split are counted as one. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.codesize.NcssTypeCountRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codesize/NcssTypeCountRule.java) + +**Example(s):** + +``` +public class Foo extends Bar { + public Foo() { + //this class only has 6 NCSS lines + super(); + + + + + + super.foo(); + } +} +``` + +**This rule has the following properties:** + +|Name|Default Value|Description| +|----|-------------|-----------| +|topscore||Top score value| +|minimum||Minimum reporting threshold| +|sigma||Sigma value| + +## NPathComplexity + +**Since:** PMD 3.9 + +**Priority:** Medium (3) + +The NPath complexity of a method is the number of acyclic execution paths through that method. +A threshold of 200 is generally considered the point where measures should be taken to reduce +complexity and increase readability. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.codesize.NPathComplexityRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codesize/NPathComplexityRule.java) + +**Example(s):** + +``` +void bar() { // this is something more complex than it needs to be, + if (y) { // it should be broken down into smaller methods or functions + for (j = 0; j < m; j++) { + if (j > r) { + doSomething(); + while (f < 5 ) { + anotherThing(); + f -= 27; + } + } else { + tryThis(); + } + } + } + if ( r - n > 45) { + while (doMagic()) { + findRabbits(); + } + } + try { + doSomethingDangerous(); + } catch (Exception ex) { + makeAmends(); + } finally { + dontDoItAgain(); + } + } +} +``` + +**This rule has the following properties:** + +|Name|Default Value|Description| +|----|-------------|-----------| +|topscore||Top score value| +|minimum||Minimum reporting threshold| +|sigma||Sigma value| + +## StdCyclomaticComplexity + +Deprecated + +**Since:** PMD 5.1.2 + +**Priority:** Medium (3) + +Complexity directly affects maintenance costs is determined by the number of decision points in a method +plus one for the method entry. The decision points include 'if', 'while', 'for', and 'case labels' calls. +Generally, numbers ranging from 1-4 denote low complexity, 5-7 denote moderate complexity, 8-10 denote +high complexity, and 11+ is very high complexity. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.codesize.StdCyclomaticComplexityRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codesize/StdCyclomaticComplexityRule.java) + +**Example(s):** + +``` +public class Foo { // This has a Cyclomatic Complexity = 12 +1 public void example() { +2 if (a == b || (c == d && e == f)) { // Only one +3 if (a1 == b1) { + fiddle(); +4 } else if a2 == b2) { + fiddle(); + } else { + fiddle(); + } +5 } else if (c == d) { +6 while (c == d) { + fiddle(); + } +7 } else if (e == f) { +8 for (int n = 0; n < h; n++) { + fiddle(); + } + } else{ + switch (z) { +9 case 1: + fiddle(); + break; +10 case 2: + fiddle(); + break; +11 case 3: + fiddle(); + break; +12 default: + fiddle(); + break; + } + } + } +} +``` + +**This rule has the following properties:** + +|Name|Default Value|Description| +|----|-------------|-----------| +|showMethodsComplexity|true|Add method average violations to the report| +|showClassesComplexity|true|Add class average violations to the report| +|reportLevel|10|Cyclomatic Complexity reporting threshold| + +## TooManyFields + +**Since:** PMD 3.0 + +**Priority:** Medium (3) + +Classes that have too many fields can become unwieldy and could be redesigned to have fewer fields, +possibly through grouping related fields in new objects. For example, a class with individual +city/state/zip fields could park them within a single Address field. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.codesize.TooManyFieldsRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codesize/TooManyFieldsRule.java) + +**Example(s):** + +``` +public class Person { // too many separate fields + int birthYear; + int birthMonth; + int birthDate; + float height; + float weight; +} + +public class Person { // this is more manageable + Date birthDate; + BodyMeasurements measurements; +} +``` + +**This rule has the following properties:** + +|Name|Default Value|Description| +|----|-------------|-----------| +|maxfields|15|Max allowable fields| + +## TooManyMethods + +**Since:** PMD 4.2 + +**Priority:** Medium (3) + +A class with too many methods is probably a good suspect for refactoring, in order to reduce its complexity and find a way to +have more fine grained objects. + +``` +//ClassOrInterfaceDeclaration/ClassOrInterfaceBody + [ + count(./ClassOrInterfaceBodyDeclaration/MethodDeclaration/MethodDeclarator[ + not ( + starts-with(@Image,'get') + or + starts-with(@Image,'set') + or + starts-with(@Image,'is') + ) + ]) > $maxmethods + ] +``` + +**This rule has the following properties:** + +|Name|Default Value|Description| +|----|-------------|-----------| +|maxmethods|10|The method count reporting threshold| + diff --git a/docs/pages/pmd/rules/java/comments.md b/docs/pages/pmd/rules/java/comments.md new file mode 100644 index 000000000..61cae2a6e --- /dev/null +++ b/docs/pages/pmd/rules/java/comments.md @@ -0,0 +1,144 @@ +--- +title: Comments +summary: Rules intended to catch errors related to code comments +permalink: pmd_rules_java_comments.html +folder: pmd/rules/java +sidebaractiveurl: /pmd_rules_java.html +editmepath: ../pmd-java/src/main/resources/rulesets/java/comments.xml +--- +## CommentContent + +**Since:** PMD 5.0 + +**Priority:** Medium (3) + +A rule for the politically correct... we don't want to offend anyone. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.comments.CommentContentRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/comments/CommentContentRule.java) + +**Example(s):** + +``` +// OMG, this is horrible, Bob is an idiot !!! +``` + +**This rule has the following properties:** + +|Name|Default Value|Description| +|----|-------------|-----------| +|disallowedTerms|[idiot, jerk]|Illegal terms or phrases| +|caseSensitive|false|Case sensitive| +|wordsAreRegex|false|Use regular expressions| + +## CommentDefaultAccessModifier + +**Since:** PMD 5.4.0 + +**Priority:** Medium (3) + +To avoid mistakes if we want that a Method, Constructor, Field or Nested class have a default access modifier + we must add a comment at the beginning of it's declaration. + By default the comment must be /* default */, if you want another, you have to provide a regexp. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.comments.CommentDefaultAccessModifierRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/comments/CommentDefaultAccessModifierRule.java) + +**Example(s):** + +``` +public class Foo { + final String stringValue = "some string"; + String getString() { + return stringValue; + } + + class NestedFoo { + } + } + + // should be + public class Foo { + /* default */ final String stringValue = "some string"; + /* default */ String getString() { + return stringValue; + } + + /* default */ class NestedFoo { + } + } +``` + +**This rule has the following properties:** + +|Name|Default Value|Description| +|----|-------------|-----------| +|regex||Regular expression| + +## CommentRequired + +**Since:** PMD 5.1 + +**Priority:** Medium (3) + +Denotes whether comments are required (or unwanted) for specific language elements. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.comments.CommentRequiredRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/comments/CommentRequiredRule.java) + +**Example(s):** + +``` +/** +* +* +* @author George Bush +*/ +``` + +**This rule has the following properties:** + +|Name|Default Value|Description| +|----|-------------|-----------| +|serialVersionUIDCommentRequired|Ignored|serial version UID commts. Possible values: [Required, Ignored, Unwanted]| +|enumCommentRequirement|Required|Enum comments. Possible values: [Required, Ignored, Unwanted]| +|protectedMethodCommentRequirement|Required|Protected method constructor comments. Possible values: [Required, Ignored, Unwanted]| +|publicMethodCommentRequirement|Required|Public method and constructor comments. Possible values: [Required, Ignored, Unwanted]| +|fieldCommentRequirement|Required|Field comments. Possible values: [Required, Ignored, Unwanted]| +|headerCommentRequirement|Required|Header comments. Possible values: [Required, Ignored, Unwanted]| + +## CommentSize + +**Since:** PMD 5.0 + +**Priority:** Medium (3) + +Determines whether the dimensions of non-header comments found are within the specified limits. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.comments.CommentSizeRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/comments/CommentSizeRule.java) + +**Example(s):** + +``` +/** +* +* too many lines! +* +* +* +* +* +* +* +* +* +* +* +* +*/ +``` + +**This rule has the following properties:** + +|Name|Default Value|Description| +|----|-------------|-----------| +|maxLines|6|Maximum lines| +|maxLineLength|80|Maximum line length| + diff --git a/docs/pages/pmd/rules/java/controversial.md b/docs/pages/pmd/rules/java/controversial.md new file mode 100644 index 000000000..6a42df6e7 --- /dev/null +++ b/docs/pages/pmd/rules/java/controversial.md @@ -0,0 +1,721 @@ +--- +title: Controversial +summary: The Controversial ruleset contains rules that, for whatever reason, are considered controversial. They are held here to allow people to include them as they see fit within their custom rulesets. +permalink: pmd_rules_java_controversial.html +folder: pmd/rules/java +sidebaractiveurl: /pmd_rules_java.html +editmepath: ../pmd-java/src/main/resources/rulesets/java/controversial.xml +--- +## AssignmentInOperand + +**Since:** PMD 1.03 + +**Priority:** Medium (3) + +Avoid assignments in operands; this can make code more complicated and harder to read. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.controversial.AssignmentInOperandRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/controversial/AssignmentInOperandRule.java) + +**Example(s):** + +``` +public void bar() { + int x = 2; + if ((x = getX()) == 3) { + System.out.println("3!"); + } +} +``` + +**This rule has the following properties:** + +|Name|Default Value|Description| +|----|-------------|-----------| +|allowIncrementDecrement|false|Allow increment or decrement operators within the conditional expression of an if, for, or while statement| +|allowWhile|false|Allow assignment within the conditional expression of a while statement| +|allowFor|false|Allow assignment within the conditional expression of a for statement| +|allowIf|false|Allow assignment within the conditional expression of an if statement| + +## AtLeastOneConstructor + +**Since:** PMD 1.04 + +**Priority:** Medium (3) + +Each class should declare at least one constructor. + +``` +//ClassOrInterfaceDeclaration[ + not(ClassOrInterfaceBody/ClassOrInterfaceBodyDeclaration/ConstructorDeclaration) + and + (@Static = 'false') + and + (count(./descendant::MethodDeclaration[@Static = 'true']) < 1) +] + [@Interface='false'] +``` + +**Example(s):** + +``` +public class Foo { + // missing constructor + public void doSomething() { ... } + public void doOtherThing { ... } +} +``` + +## AvoidAccessibilityAlteration + +**Since:** PMD 4.1 + +**Priority:** Medium (3) + +Methods such as getDeclaredConstructors(), getDeclaredConstructor(Class[]) and setAccessible(), +as the interface PrivilegedAction, allows for the runtime alteration of variable, class, or +method visibility, even if they are private. This violates the principle of encapsulation. + +``` +//PrimaryExpression[ + ( + (PrimarySuffix[ + ends-with(@Image,'getDeclaredConstructors') + or + ends-with(@Image,'getDeclaredConstructor') + or + ends-with(@Image,'setAccessible') + ]) + or + (PrimaryPrefix/Name[ + ends-with(@Image,'getDeclaredConstructor') + or + ends-with(@Image,'getDeclaredConstructors') + or + starts-with(@Image,'AccessibleObject.setAccessible') + ]) + ) + and + (//ImportDeclaration/Name[ + contains(@Image,'java.security.PrivilegedAction')]) + ] +``` + +**Example(s):** + +``` +import java.lang.reflect.AccessibleObject; +import java.lang.reflect.Method; +import java.security.PrivilegedAction; + +public class Violation { + public void invalidCallsInMethod() throws SecurityException, NoSuchMethodException { + // Possible call to forbidden getDeclaredConstructors + Class[] arrayOfClass = new Class[1]; + this.getClass().getDeclaredConstructors(); + this.getClass().getDeclaredConstructor(arrayOfClass); + Class clazz = this.getClass(); + clazz.getDeclaredConstructor(arrayOfClass); + clazz.getDeclaredConstructors(); + // Possible call to forbidden setAccessible + clazz.getMethod("", arrayOfClass).setAccessible(false); + AccessibleObject.setAccessible(null, false); + Method.setAccessible(null, false); + Method[] methodsArray = clazz.getMethods(); + int nbMethod; + for ( nbMethod = 0; nbMethod < methodsArray.length; nbMethod++ ) { + methodsArray[nbMethod].setAccessible(false); + } + + // Possible call to forbidden PrivilegedAction + PrivilegedAction priv = (PrivilegedAction) new Object(); priv.run(); + } +} +``` + +## AvoidFinalLocalVariable + +**Since:** PMD 4.1 + +**Priority:** Medium (3) + +Avoid using final local variables, turn them into fields. + +``` +//LocalVariableDeclaration[ + @Final = 'true' + and not(../../ForStatement) + and + ( + (count(VariableDeclarator/VariableInitializer) = 0) + or + (VariableDeclarator/VariableInitializer/Expression/PrimaryExpression/PrimaryPrefix/Literal) + ) +] +``` + +**Example(s):** + +``` +public class MyClass { + public void foo() { + final String finalLocalVariable; + } +} +``` + +## AvoidLiteralsInIfCondition + +**Since:** PMD 4.2.6 + +**Priority:** Medium (3) + +Avoid using hard-coded literals in conditional statements. By declaring them as static variables +or private members with descriptive names maintainability is enhanced. By default, the literals "-1" and "0" are ignored. +More exceptions can be defined with the property "ignoreMagicNumbers". + +``` +//IfStatement/Expression/*/PrimaryExpression/PrimaryPrefix/Literal +[not(NullLiteral)] +[not(BooleanLiteral)] +[empty(index-of(tokenize($ignoreMagicNumbers, ','), @Image))] +``` + +**Example(s):** + +``` +private static final int MAX_NUMBER_OF_REQUESTS = 10; + +public void checkRequests() { + + if (i == 10) { // magic number, buried in a method + doSomething(); + } + + if (i == MAX_NUMBER_OF_REQUESTS) { // preferred approach + doSomething(); + } + + if (aString.indexOf('.') != -1) {} // magic number -1, by default ignored + if (aString.indexOf('.') >= 0) { } // alternative approach + + if (aDouble > 0.0) {} // magic number 0.0 + if (aDouble >= Double.MIN_VALUE) {} // preferred approach +} +``` + +**This rule has the following properties:** + +|Name|Default Value|Description| +|----|-------------|-----------| +|ignoreMagicNumbers|-1,0|Comma-separated list of magic numbers, that should be ignored| + +## AvoidPrefixingMethodParameters + +**Since:** PMD 5.0 + +**Priority:** Medium Low (4) + +Prefixing parameters by 'in' or 'out' pollutes the name of the parameters and reduces code readability. +To indicate whether or not a parameter will be modify in a method, its better to document method +behavior with Javadoc. + +``` +//MethodDeclaration/MethodDeclarator/FormalParameters/FormalParameter/VariableDeclaratorId[ + pmd:matches(@Image,'^in[A-Z].*','^out[A-Z].*','^in$','^out$') +] +``` + +**Example(s):** + +``` +// Not really clear +public class Foo { + public void bar( + int inLeftOperand, + Result outRightOperand) { + outRightOperand.setValue(inLeftOperand * outRightOperand.getValue()); + } +} +``` + +``` +// Far more useful +public class Foo { + /** + * + * @param leftOperand, (purpose), not modified by method. + * @param rightOperand (purpose), will be modified by the method: contains the result. + */ + public void bar( + int leftOperand, + Result rightOperand) { + rightOperand.setValue(leftOperand * rightOperand.getValue()); + } +} +``` + +## AvoidUsingNativeCode + +**Since:** PMD 4.1 + +**Priority:** Medium High (2) + +Unnecessary reliance on Java Native Interface (JNI) calls directly reduces application portability +and increases the maintenance burden. + +``` +//Name[starts-with(@Image,'System.loadLibrary')] +``` + +**Example(s):** + +``` +public class SomeJNIClass { + + public SomeJNIClass() { + System.loadLibrary("nativelib"); + } + + static { + System.loadLibrary("nativelib"); + } + + public void invalidCallsInMethod() throws SecurityException, NoSuchMethodException { + System.loadLibrary("nativelib"); + } +} +``` + +## AvoidUsingShortType + +**Since:** PMD 4.1 + +**Priority:** High (1) + +Java uses the 'short' type to reduce memory usage, not to optimize calculation. In fact, the JVM does not have any +arithmetic capabilities for the short type: the JVM must convert the short into an int, do the proper calculation +and convert the int back to a short. Thus any storage gains found through use of the 'short' type may be offset by +adverse impacts on performance. + +``` +//PrimitiveType[@Image = 'short'][name(../..) != 'CastExpression'] +``` + +**Example(s):** + +``` +public class UsingShort { + private short doNotUseShort = 0; + + public UsingShort() { + short shouldNotBeUsed = 1; + doNotUseShort += shouldNotBeUsed; + } +} +``` + +## AvoidUsingVolatile + +**Since:** PMD 4.1 + +**Priority:** Medium High (2) + +Use of the keyword 'volatile' is generally used to fine tune a Java application, and therefore, requires +a good expertise of the Java Memory Model. Moreover, its range of action is somewhat misknown. Therefore, +the volatile keyword should not be used for maintenance purpose and portability. + +``` +//FieldDeclaration[ + contains(@Volatile,'true') + ] +``` + +**Example(s):** + +``` +public class ThrDeux { + private volatile String var1; // not suggested + private String var2; // preferred +} +``` + +## CallSuperInConstructor + +**Since:** PMD 3.0 + +**Priority:** Medium (3) + +It is a good practice to call super() in a constructor. If super() is not called but +another constructor (such as an overloaded constructor) is called, this rule will not report it. + +``` +//ClassOrInterfaceDeclaration[ count (ExtendsList/*) > 0 ] +/ClassOrInterfaceBody + /ClassOrInterfaceBodyDeclaration + /ConstructorDeclaration[ count (.//ExplicitConstructorInvocation)=0 ] +``` + +**Example(s):** + +``` +public class Foo extends Bar{ + public Foo() { + // call the constructor of Bar + super(); + } + public Foo(int code) { + // do something with code + this(); + // no problem with this + } +} +``` + +## DataflowAnomalyAnalysis + +**Since:** PMD 3.9 + +**Priority:** Low (5) + +The dataflow analysis tracks local definitions, undefinitions and references to variables on different paths on the data flow. +From those informations there can be found various problems. + +1. UR - Anomaly: There is a reference to a variable that was not defined before. This is a bug and leads to an error. +2. DU - Anomaly: A recently defined variable is undefined. These anomalies may appear in normal source text. +3. DD - Anomaly: A recently defined variable is redefined. This is ominous but don't have to be a bug. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.controversial.DataflowAnomalyAnalysisRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/controversial/DataflowAnomalyAnalysisRule.java) + +**Example(s):** + +``` +public void foo() { + int buz = 5; + buz = 6; // redefinition of buz -> dd-anomaly + foo(buz); + buz = 2; +} // buz is undefined when leaving scope -> du-anomaly +``` + +**This rule has the following properties:** + +|Name|Default Value|Description| +|----|-------------|-----------| +|maxViolations|100|Maximum number of anomalies per class| +|maxPaths|1000|Maximum number of checked paths per method. A lower value will increase the performance of the rule but may decrease anomalies found.| + +## DefaultPackage + +**Since:** PMD 3.4 + +**Priority:** Medium (3) + +Use explicit scoping instead of accidental usage of default package private level. +The rule allows methods and fields annotated with Guava's @VisibleForTesting. + +``` +//ClassOrInterfaceDeclaration[@Interface='false'] +/ClassOrInterfaceBody +/ClassOrInterfaceBodyDeclaration +[not(Annotation//Name[ends-with(@Image, 'VisibleForTesting')])] +[ +FieldDeclaration[@PackagePrivate='true'] +or MethodDeclaration[@PackagePrivate='true'] +] +``` + +## DoNotCallGarbageCollectionExplicitly + +**Since:** PMD 4.2 + +**Priority:** Medium High (2) + +Calls to System.gc(), Runtime.getRuntime().gc(), and System.runFinalization() are not advised. Code should have the +same behavior whether the garbage collection is disabled using the option -Xdisableexplicitgc or not. +Moreover, "modern" jvms do a very good job handling garbage collections. If memory usage issues unrelated to memory +leaks develop within an application, it should be dealt with JVM options rather than within the code itself. + +``` +//Name[ +(starts-with(@Image, 'System.') and +(starts-with(@Image, 'System.gc') or +starts-with(@Image, 'System.runFinalization'))) or +( +starts-with(@Image,'Runtime.getRuntime') and +../../PrimarySuffix[ends-with(@Image,'gc')] +) +] +``` + +**Example(s):** + +``` +public class GCCall { + public GCCall() { + // Explicit gc call ! + System.gc(); + } + + public void doSomething() { + // Explicit gc call ! + Runtime.getRuntime().gc(); + } + + public explicitGCcall() { + // Explicit gc call ! + System.gc(); + } + + public void doSomething() { + // Explicit gc call ! + Runtime.getRuntime().gc(); + } +} +``` + +## DontImportSun + +**Since:** PMD 1.5 + +**Priority:** Medium Low (4) + +Avoid importing anything from the 'sun.*' packages. These packages are not portable and are likely to change. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.controversial.DontImportSunRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/controversial/DontImportSunRule.java) + +**Example(s):** + +``` +import sun.misc.foo; +public class Foo {} +``` + +## NullAssignment + +**Since:** PMD 1.02 + +**Priority:** Medium (3) + +Assigning a "null" to a variable (outside of its declaration) is usually bad form. Sometimes, this type +of assignment is an indication that the programmer doesn't completely understand what is going on in the code. + +NOTE: This sort of assignment may used in some cases to dereference objects and encourage garbage collection. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.controversial.NullAssignmentRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/controversial/NullAssignmentRule.java) + +**Example(s):** + +``` +public void bar() { + Object x = null; // this is OK + x = new Object(); + // big, complex piece of code here + x = null; // this is not required + // big, complex piece of code here +} +``` + +## OneDeclarationPerLine + +**Since:** PMD 5.0 + +**Priority:** Medium Low (4) + +Java allows the use of several variables declaration of the same type on one line. However, it +can lead to quite messy code. This rule looks for several declarations on the same line. + +``` +//LocalVariableDeclaration + [count(VariableDeclarator) > 1] + [$strictMode or count(distinct-values(VariableDeclarator/@BeginLine)) != count(VariableDeclarator)] +``` + +**Example(s):** + +``` +String name; // separate declarations +String lastname; + +String name, lastname; // combined declaration, a violation + +String name, + lastname; // combined declaration on multiple lines, no violation by default. + // Set property strictMode to true to mark this as violation. +``` + +**This rule has the following properties:** + +|Name|Default Value|Description| +|----|-------------|-----------| +|strictMode|false|If true, mark combined declaration even if the declarations are on separate lines.| + +## OnlyOneReturn + +**Since:** PMD 1.0 + +**Priority:** Medium (3) + +A method should have only one exit point, and that should be the last statement in the method. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.controversial.OnlyOneReturnRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/controversial/OnlyOneReturnRule.java) + +**Example(s):** + +``` +public class OneReturnOnly1 { + public void foo(int x) { + if (x > 0) { + return "hey"; // first exit + } + return "hi"; // second exit + } +} +``` + +## SuspiciousOctalEscape + +**Since:** PMD 1.5 + +**Priority:** Medium (3) + +A suspicious octal escape sequence was found inside a String literal. +The Java language specification (section 3.10.6) says an octal +escape sequence inside a literal String shall consist of a backslash +followed by: + + OctalDigit | OctalDigit OctalDigit | ZeroToThree OctalDigit OctalDigit + +Any octal escape sequence followed by non-octal digits can be confusing, +e.g. "\038" is interpreted as the octal escape sequence "\03" followed by +the literal character "8". + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.controversial.SuspiciousOctalEscapeRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/controversial/SuspiciousOctalEscapeRule.java) + +**Example(s):** + +``` +public void foo() { + // interpreted as octal 12, followed by character '8' + System.out.println("suspicious: \128"); +} +``` + +## UnnecessaryConstructor + +**Since:** PMD 1.0 + +**Priority:** Medium (3) + +This rule detects when a constructor is not necessary; i.e., when there is only one constructor, +its public, has an empty body, and takes no arguments. + +``` +//ClassOrInterfaceBody[count(ClassOrInterfaceBodyDeclaration/ConstructorDeclaration)=1] +/ClassOrInterfaceBodyDeclaration/ConstructorDeclaration +[@Public='true'] +[not(FormalParameters/*)] +[not(BlockStatement)] +[not(NameList)] +[count(ExplicitConstructorInvocation/Arguments/ArgumentList/Expression)=0] +``` + +**Example(s):** + +``` +public class Foo { + public Foo() {} +} +``` + +## UnnecessaryParentheses + +**Since:** PMD 3.1 + +**Priority:** Medium (3) + +Sometimes expressions are wrapped in unnecessary parentheses, making them look like function calls. + +``` +//Expression + /PrimaryExpression + /PrimaryPrefix + /Expression[count(*)=1] + /PrimaryExpression + /PrimaryPrefix +``` + +**Example(s):** + +``` +public class Foo { + boolean bar() { + return (true); + } +} +``` + +## UseConcurrentHashMap + +**Since:** PMD 4.2.6 + +**Priority:** Medium (3) + +Since Java5 brought a new implementation of the Map designed for multi-threaded access, you can +perform efficient map reads without blocking other threads. + +``` +//Type[../VariableDeclarator/VariableInitializer//AllocationExpression/ClassOrInterfaceType[@Image != 'ConcurrentHashMap']] +/ReferenceType/ClassOrInterfaceType[@Image = 'Map'] +``` + +**Example(s):** + +``` +public class ConcurrentApp { + public void getMyInstance() { + Map map1 = new HashMap(); // fine for single-threaded access + Map map2 = new ConcurrentHashMap(); // preferred for use with multiple threads + + // the following case will be ignored by this rule + Map map3 = someModule.methodThatReturnMap(); // might be OK, if the returned map is already thread-safe + } +} +``` + +## UseObjectForClearerAPI + +**Since:** PMD 4.2.6 + +**Priority:** Medium (3) + +When you write a public method, you should be thinking in terms of an API. If your method is public, it means other class +will use it, therefore, you want (or need) to offer a comprehensive and evolutive API. If you pass a lot of information +as a simple series of Strings, you may think of using an Object to represent all those information. You'll get a simpler +API (such as doWork(Workload workload), rather than a tedious series of Strings) and more importantly, if you need at some +point to pass extra data, you'll be able to do so by simply modifying or extending Workload without any modification to +your API. + +``` +//MethodDeclaration[@Public]/MethodDeclarator/FormalParameters[ + count(FormalParameter/Type/ReferenceType/ClassOrInterfaceType[@Image = 'String']) > 3 +] +``` + +**Example(s):** + +``` +public class MyClass { + public void connect(String username, + String pssd, + String databaseName, + String databaseAdress) + // Instead of those parameters object + // would ensure a cleaner API and permit + // to add extra data transparently (no code change): + // void connect(UserData data); + { + + } +} +``` + diff --git a/docs/pages/pmd/rules/java/coupling.md b/docs/pages/pmd/rules/java/coupling.md new file mode 100644 index 000000000..0a1e5330b --- /dev/null +++ b/docs/pages/pmd/rules/java/coupling.md @@ -0,0 +1,178 @@ +--- +title: Coupling +summary: Rules which find instances of high or inappropriate coupling between objects and packages. +permalink: pmd_rules_java_coupling.html +folder: pmd/rules/java +sidebaractiveurl: /pmd_rules_java.html +editmepath: ../pmd-java/src/main/resources/rulesets/java/coupling.xml +--- +## CouplingBetweenObjects + +**Since:** PMD 1.04 + +**Priority:** Medium (3) + +This rule counts the number of unique attributes, local variables, and return types within an object. +A number higher than the specified threshold can indicate a high degree of coupling. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.coupling.CouplingBetweenObjectsRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/coupling/CouplingBetweenObjectsRule.java) + +**Example(s):** + +``` +import com.Blah; +import org.Bar; +import org.Bardo; + +public class Foo { + private Blah var1; + private Bar var2; + + //followed by many imports of unique objects + void ObjectC doWork() { + Bardo var55; + ObjectA var44; + ObjectZ var93; + return something; + } +} +``` + +**This rule has the following properties:** + +|Name|Default Value|Description| +|----|-------------|-----------| +|threshold|20|Unique type reporting threshold| + +## ExcessiveImports + +**Since:** PMD 1.04 + +**Priority:** Medium (3) + +A high number of imports can indicate a high degree of coupling within an object. This rule +counts the number of unique imports and reports a violation if the count is above the +user-specified threshold. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.coupling.ExcessiveImportsRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/coupling/ExcessiveImportsRule.java) + +**Example(s):** + +``` +import blah.blah.Baz; +import blah.blah.Bif; +// 18 others from the same package elided +public class Foo { + public void doWork() {} +} +``` + +**This rule has the following properties:** + +|Name|Default Value|Description| +|----|-------------|-----------| +|topscore||Top score value| +|minimum||Minimum reporting threshold| +|sigma||Sigma value| + +## LawOfDemeter + +**Since:** PMD 5.0 + +**Priority:** Medium (3) + +The Law of Demeter is a simple rule, that says "only talk to friends". It helps to reduce coupling between classes or objects. +See also the references: +Andrew Hunt, David Thomas, and Ward Cunningham. The Pragmatic Programmer. From Journeyman to Master. Addison-Wesley Longman, Amsterdam, October 1999.; +K.J. Lieberherr and I.M. Holland. Assuring good style for object-oriented programs. Software, IEEE, 6(5):38–48, 1989.; +http://www.ccs.neu.edu/home/lieber/LoD.html; +http://en.wikipedia.org/wiki/Law_of_Demeter + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.coupling.LawOfDemeterRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/coupling/LawOfDemeterRule.java) + +**Example(s):** + +``` +public class Foo { + /** + * This example will result in two violations. + */ + public void example(Bar b) { + // this method call is ok, as b is a parameter of "example" + C c = b.getC(); + + // this method call is a violation, as we are using c, which we got from B. + // We should ask b directly instead, e.g. "b.doItOnC();" + c.doIt(); + + // this is also a violation, just expressed differently as a method chain without temporary variables. + b.getC().doIt(); + + // a constructor call, not a method call. + D d = new D(); + // this method call is ok, because we have create the new instance of D locally. + d.doSomethingElse(); + } +} +``` + +## LooseCoupling + +**Since:** PMD 0.7 + +**Priority:** Medium (3) + +The use of implementation types as object references limits your ability to use alternate +implementations in the future as requirements change. Whenever available, referencing objects +by their interface types provides much more flexibility. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.coupling.LooseCouplingRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/coupling/LooseCouplingRule.java) + +**Example(s):** + +``` +// sub-optimal approach +private ArrayList list = new ArrayList(); + +public HashSet getFoo() { + return new HashSet(); +} + + // preferred approach +private List list = new ArrayList(); + +public Set getFoo() { + return new HashSet(); +} +``` + +## LoosePackageCoupling + +**Since:** PMD 5.0 + +**Priority:** Medium (3) + +Avoid using classes from the configured package hierarchy outside of the package hierarchy, +except when using one of the configured allowed classes. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.coupling.LoosePackageCouplingRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/coupling/LoosePackageCouplingRule.java) + +**Example(s):** + +``` +package some.package; + +import some.other.package.subpackage.subsubpackage.DontUseThisClass; + +public class Bar { + DontUseThisClass boo = new DontUseThisClass(); +} +``` + +**This rule has the following properties:** + +|Name|Default Value|Description| +|----|-------------|-----------| +|classes|[]|Allowed classes| +|packages|[]|Restricted packages| + diff --git a/docs/pages/pmd/rules/java/design.md b/docs/pages/pmd/rules/java/design.md new file mode 100644 index 000000000..7f26c79c6 --- /dev/null +++ b/docs/pages/pmd/rules/java/design.md @@ -0,0 +1,1797 @@ +--- +title: Design +summary: The Design ruleset contains rules that flag suboptimal code implementations. Alternate approaches are suggested. +permalink: pmd_rules_java_design.html +folder: pmd/rules/java +sidebaractiveurl: /pmd_rules_java.html +editmepath: ../pmd-java/src/main/resources/rulesets/java/design.xml +--- +## AbstractClassWithoutAbstractMethod + +**Since:** PMD 3.0 + +**Priority:** Medium (3) + +The abstract class does not contain any abstract methods. An abstract class suggests +an incomplete implementation, which is to be completed by subclasses implementing the +abstract methods. If the class is intended to be used as a base class only (not to be instantiated +directly) a protected constructor can be provided prevent direct instantiation. + +``` +//ClassOrInterfaceDeclaration + [@Abstract='true' + and count( .//MethodDeclaration[@Abstract='true'] )=0 ] + [count(ImplementsList)=0] + [count(.//ExtendsList)=0] +``` + +**Example(s):** + +``` +public abstract class Foo { + void int method1() { ... } + void int method2() { ... } + // consider using abstract methods or removing + // the abstract modifier and adding protected constructors +} +``` + +## AbstractClassWithoutAnyMethod + +**Since:** PMD 4.2 + +**Priority:** High (1) + +If an abstract class does not provides any methods, it may be acting as a simple data container +that is not meant to be instantiated. In this case, it is probably better to use a private or +protected constructor in order to prevent instantiation than make the class misleadingly abstract. + +``` +//ClassOrInterfaceDeclaration[ + (@Abstract = 'true') + and + (count(//MethodDeclaration) + count(//ConstructorDeclaration) = 0) +] +``` + +**Example(s):** + +``` +public class abstract Example { + String field; + int otherField; +} +``` + +## AccessorClassGeneration + +**Since:** PMD 1.04 + +**Priority:** Medium (3) + +Instantiation by way of private constructors from outside of the constructor's class often causes the +generation of an accessor. A factory method, or non-privatization of the constructor can eliminate this +situation. The generated class file is actually an interface. It gives the accessing class the ability +to invoke a new hidden package scope constructor that takes the interface as a supplementary parameter. +This turns a private constructor effectively into one with package scope, and is challenging to discern. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.design.AccessorClassGenerationRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/AccessorClassGenerationRule.java) + +**Example(s):** + +``` +public class Outer { + void method(){ + Inner ic = new Inner();//Causes generation of accessor class + } + public class Inner { + private Inner(){} + } +} +``` + +## AccessorMethodGeneration + +**Since:** PMD 5.5.4 + +**Priority:** Medium (3) + +When accessing a private field / method from another class, the Java compiler will generate a accessor methods +with package-private visibility. This adds overhead, and to the dex method count on Android. This situation can +be avoided by changing the visibility of the field / method from private to package-private. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.design.AccessorMethodGenerationRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/AccessorMethodGenerationRule.java) + +**Example(s):** + +``` +public class OuterClass { + private int counter; + /* package */ int id; + + public class InnerClass { + InnerClass() { + OuterClass.this.counter++; // wrong accessor method will be generated + } + + public int getOuterClassId() { + return OuterClass.this.id; // id is package-private, no accessor method needed + } + } +} +``` + +## AssignmentToNonFinalStatic + +**Since:** PMD 2.2 + +**Priority:** Medium (3) + +Identifies a possible unsafe usage of a static field. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.design.AssignmentToNonFinalStaticRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/AssignmentToNonFinalStaticRule.java) + +**Example(s):** + +``` +public class StaticField { + static int x; + public FinalFields(int y) { + x = y; // unsafe + } +} +``` + +## AvoidDeeplyNestedIfStmts + +**Since:** PMD 1.0 + +**Priority:** Medium (3) + +Avoid creating deeply nested if-then statements since they are harder to read and error-prone to maintain. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.design.AvoidDeeplyNestedIfStmtsRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/AvoidDeeplyNestedIfStmtsRule.java) + +**Example(s):** + +``` +public class Foo { + public void bar(int x, int y, int z) { + if (x>y) { + if (y>z) { + if (z==x) { + // !! too deep + } + } + } + } +} +``` + +**This rule has the following properties:** + +|Name|Default Value|Description| +|----|-------------|-----------| +|problemDepth|3|The if statement depth reporting threshold| + +## AvoidInstanceofChecksInCatchClause + +**Since:** PMD 3.0 + +**Priority:** Medium (3) + +Each caught exception type should be handled in its own catch clause. + +``` +//CatchStatement/FormalParameter + /following-sibling::Block//InstanceOfExpression/PrimaryExpression/PrimaryPrefix + /Name[ + @Image = ./ancestor::Block/preceding-sibling::FormalParameter + /VariableDeclaratorId/@Image + ] +``` + +**Example(s):** + +``` +try { // Avoid this + // do something +} catch (Exception ee) { + if (ee instanceof IOException) { + cleanup(); + } +} +try { // Prefer this: + // do something +} catch (IOException ee) { + cleanup(); +} +``` + +## AvoidProtectedFieldInFinalClass + +**Since:** PMD 2.1 + +**Priority:** Medium (3) + +Do not use protected fields in final classes since they cannot be subclassed. +Clarify your intent by using private or package access modifiers instead. + +``` +//ClassOrInterfaceDeclaration[@Final='true'] +/ClassOrInterfaceBody/ClassOrInterfaceBodyDeclaration +/FieldDeclaration[@Protected='true'] +``` + +**Example(s):** + +``` +public final class Bar { + private int x; + protected int y; // bar cannot be subclassed, so is y really private or package visible? + Bar() {} +} +``` + +## AvoidProtectedMethodInFinalClassNotExtending + +**Since:** PMD 5.1 + +**Priority:** Medium (3) + +Do not use protected methods in most final classes since they cannot be subclassed. This should +only be allowed in final classes that extend other classes with protected methods (whose +visibility cannot be reduced). Clarify your intent by using private or package access modifiers instead. + +``` +//ClassOrInterfaceDeclaration[@Final='true' and not(ExtendsList)] +/ClassOrInterfaceBody/ClassOrInterfaceBodyDeclaration +/MethodDeclaration[@Protected='true'][MethodDeclarator/@Image != 'finalize'] +``` + +**Example(s):** + +``` +public final class Foo { + private int bar() {} + protected int baz() {} // Foo cannot be subclassed, and doesn't extend anything, so is baz() really private or package visible? +} +``` + +## AvoidReassigningParameters + +**Since:** PMD 1.0 + +**Priority:** Medium High (2) + +Reassigning values to incoming parameters is not recommended. Use temporary local variables instead. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.design.AvoidReassigningParametersRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/AvoidReassigningParametersRule.java) + +**Example(s):** + +``` +public class Foo { + private void foo(String bar) { + bar = "something else"; + } +} +``` + +## AvoidSynchronizedAtMethodLevel + +**Since:** PMD 3.0 + +**Priority:** Medium (3) + +Method-level synchronization can cause problems when new code is added to the method. +Block-level synchronization helps to ensure that only the code that needs synchronization +gets it. + +``` +//MethodDeclaration[@Synchronized='true'] +``` + +**Example(s):** + +``` +public class Foo { + // Try to avoid this: + synchronized void foo() { + } + // Prefer this: + void bar() { + synchronized(this) { + } + } + + // Try to avoid this for static methods: + static synchronized void fooStatic() { + } + + // Prefer this: + static void barStatic() { + synchronized(Foo.class) { + } + } +} +``` + +## BadComparison + +**Since:** PMD 1.8 + +**Priority:** Medium (3) + +Avoid equality comparisons with Double.NaN. Due to the implicit lack of representation +precision when comparing floating point numbers these are likely to cause logic errors. + +``` +//EqualityExpression[@Image='=='] + /PrimaryExpression/PrimaryPrefix + /Name[@Image='Double.NaN' or @Image='Float.NaN'] +``` + +**Example(s):** + +``` +boolean x = (y == Double.NaN); +``` + +## ClassWithOnlyPrivateConstructorsShouldBeFinal + +**Since:** PMD 4.1 + +**Priority:** High (1) + +A class with only private constructors should be final, unless the private constructor +is invoked by a inner class. + +``` +TypeDeclaration[count(../TypeDeclaration) = 1]/ClassOrInterfaceDeclaration +[@Final = 'false'] +[count(./ClassOrInterfaceBody/ClassOrInterfaceBodyDeclaration/ConstructorDeclaration[@Private = 'true']) >= 1 ] +[count(./ClassOrInterfaceBody/ClassOrInterfaceBodyDeclaration/ConstructorDeclaration[(@Public = 'true') or (@Protected = 'true') or (@PackagePrivate = 'true')]) = 0 ] +[not(.//ClassOrInterfaceDeclaration)] +``` + +**Example(s):** + +``` +public class Foo { //Should be final + private Foo() { } +} +``` + +## CloseResource + +**Since:** PMD 1.2.2 + +**Priority:** Medium (3) + +Ensure that resources (like Connection, Statement, and ResultSet objects) are always closed after use. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.design.CloseResourceRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/CloseResourceRule.java) + +**Example(s):** + +``` +public class Bar { + public void foo() { + Connection c = pool.getConnection(); + try { + // do stuff + } catch (SQLException ex) { + // handle exception + } finally { + // oops, should close the connection using 'close'! + // c.close(); + } + } +} +``` + +**This rule has the following properties:** + +|Name|Default Value|Description| +|----|-------------|-----------| +|closeAsDefaultTarget|true|Consider 'close' as a target by default| +|types|[java.sql.Connection, java.sql.Statement, java.sql.ResultSet]|Affected types| +|closeTargets|[]|Methods which may close this resource| + +## CompareObjectsWithEquals + +**Since:** PMD 3.2 + +**Priority:** Medium (3) + +Use equals() to compare object references; avoid comparing them with ==. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.design.CompareObjectsWithEqualsRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/CompareObjectsWithEqualsRule.java) + +**Example(s):** + +``` +class Foo { + boolean bar(String a, String b) { + return a == b; + } +} +``` + +## ConfusingTernary + +**Since:** PMD 1.9 + +**Priority:** Medium (3) + +Avoid negation within an "if" expression with an "else" clause. For example, rephrase: + + if (x != y) diff(); else same(); +as: + if (x == y) same(); else diff(); + +Most "if (x != y)" cases without an "else" are often return cases, so consistent use of this +rule makes the code easier to read. Also, this resolves trivial ordering problems, such +as "does the error case go first?" or "does the common case go first?". + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.design.ConfusingTernaryRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/ConfusingTernaryRule.java) + +**Example(s):** + +``` +boolean bar(int x, int y) { + return (x != y) ? diff : same; + } +``` + +**This rule has the following properties:** + +|Name|Default Value|Description| +|----|-------------|-----------| +|ignoreElseIf|false|Ignore conditions with an else-if case| + +## ConstantsInInterface + +**Since:** PMD 5.5 + +**Priority:** Medium (3) + +Avoid constants in interfaces. Interfaces should define types, constants are implementation details +better placed in classes or enums. See Effective Java, item 19. + +``` +//ClassOrInterfaceDeclaration[@Interface='true'][$ignoreIfHasMethods='false' or not(.//MethodDeclaration)]//FieldDeclaration +``` + +**Example(s):** + +``` +public interface ConstantInterface { + public static final int CONST1 = 1; // violation, no fields allowed in interface! + static final int CONST2 = 1; // violation, no fields allowed in interface! + final int CONST3 = 1; // violation, no fields allowed in interface! + int CONST4 = 1; // violation, no fields allowed in interface! +} + +// with ignoreIfHasMethods = false +public interface AnotherConstantInterface { + public static final int CONST1 = 1; // violation, no fields allowed in interface! + + int anyMethod(); +} + +// with ignoreIfHasMethods = true +public interface YetAnotherConstantInterface { + public static final int CONST1 = 1; // no violation + + int anyMethod(); +} +``` + +**This rule has the following properties:** + +|Name|Default Value|Description| +|----|-------------|-----------| +|ignoreIfHasMethods|true|Whether to ignore constants in interfaces if the interface defines any methods| + +## ConstructorCallsOverridableMethod + +**Since:** PMD 1.04 + +**Priority:** High (1) + +Calling overridable methods during construction poses a risk of invoking methods on an incompletely +constructed object and can be difficult to debug. +It may leave the sub-class unable to construct its superclass or forced to replicate the construction +process completely within itself, losing the ability to call super(). If the default constructor +contains a call to an overridable method, the subclass may be completely uninstantiable. Note that +this includes method calls throughout the control flow graph - i.e., if a constructor Foo() calls a +private method bar() that calls a public method buz(), this denotes a problem. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.design.ConstructorCallsOverridableMethodRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/ConstructorCallsOverridableMethodRule.java) + +**Example(s):** + +``` +public class SeniorClass { + public SeniorClass(){ + toString(); //may throw NullPointerException if overridden + } + public String toString(){ + return "IAmSeniorClass"; + } +} +public class JuniorClass extends SeniorClass { + private String name; + public JuniorClass(){ + super(); //Automatic call leads to NullPointerException + name = "JuniorClass"; + } + public String toString(){ + return name.toUpperCase(); + } +} +``` + +## DefaultLabelNotLastInSwitchStmt + +**Since:** PMD 1.5 + +**Priority:** Medium (3) + +By convention, the default label should be the last label in a switch statement. + +``` +//SwitchStatement + [not(SwitchLabel[position() = last()][@Default='true'])] + [SwitchLabel[@Default='true']] +``` + +**Example(s):** + +``` +public class Foo { + void bar(int a) { + switch (a) { + case 1: // do something + break; + default: // the default case should be last, by convention + break; + case 2: + break; + } + } +} +``` + +## EmptyMethodInAbstractClassShouldBeAbstract + +**Since:** PMD 4.1 + +**Priority:** High (1) + +Empty or auto-generated methods in an abstract class should be tagged as abstract. This helps to remove their inapproprate +usage by developers who should be implementing their own versions in the concrete subclasses. + +``` +//ClassOrInterfaceDeclaration[@Abstract = 'true'] + /ClassOrInterfaceBody + /ClassOrInterfaceBodyDeclaration + /MethodDeclaration[@Abstract = 'false' and @Native = 'false'] + [ + ( boolean(./Block[count(./BlockStatement) = 1]/BlockStatement/Statement/ReturnStatement/Expression/PrimaryExpression/PrimaryPrefix/Literal/NullLiteral) = 'true' ) + or + ( boolean(./Block[count(./BlockStatement) = 1]/BlockStatement/Statement/ReturnStatement/Expression/PrimaryExpression/PrimaryPrefix/Literal[@Image = '0']) = 'true' ) + or + ( boolean(./Block[count(./BlockStatement) = 1]/BlockStatement/Statement/ReturnStatement/Expression/PrimaryExpression/PrimaryPrefix/Literal[string-length(@Image) = 2]) = 'true' ) + or + (./Block[count(./BlockStatement) = 1]/BlockStatement/Statement/EmptyStatement) + or + ( count (./Block/*) = 0 ) + ] +``` + +**Example(s):** + +``` +public abstract class ShouldBeAbstract { + public Object couldBeAbstract() { + // Should be abstract method ? + return null; + } + + public void couldBeAbstract() { + } +} +``` + +## EqualsNull + +**Since:** PMD 1.9 + +**Priority:** High (1) + +Tests for null should not use the equals() method. The '==' operator should be used instead. + +``` +//PrimaryExpression + [ + PrimaryPrefix[Name[ends-with(@Image, 'equals')]] + [following-sibling::node()/Arguments/ArgumentList[count(Expression)=1] + /Expression/PrimaryExpression/PrimaryPrefix/Literal/NullLiteral] + + or + + PrimarySuffix[ends-with(@Image, 'equals')] + [following-sibling::node()/Arguments/ArgumentList[count(Expression)=1] + /Expression/PrimaryExpression/PrimaryPrefix/Literal/NullLiteral] + + ] +``` + +**Example(s):** + +``` +String x = "foo"; + +if (x.equals(null)) { // bad form + doSomething(); + } + +if (x == null) { // preferred + doSomething(); + } +``` + +## FieldDeclarationsShouldBeAtStartOfClass + +**Since:** PMD 5.0 + +**Priority:** Medium (3) + +Fields should be declared at the top of the class, before any method declarations, constructors, initializers or inner classes. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.design.FieldDeclarationsShouldBeAtStartOfClassRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/FieldDeclarationsShouldBeAtStartOfClassRule.java) + +**Example(s):** + +``` +public class HelloWorldBean { + + // Field declared before methods / inner classes - OK + private String _thing; + + public String getMessage() { + return "Hello World!"; + } + + // Field declared after methods / inner classes - avoid this + private String _fieldInWrongLocation; +} +``` + +**This rule has the following properties:** + +|Name|Default Value|Description| +|----|-------------|-----------| +|ignoreInterfaceDeclarations|false|Ignore Interface Declarations that precede fields.| +|ignoreAnonymousClassDeclarations|true|Ignore Field Declarations, that are initialized with anonymous class declarations| +|ignoreEnumDeclarations|true|Ignore Enum Declarations that precede fields.| + +## FinalFieldCouldBeStatic + +**Since:** PMD 1.1 + +**Priority:** Medium (3) + +If a final field is assigned to a compile-time constant, it could be made static, thus saving overhead +in each object at runtime. + +``` +//FieldDeclaration + [@Final='true' and @Static='false'] + /VariableDeclarator/VariableInitializer/Expression + /PrimaryExpression[not(PrimarySuffix)]/PrimaryPrefix/Literal +``` + +**Example(s):** + +``` +public class Foo { + public final int BAR = 42; // this could be static and save some space +} +``` + +## GodClass + +**Since:** PMD 5.0 + +**Priority:** Medium (3) + +The God Class rule detects the God Class design flaw using metrics. God classes do too many things, +are very big and overly complex. They should be split apart to be more object-oriented. +The rule uses the detection strategy described in "Object-Oriented Metrics in Practice". +The violations are reported against the entire class. See also the references: +Michele Lanza and Radu Marinescu. Object-Oriented Metrics in Practice: +Using Software Metrics to Characterize, Evaluate, and Improve the Design +of Object-Oriented Systems. Springer, Berlin, 1 edition, October 2006. Page 80. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.design.GodClassRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/GodClassRule.java) + +## IdempotentOperations + +**Since:** PMD 2.0 + +**Priority:** Medium (3) + +Avoid idempotent operations - they have no effect. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.design.IdempotentOperationsRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/IdempotentOperationsRule.java) + +**Example(s):** + +``` +public class Foo { + public void bar() { + int x = 2; + x = x; + } +} +``` + +## ImmutableField + +**Since:** PMD 2.0 + +**Priority:** Medium (3) + +Identifies private fields whose values never change once they are initialized either in the declaration +of the field or by a constructor. This helps in converting existing classes to becoming immutable ones. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.design.ImmutableFieldRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/ImmutableFieldRule.java) + +**Example(s):** + +``` +public class Foo { + private int x; // could be final + public Foo() { + x = 7; + } + public void foo() { + int a = x + 2; + } +} +``` + +## InstantiationToGetClass + +**Since:** PMD 2.0 + +**Priority:** Medium Low (4) + +Avoid instantiating an object just to call getClass() on it; use the .class public member instead. + +``` +//PrimarySuffix + [@Image='getClass'] + [parent::PrimaryExpression + [PrimaryPrefix/AllocationExpression] + [count(PrimarySuffix) = 2] + ] +``` + +**Example(s):** + +``` +// replace this +Class c = new String().getClass(); + + // with this: +Class c = String.class; +``` + +## LogicInversion + +**Since:** PMD 5.0 + +**Priority:** Medium (3) + +Use opposite operator instead of negating the whole expression with a logic complement operator. + +``` +//UnaryExpressionNotPlusMinus[@Image='!']/PrimaryExpression/PrimaryPrefix/Expression[EqualityExpression or RelationalExpression] +``` + +**Example(s):** + +``` +public boolean bar(int a, int b) { + + if (!(a == b)) { // use != + return false; + } + + if (!(a < b)) { // use >= + return false; + } + + return true; +} +``` + +## MissingBreakInSwitch + +**Since:** PMD 3.0 + +**Priority:** Medium (3) + +Switch statements without break or return statements for each case option +may indicate problematic behaviour. Empty cases are ignored as these indicate an intentional fall-through. + +``` +//SwitchStatement +[(count(.//BreakStatement) + + count(BlockStatement//Statement/ReturnStatement) + + count(BlockStatement//Statement/ContinueStatement) + + count(BlockStatement//Statement/ThrowStatement) + + count(BlockStatement//Statement/IfStatement[@Else='true' and Statement[2][ReturnStatement|ContinueStatement|ThrowStatement]]/Statement[1][ReturnStatement|ContinueStatement|ThrowStatement]) + + count(SwitchLabel[name(following-sibling::node()) = 'SwitchLabel']) + + count(SwitchLabel[count(following-sibling::node()) = 0]) + < count (SwitchLabel))] +``` + +**Example(s):** + +``` +public void bar(int status) { + switch(status) { + case CANCELLED: + doCancelled(); + // break; hm, should this be commented out? + case NEW: + doNew(); + // is this really a fall-through? + case REMOVED: + doRemoved(); + // what happens if you add another case after this one? + case OTHER: // empty case - this is interpreted as an intentional fall-through + case ERROR: + doErrorHandling(); + break; + } +} +``` + +## MissingStaticMethodInNonInstantiatableClass + +**Since:** PMD 3.0 + +**Priority:** Medium (3) + +A class that has private constructors and does not have any static methods or fields cannot be used. + +``` +//ClassOrInterfaceDeclaration[@Nested='false'] +[ + ( + count(./ClassOrInterfaceBody/ClassOrInterfaceBodyDeclaration/ConstructorDeclaration)>0 + and + count(./ClassOrInterfaceBody/ClassOrInterfaceBodyDeclaration/ConstructorDeclaration) = count(./ClassOrInterfaceBody/ClassOrInterfaceBodyDeclaration/ConstructorDeclaration[@Private='true']) + ) + and + count(.//MethodDeclaration[@Static='true'])=0 + and + count(.//FieldDeclaration[@Private='false'][@Static='true'])=0 + and + count(.//ClassOrInterfaceDeclaration[@Nested='true'] + [@Public='true'] + [@Static='true'] + [count(./ClassOrInterfaceBody/ClassOrInterfaceBodyDeclaration/ConstructorDeclaration[@Public='true']) > 0] + [count(./ClassOrInterfaceBody/ClassOrInterfaceBodyDeclaration/MethodDeclaration + [@Public='true'] + [./ResultType/Type/ReferenceType/ClassOrInterfaceType + [@Image = //ClassOrInterfaceDeclaration[@Nested='false']/@Image] + ] + ) > 0] + ) = 0 + and + count(//ClassOrInterfaceDeclaration + [@Nested='true'] + [@Static='true'] + [@Public='true'] + [.//MethodDeclaration + [@Public='true'] + [.//ReturnStatement//AllocationExpression + [ClassOrInterfaceType + [@Image = //ClassOrInterfaceDeclaration/@Image] + ] + [./Arguments//PrimaryPrefix/@ThisModifier='true'] + ] + ] + ) = 0 +] +``` + +**Example(s):** + +``` +// This class is unusable, since it cannot be +// instantiated (private constructor), +// and no static method can be called. + +public class Foo { + private Foo() {} + void foo() {} +} +``` + +## NonCaseLabelInSwitchStatement + +**Since:** PMD 1.5 + +**Priority:** Medium (3) + +A non-case label (e.g. a named break/continue label) was present in a switch statement. +This legal, but confusing. It is easy to mix up the case labels and the non-case labels. + +``` +//SwitchStatement//BlockStatement/Statement/LabeledStatement +``` + +**Example(s):** + +``` +public class Foo { + void bar(int a) { + switch (a) { + case 1: + // do something + break; + mylabel: // this is legal, but confusing! + break; + default: + break; + } + } +} +``` + +## NonStaticInitializer + +**Since:** PMD 1.5 + +**Priority:** Medium (3) + +A non-static initializer block will be called any time a constructor is invoked (just prior to +invoking the constructor). While this is a valid language construct, it is rarely used and is +confusing. + +``` +//Initializer[@Static='false'] +``` + +**Example(s):** + +``` +public class MyClass { + // this block gets run before any call to a constructor + { + System.out.println("I am about to construct myself"); + } +} +``` + +## NonThreadSafeSingleton + +**Since:** PMD 3.4 + +**Priority:** Medium (3) + +Non-thread safe singletons can result in bad state changes. Eliminate +static singletons if possible by instantiating the object directly. Static +singletons are usually not needed as only a single instance exists anyway. +Other possible fixes are to synchronize the entire method or to use an +[initialize-on-demand holder class](https://en.wikipedia.org/wiki/Initialization-on-demand_holder_idiom). + +Refrain from using the double-checked locking pattern. The Java Memory Model doesn't +guarantee it to work unless the variable is declared as `volatile`, adding an uneeded +performance penalty. [Reference](http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html) + +See Effective Java, item 48. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.design.NonThreadSafeSingletonRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/NonThreadSafeSingletonRule.java) + +**Example(s):** + +``` +private static Foo foo = null; + +//multiple simultaneous callers may see partially initialized objects +public static Foo getFoo() { + if (foo==null) { + foo = new Foo(); + } + return foo; +} +``` + +**This rule has the following properties:** + +|Name|Default Value|Description| +|----|-------------|-----------| +|checkNonStaticFields|false|Check for non-static fields. Do not set this to true and checkNonStaticMethods to false.| +|checkNonStaticMethods|true|Check for non-static methods. Do not set this to false and checkNonStaticFields to true.| + +## OptimizableToArrayCall + +**Since:** PMD 1.8 + +**Priority:** Medium (3) + +Calls to a collection's toArray() method should specify target arrays sized to match the size of the +collection. Initial arrays that are too small are discarded in favour of new ones that have to be created +that are the proper size. + +``` +//PrimaryExpression +[PrimaryPrefix/Name[ends-with(@Image, 'toArray')]] +[ +PrimarySuffix/Arguments/ArgumentList/Expression + /PrimaryExpression/PrimaryPrefix/AllocationExpression + /ArrayDimsAndInits/Expression/PrimaryExpression/PrimaryPrefix/Literal[@Image='0'] +] +``` + +**Example(s):** + +``` +List foos = getFoos(); + + // inefficient, the array will be discarded +Foo[] fooArray = foos.toArray(new Foo[0]); + + // much better; this one sizes the destination array, + // avoiding of a new one via reflection +Foo[] fooArray = foos.toArray(new Foo[foos.size()]); +``` + +## PositionLiteralsFirstInCaseInsensitiveComparisons + +**Since:** PMD 5.1 + +**Priority:** Medium (3) + +Position literals first in comparisons, if the second argument is null then NullPointerExceptions +can be avoided, they will just return false. + +``` +//PrimaryExpression[ + PrimaryPrefix[Name + [ + (ends-with(@Image, '.equalsIgnoreCase')) + ] + ] + [ + (../PrimarySuffix/Arguments/ArgumentList/Expression/PrimaryExpression/PrimaryPrefix/Literal) + and + ( count(../PrimarySuffix/Arguments/ArgumentList/Expression) = 1 ) + ] +] +[not(ancestor::Expression/ConditionalAndExpression//EqualityExpression[@Image='!=']//NullLiteral)] +[not(ancestor::Expression/ConditionalOrExpression//EqualityExpression[@Image='==']//NullLiteral)] +``` + +**Example(s):** + +``` +class Foo { + boolean bar(String x) { + return x.equalsIgnoreCase("2"); // should be "2".equalsIgnoreCase(x) + } +} +``` + +## PositionLiteralsFirstInComparisons + +**Since:** PMD 3.3 + +**Priority:** Medium (3) + +Position literals first in comparisons, if the second argument is null then NullPointerExceptions +can be avoided, they will just return false. + +``` +//PrimaryExpression[ + PrimaryPrefix[Name + [ + (ends-with(@Image, '.equals')) + ] + ] + [ + (../PrimarySuffix/Arguments/ArgumentList/Expression/PrimaryExpression/PrimaryPrefix/Literal[@StringLiteral='true']) + and + ( count(../PrimarySuffix/Arguments/ArgumentList/Expression) = 1 ) + ] +] +[not(ancestor::Expression/ConditionalAndExpression//EqualityExpression[@Image='!=']//NullLiteral)] +[not(ancestor::Expression/ConditionalOrExpression//EqualityExpression[@Image='==']//NullLiteral)] +``` + +**Example(s):** + +``` +class Foo { + boolean bar(String x) { + return x.equals("2"); // should be "2".equals(x) + } +} +``` + +## PreserveStackTrace + +**Since:** PMD 3.7 + +**Priority:** Medium (3) + +Throwing a new exception from a catch block without passing the original exception into the +new exception will cause the original stack trace to be lost making it difficult to debug +effectively. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.design.PreserveStackTraceRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/PreserveStackTraceRule.java) + +**Example(s):** + +``` +public class Foo { + void good() { + try{ + Integer.parseInt("a"); + } catch (Exception e) { + throw new Exception(e); // first possibility to create exception chain + } + try { + Integer.parseInt("a"); + } catch (Exception e) { + throw (IllegalStateException)new IllegalStateException().initCause(e); // second possibility to create exception chain. + } + } + void bad() { + try{ + Integer.parseInt("a"); + } catch (Exception e) { + throw new Exception(e.getMessage()); + } + } +} +``` + +## ReturnEmptyArrayRatherThanNull + +**Since:** PMD 4.2 + +**Priority:** High (1) + +For any method that returns an array, it is a better to return an empty array rather than a +null reference. This removes the need for null checking all results and avoids inadvertent +NullPointerExceptions. + +``` +//MethodDeclaration + [ + (./ResultType/Type[@Array='true']) + and + (./Block/BlockStatement/Statement/ReturnStatement/Expression/PrimaryExpression/PrimaryPrefix/Literal/NullLiteral) + ] +``` + +**Example(s):** + +``` +public class Example { + // Not a good idea... + public int[] badBehavior() { + // ... + return null; + } + + // Good behavior + public String[] bonnePratique() { + //... + return new String[0]; + } +} +``` + +## SimpleDateFormatNeedsLocale + +**Since:** PMD 2.0 + +**Priority:** Medium (3) + +Be sure to specify a Locale when creating SimpleDateFormat instances to ensure that locale-appropriate +formatting is used. + +``` +//AllocationExpression + [ClassOrInterfaceType[@Image='SimpleDateFormat']] + [Arguments[@ArgumentCount=1]] +``` + +**Example(s):** + +``` +public class Foo { + // Should specify Locale.US (or whatever) + private SimpleDateFormat sdf = new SimpleDateFormat("pattern"); +} +``` + +## SimplifyBooleanExpressions + +**Since:** PMD 1.05 + +**Priority:** Medium (3) + +Avoid unnecessary comparisons in boolean expressions, they serve no purpose and impacts readability. + +``` +//EqualityExpression/PrimaryExpression + /PrimaryPrefix/Literal/BooleanLiteral +``` + +**Example(s):** + +``` +public class Bar { + // can be simplified to + // bar = isFoo(); + private boolean bar = (isFoo() == true); + + public isFoo() { return false;} +} +``` + +## SimplifyBooleanReturns + +**Since:** PMD 0.9 + +**Priority:** Medium (3) + +Avoid unnecessary if-then-else statements when returning a boolean. The result of +the conditional test can be returned instead. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.design.SimplifyBooleanReturnsRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/SimplifyBooleanReturnsRule.java) + +**Example(s):** + +``` +public boolean isBarEqualTo(int x) { + + if (bar == x) { // this bit of code... + return true; + } else { + return false; + } +} + +public boolean isBarEqualTo(int x) { + + return bar == x; // can be replaced with this +} +``` + +## SimplifyConditional + +**Since:** PMD 3.1 + +**Priority:** Medium (3) + +No need to check for null before an instanceof; the instanceof keyword returns false when given a null argument. + +``` +//Expression + [ConditionalOrExpression + [EqualityExpression[@Image='=='] + //NullLiteral + and + UnaryExpressionNotPlusMinus + [@Image='!']//InstanceOfExpression[PrimaryExpression + //Name/@Image = ancestor::ConditionalOrExpression/EqualityExpression + /PrimaryExpression/PrimaryPrefix/Name/@Image] + and + (count(UnaryExpressionNotPlusMinus) + 1 = count(*)) + ] +or +ConditionalAndExpression + [EqualityExpression[@Image='!=']//NullLiteral + and +InstanceOfExpression + [PrimaryExpression[count(PrimarySuffix[@ArrayDereference='true'])=0] + //Name[not(contains(@Image,'.'))]/@Image = ancestor::ConditionalAndExpression + /EqualityExpression/PrimaryExpression/PrimaryPrefix/Name/@Image] + and +(count(InstanceOfExpression) + 1 = count(*)) + ] +] +``` + +**Example(s):** + +``` +class Foo { + void bar(Object x) { + if (x != null && x instanceof Bar) { + // just drop the "x != null" check + } + } +} +``` + +## SingleMethodSingleton + +**Since:** PMD 5.4 + +**Priority:** Medium High (2) + +Some classes contain overloaded getInstance. The problem with overloaded getInstance methods +is that the instance created using the overloaded method is not cached and so, + for each call and new objects will be created for every invocation. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.design.SingleMethodSingletonRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/SingleMethodSingletonRule.java) + +**Example(s):** + +``` +public class Singleton { + + private static Singleton singleton = new Singleton( ); + + private Singleton(){ } + +public static Singleton getInstance( ) { + return singleton; +} +public static Singleton getInstance(Object obj){ + Singleton singleton = (Singleton) obj; + return singleton; //violation +} +} +``` + +## SingletonClassReturningNewInstance + +**Since:** PMD 5.4 + +**Priority:** Medium High (2) + +Some classes contain overloaded getInstance. The problem with overloaded getInstance methods +is that the instance created using the overloaded method is not cached and so, + for each call and new objects will be created for every invocation. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.design.SingletonClassReturningNewInstanceRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/SingletonClassReturningNewInstanceRule.java) + +**Example(s):** + +``` +class Singleton { + private static Singleton instance = null; + public static Singleton getInstance() { + synchronized(Singleton.class){ + return new Singleton(); + } + } +} +``` + +## SingularField + +**Since:** PMD 3.1 + +**Priority:** Medium (3) + +Fields whose scopes are limited to just single methods do not rely on the containing +object to provide them to other methods. They may be better implemented as local variables +within those methods. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.design.SingularFieldRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/SingularFieldRule.java) + +**Example(s):** + +``` +public class Foo { + private int x; // no reason to exist at the Foo instance level + public void foo(int y) { + x = y + 5; + return x; + } +} +``` + +**This rule has the following properties:** + +|Name|Default Value|Description| +|----|-------------|-----------| +|disallowNotAssignment|false|Disallow violations where the first usage is not an assignment| +|checkInnerClasses|false|Check inner classes| + +## SwitchDensity + +**Since:** PMD 1.02 + +**Priority:** Medium (3) + +A high ratio of statements to labels in a switch statement implies that the switch statement +is overloaded. Consider moving the statements into new methods or creating subclasses based +on the switch variable. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.design.SwitchDensityRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/SwitchDensityRule.java) + +**Example(s):** + +``` +public class Foo { + public void bar(int x) { + switch (x) { + case 1: { + // lots of statements + break; + } case 2: { + // lots of statements + break; + } + } + } +} +``` + +**This rule has the following properties:** + +|Name|Default Value|Description| +|----|-------------|-----------| +|topscore||Top score value| +|minimum||Minimum reporting threshold| +|sigma||Sigma value| + +## SwitchStmtsShouldHaveDefault + +**Since:** PMD 1.0 + +**Priority:** Medium (3) + +All switch statements should include a default option to catch any unspecified values. + +``` +//SwitchStatement[not(SwitchLabel[@Default='true'])] +``` + +**Example(s):** + +``` +public void bar() { + int x = 2; + switch (x) { + case 1: int j = 6; + case 2: int j = 8; + // missing default: here + } +} +``` + +## TooFewBranchesForASwitchStatement + +**Since:** PMD 4.2 + +**Priority:** Medium (3) + +Switch statements are indended to be used to support complex branching behaviour. Using a switch for only a few +cases is ill-advised, since switches are not as easy to understand as if-then statements. In these cases use the +if-then statement to increase code readability. + +``` +//SwitchStatement[ + (count(.//SwitchLabel) < $minimumNumberCaseForASwitch) +] +``` + +**Example(s):** + +``` +// With a minimumNumberCaseForASwitch of 3 +public class Foo { + public void bar() { + switch (condition) { + case ONE: + instruction; + break; + default: + break; // not enough for a 'switch' stmt, a simple 'if' stmt would have been more appropriate + } + } +} +``` + +**This rule has the following properties:** + +|Name|Default Value|Description| +|----|-------------|-----------| +|minimumNumberCaseForASwitch|3|Minimum number of branches for a switch| + +## UncommentedEmptyConstructor + +**Since:** PMD 3.4 + +**Priority:** Medium (3) + +Uncommented Empty Constructor finds instances where a constructor does not +contain statements, but there is no comment. By explicitly commenting empty +constructors it is easier to distinguish between intentional (commented) +and unintentional empty constructors. + +``` +//ConstructorDeclaration[@Private='false'][count(BlockStatement) = 0 and ($ignoreExplicitConstructorInvocation = 'true' or not(ExplicitConstructorInvocation)) and @containsComment = 'false'] +``` + +**Example(s):** + +``` +public Foo() { + // This constructor is intentionally empty. Nothing special is needed here. +} +``` + +**This rule has the following properties:** + +|Name|Default Value|Description| +|----|-------------|-----------| +|ignoreExplicitConstructorInvocation|false|Ignore explicit constructor invocation when deciding whether constructor is empty or not| + +## UncommentedEmptyMethodBody + +**Since:** PMD 3.4 + +**Priority:** Medium (3) + +Uncommented Empty Method Body finds instances where a method body does not contain +statements, but there is no comment. By explicitly commenting empty method bodies +it is easier to distinguish between intentional (commented) and unintentional +empty methods. + +``` +//MethodDeclaration/Block[count(BlockStatement) = 0 and @containsComment = 'false'] +``` + +**Example(s):** + +``` +public void doSomething() { +} +``` + +## UnnecessaryLocalBeforeReturn + +**Since:** PMD 3.3 + +**Priority:** Medium (3) + +Avoid the creation of unnecessary local variables + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.design.UnnecessaryLocalBeforeReturnRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/UnnecessaryLocalBeforeReturnRule.java) + +**Example(s):** + +``` +public class Foo { + public int foo() { + int x = doSomething(); + return x; // instead, just 'return doSomething();' + } +} +``` + +**This rule has the following properties:** + +|Name|Default Value|Description| +|----|-------------|-----------| +|statementOrderMatters|true|If set to false this rule no longer requires the variable declaration and return statement to be on consecutive lines. Any variable that is used solely in a return statement will be reported.| + +## UnsynchronizedStaticDateFormatter + +**Since:** PMD 3.6 + +**Priority:** Medium (3) + +SimpleDateFormat instances are not synchronized. Sun recommends using separate format instances +for each thread. If multiple threads must access a static formatter, the formatter must be +synchronized either on method or block level. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.design.UnsynchronizedStaticDateFormatterRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/UnsynchronizedStaticDateFormatterRule.java) + +**Example(s):** + +``` +public class Foo { + private static final SimpleDateFormat sdf = new SimpleDateFormat(); + void bar() { + sdf.format(); // poor, no thread-safety + } + synchronized void foo() { + sdf.format(); // preferred + } +} +``` + +## UseCollectionIsEmpty + +**Since:** PMD 3.9 + +**Priority:** Medium (3) + +The isEmpty() method on java.util.Collection is provided to determine if a collection has any elements. +Comparing the value of size() to 0 does not convey intent as well as the isEmpty() method. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.design.UseCollectionIsEmptyRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/UseCollectionIsEmptyRule.java) + +**Example(s):** + +``` +public class Foo { + void good() { + List foo = getList(); + if (foo.isEmpty()) { + // blah + } + } + + void bad() { + List foo = getList(); + if (foo.size() == 0) { + // blah + } + } +} +``` + +## UseLocaleWithCaseConversions + +**Since:** PMD 2.0 + +**Priority:** Medium (3) + +When doing String.toLowerCase()/toUpperCase() conversions, use Locales to avoids problems with languages that +have unusual conventions, i.e. Turkish. + +``` +//PrimaryExpression +[ +PrimaryPrefix +[Name[ends-with(@Image, 'toLowerCase') or ends-with(@Image, 'toUpperCase')]] +[following-sibling::PrimarySuffix[position() = 1]/Arguments[@ArgumentCount=0]] + +or + +PrimarySuffix +[ends-with(@Image, 'toLowerCase') or ends-with(@Image, 'toUpperCase')] +[following-sibling::PrimarySuffix[position() = 1]/Arguments[@ArgumentCount=0]] +] +[not(PrimaryPrefix/Name[ends-with(@Image, 'toHexString')])] +``` + +**Example(s):** + +``` +class Foo { + // BAD + if (x.toLowerCase().equals("list")) { } + /* + This will not match "LIST" when in Turkish locale + The above could be + if (x.toLowerCase(Locale.US).equals("list")) { } + or simply + if (x.equalsIgnoreCase("list")) { } + */ + // GOOD + String z = a.toLowerCase(Locale.EN); +} +``` + +## UseNotifyAllInsteadOfNotify + +**Since:** PMD 3.0 + +**Priority:** Medium (3) + +Thread.notify() awakens a thread monitoring the object. If more than one thread is monitoring, then only +one is chosen. The thread chosen is arbitrary; thus its usually safer to call notifyAll() instead. + +``` +//StatementExpression/PrimaryExpression +[PrimarySuffix/Arguments[@ArgumentCount = '0']] +[ +PrimaryPrefix[./Name[@Image='notify' or ends-with(@Image,'.notify')] +or ../PrimarySuffix/@Image='notify' +or (./AllocationExpression and ../PrimarySuffix[@Image='notify']) +] +] +``` + +**Example(s):** + +``` +void bar() { + x.notify(); + // If many threads are monitoring x, only one (and you won't know which) will be notified. + // use instead: + x.notifyAll(); + } +``` + +## UseUtilityClass + +**Since:** PMD 0.3 + +**Priority:** Medium (3) + +For classes that only have static methods, consider making them utility classes. +Note that this doesn't apply to abstract classes, since their subclasses may +well include non-static methods. Also, if you want this class to be a utility class, +remember to add a private constructor to prevent instantiation. +(Note, that this use was known before PMD 5.1.0 as UseSingleton). + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.design.UseUtilityClassRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/UseUtilityClassRule.java) + +**Example(s):** + +``` +public class MaybeAUtility { + public static void foo() {} + public static void bar() {} +} +``` + +## UseVarargs + +**Since:** PMD 5.0 + +**Priority:** Medium Low (4) + +**Minimum Language Version:** Java 1.5 + +Java 5 introduced the varargs parameter declaration for methods and constructors. This syntactic +sugar provides flexibility for users of these methods and constructors, allowing them to avoid +having to deal with the creation of an array. + +``` +//FormalParameters/FormalParameter + [position()=last()] + [@Array='true'] + [@Varargs='false'] + [not (./Type/ReferenceType[@Array='true'][PrimitiveType[@Image='byte']])] + [not (./Type/ReferenceType[ClassOrInterfaceType[@Image='Byte']])] + [not (./Type/PrimitiveType[@Image='byte'])] + [not (ancestor::MethodDeclaration/preceding-sibling::Annotation/*/Name[@Image='Override'])] + [not( + ancestor::MethodDeclaration + [@Public='true' and @Static='true'] + [child::ResultType[@Void='true']] and + ancestor::MethodDeclarator[@Image='main'] and + ..[@ParameterCount='1'] and + ./Type/ReferenceType[ClassOrInterfaceType[@Image='String']] + )] +``` + +**Example(s):** + +``` +public class Foo { + public void foo(String s, Object[] args) { + // Do something here... + } + + public void bar(String s, Object... args) { + // Ahh, varargs tastes much better... + } +} +``` + diff --git a/docs/pages/pmd/rules/java/empty.md b/docs/pages/pmd/rules/java/empty.md new file mode 100644 index 000000000..28767e824 --- /dev/null +++ b/docs/pages/pmd/rules/java/empty.md @@ -0,0 +1,301 @@ +--- +title: Empty Code +summary: The Empty Code ruleset contains rules that find empty statements of any kind (empty method, empty block statement, empty try or catch block,...). +permalink: pmd_rules_java_empty.html +folder: pmd/rules/java +sidebaractiveurl: /pmd_rules_java.html +editmepath: ../pmd-java/src/main/resources/rulesets/java/empty.xml +--- +## EmptyCatchBlock + +**Since:** PMD 0.1 + +**Priority:** Medium (3) + +Empty Catch Block finds instances where an exception is caught, but nothing is done. +In most circumstances, this swallows an exception which should either be acted on +or reported. + +``` +//CatchStatement + [count(Block/BlockStatement) = 0 and ($allowCommentedBlocks != 'true' or Block/@containsComment = 'false')] + [FormalParameter/Type/ReferenceType + /ClassOrInterfaceType[@Image != 'InterruptedException' and @Image != 'CloneNotSupportedException'] + ] + [FormalParameter/VariableDeclaratorId[not(matches(@Image, $allowExceptionNameRegex))]] +``` + +**Example(s):** + +``` +public void doSomething() { + try { + FileInputStream fis = new FileInputStream("/tmp/bugger"); + } catch (IOException ioe) { + // not good + } +} +``` + +**This rule has the following properties:** + +|Name|Default Value|Description| +|----|-------------|-----------| +|allowCommentedBlocks|false|Empty blocks containing comments will be skipped| +|allowExceptionNameRegex|^$|Empty blocks catching exceptions with names matching this regular expression will be skipped| + +## EmptyFinallyBlock + +**Since:** PMD 0.4 + +**Priority:** Medium (3) + +Empty finally blocks serve no purpose and should be removed. + +``` +//FinallyStatement[count(Block/BlockStatement) = 0] +``` + +**Example(s):** + +``` +public class Foo { + public void bar() { + try { + int x=2; + } finally { + // empty! + } + } +} +``` + +## EmptyIfStmt + +**Since:** PMD 0.1 + +**Priority:** Medium (3) + +Empty If Statement finds instances where a condition is checked but nothing is done about it. + +``` +//IfStatement/Statement + [EmptyStatement or Block[count(*) = 0]] +``` + +**Example(s):** + +``` +public class Foo { + void bar(int x) { + if (x == 0) { + // empty! + } + } +} +``` + +## EmptyInitializer + +**Since:** PMD 5.0 + +**Priority:** Medium (3) + +Empty initializers serve no purpose and should be removed. + +``` +//Initializer/Block[count(*)=0] +``` + +**Example(s):** + +``` +public class Foo { + + static {} // Why ? + + {} // Again, why ? + +} +``` + +## EmptyStatementBlock + +**Since:** PMD 5.0 + +**Priority:** Medium (3) + +Empty block statements serve no purpose and should be removed. + +``` +//BlockStatement/Statement/Block[count(*) = 0] +``` + +**Example(s):** + +``` +public class Foo { + + private int _bar; + + public void setBar(int bar) { + { _bar = bar; } // Why not? + {} // But remove this. + } + +} +``` + +## EmptyStatementNotInLoop + +**Since:** PMD 1.5 + +**Priority:** Medium (3) + +An empty statement (or a semicolon by itself) that is not used as the sole body of a 'for' +or 'while' loop is probably a bug. It could also be a double semicolon, which has no purpose +and should be removed. + +``` +//EmptyStatement + [not( + ../../../ForStatement + or ../../../WhileStatement + or ../../../BlockStatement/ClassOrInterfaceDeclaration + or ../../../../../../ForStatement/Statement[1] + /Block[1]/BlockStatement[1]/Statement/EmptyStatement + or ../../../../../../WhileStatement/Statement[1] + /Block[1]/BlockStatement[1]/Statement/EmptyStatement) + ] +``` + +**Example(s):** + +``` +public void doit() { + // this is probably not what you meant to do + ; + // the extra semicolon here this is not necessary + System.out.println("look at the extra semicolon");; +} +``` + +## EmptyStaticInitializer + +**Since:** PMD 1.5 + +**Priority:** Medium (3) + +An empty static initializer serve no purpose and should be removed. + +``` +//Initializer[@Static='true']/Block[count(*)=0] +``` + +**Example(s):** + +``` +public class Foo { + static { + // empty + } +} +``` + +## EmptySwitchStatements + +**Since:** PMD 1.0 + +**Priority:** Medium (3) + +Empty switch statements serve no purpose and should be removed. + +``` +//SwitchStatement[count(*) = 1] +``` + +**Example(s):** + +``` +public void bar() { + int x = 2; + switch (x) { + // once there was code here + // but it's been commented out or something + } +} +``` + +## EmptySynchronizedBlock + +**Since:** PMD 1.3 + +**Priority:** Medium (3) + +Empty synchronized blocks serve no purpose and should be removed. + +``` +//SynchronizedStatement/Block[1][count(*) = 0] +``` + +**Example(s):** + +``` +public class Foo { + public void bar() { + synchronized (this) { + // empty! + } + } +} +``` + +## EmptyTryBlock + +**Since:** PMD 0.4 + +**Priority:** Medium (3) + +Avoid empty try blocks - what's the point? + +``` +//TryStatement[not(ResourceSpecification)]/Block[1][count(*) = 0] +``` + +**Example(s):** + +``` +public class Foo { + public void bar() { + try { + } catch (Exception e) { + e.printStackTrace(); + } + } +} +``` + +## EmptyWhileStmt + +**Since:** PMD 0.2 + +**Priority:** Medium (3) + +Empty While Statement finds all instances where a while statement does nothing. +If it is a timing loop, then you should use Thread.sleep() for it; if it is +a while loop that does a lot in the exit expression, rewrite it to make it clearer. + +``` +//WhileStatement/Statement[./Block[count(*) = 0] or ./EmptyStatement] +``` + +**Example(s):** + +``` +void bar(int a, int b) { + while (a == b) { + // empty! + } +} +``` + diff --git a/docs/pages/pmd/rules/java/finalizers.md b/docs/pages/pmd/rules/java/finalizers.md new file mode 100644 index 000000000..2bcac84b5 --- /dev/null +++ b/docs/pages/pmd/rules/java/finalizers.md @@ -0,0 +1,156 @@ +--- +title: Finalizer +summary: These rules deal with different problems that can occur with finalizers. +permalink: pmd_rules_java_finalizers.html +folder: pmd/rules/java +sidebaractiveurl: /pmd_rules_java.html +editmepath: ../pmd-java/src/main/resources/rulesets/java/finalizers.xml +--- +## AvoidCallingFinalize + +**Since:** PMD 3.0 + +**Priority:** Medium (3) + +The method Object.finalize() is called by the garbage collector on an object when garbage collection determines +that there are no more references to the object. It should not be invoked by application logic. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.finalizers.AvoidCallingFinalizeRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/finalizers/AvoidCallingFinalizeRule.java) + +**Example(s):** + +``` +void foo() { + Bar b = new Bar(); + b.finalize(); +} +``` + +## EmptyFinalizer + +**Since:** PMD 1.5 + +**Priority:** Medium (3) + +Empty finalize methods serve no purpose and should be removed. + +``` +//MethodDeclaration[MethodDeclarator[@Image='finalize'][not(FormalParameters/*)]] + /Block[count(*)=0] +``` + +**Example(s):** + +``` +public class Foo { + protected void finalize() {} +} +``` + +## FinalizeDoesNotCallSuperFinalize + +**Since:** PMD 1.5 + +**Priority:** Medium (3) + +If the finalize() is implemented, its last action should be to call super.finalize. + +``` +//MethodDeclaration[MethodDeclarator[@Image='finalize'][not(FormalParameters/*)]] + /Block + /BlockStatement[last()] + [not(Statement/StatementExpression/PrimaryExpression + [./PrimaryPrefix[@SuperModifier='true']] + [./PrimarySuffix[@Image='finalize']] + ) + ] + [not(Statement/TryStatement/FinallyStatement + /Block/BlockStatement/Statement/StatementExpression/PrimaryExpression + [./PrimaryPrefix[@SuperModifier='true']] + [./PrimarySuffix[@Image='finalize']] + ) + ] +``` + +**Example(s):** + +``` +protected void finalize() { + something(); + // neglected to call super.finalize() +} +``` + +## FinalizeOnlyCallsSuperFinalize + +**Since:** PMD 1.5 + +**Priority:** Medium (3) + +If the finalize() is implemented, it should do something besides just calling super.finalize(). + +``` +//MethodDeclaration[MethodDeclarator[@Image="finalize"][not(FormalParameters/*)]] + /Block[count(BlockStatement)=1] + /BlockStatement[ + Statement/StatementExpression/PrimaryExpression + [./PrimaryPrefix[@SuperModifier='true']] + [./PrimarySuffix[@Image='finalize']] + ] +``` + +**Example(s):** + +``` +protected void finalize() { + super.finalize(); +} +``` + +## FinalizeOverloaded + +**Since:** PMD 1.5 + +**Priority:** Medium (3) + +Methods named finalize() should not have parameters. It is confusing and most likely an attempt to +overload Object.finalize(). It will not be called by the VM. + +``` +//MethodDeclaration + /MethodDeclarator[@Image='finalize'][FormalParameters[count(*)>0]] +``` + +**Example(s):** + +``` +public class Foo { + // this is confusing and probably a bug + protected void finalize(int a) { + } +} +``` + +## FinalizeShouldBeProtected + +**Since:** PMD 1.1 + +**Priority:** Medium (3) + +When overriding the finalize(), the new method should be set as protected. If made public, +other classes may invoke it at inappropriate times. + +``` +//MethodDeclaration[@Protected="false"] + /MethodDeclarator[@Image="finalize"] + [not(FormalParameters/*)] +``` + +**Example(s):** + +``` +public void finalize() { + // do something +} +``` + diff --git a/docs/pages/pmd/rules/java/imports.md b/docs/pages/pmd/rules/java/imports.md new file mode 100644 index 000000000..5949d3ac8 --- /dev/null +++ b/docs/pages/pmd/rules/java/imports.md @@ -0,0 +1,142 @@ +--- +title: Import Statements +summary: These rules deal with different problems that can occur with import statements. +permalink: pmd_rules_java_imports.html +folder: pmd/rules/java +sidebaractiveurl: /pmd_rules_java.html +editmepath: ../pmd-java/src/main/resources/rulesets/java/imports.xml +--- +## DontImportJavaLang + +**Since:** PMD 0.5 + +**Priority:** Medium Low (4) + +Avoid importing anything from the package 'java.lang'. These classes are automatically imported (JLS 7.5.3). + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.imports.DontImportJavaLangRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/imports/DontImportJavaLangRule.java) + +**Example(s):** + +``` +import java.lang.String; // this is unnecessary + +public class Foo {} + +// --- in another source code file... + +import java.lang.*; // this is bad + +public class Foo {} +``` + +## DuplicateImports + +**Since:** PMD 0.5 + +**Priority:** Medium Low (4) + +Duplicate or overlapping import statements should be avoided. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.imports.DuplicateImportsRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/imports/DuplicateImportsRule.java) + +**Example(s):** + +``` +import java.lang.String; +import java.lang.*; +public class Foo {} +``` + +## ImportFromSamePackage + +**Since:** PMD 1.02 + +**Priority:** Medium (3) + +There is no need to import a type that lives in the same package. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.imports.ImportFromSamePackageRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/imports/ImportFromSamePackageRule.java) + +**Example(s):** + +``` +package foo; + + import foo.Buz; // no need for this + import foo.*; // or this + + public class Bar{} +``` + +## TooManyStaticImports + +**Since:** PMD 4.1 + +**Priority:** Medium (3) + +If you overuse the static import feature, it can make your program unreadable and +unmaintainable, polluting its namespace with all the static members you import. +Readers of your code (including you, a few months after you wrote it) will not know +which class a static member comes from (Sun 1.5 Language Guide). + +``` +.[count(ImportDeclaration[@Static = 'true']) > $maximumStaticImports] +``` + +**Example(s):** + +``` +import static Lennon; +import static Ringo; +import static George; +import static Paul; +import static Yoko; // Too much ! +``` + +**This rule has the following properties:** + +|Name|Default Value|Description| +|----|-------------|-----------| +|maximumStaticImports|4|All static imports can be disallowed by setting this to 0| + +## UnnecessaryFullyQualifiedName + +**Since:** PMD 5.0 + +**Priority:** Medium Low (4) + +Import statements allow the use of non-fully qualified names. The use of a fully qualified name +which is covered by an import statement is redundant. Consider using the non-fully qualified name. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.imports.UnnecessaryFullyQualifiedNameRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/imports/UnnecessaryFullyQualifiedNameRule.java) + +**Example(s):** + +``` +import java.util.List; + +public class Foo { + private java.util.List list1; // Unnecessary FQN + private List list2; // More appropriate given import of 'java.util.List' +} +``` + +## UnusedImports + +**Since:** PMD 1.0 + +**Priority:** Medium Low (4) + +Avoid the use of unused import statements to prevent unwanted dependencies. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.imports.UnusedImportsRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/imports/UnusedImportsRule.java) + +**Example(s):** + +``` +// this is bad +import java.io.File; +public class Foo {} +``` + diff --git a/docs/pages/pmd/rules/java/j2ee.md b/docs/pages/pmd/rules/java/j2ee.md new file mode 100644 index 000000000..c626ab806 --- /dev/null +++ b/docs/pages/pmd/rules/java/j2ee.md @@ -0,0 +1,288 @@ +--- +title: J2EE +summary: Rules specific to the use of J2EE implementations. +permalink: pmd_rules_java_j2ee.html +folder: pmd/rules/java +sidebaractiveurl: /pmd_rules_java.html +editmepath: ../pmd-java/src/main/resources/rulesets/java/j2ee.xml +--- +## DoNotCallSystemExit + +**Since:** PMD 4.1 + +**Priority:** Medium (3) + +Web applications should not call System.exit(), since only the web container or the +application server should stop the JVM. This rule also checks for the equivalent call Runtime.getRuntime().exit(). + +``` +//Name[ + starts-with(@Image,'System.exit') + or + (starts-with(@Image,'Runtime.getRuntime') and ../../PrimarySuffix[ends-with(@Image,'exit')]) +] +``` + +**Example(s):** + +``` +public void bar() { + System.exit(0); // never call this when running in an application server! +} +public void foo() { + Runtime.getRuntime().exit(0); // never stop the JVM manually, the container will do this. +} +``` + +## DoNotUseThreads + +**Since:** PMD 4.1 + +**Priority:** Medium (3) + +The J2EE specification explicitly forbids the use of threads. + +``` +//ClassOrInterfaceType[@Image = 'Thread' or @Image = 'Runnable'] +``` + +**Example(s):** + +``` +// This is not allowed +public class UsingThread extends Thread { + + } + // Neither this, +public class OtherThread implements Runnable { + // Nor this ... + public void methode() { + Runnable thread = new Thread(); thread.run(); + } +} +``` + +## LocalHomeNamingConvention + +**Since:** PMD 4.0 + +**Priority:** Medium Low (4) + +The Local Home interface of a Session EJB should be suffixed by 'LocalHome'. + +``` +//ClassOrInterfaceDeclaration +[ + ( + (./ExtendsList/ClassOrInterfaceType[ends-with(@Image,'EJBLocalHome')]) + ) + and + not + ( + ends-with(@Image,'LocalHome') + ) +] +``` + +**Example(s):** + +``` +public interface MyBeautifulLocalHome extends javax.ejb.EJBLocalHome {}// proper name + + public interface MissingProperSuffix extends javax.ejb.EJBLocalHome {} // non-standard name +``` + +## LocalInterfaceSessionNamingConvention + +**Since:** PMD 4.0 + +**Priority:** Medium Low (4) + +The Local Interface of a Session EJB should be suffixed by 'Local'. + +``` +//ClassOrInterfaceDeclaration +[ + ( + (./ExtendsList/ClassOrInterfaceType[ends-with(@Image,'EJBLocalObject')]) + ) + and + not + ( + ends-with(@Image,'Local') + ) +] +``` + +**Example(s):** + +``` +public interface MyLocal extends javax.ejb.EJBLocalObject {} // proper name + + public interface MissingProperSuffix extends javax.ejb.EJBLocalObject {} // non-standard name +``` + +## MDBAndSessionBeanNamingConvention + +**Since:** PMD 4.0 + +**Priority:** Medium Low (4) + +The EJB Specification states that any MessageDrivenBean or SessionBean should be suffixed by 'Bean'. + +``` +//TypeDeclaration/ClassOrInterfaceDeclaration +[ + ( + (./ImplementsList/ClassOrInterfaceType[ends-with(@Image,'SessionBean')]) + or + (./ImplementsList/ClassOrInterfaceType[ends-with(@Image,'MessageDrivenBean')]) + ) + and + not + ( + ends-with(@Image,'Bean') + ) +] +``` + +**Example(s):** + +``` +public class SomeBean implements SessionBean{} // proper name + +public class MissingTheProperSuffix implements SessionBean {} // non-standard name +``` + +## RemoteInterfaceNamingConvention + +**Since:** PMD 4.0 + +**Priority:** Medium Low (4) + +Remote Interface of a Session EJB should not have a suffix. + +``` +//ClassOrInterfaceDeclaration +[ + ( + (./ExtendsList/ClassOrInterfaceType[ends-with(@Image,'EJBObject')]) + ) + and + ( + ends-with(@Image,'Session') + or + ends-with(@Image,'EJB') + or + ends-with(@Image,'Bean') + ) +] +``` + +**Example(s):** + +``` +/* Poor Session suffix */ + public interface BadSuffixSession extends javax.ejb.EJBObject {} + + /* Poor EJB suffix */ + public interface BadSuffixEJB extends javax.ejb.EJBObject {} + + /* Poor Bean suffix */ + public interface BadSuffixBean extends javax.ejb.EJBObject {} +``` + +## RemoteSessionInterfaceNamingConvention + +**Since:** PMD 4.0 + +**Priority:** Medium Low (4) + +A Remote Home interface type of a Session EJB should be suffixed by 'Home'. + +``` +//ClassOrInterfaceDeclaration +[ + ( + (./ExtendsList/ClassOrInterfaceType[ends-with(@Image,'EJBHome')]) + ) + and + not + ( + ends-with(@Image,'Home') + ) +] +``` + +**Example(s):** + +``` +public interface MyBeautifulHome extends javax.ejb.EJBHome {} // proper name + +public interface MissingProperSuffix extends javax.ejb.EJBHome {} // non-standard name +``` + +## StaticEJBFieldShouldBeFinal + +**Since:** PMD 4.1 + +**Priority:** Medium (3) + +According to the J2EE specification, an EJB should not have any static fields +with write access. However, static read-only fields are allowed. This ensures proper +behavior especially when instances are distributed by the container on several JREs. + +``` +//ClassOrInterfaceDeclaration[ + ( + (./ImplementsList/ClassOrInterfaceType[ends-with(@Image,'SessionBean')]) + or + (./ImplementsList/ClassOrInterfaceType[ends-with(@Image,'EJBHome')]) + or + (./ImplementsList/ClassOrInterfaceType[ends-with(@Image,'EJBLocalObject')]) + or + (./ImplementsList/ClassOrInterfaceType[ends-with(@Image,'EJBLocalHome')]) + or + (./ExtendsList/ClassOrInterfaceType[ends-with(@Image,'EJBObject')]) + ) + and + (./ClassOrInterfaceBody/ClassOrInterfaceBodyDeclaration[ + (./FieldDeclaration[@Static = 'true']) + and + (./FieldDeclaration[@Final = 'false']) + ]) +] +``` + +**Example(s):** + +``` +public class SomeEJB extends EJBObject implements EJBLocalHome { + + private static int CountA; // poor, field can be edited + + private static final int CountB; // preferred, read-only access +} +``` + +## UseProperClassLoader + +**Since:** PMD 3.7 + +**Priority:** Medium (3) + +In J2EE, the getClassLoader() method might not work as expected. Use +Thread.currentThread().getContextClassLoader() instead. + +``` +//PrimarySuffix[@Image='getClassLoader'] +``` + +**Example(s):** + +``` +public class Foo { + ClassLoader cl = Bar.class.getClassLoader(); +} +``` + diff --git a/docs/pages/pmd/rules/java/javabeans.md b/docs/pages/pmd/rules/java/javabeans.md new file mode 100644 index 000000000..b00eeb182 --- /dev/null +++ b/docs/pages/pmd/rules/java/javabeans.md @@ -0,0 +1,76 @@ +--- +title: JavaBeans +summary: The JavaBeans Ruleset catches instances of bean rules not being followed. +permalink: pmd_rules_java_javabeans.html +folder: pmd/rules/java +sidebaractiveurl: /pmd_rules_java.html +editmepath: ../pmd-java/src/main/resources/rulesets/java/javabeans.xml +--- +## BeanMembersShouldSerialize + +**Since:** PMD 1.1 + +**Priority:** Medium (3) + +If a class is a bean, or is referenced by a bean directly or indirectly it needs to be serializable. +Member variables need to be marked as transient, static, or have accessor methods in the class. Marking +variables as transient is the safest and easiest modification. Accessor methods should follow the Java +naming conventions, i.e. for a variable named foo, getFoo() and setFoo() accessor methods should be provided. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.javabeans.BeanMembersShouldSerializeRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/javabeans/BeanMembersShouldSerializeRule.java) + +**Example(s):** + +``` +private transient int someFoo; // good, it's transient +private static int otherFoo; // also OK +private int moreFoo; // OK, has proper accessors, see below +private int badFoo; // bad, should be marked transient + +private void setMoreFoo(int moreFoo){ + this.moreFoo = moreFoo; +} + +private int getMoreFoo(){ + return this.moreFoo; +} +``` + +**This rule has the following properties:** + +|Name|Default Value|Description| +|----|-------------|-----------| +|prefix||A variable prefix to skip, i.e., m_| + +## MissingSerialVersionUID + +**Since:** PMD 3.0 + +**Priority:** Medium (3) + +Serializable classes should provide a serialVersionUID field. + +``` +//ClassOrInterfaceDeclaration + [ + count(ClassOrInterfaceBody/ClassOrInterfaceBodyDeclaration + /FieldDeclaration/VariableDeclarator/VariableDeclaratorId[@Image='serialVersionUID']) = 0 +and + count(ImplementsList + [ClassOrInterfaceType/@Image='Serializable' + or ClassOrInterfaceType/@Image='java.io.Serializable']) =1 +and + @Abstract = 'false' +] +``` + +**Example(s):** + +``` +public class Foo implements java.io.Serializable { + String name; + // Define serialization id to avoid serialization related bugs + // i.e., public static final long serialVersionUID = 4328743; +} +``` + diff --git a/docs/pages/pmd/rules/java/junit.md b/docs/pages/pmd/rules/java/junit.md new file mode 100644 index 000000000..79989296a --- /dev/null +++ b/docs/pages/pmd/rules/java/junit.md @@ -0,0 +1,381 @@ +--- +title: JUnit +summary: These rules deal with different problems that can occur with JUnit tests. +permalink: pmd_rules_java_junit.html +folder: pmd/rules/java +sidebaractiveurl: /pmd_rules_java.html +editmepath: ../pmd-java/src/main/resources/rulesets/java/junit.xml +--- +## JUnitAssertionsShouldIncludeMessage + +**Since:** PMD 1.04 + +**Priority:** Medium (3) + +JUnit assertions should include an informative message - i.e., use the three-argument version of +assertEquals(), not the two-argument version. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.junit.JUnitAssertionsShouldIncludeMessageRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/junit/JUnitAssertionsShouldIncludeMessageRule.java) + +**Example(s):** + +``` +public class Foo extends TestCase { + public void testSomething() { + assertEquals("foo", "bar"); + // Use the form: + // assertEquals("Foo does not equals bar", "foo", "bar"); + // instead + } +} +``` + +## JUnitSpelling + +**Since:** PMD 1.0 + +**Priority:** Medium (3) + +Some JUnit framework methods are easy to misspell. + +``` +//MethodDeclarator[(not(@Image = 'setUp') + and translate(@Image, 'SETuP', 'setUp') = 'setUp') + or (not(@Image = 'tearDown') + and translate(@Image, 'TEARdOWN', 'tearDown') = 'tearDown')] + [FormalParameters[count(*) = 0]] +[ancestor::ClassOrInterfaceDeclaration[//ClassOrInterfaceType[pmd-java:typeof(@Image, 'junit.framework.TestCase','TestCase')] or //MarkerAnnotation/Name[pmd-java:typeof(@Image, 'org.junit.Test', 'Test')]]] +``` + +**Example(s):** + +``` +import junit.framework.*; + +public class Foo extends TestCase { + public void setup() {} // oops, should be setUp + public void TearDown() {} // oops, should be tearDown +} +``` + +## JUnitStaticSuite + +**Since:** PMD 1.0 + +**Priority:** Medium (3) + +The suite() method in a JUnit test needs to be both public and static. + +``` +//MethodDeclaration[not(@Static='true') or not(@Public='true')] +[MethodDeclarator/@Image='suite'] +[MethodDeclarator/FormalParameters/@ParameterCount=0] +[ancestor::ClassOrInterfaceDeclaration[//ClassOrInterfaceType[pmd-java:typeof(@Image, 'junit.framework.TestCase','TestCase')] or //MarkerAnnotation/Name[pmd-java:typeof(@Image, 'org.junit.Test', 'Test')]]] +``` + +**Example(s):** + +``` +import junit.framework.*; + +public class Foo extends TestCase { + public void suite() {} // oops, should be static + private static void suite() {} // oops, should be public +} +``` + +## JUnitTestContainsTooManyAsserts + +**Since:** PMD 5.0 + +**Priority:** Medium (3) + +JUnit tests should not contain too many asserts. Many asserts are indicative of a complex test, for which +it is harder to verify correctness. Consider breaking the test scenario into multiple, shorter test scenarios. +Customize the maximum number of assertions used by this Rule to suit your needs. + +``` +//MethodDeclarator[(@Image[fn:matches(.,'^test')] or ../../Annotation/MarkerAnnotation/Name[@Image='Test']) and count(..//PrimaryPrefix/Name[@Image[fn:matches(.,'^assert')]]) > $maximumAsserts] +``` + +**Example(s):** + +``` +public class MyTestCase extends TestCase { + // Ok + public void testMyCaseWithOneAssert() { + boolean myVar = false; + assertFalse("should be false", myVar); + } + + // Bad, too many asserts (assuming max=1) + public void testMyCaseWithMoreAsserts() { + boolean myVar = false; + assertFalse("myVar should be false", myVar); + assertEquals("should equals false", false, myVar); + } +} +``` + +**This rule has the following properties:** + +|Name|Default Value|Description| +|----|-------------|-----------| +|maximumAsserts|1|Maximum number of Asserts in a test method| + +## JUnitTestsShouldIncludeAssert + +**Since:** PMD 2.0 + +**Priority:** Medium (3) + +JUnit tests should include at least one assertion. This makes the tests more robust, and using assert +with messages provide the developer a clearer idea of what the test does. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.junit.JUnitTestsShouldIncludeAssertRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/junit/JUnitTestsShouldIncludeAssertRule.java) + +**Example(s):** + +``` +public class Foo extends TestCase { + public void testSomething() { + Bar b = findBar(); + // This is better than having a NullPointerException + // assertNotNull("bar not found", b); + b.work(); + } +} +``` + +## SimplifyBooleanAssertion + +**Since:** PMD 3.6 + +**Priority:** Medium (3) + +Avoid negation in an assertTrue or assertFalse test. + +For example, rephrase: + + assertTrue(!expr); + +as: + + assertFalse(expr); + +``` +//StatementExpression +[ +.//Name[@Image='assertTrue' or @Image='assertFalse'] +and +PrimaryExpression/PrimarySuffix/Arguments/ArgumentList + /Expression/UnaryExpressionNotPlusMinus[@Image='!'] +/PrimaryExpression/PrimaryPrefix +] +[ancestor::ClassOrInterfaceDeclaration[//ClassOrInterfaceType[pmd-java:typeof(@Image, 'junit.framework.TestCase','TestCase')] or //MarkerAnnotation/Name[pmd-java:typeof(@Image, 'org.junit.Test', 'Test')]]] +``` + +**Example(s):** + +``` +public class SimpleTest extends TestCase { + public void testX() { + assertTrue("not empty", !r.isEmpty()); // replace with assertFalse("not empty", r.isEmpty()) + assertFalse(!r.isEmpty()); // replace with assertTrue(r.isEmpty()) + } +} +``` + +## TestClassWithoutTestCases + +**Since:** PMD 3.0 + +**Priority:** Medium (3) + +Test classes end with the suffix Test. Having a non-test class with that name is not a good practice, +since most people will assume it is a test case. Test classes have test methods named testXXX. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.junit.TestClassWithoutTestCasesRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/junit/TestClassWithoutTestCasesRule.java) + +**Example(s):** + +``` +//Consider changing the name of the class if it is not a test +//Consider adding test methods if it is a test +public class CarTest { + public static void main(String[] args) { + // do something + } + // code +} +``` + +## UnnecessaryBooleanAssertion + +**Since:** PMD 3.0 + +**Priority:** Medium (3) + +A JUnit test assertion with a boolean literal is unnecessary since it always will evaluate to the same thing. +Consider using flow control (in case of assertTrue(false) or similar) or simply removing +statements like assertTrue(true) and assertFalse(false). If you just want a test to halt after finding +an error, use the fail() method and provide an indication message of why it did. + +``` +//StatementExpression +[ +PrimaryExpression/PrimaryPrefix/Name[@Image='assertTrue' or @Image='assertFalse'] +and +PrimaryExpression/PrimarySuffix/Arguments/ArgumentList/Expression +[PrimaryExpression/PrimaryPrefix/Literal/BooleanLiteral +or +UnaryExpressionNotPlusMinus[@Image='!'] +/PrimaryExpression/PrimaryPrefix[Literal/BooleanLiteral or Name[count(../../*)=1]]] +] +[ancestor::ClassOrInterfaceDeclaration[//ClassOrInterfaceType[pmd-java:typeof(@Image, 'junit.framework.TestCase','TestCase')] or //MarkerAnnotation/Name[pmd-java:typeof(@Image, 'org.junit.Test', 'Test')]]] +``` + +**Example(s):** + +``` +public class SimpleTest extends TestCase { + public void testX() { + assertTrue(true); // serves no real purpose + } +} +``` + +## UseAssertEqualsInsteadOfAssertTrue + +**Since:** PMD 3.1 + +**Priority:** Medium (3) + +This rule detects JUnit assertions in object equality. These assertions should be made by more specific methods, like assertEquals. + +``` +//PrimaryExpression[ + PrimaryPrefix/Name[@Image = 'assertTrue'] +][ + PrimarySuffix/Arguments/ArgumentList/Expression/PrimaryExpression/PrimaryPrefix/Name + [ends-with(@Image, '.equals')] +] +[ancestor::ClassOrInterfaceDeclaration[//ClassOrInterfaceType[pmd-java:typeof(@Image, 'junit.framework.TestCase','TestCase')] or //MarkerAnnotation/Name[pmd-java:typeof(@Image, 'org.junit.Test', 'Test')]]] +``` + +**Example(s):** + +``` +public class FooTest extends TestCase { + void testCode() { + Object a, b; + assertTrue(a.equals(b)); // bad usage + assertEquals(?a should equals b?, a, b); // good usage + } +} +``` + +## UseAssertNullInsteadOfAssertTrue + +**Since:** PMD 3.5 + +**Priority:** Medium (3) + +This rule detects JUnit assertions in object references equality. These assertions should be made by +more specific methods, like assertNull, assertNotNull. + +``` +//PrimaryExpression[ + PrimaryPrefix/Name[@Image = 'assertTrue' or @Image = 'assertFalse'] +][ + PrimarySuffix/Arguments/ArgumentList[ + Expression/EqualityExpression/PrimaryExpression/PrimaryPrefix/Literal/NullLiteral + ] +] +[ancestor::ClassOrInterfaceDeclaration[//ClassOrInterfaceType[pmd-java:typeof(@Image, 'junit.framework.TestCase','TestCase')] or //MarkerAnnotation/Name[pmd-java:typeof(@Image, 'org.junit.Test', 'Test')]]] +``` + +**Example(s):** + +``` +public class FooTest extends TestCase { + void testCode() { + Object a = doSomething(); + assertTrue(a==null); // bad usage + assertNull(a); // good usage + assertTrue(a != null); // bad usage + assertNotNull(a); // good usage + } + } +``` + +## UseAssertSameInsteadOfAssertTrue + +**Since:** PMD 3.1 + +**Priority:** Medium (3) + +This rule detects JUnit assertions in object references equality. These assertions should be made +by more specific methods, like assertSame, assertNotSame. + +``` +//PrimaryExpression[ + PrimaryPrefix/Name + [@Image = 'assertTrue' or @Image = 'assertFalse'] +] +[PrimarySuffix/Arguments + /ArgumentList/Expression + /EqualityExpression[count(.//NullLiteral) = 0]] +[ancestor::ClassOrInterfaceDeclaration[//ClassOrInterfaceType[pmd-java:typeof(@Image, 'junit.framework.TestCase','TestCase')] or //MarkerAnnotation/Name[pmd-java:typeof(@Image, 'org.junit.Test', 'Test')]]] +``` + +**Example(s):** + +``` +public class FooTest extends TestCase { + void testCode() { + Object a, b; + assertTrue(a == b); // bad usage + assertSame(a, b); // good usage + } +} +``` + +## UseAssertTrueInsteadOfAssertEquals + +**Since:** PMD 5.0 + +**Priority:** Medium (3) + +When asserting a value is the same as a literal or Boxed boolean, use assertTrue/assertFalse, instead of assertEquals. + +``` +//PrimaryExpression[PrimaryPrefix/Name[@Image = 'assertEquals']] +[ + PrimarySuffix/Arguments/ArgumentList/Expression/PrimaryExpression/PrimaryPrefix/Literal/BooleanLiteral + or + PrimarySuffix/Arguments/ArgumentList/Expression/PrimaryExpression/PrimaryPrefix + /Name[(@Image = 'Boolean.TRUE' or @Image = 'Boolean.FALSE')] +] +``` + +**Example(s):** + +``` +public class MyTestCase extends TestCase { + public void testMyCase() { + boolean myVar = true; + // Ok + assertTrue("myVar is true", myVar); + // Bad + assertEquals("myVar is true", true, myVar); + // Bad + assertEquals("myVar is false", false, myVar); + // Bad + assertEquals("myVar is true", Boolean.TRUE, myVar); + // Bad + assertEquals("myVar is false", Boolean.FALSE, myVar); + } +} +``` + diff --git a/docs/pages/pmd/rules/java/logging-jakarta-commons.md b/docs/pages/pmd/rules/java/logging-jakarta-commons.md new file mode 100644 index 000000000..2575ed403 --- /dev/null +++ b/docs/pages/pmd/rules/java/logging-jakarta-commons.md @@ -0,0 +1,151 @@ +--- +title: Jakarta Commons Logging +summary: The Jakarta Commons Logging ruleset contains a collection of rules that find questionable usages of that framework. +permalink: pmd_rules_java_logging-jakarta-commons.html +folder: pmd/rules/java +sidebaractiveurl: /pmd_rules_java.html +editmepath: ../pmd-java/src/main/resources/rulesets/java/logging-jakarta-commons.xml +--- +## GuardDebugLogging + +**Since:** PMD 4.3 + +**Priority:** Medium (3) + +When log messages are composed by concatenating strings, the whole section should be guarded + by a isDebugEnabled() check to avoid performance and memory issues. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.logging.GuardDebugLoggingRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/logging/GuardDebugLoggingRule.java) + +**Example(s):** + +``` +public class Test { + private static final Log __log = LogFactory.getLog(Test.class); + public void test() { + // okay: + __log.debug("log something"); + + // okay: + __log.debug("log something with exception", e); + + // bad: + __log.debug("log something" + " and " + "concat strings"); + + // bad: + __log.debug("log something" + " and " + "concat strings", e); + + // good: + if (__log.isDebugEnabled()) { + __log.debug("bla" + "",e ); + } + } +} +``` + +**This rule has the following properties:** + +|Name|Default Value|Description| +|----|-------------|-----------| +|guardsMethods|[]|method use to guard the log statement| +|logLevels|[]|LogLevels to guard| + +## GuardLogStatement + +**Since:** PMD 5.1.0 + +**Priority:** Medium High (2) + +Whenever using a log level, one should check if the loglevel is actually enabled, or +otherwise skip the associate String creation and manipulation. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.logging.GuardLogStatementRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/logging/GuardLogStatementRule.java) + +**Example(s):** + +``` +// Add this for performance + if (log.isDebugEnabled() { ... + log.debug("log something" + " and " + "concat strings"); +``` + +**This rule has the following properties:** + +|Name|Default Value|Description| +|----|-------------|-----------| +|guardsMethods|[]|method use to guard the log statement| +|logLevels|[]|LogLevels to guard| + +## ProperLogger + +**Since:** PMD 3.3 + +**Priority:** Medium (3) + +A logger should normally be defined private static final and be associated with the correct class. +Private final Log log; is also allowed for rare cases where loggers need to be passed around, +with the restriction that the logger needs to be passed into the constructor. + +``` +//ClassOrInterfaceBodyDeclaration[FieldDeclaration//ClassOrInterfaceType[@Image='Log'] + and + not(FieldDeclaration[@Final='true'][@Static='true'][@Private='true'][.//VariableDeclaratorId[@Image=$staticLoggerName]] + //ArgumentList//ClassOrInterfaceType/@Image = ancestor::ClassOrInterfaceDeclaration/@Image) + and + not(FieldDeclaration[@Final='true'][@Private='true'][.//VariableDeclaratorId[@Image='log']] + [count(.//VariableInitializer)=0] + [ancestor::ClassOrInterfaceBody//StatementExpression[.//PrimaryExpression/descendant::*[@Image='log']][count(.//AllocationExpression)=0]] + )] +``` + +**Example(s):** + +``` +public class Foo { + + private static final Log LOG = LogFactory.getLog(Foo.class); // proper way + + protected Log LOG = LogFactory.getLog(Testclass.class); // wrong approach +} +``` + +**This rule has the following properties:** + +|Name|Default Value|Description| +|----|-------------|-----------| +|staticLoggerName|LOG|Name of the static Logger variable| + +## UseCorrectExceptionLogging + +**Since:** PMD 3.2 + +**Priority:** Medium (3) + +To make sure the full stacktrace is printed out, use the logging statement with two arguments: a String and a Throwable. + +``` +//CatchStatement/Block/BlockStatement/Statement/StatementExpression +/PrimaryExpression[PrimaryPrefix/Name[starts-with(@Image, +concat(ancestor::ClassOrInterfaceDeclaration/ClassOrInterfaceBody/ClassOrInterfaceBodyDeclaration/FieldDeclaration +[Type//ClassOrInterfaceType[@Image='Log']] +/VariableDeclarator/VariableDeclaratorId/@Image, '.'))]] +[PrimarySuffix/Arguments[@ArgumentCount='1']] +[PrimarySuffix/Arguments//Name/@Image = ancestor::CatchStatement/FormalParameter/VariableDeclaratorId/@Image] +``` + +**Example(s):** + +``` +public class Main { + private static final Log _LOG = LogFactory.getLog( Main.class ); + void bar() { + try { + } catch( Exception e ) { + _LOG.error( e ); //Wrong! + } catch( OtherException oe ) { + _LOG.error( oe.getMessage(), oe ); //Correct + } + } +} +``` + diff --git a/docs/pages/pmd/rules/java/logging-java.md b/docs/pages/pmd/rules/java/logging-java.md new file mode 100644 index 000000000..751fbd507 --- /dev/null +++ b/docs/pages/pmd/rules/java/logging-java.md @@ -0,0 +1,159 @@ +--- +title: Java Logging +summary: The Java Logging ruleset contains a collection of rules that find questionable usages of the logger. +permalink: pmd_rules_java_logging-java.html +folder: pmd/rules/java +sidebaractiveurl: /pmd_rules_java.html +editmepath: ../pmd-java/src/main/resources/rulesets/java/logging-java.xml +--- +## AvoidPrintStackTrace + +**Since:** PMD 3.2 + +**Priority:** Medium (3) + +Avoid printStackTrace(); use a logger call instead. + +``` +//PrimaryExpression + [PrimaryPrefix/Name[contains(@Image,'printStackTrace')]] + [PrimarySuffix[not(boolean(Arguments/ArgumentList/Expression))]] +``` + +**Example(s):** + +``` +class Foo { + void bar() { + try { + // do something + } catch (Exception e) { + e.printStackTrace(); + } + } +} +``` + +## GuardLogStatementJavaUtil + +**Since:** PMD 5.1.0 + +**Priority:** Medium High (2) + +Whenever using a log level, one should check if the loglevel is actually enabled, or +otherwise skip the associate String creation and manipulation. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.logging.GuardLogStatementJavaUtilRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/logging/GuardLogStatementJavaUtilRule.java) + +**Example(s):** + +``` +// Add this for performance + if (log.isLoggable(Level.FINE)) { ... + log.fine("log something" + " and " + "concat strings"); +``` + +**This rule has the following properties:** + +|Name|Default Value|Description| +|----|-------------|-----------| +|guardsMethods|[]|method use to guard the log statement| +|logLevels|[]|LogLevels to guard| + +## InvalidSlf4jMessageFormat + +**Since:** PMD 5.5.0 + +**Priority:** Low (5) + +Check for messages in slf4j loggers with non matching number of arguments and placeholders. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.logging.InvalidSlf4jMessageFormatRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/logging/InvalidSlf4jMessageFormatRule.java) + +**Example(s):** + +``` +LOGGER.error("forget the arg {}"); +LOGGER.error("too many args {}", "arg1", "arg2"); +LOGGER.error("param {}", "arg1", new IllegalStateException("arg")); //The exception is shown separately, so is correct. +``` + +## LoggerIsNotStaticFinal + +**Since:** PMD 2.0 + +**Priority:** Medium High (2) + +In most cases, the Logger reference can be declared as static and final. + +``` +//VariableDeclarator + [parent::FieldDeclaration] + [../Type/ReferenceType + /ClassOrInterfaceType[@Image='Logger'] + and + (..[@Final='false'] or ..[@Static = 'false'] ) ] +``` + +**Example(s):** + +``` +public class Foo{ + Logger log = Logger.getLogger(Foo.class.getName()); // not recommended + + static final Logger log = Logger.getLogger(Foo.class.getName()); // preferred approach +} +``` + +## MoreThanOneLogger + +**Since:** PMD 2.0 + +**Priority:** Medium High (2) + +Normally only one logger is used in each class. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.logging.MoreThanOneLoggerRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/logging/MoreThanOneLoggerRule.java) + +**Example(s):** + +``` +public class Foo { + Logger log = Logger.getLogger(Foo.class.getName()); + // It is very rare to see two loggers on a class, normally + // log information is multiplexed by levels + Logger log2= Logger.getLogger(Foo.class.getName()); +} +``` + +## SystemPrintln + +**Since:** PMD 2.1 + +**Priority:** Medium High (2) + +References to System.(out|err).print are usually intended for debugging purposes and can remain in +the codebase even in production code. By using a logger one can enable/disable this behaviour at +will (and by priority) and avoid clogging the Standard out log. + +``` +//Name[ + starts-with(@Image, 'System.out.print') + or + starts-with(@Image, 'System.err.print') + ] +``` + +**Example(s):** + +``` +class Foo{ + Logger log = Logger.getLogger(Foo.class.getName()); + public void testA () { + System.out.println("Entering test"); + // Better use this + log.fine("Entering test"); + } +} +``` + diff --git a/docs/pages/pmd/rules/java/migrating.md b/docs/pages/pmd/rules/java/migrating.md new file mode 100644 index 000000000..b2b884c6c --- /dev/null +++ b/docs/pages/pmd/rules/java/migrating.md @@ -0,0 +1,371 @@ +--- +title: Migration +summary: Contains rules about migrating from one JDK version to another. Don't use these rules directly, rather, use a wrapper ruleset such as migrating_to_13.xml. +permalink: pmd_rules_java_migrating.html +folder: pmd/rules/java +sidebaractiveurl: /pmd_rules_java.html +editmepath: ../pmd-java/src/main/resources/rulesets/java/migrating.xml +--- +## AvoidAssertAsIdentifier + +**Since:** PMD 3.4 + +**Priority:** Medium High (2) + +Use of the term 'assert' will conflict with newer versions of Java since it is a reserved word. + +``` +//VariableDeclaratorId[@Image='assert'] +``` + +**Example(s):** + +``` +public class A { + public class foo { + String assert = "foo"; + } +} +``` + +## AvoidEnumAsIdentifier + +**Since:** PMD 3.4 + +**Priority:** Medium High (2) + +Use of the term 'enum' will conflict with newer versions of Java since it is a reserved word. + +``` +//VariableDeclaratorId[@Image='enum'] +``` + +**Example(s):** + +``` +public class A { + public class foo { + String enum = "foo"; + } +} +``` + +## ByteInstantiation + +**Since:** PMD 4.0 + +**Priority:** Medium High (2) + +Calling new Byte() causes memory allocation that can be avoided by the static Byte.valueOf(). +It makes use of an internal cache that recycles earlier instances making it more memory efficient. + +``` +//PrimaryPrefix/AllocationExpression +[not (ArrayDimsAndInits) +and (ClassOrInterfaceType/@Image='Byte' +or ClassOrInterfaceType/@Image='java.lang.Byte')] +``` + +**Example(s):** + +``` +public class Foo { + private Byte i = new Byte(0); // change to Byte i = Byte.valueOf(0); +} +``` + +## IntegerInstantiation + +**Since:** PMD 3.5 + +**Priority:** Medium High (2) + +Calling new Integer() causes memory allocation that can be avoided by the static Integer.valueOf(). +It makes use of an internal cache that recycles earlier instances making it more memory efficient. + +``` +//PrimaryPrefix + /AllocationExpression + [not (ArrayDimsAndInits) + and (ClassOrInterfaceType/@Image='Integer' + or ClassOrInterfaceType/@Image='java.lang.Integer')] +``` + +**Example(s):** + +``` +public class Foo { + private Integer i = new Integer(0); // change to Integer i = Integer.valueOf(0); +} +``` + +## JUnit4SuitesShouldUseSuiteAnnotation + +**Since:** PMD 4.0 + +**Priority:** Medium (3) + +In JUnit 3, test suites are indicated by the suite() method. In JUnit 4, suites are indicated +through the @RunWith(Suite.class) annotation. + +``` +//ClassOrInterfaceBodyDeclaration[MethodDeclaration/MethodDeclarator[@Image='suite']] +[MethodDeclaration/ResultType/Type/ReferenceType/ClassOrInterfaceType[@Image='Test' or @Image = 'junit.framework.Test']] +[not(MethodDeclaration/Block//ClassOrInterfaceType[@Image='JUnit4TestAdapter'])] +``` + +**Example(s):** + +``` +public class BadExample extends TestCase{ + + public static Test suite(){ + return new Suite(); + } +} + +@RunWith(Suite.class) +@SuiteClasses( { TestOne.class, TestTwo.class }) +public class GoodTest { +} +``` + +## JUnit4TestShouldUseAfterAnnotation + +**Since:** PMD 4.0 + +**Priority:** Medium (3) + +In JUnit 3, the tearDown method was used to clean up all data entities required in running tests. +JUnit 4 skips the tearDown method and executes all methods annotated with @After after running each test + +``` +//CompilationUnit[not(ImportDeclaration/Name[starts-with(@Image, "org.testng")])] +//ClassOrInterfaceBodyDeclaration[MethodDeclaration/MethodDeclarator[@Image='tearDown']] +[count(Annotation//Name[@Image='After'])=0] +``` + +**Example(s):** + +``` +public class MyTest { + public void tearDown() { + bad(); + } +} +public class MyTest2 { + @After public void tearDown() { + good(); + } +} +``` + +## JUnit4TestShouldUseBeforeAnnotation + +**Since:** PMD 4.0 + +**Priority:** Medium (3) + +In JUnit 3, the setUp method was used to set up all data entities required in running tests. +JUnit 4 skips the setUp method and executes all methods annotated with @Before before all tests + +``` +//CompilationUnit[not(ImportDeclaration/Name[starts-with(@Image, "org.testng")])] +//ClassOrInterfaceBodyDeclaration[MethodDeclaration/MethodDeclarator[@Image='setUp']] +[count(Annotation//Name[@Image='Before'])=0] +``` + +**Example(s):** + +``` +public class MyTest { + public void setUp() { + bad(); + } +} +public class MyTest2 { + @Before public void setUp() { + good(); + } +} +``` + +## JUnit4TestShouldUseTestAnnotation + +**Since:** PMD 4.0 + +**Priority:** Medium (3) + +In JUnit 3, the framework executed all methods which started with the word test as a unit test. +In JUnit 4, only methods annotated with the @Test annotation are executed. + +``` +//ClassOrInterfaceBodyDeclaration[MethodDeclaration[@Public='true']/MethodDeclarator[starts-with(@Image,'test')]] +[count(Annotation//Name[@Image='Test'])=0] +``` + +**Example(s):** + +``` +public class MyTest { + public void testBad() { + doSomething(); + } + + @Test + public void testGood() { + doSomething(); + } +} +``` + +## JUnitUseExpected + +**Since:** PMD 4.0 + +**Priority:** Medium (3) + +In JUnit4, use the @Test(expected) annotation to denote tests that should throw exceptions. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.migrating.JUnitUseExpectedRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/migrating/JUnitUseExpectedRule.java) + +**Example(s):** + +``` +public class MyTest { + @Test + public void testBad() { + try { + doSomething(); + fail("should have thrown an exception"); + } catch (Exception e) { + } + } + + @Test(expected=Exception.class) + public void testGood() { + doSomething(); + } +} +``` + +## LongInstantiation + +**Since:** PMD 4.0 + +**Priority:** Medium High (2) + +Calling new Long() causes memory allocation that can be avoided by the static Long.valueOf(). +It makes use of an internal cache that recycles earlier instances making it more memory efficient. + +``` +//PrimaryPrefix +/AllocationExpression +[not (ArrayDimsAndInits) +and (ClassOrInterfaceType/@Image='Long' +or ClassOrInterfaceType/@Image='java.lang.Long')] +``` + +**Example(s):** + +``` +public class Foo { + private Long i = new Long(0); // change to Long i = Long.valueOf(0); +} +``` + +## ReplaceEnumerationWithIterator + +**Since:** PMD 3.4 + +**Priority:** Medium (3) + +Consider replacing Enumeration usages with the newer java.util.Iterator + +``` +//ImplementsList/ClassOrInterfaceType[@Image='Enumeration'] +``` + +**Example(s):** + +``` +public class Foo implements Enumeration { + private int x = 42; + public boolean hasMoreElements() { + return true; + } + public Object nextElement() { + return String.valueOf(i++); + } +} +``` + +## ReplaceHashtableWithMap + +**Since:** PMD 3.4 + +**Priority:** Medium (3) + +Consider replacing Hashtable usage with the newer java.util.Map if thread safety is not required. + +``` +//Type/ReferenceType/ClassOrInterfaceType[@Image='Hashtable'] +``` + +**Example(s):** + +``` +public class Foo { + void bar() { + Hashtable h = new Hashtable(); + } +} +``` + +## ReplaceVectorWithList + +**Since:** PMD 3.4 + +**Priority:** Medium (3) + +Consider replacing Vector usages with the newer java.util.ArrayList if expensive thread-safe operations are not required. + +``` +//Type/ReferenceType/ClassOrInterfaceType[@Image='Vector'] +``` + +**Example(s):** + +``` +public class Foo { + void bar() { + Vector v = new Vector(); + } +} +``` + +## ShortInstantiation + +**Since:** PMD 4.0 + +**Priority:** Medium High (2) + +Calling new Short() causes memory allocation that can be avoided by the static Short.valueOf(). +It makes use of an internal cache that recycles earlier instances making it more memory efficient. + +``` +//PrimaryPrefix +/AllocationExpression +[not (ArrayDimsAndInits) +and (ClassOrInterfaceType/@Image='Short' +or ClassOrInterfaceType/@Image='java.lang.Short')] +``` + +**Example(s):** + +``` +public class Foo { + private Short i = new Short(0); // change to Short i = Short.valueOf(0); +} +``` + diff --git a/docs/pages/pmd/rules/java/naming.md b/docs/pages/pmd/rules/java/naming.md new file mode 100644 index 000000000..7de5e4d40 --- /dev/null +++ b/docs/pages/pmd/rules/java/naming.md @@ -0,0 +1,553 @@ +--- +title: Naming +summary: The Naming Ruleset contains rules regarding preferred usage of names and identifiers. +permalink: pmd_rules_java_naming.html +folder: pmd/rules/java +sidebaractiveurl: /pmd_rules_java.html +editmepath: ../pmd-java/src/main/resources/rulesets/java/naming.xml +--- +## AbstractNaming + +**Since:** PMD 1.4 + +**Priority:** Medium (3) + +Abstract classes should be named 'AbstractXXX'. + +``` +//ClassOrInterfaceDeclaration + [@Abstract='true' and @Interface='false'] + [not (starts-with(@Image,'Abstract'))] +| +//ClassOrInterfaceDeclaration + [@Abstract='false'] + [$strict='true'] + [starts-with(@Image, 'Abstract')] +``` + +**Example(s):** + +``` +public abstract class Foo { // should be AbstractFoo +} +``` + +**This rule has the following properties:** + +|Name|Default Value|Description| +|----|-------------|-----------| +|strict|true|Also flag classes, that are named Abstract, but are not abstract.| + +## AvoidDollarSigns + +**Since:** PMD 1.5 + +**Priority:** Medium (3) + +Avoid using dollar signs in variable/method/class/interface names. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.naming.AvoidDollarSignsRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/naming/AvoidDollarSignsRule.java) + +**Example(s):** + +``` +public class Fo$o { // not a recommended name +} +``` + +## AvoidFieldNameMatchingMethodName + +**Since:** PMD 3.0 + +**Priority:** Medium (3) + +It can be confusing to have a field name with the same name as a method. While this is permitted, +having information (field) and actions (method) is not clear naming. Developers versed in +Smalltalk often prefer this approach as the methods denote accessor methods. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.naming.AvoidFieldNameMatchingMethodNameRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/naming/AvoidFieldNameMatchingMethodNameRule.java) + +**Example(s):** + +``` +public class Foo { + Object bar; + // bar is data or an action or both? + void bar() { + } +} +``` + +## AvoidFieldNameMatchingTypeName + +**Since:** PMD 3.0 + +**Priority:** Medium (3) + +It is somewhat confusing to have a field name matching the declaring class name. +This probably means that type and/or field names should be chosen more carefully. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.naming.AvoidFieldNameMatchingTypeNameRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/naming/AvoidFieldNameMatchingTypeNameRule.java) + +**Example(s):** + +``` +public class Foo extends Bar { + int foo; // There is probably a better name that can be used +} +``` + +## BooleanGetMethodName + +**Since:** PMD 4.0 + +**Priority:** Medium Low (4) + +Methods that return boolean results should be named as predicate statements to denote this. +I.e, 'isReady()', 'hasValues()', 'canCommit()', 'willFail()', etc. Avoid the use of the 'get' +prefix for these methods. + +``` +//MethodDeclaration[ +MethodDeclarator[count(FormalParameters/FormalParameter) = 0 or $checkParameterizedMethods = 'true'] + [starts-with(@Image, 'get')] +and +ResultType/Type/PrimitiveType[@Image = 'boolean'] +and not(../Annotation//Name[@Image = 'Override']) +] +``` + +**Example(s):** + +``` +public boolean getFoo(); // bad +public boolean isFoo(); // ok +public boolean getFoo(boolean bar); // ok, unless checkParameterizedMethods=true +``` + +**This rule has the following properties:** + +|Name|Default Value|Description| +|----|-------------|-----------| +|checkParameterizedMethods|false|Check parameterized methods| + +## ClassNamingConventions + +**Since:** PMD 1.2 + +**Priority:** High (1) + +Class names should always begin with an upper case character. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.naming.ClassNamingConventionsRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/naming/ClassNamingConventionsRule.java) + +**Example(s):** + +``` +public class Foo {} +``` + +## GenericsNaming + +**Since:** PMD 4.2.6 + +**Priority:** Medium Low (4) + +Names for references to generic values should be limited to a single uppercase letter. + +``` +//TypeDeclaration/ClassOrInterfaceDeclaration/TypeParameters/TypeParameter[ + string-length(@Image) > 1 + or + string:upper-case(@Image) != @Image +] +``` + +**Example(s):** + +``` +public interface GenericDao extends BaseDao { + // This is ok... +} + +public interface GenericDao { + // Also this +} + +public interface GenericDao { + // 'e' should be an 'E' +} + +public interface GenericDao { + // 'EF' is not ok. +} +``` + +## LongVariable + +**Since:** PMD 0.3 + +**Priority:** Medium (3) + +Fields, formal arguments, or local variable names that are too long can make the code difficult to follow. + +``` +//VariableDeclaratorId[string-length(@Image) > $minimum] +``` + +**Example(s):** + +``` +public class Something { + int reallyLongIntName = -3; // VIOLATION - Field + public static void main( String argumentsList[] ) { // VIOLATION - Formal + int otherReallyLongName = -5; // VIOLATION - Local + for (int interestingIntIndex = 0; // VIOLATION - For + interestingIntIndex < 10; + interestingIntIndex ++ ) { + } +} +``` + +**This rule has the following properties:** + +|Name|Default Value|Description| +|----|-------------|-----------| +|minimum|17|The variable length reporting threshold| + +## MethodNamingConventions + +**Since:** PMD 1.2 + +**Priority:** High (1) + +Method names should always begin with a lower case character, and should not contain underscores. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.naming.MethodNamingConventionsRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/naming/MethodNamingConventionsRule.java) + +**Example(s):** + +``` +public class Foo { + public void fooStuff() { + } +} +``` + +**This rule has the following properties:** + +|Name|Default Value|Description| +|----|-------------|-----------| +|checkNativeMethods|true|Check native methods| + +## MethodWithSameNameAsEnclosingClass + +**Since:** PMD 1.5 + +**Priority:** Medium (3) + +Non-constructor methods should not have the same name as the enclosing class. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.naming.MethodWithSameNameAsEnclosingClassRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/naming/MethodWithSameNameAsEnclosingClassRule.java) + +**Example(s):** + +``` +public class MyClass { + + public MyClass() {} // this is OK because it is a constructor + + public void MyClass() {} // this is bad because it is a method +} +``` + +## MisleadingVariableName + +**Since:** PMD 3.4 + +**Priority:** Medium (3) + +Detects when a non-field has a name starting with 'm_'. This usually denotes a field and could be confusing. + +``` +//VariableDeclaratorId +[starts-with(@Image, 'm_')] +[not (../../../FieldDeclaration)] +``` + +**Example(s):** + +``` +public class Foo { + private int m_foo; // OK + public void bar(String m_baz) { // Bad + int m_boz = 42; // Bad + } +} +``` + +## NoPackage + +**Since:** PMD 3.3 + +**Priority:** Medium (3) + +Detects when a class or interface does not have a package definition. + +``` +//ClassOrInterfaceDeclaration[count(preceding::PackageDeclaration) = 0] +``` + +**Example(s):** + +``` +// no package declaration +public class ClassInDefaultPackage { +} +``` + +## PackageCase + +**Since:** PMD 3.3 + +**Priority:** Medium (3) + +Detects when a package definition contains uppercase characters. + +``` +//PackageDeclaration/Name[lower-case(@Image)!=@Image] +``` + +**Example(s):** + +``` +package com.MyCompany; // should be lowercase name + +public class SomeClass { +} +``` + +## ShortClassName + +**Since:** PMD 5.0 + +**Priority:** Medium Low (4) + +Short Classnames with fewer than e.g. five characters are not recommended. + +``` +//ClassOrInterfaceDeclaration[string-length(@Image) < $minimum] +``` + +**Example(s):** + +``` +public class Foo { +} +``` + +**This rule has the following properties:** + +|Name|Default Value|Description| +|----|-------------|-----------| +|minimum|5|Number of characters that are required as a minimum for a class name.| + +## ShortMethodName + +**Since:** PMD 0.3 + +**Priority:** Medium (3) + +Method names that are very short are not helpful to the reader. + +``` +//MethodDeclarator[string-length(@Image) < $minimum] +``` + +**Example(s):** + +``` +public class ShortMethod { + public void a( int i ) { // Violation + } +} +``` + +**This rule has the following properties:** + +|Name|Default Value|Description| +|----|-------------|-----------| +|minimum|3|Number of characters that are required as a minimum for a method name.| + +## ShortVariable + +**Since:** PMD 0.3 + +**Priority:** Medium (3) + +Fields, local variables, or parameter names that are very short are not helpful to the reader. + +``` +//VariableDeclaratorId[string-length(@Image) < $minimum] + [not(ancestor::ForInit)] + [not(../../VariableDeclarator and ../../../LocalVariableDeclaration and ../../../../ForStatement)] + [not((ancestor::FormalParameter) and (ancestor::TryStatement))] +``` + +**Example(s):** + +``` +public class Something { + private int q = 15; // field - too short + public static void main( String as[] ) { // formal arg - too short + int r = 20 + q; // local var - too short + for (int i = 0; i < 10; i++) { // not a violation (inside 'for' loop) + r += q; + } + for (Integer i : numbers) { // not a violation (inside 'for-each' loop) + r += q; + } + } +} +``` + +**This rule has the following properties:** + +|Name|Default Value|Description| +|----|-------------|-----------| +|minimum|3|Number of characters that are required as a minimum for a variable name.| + +## SuspiciousConstantFieldName + +**Since:** PMD 2.0 + +**Priority:** Medium (3) + +Field names using all uppercase characters - Sun's Java naming conventions indicating constants - should +be declared as final. + +``` +//ClassOrInterfaceDeclaration[@Interface='false'] + /ClassOrInterfaceBody/ClassOrInterfaceBodyDeclaration/FieldDeclaration + [@Final='false'] + [VariableDeclarator/VariableDeclaratorId[upper-case(@Image)=@Image]] +``` + +**Example(s):** + +``` +public class Foo { + // this is bad, since someone could accidentally + // do PI = 2.71828; which is actually e + // final double PI = 3.16; is ok + double PI = 3.16; +} +``` + +## SuspiciousEqualsMethodName + +**Since:** PMD 2.0 + +**Priority:** Medium High (2) + +The method name and parameter number are suspiciously close to equals(Object), which can denote an +intention to override the equals(Object) method. + +``` +//MethodDeclarator[@Image = 'equals'] +[ + (count(FormalParameters/*) = 1 + and not (FormalParameters/FormalParameter/Type/ReferenceType/ClassOrInterfaceType + [@Image = 'Object' or @Image = 'java.lang.Object']) + or not (../ResultType/Type/PrimitiveType[@Image = 'boolean']) + ) or ( + count(FormalParameters/*) = 2 + and ../ResultType/Type/PrimitiveType[@Image = 'boolean'] + and FormalParameters//ClassOrInterfaceType[@Image = 'Object' or @Image = 'java.lang.Object'] + and not(../../Annotation/MarkerAnnotation/Name[@Image='Override']) + ) +] +| //MethodDeclarator[@Image = 'equal'] +[ + count(FormalParameters/*) = 1 + and FormalParameters/FormalParameter/Type/ReferenceType/ClassOrInterfaceType + [@Image = 'Object' or @Image = 'java.lang.Object'] +] +``` + +**Example(s):** + +``` +public class Foo { + public int equals(Object o) { + // oops, this probably was supposed to be boolean equals + } + public boolean equals(String s) { + // oops, this probably was supposed to be equals(Object) + } + public boolean equals(Object o1, Object o2) { + // oops, this probably was supposed to be equals(Object) + } +} +``` + +## SuspiciousHashcodeMethodName + +**Since:** PMD 1.5 + +**Priority:** Medium (3) + +The method name and return type are suspiciously close to hashCode(), which may denote an intention +to override the hashCode() method. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.naming.SuspiciousHashcodeMethodNameRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/naming/SuspiciousHashcodeMethodNameRule.java) + +**Example(s):** + +``` +public class Foo { + public int hashcode() { // oops, this probably was supposed to be 'hashCode' + + } +} +``` + +## VariableNamingConventions + +**Since:** PMD 1.2 + +**Priority:** High (1) + +A variable naming conventions rule - customize this to your liking. Currently, it +checks for final variables that should be fully capitalized and non-final variables +that should not include underscores. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.naming.VariableNamingConventionsRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/naming/VariableNamingConventionsRule.java) + +**Example(s):** + +``` +public class Foo { + public static final int MY_NUM = 0; + public String myTest = ""; + DataModule dmTest = new DataModule(); +} +``` + +**This rule has the following properties:** + +|Name|Default Value|Description| +|----|-------------|-----------| +|parameterSuffix|[]|Method parameter variable suffixes| +|parameterPrefix|[]|Method parameter variable prefixes| +|localSuffix|[]|Local variable suffixes| +|localPrefix|[]|Local variable prefixes| +|memberSuffix|[]|Member variable suffixes| +|memberPrefix|[]|Member variable prefixes| +|staticSuffix|[]|Static variable suffixes| +|checkParameters|true|Check constructor and method parameter variables| +|checkNativeMethodParameters|true|Check method parameter of native methods| +|staticPrefix|[]|Static variable prefixes| +|checkLocals|true|Check local variables| +|checkMembers|true|Check member variables| + diff --git a/docs/pages/pmd/rules/java/optimizations.md b/docs/pages/pmd/rules/java/optimizations.md new file mode 100644 index 000000000..de38a654f --- /dev/null +++ b/docs/pages/pmd/rules/java/optimizations.md @@ -0,0 +1,381 @@ +--- +title: Optimization +summary: These rules deal with different optimizations that generally apply to best practices. +permalink: pmd_rules_java_optimizations.html +folder: pmd/rules/java +sidebaractiveurl: /pmd_rules_java.html +editmepath: ../pmd-java/src/main/resources/rulesets/java/optimizations.xml +--- +## AddEmptyString + +**Since:** PMD 4.0 + +**Priority:** Medium (3) + +The conversion of literals to strings by concatenating them with empty strings is inefficient. +It is much better to use one of the type-specific toString() methods instead. + +``` +//AdditiveExpression/PrimaryExpression/PrimaryPrefix/Literal[@Image='""'] +``` + +**Example(s):** + +``` +String s = "" + 123; // inefficient +String t = Integer.toString(456); // preferred approach +``` + +## AvoidArrayLoops + +**Since:** PMD 3.5 + +**Priority:** Medium (3) + +Instead of manually copying data between two arrays, use the efficient System.arraycopy method instead. + +``` +//Statement[(ForStatement or WhileStatement) and +count(*//AssignmentOperator[@Image = '='])=1 +and +*/Statement +[ +./Block/BlockStatement/Statement/StatementExpression/PrimaryExpression +/PrimaryPrefix/Name/../../PrimarySuffix/Expression +[(PrimaryExpression or AdditiveExpression) and count +(.//PrimaryPrefix/Name)=1]//PrimaryPrefix/Name/@Image +and +./Block/BlockStatement/Statement/StatementExpression/Expression/PrimaryExpression +/PrimaryPrefix/Name/../../PrimarySuffix[count +(..//PrimarySuffix)=1]/Expression[(PrimaryExpression +or AdditiveExpression) and count(.//PrimaryPrefix/Name)=1] +//PrimaryPrefix/Name/@Image +]] +``` + +**Example(s):** + +``` +public class Test { + public void bar() { + int[] a = new int[10]; + int[] b = new int[10]; + for (int i=0;i<10;i++) { + b[i]=a[i]; + } + } +} + // this will trigger the rule + for (int i=0;i<10;i++) { + b[i]=a[c[i]]; + } + + } +} +``` + +## AvoidInstantiatingObjectsInLoops + +**Since:** PMD 2.2 + +**Priority:** Medium (3) + +New objects created within loops should be checked to see if they can created outside them and reused. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.optimizations.AvoidInstantiatingObjectsInLoopsRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/optimizations/AvoidInstantiatingObjectsInLoopsRule.java) + +**Example(s):** + +``` +public class Something { + public static void main( String as[] ) { + for (int i = 0; i < 10; i++) { + Foo f = new Foo(); // Avoid this whenever you can it's really expensive + } + } +} +``` + +## LocalVariableCouldBeFinal + +**Since:** PMD 2.2 + +**Priority:** Medium (3) + +A local variable assigned only once can be declared final. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.optimizations.LocalVariableCouldBeFinalRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/optimizations/LocalVariableCouldBeFinalRule.java) + +**Example(s):** + +``` +public class Bar { + public void foo () { + String txtA = "a"; // if txtA will not be assigned again it is better to do this: + final String txtB = "b"; + } +} +``` + +## MethodArgumentCouldBeFinal + +**Since:** PMD 2.2 + +**Priority:** Medium (3) + +A method argument that is never re-assigned within the method can be declared final. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.optimizations.MethodArgumentCouldBeFinalRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/optimizations/MethodArgumentCouldBeFinalRule.java) + +**Example(s):** + +``` +public void foo1 (String param) { // do stuff with param never assigning it + +} + +public void foo2 (final String param) { // better, do stuff with param never assigning it + +} +``` + +## PrematureDeclaration + +**Since:** PMD 5.0 + +**Priority:** Medium (3) + +Checks for variables that are defined before they might be used. A reference is deemed to be premature if it is created right before a block of code that doesn't use it that also has the ability to return or throw an exception. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.optimizations.PrematureDeclarationRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/optimizations/PrematureDeclarationRule.java) + +**Example(s):** + +``` +public int getLength(String[] strings) { + + int length = 0; // declared prematurely + + if (strings == null || strings.length == 0) return 0; + + for (String str : strings) { + length += str.length(); + } + + return length; +} +``` + +## RedundantFieldInitializer + +**Since:** PMD 5.0 + +**Priority:** Medium (3) + +Java will initialize fields with known default values so any explicit initialization of those same defaults +is redundant and results in a larger class file (approximately three additional bytecode instructions per field). + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.optimizations.RedundantFieldInitializerRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/optimizations/RedundantFieldInitializerRule.java) + +**Example(s):** + +``` +public class C { + boolean b = false; // examples of redundant initializers + byte by = 0; + short s = 0; + char c = 0; + int i = 0; + long l = 0; + + float f = .0f; // all possible float literals + doable d = 0d; // all possible double literals + Object o = null; + + MyClass mca[] = null; + int i1 = 0, ia1[] = null; + + class Nested { + boolean b = false; + } +} +``` + +## SimplifyStartsWith + +**Since:** PMD 3.1 + +**Priority:** Medium (3) + +Since it passes in a literal of length 1, calls to (string).startsWith can be rewritten using (string).charAt(0) +at the expense of some readability. + +``` +//PrimaryExpression + [PrimaryPrefix/Name + [ends-with(@Image, '.startsWith')] or PrimarySuffix[@Image='startsWith']] + [PrimarySuffix/Arguments/ArgumentList + /Expression/PrimaryExpression/PrimaryPrefix + /Literal + [string-length(@Image)=3] + [starts-with(@Image, '"')] + [ends-with(@Image, '"')] + ] +``` + +**Example(s):** + +``` +public class Foo { + + boolean checkIt(String x) { + return x.startsWith("a"); // suboptimal + } + + boolean fasterCheckIt(String x) { + return x.charAt(0) == 'a'; // faster approach + } +} +``` + +## UnnecessaryWrapperObjectCreation + +**Since:** PMD 3.8 + +**Priority:** Medium (3) + +Most wrapper classes provide static conversion methods that avoid the need to create intermediate objects +just to create the primitive forms. Using these avoids the cost of creating objects that also need to be +garbage-collected later. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.optimizations.UnnecessaryWrapperObjectCreationRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/optimizations/UnnecessaryWrapperObjectCreationRule.java) + +**Example(s):** + +``` +public int convert(String s) { + int i, i2; + + i = Integer.valueOf(s).intValue(); // this wastes an object + i = Integer.parseInt(s); // this is better + + i2 = Integer.valueOf(i).intValue(); // this wastes an object + i2 = i; // this is better + + String s3 = Integer.valueOf(i2).toString(); // this wastes an object + s3 = Integer.toString(i2); // this is better + + return i2; +} +``` + +## UseArrayListInsteadOfVector + +**Since:** PMD 3.0 + +**Priority:** Medium (3) + +ArrayList is a much better Collection implementation than Vector if thread-safe operation is not required. + +``` +//CompilationUnit[count(ImportDeclaration) = 0 or count(ImportDeclaration/Name[@Image='java.util.Vector']) > 0] + //AllocationExpression/ClassOrInterfaceType + [@Image='Vector' or @Image='java.util.Vector'] +``` + +**Example(s):** + +``` +public class SimpleTest extends TestCase { + public void testX() { + Collection c1 = new Vector(); + Collection c2 = new ArrayList(); // achieves the same with much better performance + } +} +``` + +## UseArraysAsList + +**Since:** PMD 3.5 + +**Priority:** Medium (3) + +The java.util.Arrays class has a "asList" method that should be used when you want to create a new List from +an array of objects. It is faster than executing a loop to copy all the elements of the array one by one. + +``` +//Statement[ + (ForStatement) and (ForStatement//VariableInitializer//Literal[@IntLiteral='true' and @Image='0']) and (count(.//IfStatement)=0) + ] + //StatementExpression[ + PrimaryExpression/PrimaryPrefix/Name[ + substring-before(@Image,'.add') = ancestor::MethodDeclaration//LocalVariableDeclaration[ + ./Type//ClassOrInterfaceType[ + @Image = 'Collection' or + @Image = 'List' or @Image='ArrayList' + ] + ] + /VariableDeclarator/VariableDeclaratorId[ + count(..//AllocationExpression/ClassOrInterfaceType[ + @Image="ArrayList" + ] + )=1 + ]/@Image + ] + and + PrimaryExpression/PrimarySuffix/Arguments/ArgumentList/Expression/PrimaryExpression/PrimaryPrefix/Name + [ + @Image = ancestor::MethodDeclaration//LocalVariableDeclaration[@Array="true"]/VariableDeclarator/VariableDeclaratorId/@Image + or + @Image = ancestor::MethodDeclaration//FormalParameter/VariableDeclaratorId/@Image + ] + /../..[count(.//PrimarySuffix) + =1]/PrimarySuffix/Expression/PrimaryExpression/PrimaryPrefix + /Name + ] +``` + +**Example(s):** + +``` +public class Test { + public void foo(Integer[] ints) { + // could just use Arrays.asList(ints) + List l= new ArrayList(10); + for (int i=0; i< 100; i++) { + l.add(ints[i]); + } + for (int i=0; i< 100; i++) { + l.add(a[i].toString()); // won't trigger the rule + } + } +} +``` + +## UseStringBufferForStringAppends + +**Since:** PMD 3.1 + +**Priority:** Medium (3) + +The use of the '+=' operator for appending strings causes the JVM to create and use an internal StringBuffer. +If a non-trivial number of these concatenations are being used then the explicit use of a StringBuilder or +threadsafe StringBuffer is recommended to avoid this. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.optimizations.UseStringBufferForStringAppendsRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/optimizations/UseStringBufferForStringAppendsRule.java) + +**Example(s):** + +``` +public class Foo { + void bar() { + String a; + a = "foo"; + a += " bar"; + // better would be: + // StringBuilder a = new StringBuilder("foo"); + // a.append(" bar); + } +} +``` + diff --git a/docs/pages/pmd/rules/java/strictexception.md b/docs/pages/pmd/rules/java/strictexception.md new file mode 100644 index 000000000..b8cf10895 --- /dev/null +++ b/docs/pages/pmd/rules/java/strictexception.md @@ -0,0 +1,342 @@ +--- +title: Strict Exceptions +summary: These rules provide some strict guidelines about throwing and catching exceptions. +permalink: pmd_rules_java_strictexception.html +folder: pmd/rules/java +sidebaractiveurl: /pmd_rules_java.html +editmepath: ../pmd-java/src/main/resources/rulesets/java/strictexception.xml +--- +## AvoidCatchingGenericException + +**Since:** PMD 4.2.6 + +**Priority:** Medium (3) + +Avoid catching generic exceptions such as NullPointerException, RuntimeException, Exception in try-catch block + +``` +//CatchStatement/FormalParameter/Type/ReferenceType/ClassOrInterfaceType[ + @Image='NullPointerException' or + @Image='Exception' or + @Image='RuntimeException'] +``` + +**Example(s):** + +``` +package com.igate.primitive; + +public class PrimitiveType { + + public void downCastPrimitiveType() { + try { + System.out.println(" i [" + i + "]"); + } catch(Exception e) { + e.printStackTrace(); + } catch(RuntimeException e) { + e.printStackTrace(); + } catch(NullPointerException e) { + e.printStackTrace(); + } + } +} +``` + +## AvoidCatchingNPE + +**Since:** PMD 1.8 + +**Priority:** Medium (3) + +Code should never throw NullPointerExceptions under normal circumstances. A catch block may hide the +original error, causing other, more subtle problems later on. + +``` +//CatchStatement/FormalParameter/Type + /ReferenceType/ClassOrInterfaceType[@Image='NullPointerException'] +``` + +**Example(s):** + +``` +public class Foo { + void bar() { + try { + // do something + } catch (NullPointerException npe) { + } + } +} +``` + +## AvoidCatchingThrowable + +**Since:** PMD 1.2 + +**Priority:** Medium (3) + +Catching Throwable errors is not recommended since its scope is very broad. It includes runtime issues such as +OutOfMemoryError that should be exposed and managed separately. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.strictexception.AvoidCatchingThrowableRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/strictexception/AvoidCatchingThrowableRule.java) + +**Example(s):** + +``` +public void bar() { + try { + // do something + } catch (Throwable th) { // should not catch Throwable + th.printStackTrace(); + } + } +``` + +## AvoidLosingExceptionInformation + +**Since:** PMD 4.2.6 + +**Priority:** Medium High (2) + +Statements in a catch block that invoke accessors on the exception without using the information +only add to code size. Either remove the invocation, or use the return result. + +``` +//CatchStatement/Block/BlockStatement/Statement/StatementExpression/PrimaryExpression/PrimaryPrefix/Name +[ + @Image = concat(../../../../../../../FormalParameter/VariableDeclaratorId/@Image, '.getMessage') + or + @Image = concat(../../../../../../../FormalParameter/VariableDeclaratorId/@Image, '.getLocalizedMessage') + or + @Image = concat(../../../../../../../FormalParameter/VariableDeclaratorId/@Image, '.getCause') + or + @Image = concat(../../../../../../../FormalParameter/VariableDeclaratorId/@Image, '.getStackTrace') + or + @Image = concat(../../../../../../../FormalParameter/VariableDeclaratorId/@Image, '.toString') +] +``` + +**Example(s):** + +``` +public void bar() { + try { + // do something + } catch (SomeException se) { + se.getMessage(); + } +} +``` + +## AvoidRethrowingException + +**Since:** PMD 3.8 + +**Priority:** Medium (3) + +Catch blocks that merely rethrow a caught exception only add to code size and runtime complexity. + +``` +//CatchStatement[FormalParameter + /VariableDeclaratorId/@Image = Block/BlockStatement/Statement + /ThrowStatement/Expression/PrimaryExpression[count(PrimarySuffix)=0]/PrimaryPrefix/Name/@Image + and count(Block/BlockStatement/Statement) =1] +``` + +**Example(s):** + +``` +public void bar() { + try { + // do something + } catch (SomeException se) { + throw se; + } +} +``` + +## AvoidThrowingNewInstanceOfSameException + +**Since:** PMD 4.2.5 + +**Priority:** Medium (3) + +Catch blocks that merely rethrow a caught exception wrapped inside a new instance of the same type only add to +code size and runtime complexity. + +``` +//CatchStatement[ + count(Block/BlockStatement/Statement) = 1 + and + FormalParameter/Type/ReferenceType/ClassOrInterfaceType/@Image = Block/BlockStatement/Statement/ThrowStatement/Expression/PrimaryExpression/PrimaryPrefix/AllocationExpression/ClassOrInterfaceType/@Image + and + count(Block/BlockStatement/Statement/ThrowStatement/Expression/PrimaryExpression/PrimaryPrefix/AllocationExpression/Arguments/ArgumentList/Expression) = 1 + and + FormalParameter/VariableDeclaratorId = Block/BlockStatement/Statement/ThrowStatement/Expression/PrimaryExpression/PrimaryPrefix/AllocationExpression/Arguments/ArgumentList/Expression/PrimaryExpression/PrimaryPrefix/Name + ] +``` + +**Example(s):** + +``` +public void bar() { + try { + // do something + } catch (SomeException se) { + // harmless comment + throw new SomeException(se); + } +} +``` + +## AvoidThrowingNullPointerException + +**Since:** PMD 1.8 + +**Priority:** High (1) + +Avoid throwing NullPointerExceptions. These are confusing because most people will assume that the +virtual machine threw it. Consider using an IllegalArgumentException instead; this will be +clearly seen as a programmer-initiated exception. + +``` +//AllocationExpression/ClassOrInterfaceType[@Image='NullPointerException'] +``` + +**Example(s):** + +``` +public class Foo { + void bar() { + throw new NullPointerException(); + } +} +``` + +## AvoidThrowingRawExceptionTypes + +**Since:** PMD 1.8 + +**Priority:** High (1) + +Avoid throwing certain exception types. Rather than throw a raw RuntimeException, Throwable, +Exception, or Error, use a subclassed exception or error instead. + +``` +//ThrowStatement//AllocationExpression + /ClassOrInterfaceType[ + (@Image='Throwable' and count(//ImportDeclaration/Name[ends-with(@Image,'Throwable')]) = 0) +or + (@Image='Exception' and count(//ImportDeclaration/Name[ends-with(@Image,'Exception')]) = 0) +or + (@Image='Error' and count(//ImportDeclaration/Name[ends-with(@Image,'Error')]) = 0) +or +( @Image='RuntimeException' and count(//ImportDeclaration/Name[ends-with(@Image,'RuntimeException')]) = 0) +] +``` + +**Example(s):** + +``` +public class Foo { + public void bar() throws Exception { + throw new Exception(); + } +} +``` + +## DoNotExtendJavaLangError + +**Since:** PMD 4.0 + +**Priority:** Medium (3) + +Errors are system exceptions. Do not extend them. + +``` +//ClassOrInterfaceDeclaration/ExtendsList/ClassOrInterfaceType + [@Image="Error" or @Image="java.lang.Error"] +``` + +**Example(s):** + +``` +public class Foo extends Error { } +``` + +## DoNotThrowExceptionInFinally + +**Since:** PMD 4.2 + +**Priority:** Medium Low (4) + +Throwing exceptions within a 'finally' block is confusing since they may mask other exceptions +or code defects. +Note: This is a PMD implementation of the Lint4j rule "A throw in a finally block" + +``` +//FinallyStatement[descendant::ThrowStatement] +``` + +**Example(s):** + +``` +public class Foo { + public void bar() { + try { + // Here do some stuff + } catch( Exception e) { + // Handling the issue + } finally { + // is this really a good idea ? + throw new Exception(); + } + } +} +``` + +## ExceptionAsFlowControl + +**Since:** PMD 1.8 + +**Priority:** Medium (3) + +Using Exceptions as form of flow control is not recommended as they obscure true exceptions when debugging. +Either add the necessary validation or use an alternate control structure. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.strictexception.ExceptionAsFlowControlRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/strictexception/ExceptionAsFlowControlRule.java) + +**Example(s):** + +``` +public void bar() { + try { + try { + } catch (Exception e) { + throw new WrapperException(e); + // this is essentially a GOTO to the WrapperException catch block + } + } catch (WrapperException e) { + // do some more stuff + } + } +``` + +## SignatureDeclareThrowsException + +**Since:** PMD 1.2 + +**Priority:** Medium (3) + +Methods that declare the generic Exception as a possible throwable are not very helpful since their +failure modes are unclear. Use a class derived from RuntimeException or a more specific checked exception. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.strictexception.SignatureDeclareThrowsExceptionRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/strictexception/SignatureDeclareThrowsExceptionRule.java) + +**Example(s):** + +``` +public void foo() throws Exception { +} +``` + diff --git a/docs/pages/pmd/rules/java/strings.md b/docs/pages/pmd/rules/java/strings.md new file mode 100644 index 000000000..8ee7c03c5 --- /dev/null +++ b/docs/pages/pmd/rules/java/strings.md @@ -0,0 +1,387 @@ +--- +title: String and StringBuffer +summary: These rules deal with different issues that can arise with manipulation of the String, StringBuffer, or StringBuilder instances. +permalink: pmd_rules_java_strings.html +folder: pmd/rules/java +sidebaractiveurl: /pmd_rules_java.html +editmepath: ../pmd-java/src/main/resources/rulesets/java/strings.xml +--- +## AppendCharacterWithChar + +**Since:** PMD 3.5 + +**Priority:** Medium (3) + +Avoid concatenating characters as strings in StringBuffer/StringBuilder.append methods. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.strings.AppendCharacterWithCharRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/strings/AppendCharacterWithCharRule.java) + +**Example(s):** + +``` +StringBuffer sb = new StringBuffer(); +sb.append("a"); // avoid this + +StringBuffer sb = new StringBuffer(); +sb.append('a'); // use this instead +``` + +## AvoidDuplicateLiterals + +**Since:** PMD 1.0 + +**Priority:** Medium (3) + +Code containing duplicate String literals can usually be improved by declaring the String as a constant field. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.strings.AvoidDuplicateLiteralsRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/strings/AvoidDuplicateLiteralsRule.java) + +**Example(s):** + +``` +private void bar() { + buz("Howdy"); + buz("Howdy"); + buz("Howdy"); + buz("Howdy"); + } + private void buz(String x) {} +``` + +**This rule has the following properties:** + +|Name|Default Value|Description| +|----|-------------|-----------| +|exceptionfile||File containing strings to skip (one string per line), only used if ignore list is not set| +|separator|,|Ignore list separator| +|exceptionList||Strings to ignore| +|maxDuplicateLiterals|4|Max duplicate literals| +|minimumLength|3|Minimum string length to check| +|skipAnnotations|false|Skip literals within annotations| + +## AvoidStringBufferField + +**Since:** PMD 4.2 + +**Priority:** Medium (3) + +StringBuffers/StringBuilders can grow considerably, and so may become a source of memory leaks +if held within objects with long lifetimes. + +``` +//FieldDeclaration/Type/ReferenceType/ClassOrInterfaceType[@Image = 'StringBuffer' or @Image = 'StringBuilder'] +``` + +**Example(s):** + +``` +public class Foo { + private StringBuffer buffer; // potential memory leak as an instance variable; +} +``` + +## ConsecutiveAppendsShouldReuse + +**Since:** PMD 5.1 + +**Priority:** Medium (3) + +Consecutive calls to StringBuffer/StringBuilder .append should be chained, reusing the target object. This can improve the performance +by producing a smaller bytecode, reducing overhead and improving inlining. A complete analysis can be found [here](https://github.com/pmd/pmd/issues/202#issuecomment-274349067) + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.strings.ConsecutiveAppendsShouldReuseRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/strings/ConsecutiveAppendsShouldReuseRule.java) + +**Example(s):** + +``` +String foo = " "; + +StringBuffer buf = new StringBuffer(); +buf.append("Hello"); // poor +buf.append(foo); +buf.append("World"); + +StringBuffer buf = new StringBuffer(); +buf.append("Hello").append(foo).append("World"); // good +``` + +## ConsecutiveLiteralAppends + +**Since:** PMD 3.5 + +**Priority:** Medium (3) + +Consecutively calling StringBuffer/StringBuilder.append with String literals + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.strings.ConsecutiveLiteralAppendsRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/strings/ConsecutiveLiteralAppendsRule.java) + +**Example(s):** + +``` +StringBuffer buf = new StringBuffer(); +buf.append("Hello").append(" ").append("World"); // poor +buf.append("Hello World"); // good +``` + +**This rule has the following properties:** + +|Name|Default Value|Description| +|----|-------------|-----------| +|threshold|1|Max consecutive appends| + +## InefficientEmptyStringCheck + +**Since:** PMD 3.6 + +**Priority:** Medium (3) + +String.trim().length() is an inefficient way to check if a String is really empty, as it +creates a new String object just to check its size. Consider creating a static function that +loops through a string, checking Character.isWhitespace() on each character and returning +false if a non-whitespace character is found. You can refer to Apache's StringUtils#isBlank (in commons-lang) +or Spring's StringUtils#hasText (in the Springs framework) for existing implementations. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.strings.InefficientEmptyStringCheckRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/strings/InefficientEmptyStringCheckRule.java) + +**Example(s):** + +``` +public void bar(String string) { + if (string != null && string.trim().size() > 0) { + doSomething(); + } +} +``` + +## InefficientStringBuffering + +**Since:** PMD 3.4 + +**Priority:** Medium (3) + +Avoid concatenating non-literals in a StringBuffer constructor or append() since intermediate buffers will +need to be be created and destroyed by the JVM. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.strings.InefficientStringBufferingRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/strings/InefficientStringBufferingRule.java) + +**Example(s):** + +``` +// Avoid this, two buffers are actually being created here +StringBuffer sb = new StringBuffer("tmp = "+System.getProperty("java.io.tmpdir")); + + // do this instead +StringBuffer sb = new StringBuffer("tmp = "); +sb.append(System.getProperty("java.io.tmpdir")); +``` + +## InsufficientStringBufferDeclaration + +**Since:** PMD 3.6 + +**Priority:** Medium (3) + +Failing to pre-size a StringBuffer or StringBuilder properly could cause it to re-size many times +during runtime. This rule attempts to determine the total number the characters that are actually +passed into StringBuffer.append(), but represents a best guess "worst case" scenario. An empty +StringBuffer/StringBuilder constructor initializes the object to 16 characters. This default +is assumed if the length of the constructor can not be determined. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.strings.InsufficientStringBufferDeclarationRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/strings/InsufficientStringBufferDeclarationRule.java) + +**Example(s):** + +``` +StringBuffer bad = new StringBuffer(); +bad.append("This is a long string that will exceed the default 16 characters"); + +StringBuffer good = new StringBuffer(41); +good.append("This is a long string, which is pre-sized"); +``` + +## StringBufferInstantiationWithChar + +**Since:** PMD 3.9 + +**Priority:** Medium Low (4) + +Individual character values provided as initialization arguments will be converted into integers. +This can lead to internal buffer sizes that are larger than expected. Some examples: + +new StringBuffer() // 16 +new StringBuffer(6) // 6 +new StringBuffer("hello world") // 11 + 16 = 27 +new StringBuffer('A') // chr(A) = 65 +new StringBuffer("A") // 1 + 16 = 17 + +new StringBuilder() // 16 +new StringBuilder(6) // 6 +new StringBuilder("hello world") // 11 + 16 = 27 +new StringBuilder('C') // chr(C) = 67 +new StringBuilder("A") // 1 + 16 = 17 + +``` +//AllocationExpression/ClassOrInterfaceType +[@Image='StringBuffer' or @Image='StringBuilder'] +/../Arguments/ArgumentList/Expression/PrimaryExpression +/PrimaryPrefix/ +Literal + [starts-with(@Image, "'")] + [ends-with(@Image, "'")] +``` + +**Example(s):** + +``` +// misleading instantiation, these buffers + // are actually sized to 99 characters long +StringBuffer sb1 = new StringBuffer('c'); +StringBuilder sb2 = new StringBuilder('c'); + +// in these forms, just single characters are allocated +StringBuffer sb3 = new StringBuffer("c"); +StringBuilder sb4 = new StringBuilder("c"); +``` + +## StringInstantiation + +**Since:** PMD 1.0 + +**Priority:** Medium High (2) + +Avoid instantiating String objects; this is usually unnecessary since they are immutable and can be safely shared. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.strings.StringInstantiationRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/strings/StringInstantiationRule.java) + +**Example(s):** + +``` +private String bar = new String("bar"); // just do a String bar = "bar"; +``` + +## StringToString + +**Since:** PMD 1.0 + +**Priority:** Medium (3) + +Avoid calling toString() on objects already known to be string instances; this is unnecessary. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.strings.StringToStringRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/strings/StringToStringRule.java) + +**Example(s):** + +``` +private String baz() { + String bar = "howdy"; + return bar.toString(); +} +``` + +## UnnecessaryCaseChange + +**Since:** PMD 3.3 + +**Priority:** Medium (3) + +Using equalsIgnoreCase() is faster than using toUpperCase/toLowerCase().equals() + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.strings.UnnecessaryCaseChangeRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/strings/UnnecessaryCaseChangeRule.java) + +**Example(s):** + +``` +boolean answer1 = buz.toUpperCase().equals("baz"); // should be buz.equalsIgnoreCase("baz") + +boolean answer2 = buz.toUpperCase().equalsIgnoreCase("baz"); // another unnecessary toUpperCase() +``` + +## UseEqualsToCompareStrings + +**Since:** PMD 4.1 + +**Priority:** Medium (3) + +Using '==' or '!=' to compare strings only works if intern version is used on both sides. +Use the equals() method instead. + +``` +//EqualityExpression/PrimaryExpression +[(PrimaryPrefix/Literal + [starts-with(@Image, '"')] + [ends-with(@Image, '"')] +and count(PrimarySuffix) = 0)] +``` + +**Example(s):** + +``` +public boolean test(String s) { + if (s == "one") return true; // unreliable + if ("two".equals(s)) return true; // better + return false; +} +``` + +## UseIndexOfChar + +**Since:** PMD 3.5 + +**Priority:** Medium (3) + +Use String.indexOf(char) when checking for the index of a single character; it executes faster. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.strings.UseIndexOfCharRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/strings/UseIndexOfCharRule.java) + +**Example(s):** + +``` +String s = "hello world"; + // avoid this +if (s.indexOf("d") {} + // instead do this +if (s.indexOf('d') {} +``` + +## UselessStringValueOf + +**Since:** PMD 3.8 + +**Priority:** Medium (3) + +No need to call String.valueOf to append to a string; just use the valueOf() argument directly. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.strings.UselessStringValueOfRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/strings/UselessStringValueOfRule.java) + +**Example(s):** + +``` +public String convert(int i) { + String s; + s = "a" + String.valueOf(i); // not required + s = "a" + i; // preferred approach + return s; +} +``` + +## UseStringBufferLength + +**Since:** PMD 3.4 + +**Priority:** Medium (3) + +Use StringBuffer.length() to determine StringBuffer length rather than using StringBuffer.toString().equals("") +or StringBuffer.toString().length() == ... + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.strings.UseStringBufferLengthRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/strings/UseStringBufferLengthRule.java) + +**Example(s):** + +``` +StringBuffer sb = new StringBuffer(); + +if (sb.toString().equals("")) {} // inefficient + +if (sb.length() == 0) {} // preferred +``` + diff --git a/docs/pages/pmd/rules/java/sunsecure.md b/docs/pages/pmd/rules/java/sunsecure.md new file mode 100644 index 000000000..93d766760 --- /dev/null +++ b/docs/pages/pmd/rules/java/sunsecure.md @@ -0,0 +1,54 @@ +--- +title: Security Code Guidelines +summary: These rules check the security guidelines from Sun, published at http://java.sun.com/security/seccodeguide.html#gcg +permalink: pmd_rules_java_sunsecure.html +folder: pmd/rules/java +sidebaractiveurl: /pmd_rules_java.html +editmepath: ../pmd-java/src/main/resources/rulesets/java/sunsecure.xml +--- +## ArrayIsStoredDirectly + +**Since:** PMD 2.2 + +**Priority:** Medium (3) + +Constructors and methods receiving arrays should clone objects and store the copy. +This prevents future changes from the user from affecting the original array. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.sunsecure.ArrayIsStoredDirectlyRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/sunsecure/ArrayIsStoredDirectlyRule.java) + +**Example(s):** + +``` +public class Foo { + private String [] x; + public void foo (String [] param) { + // Don't do this, make a copy of the array at least + this.x=param; + } +} +``` + +## MethodReturnsInternalArray + +**Since:** PMD 2.2 + +**Priority:** Medium (3) + +Exposing internal arrays to the caller violates object encapsulation since elements can be +removed or replaced outside of the object that owns it. It is safer to return a copy of the array. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.sunsecure.MethodReturnsInternalArrayRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/sunsecure/MethodReturnsInternalArrayRule.java) + +**Example(s):** + +``` +public class SecureSystem { + UserData [] ud; + public UserData [] getUserData() { + // Don't return directly the internal array, return a copy + return ud; + } +} +``` + diff --git a/docs/pages/pmd/rules/java/unnecessary.md b/docs/pages/pmd/rules/java/unnecessary.md new file mode 100644 index 000000000..02e4dc8fe --- /dev/null +++ b/docs/pages/pmd/rules/java/unnecessary.md @@ -0,0 +1,357 @@ +--- +title: Unnecessary +summary: The Unnecessary Ruleset contains a collection of rules for unnecessary code. +permalink: pmd_rules_java_unnecessary.html +folder: pmd/rules/java +sidebaractiveurl: /pmd_rules_java.html +editmepath: ../pmd-java/src/main/resources/rulesets/java/unnecessary.xml +--- +## UnnecessaryConversionTemporary + +**Since:** PMD 0.1 + +**Priority:** Medium (3) + +Avoid the use temporary objects when converting primitives to Strings. Use the static conversion methods +on the wrapper classes instead. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.unnecessary.UnnecessaryConversionTemporaryRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/unnecessary/UnnecessaryConversionTemporaryRule.java) + +**Example(s):** + +``` +public String convert(int x) { + String foo = new Integer(x).toString(); // this wastes an object + + return Integer.toString(x); // preferred approach +} +``` + +## UnnecessaryFinalModifier + +**Since:** PMD 3.0 + +**Priority:** Medium (3) + +When a class has the final modifier, all the methods are automatically final and do not need to be +tagged as such. Similarly, methods that can't be overridden (private methods, methods of anonymous classes, +methods of enum instance) do not need to be tagged either. + +``` +//ClassOrInterfaceDeclaration[@Final='true' and @Interface='false'] + /ClassOrInterfaceBody/ClassOrInterfaceBodyDeclaration + [count(./Annotation/MarkerAnnotation/Name[@Image='SafeVarargs' or @Image='java.lang.SafeVarargs']) = 0] + /MethodDeclaration[@Final='true'] +| //MethodDeclaration[@Final='true' and @Private='true'] +| //EnumConstant/ClassOrInterfaceBody/ClassOrInterfaceBodyDeclaration/MethodDeclaration[@Final='true'] +| //AllocationExpression/ClassOrInterfaceBody/ClassOrInterfaceBodyDeclaration/MethodDeclaration[@Final='true'] +``` + +**Example(s):** + +``` +public final class Foo { + // This final modifier is not necessary, since the class is final + // and thus, all methods are final + private final void foo() { + } +} +``` + +## UnnecessaryModifier + +**Since:** PMD 1.02 + +**Priority:** Medium (3) + +Fields in interfaces and annotations are automatically `public static final`, and methods are `public abstract`. +Classes, interfaces or annotations nested in an interface or annotation are automatically `public static` +(all nested interfaces and annotations are automatically static). +Nested enums are automatically `static`. +For historical reasons, modifiers which are implied by the context are accepted by the compiler, but are superfluous. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.unnecessary.UnnecessaryModifierRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/unnecessary/UnnecessaryModifierRule.java) + +**Example(s):** + +``` +public @interface Annotation { + public abstract void bar(); // both abstract and public are ignored by the compiler + public static final int X = 0; // public, static, and final all ignored + public static class Bar {} // public, static ignored + public static interface Baz {} // ditto +} +public interface Foo { + public abstract void bar(); // both abstract and public are ignored by the compiler + public static final int X = 0; // public, static, and final all ignored + public static class Bar {} // public, static ignored + public static interface Baz {} // ditto +} +public class Bar { + public static interface Baz {} // static ignored + public static enum FoorBar { // static ignored + FOO; + } +} +``` + +## UnnecessaryReturn + +**Since:** PMD 1.3 + +**Priority:** Medium (3) + +Avoid the use of unnecessary return statements. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.unnecessary.UnnecessaryReturnRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/unnecessary/UnnecessaryReturnRule.java) + +**Example(s):** + +``` +public class Foo { + public void bar() { + int x = 42; + return; + } +} +``` + +## UnusedNullCheckInEquals + +**Since:** PMD 3.5 + +**Priority:** Medium (3) + +After checking an object reference for null, you should invoke equals() on that object rather than passing it to another object's equals() method. + +``` +(//PrimaryPrefix[ends-with(Name/@Image, '.equals') and Name/@Image != 'Arrays.equals'] | //PrimarySuffix[@Image='equals' and not(../PrimaryPrefix/Literal)]) + /following-sibling::PrimarySuffix/Arguments/ArgumentList/Expression + /PrimaryExpression[count(PrimarySuffix)=0]/PrimaryPrefix + /Name[@Image = ./../../../../../../../../../../Expression/ConditionalAndExpression + /EqualityExpression[@Image="!=" and count(./preceding-sibling::*)=0 and + ./PrimaryExpression/PrimaryPrefix/Literal/NullLiteral] + /PrimaryExpression/PrimaryPrefix/Name/@Image] +``` + +**Example(s):** + +``` +public class Test { + + public String method1() { return "ok";} + public String method2() { return null;} + + public void method(String a) { + String b; + // I don't know it method1() can be "null" + // but I know "a" is not null.. + // I'd better write a.equals(method1()) + + if (a!=null && method1().equals(a)) { // will trigger the rule + //whatever + } + + if (method1().equals(a) && a != null) { // won't trigger the rule + //whatever + } + + if (a!=null && method1().equals(b)) { // won't trigger the rule + //whatever + } + + if (a!=null && "LITERAL".equals(a)) { // won't trigger the rule + //whatever + } + + if (a!=null && !a.equals("go")) { // won't trigger the rule + a=method2(); + if (method1().equals(a)) { + //whatever + } + } +} +} +``` + +## UselessOperationOnImmutable + +**Since:** PMD 3.5 + +**Priority:** Medium (3) + +An operation on an Immutable object (String, BigDecimal or BigInteger) won't change the object itself +since the result of the operation is a new object. Therefore, ignoring the operation result is an error. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.unnecessary.UselessOperationOnImmutableRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/unnecessary/UselessOperationOnImmutableRule.java) + +**Example(s):** + +``` +import java.math.*; + +class Test { + void method1() { + BigDecimal bd=new BigDecimal(10); + bd.add(new BigDecimal(5)); // this will trigger the rule + } + void method2() { + BigDecimal bd=new BigDecimal(10); + bd = bd.add(new BigDecimal(5)); // this won't trigger the rule + } +} +``` + +## UselessOverridingMethod + +**Since:** PMD 3.3 + +**Priority:** Medium (3) + +The overriding method merely calls the same method defined in a superclass. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.unnecessary.UselessOverridingMethodRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/unnecessary/UselessOverridingMethodRule.java) + +**Example(s):** + +``` +public void foo(String bar) { + super.foo(bar); // why bother overriding? +} + +public String foo() { + return super.foo(); // why bother overriding? +} + +@Id +public Long getId() { + return super.getId(); // OK if 'ignoreAnnotations' is false, which is the default behavior +} +``` + +**This rule has the following properties:** + +|Name|Default Value|Description| +|----|-------------|-----------| +|ignoreAnnotations|false|Ignore annotations| + +## UselessParentheses + +**Since:** PMD 5.0 + +**Priority:** Medium Low (4) + +Useless parentheses should be removed. + +``` +//Expression/PrimaryExpression/PrimaryPrefix/Expression +[count(*)=1][count(./CastExpression)=0][count(./ConditionalExpression[@Ternary='true'])=0] +[not(./AdditiveExpression[//Literal[@StringLiteral='true']])] +| +//Expression/ConditionalAndExpression/PrimaryExpression/PrimaryPrefix/Expression[ + count(*)=1 and + count(./CastExpression)=0 and + count(./EqualityExpression/MultiplicativeExpression)=0 and + count(./ConditionalExpression[@Ternary='true'])=0 and + count(./ConditionalOrExpression)=0] +| +//Expression/ConditionalOrExpression/PrimaryExpression/PrimaryPrefix/Expression[ + count(*)=1 and + count(./CastExpression)=0 and + count(./ConditionalExpression[@Ternary='true'])=0 and + count(./EqualityExpression/MultiplicativeExpression)=0] +| +//Expression/ConditionalExpression/PrimaryExpression/PrimaryPrefix/Expression[ + count(*)=1 and + count(./CastExpression)=0 and + count(./EqualityExpression)=0] +| +//Expression/AdditiveExpression[not(./PrimaryExpression/PrimaryPrefix/Literal[@StringLiteral='true'])]/PrimaryExpression[1]/PrimaryPrefix/Expression[ + count(*)=1 and + not(./CastExpression) and + not(./AdditiveExpression[@Image = '-']) and + not(./ShiftExpression) and + not(./RelationalExpression) and + not(./InstanceOfExpression) and + not(./EqualityExpression) and + not(./AndExpression) and + not(./ExclusiveOrExpression) and + not(./InclusiveOrExpression) and + not(./ConditionalAndExpression) and + not(./ConditionalOrExpression) and + not(./ConditionalExpression)] +| +//Expression/EqualityExpression/PrimaryExpression/PrimaryPrefix/Expression[ + count(*)=1 and + count(./CastExpression)=0 and + count(./AndExpression)=0 and + count(./InclusiveOrExpression)=0 and + count(./ExclusiveOrExpression)=0 and + count(./ConditionalExpression)=0 and + count(./ConditionalAndExpression)=0 and + count(./ConditionalOrExpression)=0 and + count(./EqualityExpression)=0] +``` + +**Example(s):** + +``` +public class Foo { + + private int _bar1; + private Integer _bar2; + + public void setBar(int n) { + _bar1 = Integer.valueOf((n)); // here + _bar2 = (n); // and here + } + +} +``` + +## UselessQualifiedThis + +**Since:** PMD 5.4.0 + +**Priority:** Medium (3) + +Look for qualified this usages in the same class. + +``` +//PrimaryExpression +[PrimaryPrefix/Name[@Image]] +[PrimarySuffix[@Arguments='false']] +[not(PrimarySuffix/MemberSelector)] +[ancestor::ClassOrInterfaceBodyDeclaration[1][@AnonymousInnerClass='false']] +/PrimaryPrefix/Name[@Image = ancestor::ClassOrInterfaceDeclaration[1]/@Image] +``` + +**Example(s):** + +``` +public class Foo { + final Foo otherFoo = Foo.this; // use "this" directly + + public void doSomething() { + final Foo anotherFoo = Foo.this; // use "this" directly + } + + private ActionListener returnListener() { + return new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + doSomethingWithQualifiedThis(Foo.this); // This is fine + } + }; + } + + private class Foo3 { + final Foo myFoo = Foo.this; // This is fine + } + + private class Foo2 { + final Foo2 myFoo2 = Foo2.this; // Use "this" direclty + } +} +``` + diff --git a/docs/pages/pmd/rules/java/unusedcode.md b/docs/pages/pmd/rules/java/unusedcode.md new file mode 100644 index 000000000..b120837b7 --- /dev/null +++ b/docs/pages/pmd/rules/java/unusedcode.md @@ -0,0 +1,95 @@ +--- +title: Unused Code +summary: The Unused Code ruleset contains rules that find unused or ineffective code. +permalink: pmd_rules_java_unusedcode.html +folder: pmd/rules/java +sidebaractiveurl: /pmd_rules_java.html +editmepath: ../pmd-java/src/main/resources/rulesets/java/unusedcode.xml +--- +## UnusedFormalParameter + +**Since:** PMD 0.8 + +**Priority:** Medium (3) + +Avoid passing parameters to methods or constructors without actually referencing them in the method body. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.unusedcode.UnusedFormalParameterRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/unusedcode/UnusedFormalParameterRule.java) + +**Example(s):** + +``` +public class Foo { + private void bar(String howdy) { + // howdy is not used + } +} +``` + +**This rule has the following properties:** + +|Name|Default Value|Description| +|----|-------------|-----------| +|checkAll|false|Check all methods, including non-private ones| + +## UnusedLocalVariable + +**Since:** PMD 0.1 + +**Priority:** Medium (3) + +Detects when a local variable is declared and/or assigned, but not used. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.unusedcode.UnusedLocalVariableRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/unusedcode/UnusedLocalVariableRule.java) + +**Example(s):** + +``` +public class Foo { + public void doSomething() { + int i = 5; // Unused + } +} +``` + +## UnusedPrivateField + +**Since:** PMD 0.1 + +**Priority:** Medium (3) + +Detects when a private field is declared and/or assigned a value, but not used. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.unusedcode.UnusedPrivateFieldRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/unusedcode/UnusedPrivateFieldRule.java) + +**Example(s):** + +``` +public class Something { + private static int FOO = 2; // Unused + private int i = 5; // Unused + private int j = 6; + public int addOne() { + return j++; + } +} +``` + +## UnusedPrivateMethod + +**Since:** PMD 0.7 + +**Priority:** Medium (3) + +Unused Private Method detects when a private method is declared but is unused. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.unusedcode.UnusedPrivateMethodRule](https://github.com/pmd/pmd/blob/master/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/unusedcode/UnusedPrivateMethodRule.java) + +**Example(s):** + +``` +public class Something { + private void foo() {} // unused +} +``` + diff --git a/docs/pages/pmd/rules/javascript.md b/docs/pages/pmd/rules/javascript.md deleted file mode 100644 index 27ec21b57..000000000 --- a/docs/pages/pmd/rules/javascript.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -title: JavaScript Rules -sidebar: pmd_sidebar -permalink: pmd_rules_javascript.html -folder: pmd/rules ---- diff --git a/docs/pages/pmd/rules/jsp.md b/docs/pages/pmd/rules/jsp.md index a95ff37fa..11eb83394 100644 --- a/docs/pages/pmd/rules/jsp.md +++ b/docs/pages/pmd/rules/jsp.md @@ -1,6 +1,26 @@ --- -title: JSP Rules -sidebar: pmd_sidebar +title: Java Server Pages Rules permalink: pmd_rules_jsp.html folder: pmd/rules --- +List of rulesets and rules contained in each ruleset. + +* [Basic JSF](pmd_rules_jsp_basic-jsf.html): Rules concerning basic JSF guidelines. +* [Basic JSP](pmd_rules_jsp_basic.html): Rules concerning basic JSP guidelines. + +## Basic JSF +* [DontNestJsfInJstlIteration](pmd_rules_jsp_basic-jsf.html#dontnestjsfinjstliteration): Do not nest JSF component custom actions inside a custom action that iterates over its body. + +## Basic JSP +* [DuplicateJspImports](pmd_rules_jsp_basic.html#duplicatejspimports): Avoid duplicate import statements inside JSP's. +* [IframeMissingSrcAttribute](pmd_rules_jsp_basic.html#iframemissingsrcattribute): IFrames which are missing a src element can cause security information popups in IE if you are ac... +* [JspEncoding](pmd_rules_jsp_basic.html#jspencoding): A missing 'meta' tag or page directive will trigger this rule, as well as a non-UTF-8 charset. +* [NoClassAttribute](pmd_rules_jsp_basic.html#noclassattribute): Do not use an attribute called 'class'. Use "styleclass" for CSS styles. +* [NoHtmlComments](pmd_rules_jsp_basic.html#nohtmlcomments): In a production system, HTML comments increase the payload between the application server to th... +* [NoInlineScript](pmd_rules_jsp_basic.html#noinlinescript): Avoid inlining HTML script content. Consider externalizing the HTML script using the 'src' attri... +* [NoInlineStyleInformation](pmd_rules_jsp_basic.html#noinlinestyleinformation): Style information should be put in CSS files, not in JSPs. Therefore, don't use <B> or <FON... +* [NoJspForward](pmd_rules_jsp_basic.html#nojspforward): Do not do a forward from within a JSP file. +* [NoLongScripts](pmd_rules_jsp_basic.html#nolongscripts): Scripts should be part of Tag Libraries, rather than part of JSP pages. +* [NoScriptlets](pmd_rules_jsp_basic.html#noscriptlets): Scriptlets should be factored into Tag Libraries or JSP declarations, rather than being part of J... +* [NoUnsanitizedJSPExpression](pmd_rules_jsp_basic.html#nounsanitizedjspexpression): Avoid using expressions without escaping / sanitizing. This could lead to cross site scripting - ... + diff --git a/docs/pages/pmd/rules/jsp/basic-jsf.md b/docs/pages/pmd/rules/jsp/basic-jsf.md new file mode 100644 index 000000000..2e95c66ed --- /dev/null +++ b/docs/pages/pmd/rules/jsp/basic-jsf.md @@ -0,0 +1,30 @@ +--- +title: Basic JSF +summary: Rules concerning basic JSF guidelines. +permalink: pmd_rules_jsp_basic-jsf.html +folder: pmd/rules/jsp +sidebaractiveurl: /pmd_rules_jsp.html +editmepath: ../pmd-jsp/src/main/resources/rulesets/jsp/basic-jsf.xml +--- +## DontNestJsfInJstlIteration + +**Since:** PMD 3.6 + +**Priority:** Medium (3) + +Do not nest JSF component custom actions inside a custom action that iterates over its body. + +``` +//Element[ @Name="c:forEach" ] // Element[ @NamespacePrefix="h" or @NamespacePrefix="f" ] +``` + +**Example(s):** + +``` +
      + +
    • +
      +
    +``` + diff --git a/docs/pages/pmd/rules/jsp/basic.md b/docs/pages/pmd/rules/jsp/basic.md new file mode 100644 index 000000000..ff1b4834f --- /dev/null +++ b/docs/pages/pmd/rules/jsp/basic.md @@ -0,0 +1,268 @@ +--- +title: Basic JSP +summary: Rules concerning basic JSP guidelines. +permalink: pmd_rules_jsp_basic.html +folder: pmd/rules/jsp +sidebaractiveurl: /pmd_rules_jsp.html +editmepath: ../pmd-jsp/src/main/resources/rulesets/jsp/basic.xml +--- +## DuplicateJspImports + +**Since:** PMD 3.7 + +**Priority:** Medium (3) + +Avoid duplicate import statements inside JSP's. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.jsp.rule.basic.DuplicateJspImportsRule](https://github.com/pmd/pmd/blob/master/pmd-jsp/src/main/java/net/sourceforge/pmd/lang/jsp/rule/basic/DuplicateJspImportsRule.java) + +**Example(s):** + +``` +<%@ page import=\"com.foo.MyClass,com.foo.MyClass\"%>/foo\">xxtext +``` + +## IframeMissingSrcAttribute + +**Since:** PMD 3.6 + +**Priority:** Medium High (2) + +IFrames which are missing a src element can cause security information popups in IE if you are accessing the page +through SSL. See http://support.microsoft.com/default.aspx?scid=kb;EN-US;Q261188 + +``` +//Element[upper-case(@Name)="IFRAME"][count(Attribute[upper-case(@Name)="SRC" ]) = 0] +``` + +**Example(s):** + +``` +bad example><BODY> +<iframe></iframe> +</BODY> </HTML> + +<HTML><title>good example><BODY> +<iframe src="foo"></iframe> +</BODY> </HTML> +``` + +## JspEncoding + +**Since:** PMD 4.0 + +**Priority:** Medium (3) + +A missing 'meta' tag or page directive will trigger this rule, as well as a non-UTF-8 charset. + +``` +//CompilationUnit/Content[ +not(Element[@Name="meta"][ + Attribute[@Name="content"]/AttributeValue[contains(lower-case(@Image),"charset=utf-8")] +]) +and + not(JspDirective[@Name='page']/JspDirectiveAttribute[@Name='contentType'][contains(lower-case(@Value),"charset=utf-8")]) +] +``` + +**Example(s):** + +``` +Most browsers should be able to interpret the following headers: + + <%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %> + + <meta http-equiv="Content-Type"  content="text/html; charset=UTF-8" /> +``` + +## NoClassAttribute + +**Since:** PMD 3.6 + +**Priority:** Medium High (2) + +Do not use an attribute called 'class'. Use "styleclass" for CSS styles. + +``` +//Attribute[ upper-case(@Name)="CLASS" ] +``` + +**Example(s):** + +``` +<HTML> <BODY> +<P class="MajorHeading">Some text</P> +</BODY> </HTML> +``` + +## NoHtmlComments + +**Since:** PMD 3.6 + +**Priority:** Medium High (2) + +In a production system, HTML comments increase the payload + between the application server to the client, and serve + little other purpose. Consider switching to JSP comments. + +``` +//CommentTag +``` + +**Example(s):** + +``` +<HTML><title>bad example><BODY> +<!-- HTML comment --> +</BODY> </HTML> + +<HTML><title>good example><BODY> +<%-- JSP comment --%> +</BODY> </HTML> +``` + +## NoInlineScript + +**Since:** PMD 4.0 + +**Priority:** Medium (3) + +Avoid inlining HTML script content. Consider externalizing the HTML script using the 'src' attribute on the "script" element. +Externalized script could be reused between pages. Browsers can also cache the script, reducing overall download bandwidth. + +``` +//HtmlScript[@Image != ''] +``` + +**Example(s):** + +``` +Most browsers should be able to interpret the following headers: + + <%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %> + + <meta http-equiv="Content-Type"  content="text/html; charset=UTF-8" /> +``` + +## NoInlineStyleInformation + +**Since:** PMD 3.6 + +**Priority:** Medium (3) + +Style information should be put in CSS files, not in JSPs. Therefore, don't use <B> or <FONT> tags, or attributes like "align='center'". + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.jsp.rule.basic.NoInlineStyleInformationRule](https://github.com/pmd/pmd/blob/master/pmd-jsp/src/main/java/net/sourceforge/pmd/lang/jsp/rule/basic/NoInlineStyleInformationRule.java) + +**Example(s):** + +``` +<html><body><p align='center'><b>text</b></p></body></html> +``` + +## NoJspForward + +**Since:** PMD 3.6 + +**Priority:** Medium (3) + +Do not do a forward from within a JSP file. + +``` +//Element[ @Name="jsp:forward" ] +``` + +**Example(s):** + +``` +<jsp:forward page='UnderConstruction.jsp'/> +``` + +## NoLongScripts + +**Since:** PMD 3.6 + +**Priority:** Medium High (2) + +Scripts should be part of Tag Libraries, rather than part of JSP pages. + +``` +// HtmlScript [ (@EndLine - @BeginLine > 10) ] +``` + +**Example(s):** + +``` +<HTML> +<BODY> +<!--Java Script--> +<SCRIPT language="JavaScript" type="text/javascript"> +<!-- +function calcDays(){ + var date1 = document.getElementById('d1').lastChild.data; + var date2 = document.getElementById('d2').lastChild.data; + date1 = date1.split("-"); + date2 = date2.split("-"); + var sDate = new Date(date1[0]+"/"+date1[1]+"/"+date1[2]); + var eDate = new Date(date2[0]+"/"+date2[1]+"/"+date2[2]); + var daysApart = Math.abs(Math.round((sDate-eDate)/86400000)); + document.getElementById('diffDays').lastChild.data = daysApart; +} + +onload=calcDays; +//--> +</SCRIPT> +</BODY> +</HTML> +``` + +## NoScriptlets + +**Since:** PMD 3.6 + +**Priority:** Medium (3) + +Scriptlets should be factored into Tag Libraries or JSP declarations, rather than being part of JSP pages. + +``` +//JspScriptlet + | + //Element[ upper-case(@Name)="JSP:SCRIPTLET" ] +``` + +**Example(s):** + +``` +<HTML> +<HEAD> +<% +response.setHeader("Pragma", "No-cache"); +%> +</HEAD> + <BODY> + <jsp:scriptlet>String title = "Hello world!";</jsp:scriptlet> + </BODY> +</HTML> +``` + +## NoUnsanitizedJSPExpression + +**Since:** PMD 5.1.4 + +**Priority:** Medium (3) + +Avoid using expressions without escaping / sanitizing. This could lead to cross site scripting - as the expression +would be interpreted by the browser directly (e.g. "<script>alert('hello');</script>"). + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.jsp.rule.basic.NoUnsanitizedJSPExpressionRule](https://github.com/pmd/pmd/blob/master/pmd-jsp/src/main/java/net/sourceforge/pmd/lang/jsp/rule/basic/NoUnsanitizedJSPExpressionRule.java) + +**Example(s):** + +``` +<%@ page contentType="text/html; charset=UTF-8" %> +<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %> +${expression} <!-- don't use this --> +${fn:escapeXml(expression)} <!-- instead, escape it --> +<c:out value="${expression}" /> <!-- or use c:out --> +``` + diff --git a/docs/pages/pmd/rules/plsql.md b/docs/pages/pmd/rules/plsql.md index c80914808..8263aac1d 100644 --- a/docs/pages/pmd/rules/plsql.md +++ b/docs/pages/pmd/rules/plsql.md @@ -1,6 +1,37 @@ --- title: PLSQL Rules -sidebar: pmd_sidebar permalink: pmd_rules_plsql.html folder: pmd/rules --- +List of rulesets and rules contained in each ruleset. + +* [Code Size](pmd_rules_plsql_codesize.html): The Code Size ruleset contains rules that find problems related to code size or complexity. +* [PLSQL DATETIME](pmd_rules_plsql_dates.html): The Dates ruleset deals with PLSQL DATETIME usages. +* [Strict Syntax](pmd_rules_plsql_strictsyntax.html): The Strict Syntax ruleset contains rules that highlight invalid plsql syntax, which works, but should be avoided. +* [Tom Kyte's Despair](pmd_rules_plsql_TomKytesDespair.html): Rules based on Thomas Kyte's recommendations on http://asktom.oracle.com/ and http://tkyte.blogspot.com/. + +## Code Size +* [CyclomaticComplexity](pmd_rules_plsql_codesize.html#cyclomaticcomplexity): Complexity directly affects maintenance costs is determined by the number of decision points in a... +* [ExcessiveMethodLength](pmd_rules_plsql_codesize.html#excessivemethodlength): When methods are excessively long this usually indicates that the method is doing more than itsna... +* [ExcessiveObjectLength](pmd_rules_plsql_codesize.html#excessiveobjectlength): Excessive object line lengths are usually indications that the object may be burdened with excess... +* [ExcessivePackageBodyLength](pmd_rules_plsql_codesize.html#excessivepackagebodylength): Excessive class file lengths are usually indications that the class may be burdened with excessiv... +* [ExcessivePackageSpecificationLength](pmd_rules_plsql_codesize.html#excessivepackagespecificationlength): Excessive class file lengths are usually indications that the class may be burdened with excessiv... +* [ExcessiveParameterList](pmd_rules_plsql_codesize.html#excessiveparameterlist): Methods with numerous parameters are a challenge to maintain, especially if most of them share th... +* [ExcessiveTypeLength](pmd_rules_plsql_codesize.html#excessivetypelength): Excessive class file lengths are usually indications that the class may be burdened with excessiv... +* [NcssMethodCount](pmd_rules_plsql_codesize.html#ncssmethodcount): This rule uses the NCSS (Non-Commenting Source Statements) algorithm to determine the number of l... +* [NcssObjectCount](pmd_rules_plsql_codesize.html#ncssobjectcount): This rule uses the NCSS (Non-Commenting Source Statements) algorithm to determine the number of l... +* [NPathComplexity](pmd_rules_plsql_codesize.html#npathcomplexity): The NPath complexity of a method is the number of acyclic execution paths through that method.A t... +* [TooManyFields](pmd_rules_plsql_codesize.html#toomanyfields): Classes that have too many fields can become unwieldy and could be redesigned to have fewer field... +* [TooManyMethods](pmd_rules_plsql_codesize.html#toomanymethods): A package or type with too many methods is probably a good suspect for refactoring, in order to r... + +## PLSQL DATETIME +* [TO_DATE_TO_CHAR](pmd_rules_plsql_dates.html#to_date_to_char): TO_DATE(TO_CHAR(date-variable)) used to remove time component - use TRUNC(date-veriable) +* [TO_DATEWithoutDateFormat](pmd_rules_plsql_dates.html#to_datewithoutdateformat): TO_DATE without date format- use TO_DATE(expression, date-format) +* [TO_TIMESTAMPWithoutDateFormat](pmd_rules_plsql_dates.html#to_timestampwithoutdateformat): TO_TIMESTAMP without date format- use TO_TIMESTAMP(expression, date-format) + +## Strict Syntax +* [MisplacedPragma](pmd_rules_plsql_strictsyntax.html#misplacedpragma): Oracle states that the PRAQMA AUTONOMOUS_TRANSACTION must be in the declaration block,but the cod... + +## Tom Kyte's Despair +* [TomKytesDespair](pmd_rules_plsql_TomKytesDespair.html#tomkytesdespair): "WHEN OTHERS THEN NULL" hides all errors - (Re)RAISE an exception or call RAISE_APPLICATION_ERROR + diff --git a/docs/pages/pmd/rules/plsql/TomKytesDespair.md b/docs/pages/pmd/rules/plsql/TomKytesDespair.md new file mode 100644 index 000000000..3eb56e5a2 --- /dev/null +++ b/docs/pages/pmd/rules/plsql/TomKytesDespair.md @@ -0,0 +1,65 @@ +--- +title: Tom Kyte's Despair +summary: Rules based on Thomas Kyte's recommendations on http://asktom.oracle.com/ and http://tkyte.blogspot.com/. +permalink: pmd_rules_plsql_TomKytesDespair.html +folder: pmd/rules/plsql +sidebaractiveurl: /pmd_rules_plsql.html +editmepath: ../pmd-plsql/src/main/resources/rulesets/plsql/TomKytesDespair.xml +--- +## TomKytesDespair + +**Since:** PMD 5.1 + +**Priority:** Medium (3) + +"WHEN OTHERS THEN NULL" hides all errors - (Re)RAISE an exception or call RAISE_APPLICATION_ERROR + +``` +//ExceptionHandler[QualifiedName/@Image='OTHERS' and upper-case(Statement/UnlabelledStatement/Expression/@Image)='NULL'] +``` + +**Example(s):** + +``` +CREATE OR REPLACE PACKAGE BODY update_planned_hrs +IS + +PROCEDURE set_new_planned (p_emp_id IN NUMBER, p_project_id IN NUMBER, p_hours IN NUMBER) +IS +BEGIN + UPDATE employee_on_activity ea + SET ea.ea_planned_hours = p_hours + WHERE + ea.ea_emp_id = p_emp_id + AND ea.ea_proj_id = p_project_id; + +EXCEPTION + WHEN NO_DATA_FOUND THEN + RAISE_APPLICATION_ERROR (-20100, 'No such employee or project'); + +END set_new_planned; + +FUNCTION existing_planned (p_emp_id IN NUMBER, p_project_id IN NUMBER) RETURN NUMBER + +IS + +existing_hours NUMBER(4); + +BEGIN + SELECT ea.ea_planned_hours INTO existing_hours + FROM employee_on_activity ea + WHERE + ea.ea_emp_id = p_emp_id + AND ea.ea_proj_id = p_project_id; + + RETURN (existing_hours); + + EXCEPTION + WHEN OTHERS THEN NULL; + + END existing_planned; + +END update_planned_hrs; +/ +``` + diff --git a/docs/pages/pmd/rules/plsql/codesize.md b/docs/pages/pmd/rules/plsql/codesize.md new file mode 100644 index 000000000..7bc5f1b05 --- /dev/null +++ b/docs/pages/pmd/rules/plsql/codesize.md @@ -0,0 +1,577 @@ +--- +title: Code Size +summary: The Code Size ruleset contains rules that find problems related to code size or complexity. +permalink: pmd_rules_plsql_codesize.html +folder: pmd/rules/plsql +sidebaractiveurl: /pmd_rules_plsql.html +editmepath: ../pmd-plsql/src/main/resources/rulesets/plsql/codesize.xml +--- +## CyclomaticComplexity + +**Since:** PMD 5.1 + +**Priority:** Medium (3) + +Complexity directly affects maintenance costs is determined by the number of decision points in a method +plus one for the method entry. The decision points include 'if', 'while', 'for', and 'case labels' calls. +Generally, numbers ranging from 1-4 denote low complexity, 5-7 denote moderate complexity, 8-10 denote +high complexity, and 11+ is very high complexity. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.plsql.rule.codesize.CyclomaticComplexityRule](https://github.com/pmd/pmd/blob/master/pmd-plsql/src/main/java/net/sourceforge/pmd/lang/plsql/rule/codesize/CyclomaticComplexityRule.java) + +**Example(s):** + +``` +-- Cyclomatic Complexity of 25 +CREATE OR REPLACE PACKAGE BODY pkg_pmd_working_sequence AS +1 PROCEDURE ty_logger IS BEGIN +2 IF true + THEN + DBMS_OUTPUT.PUT_LINE('IF/THEN l_Integer='||l_integer); +3 IF true + THEN + DBMS_OUTPUT.PUT_LINE('IF/THEN l_Integer='||l_integer); +4 IF true + THEN + DBMS_OUTPUT.PUT_LINE('IF/THEN l_Integer='||l_integer); +5 ELSIF false + THEN + DBMS_OUTPUT.PUT_LINE('ELSIF l_Integer='||l_integer); + ELSE + DBMS_OUTPUT.PUT_LINE('ELSIF l_Integer='||l_integer); + END IF; +6 ELSIF false + THEN + DBMS_OUTPUT.PUT_LINE('ELSIF l_Integer='||l_integer); +7 IF true + THEN + DBMS_OUTPUT.PUT_LINE('IF/THEN l_Integer='||l_integer); +8 ELSIF false + THEN + DBMS_OUTPUT.PUT_LINE('ELSIF l_Integer='||l_integer); + ELSE + DBMS_OUTPUT.PUT_LINE('ELSIF l_Integer='||l_integer); + END IF; + ELSE + DBMS_OUTPUT.PUT_LINE('ELSIF l_Integer='||l_integer); +9 IF true + THEN + DBMS_OUTPUT.PUT_LINE('IF/THEN l_Integer='||l_integer); +10 ELSIF false + THEN + DBMS_OUTPUT.PUT_LINE('ELSIF l_Integer='||l_integer); + ELSE + DBMS_OUTPUT.PUT_LINE('ELSIF l_Integer='||l_integer); + END IF; + END IF; +11 ELSIF false + THEN + DBMS_OUTPUT.PUT_LINE('ELSIF l_Integer='||l_integer); +12 IF true + THEN + DBMS_OUTPUT.PUT_LINE('IF/THEN l_Integer='||l_integer); +13 IF true + THEN + DBMS_OUTPUT.PUT_LINE('IF/THEN l_Integer='||l_integer); +14 ELSIF false + THEN + DBMS_OUTPUT.PUT_LINE('ELSIF l_Integer='||l_integer); + ELSE + DBMS_OUTPUT.PUT_LINE('ELSIF l_Integer='||l_integer); + END IF; +15 ELSIF false + THEN +16 IF true + THEN + DBMS_OUTPUT.PUT_LINE('IF/THEN l_Integer='||l_integer); +17 ELSIF false + THEN + DBMS_OUTPUT.PUT_LINE('ELSIF l_Integer='||l_integer); + ELSE + DBMS_OUTPUT.PUT_LINE('ELSIF l_Integer='||l_integer); + END IF; + DBMS_OUTPUT.PUT_LINE('ELSIF l_Integer='||l_integer); + ELSE + DBMS_OUTPUT.PUT_LINE('ELSIF l_Integer='||l_integer); + END IF; + ELSE + DBMS_OUTPUT.PUT_LINE('ELSIF l_Integer='||l_integer); +18 IF true + THEN + DBMS_OUTPUT.PUT_LINE('IF/THEN l_Integer='||l_integer); +19 IF true + THEN + DBMS_OUTPUT.PUT_LINE('IF/THEN l_Integer='||l_integer); +20 ELSIF false + THEN + DBMS_OUTPUT.PUT_LINE('ELSIF l_Integer='||l_integer); + ELSE + DBMS_OUTPUT.PUT_LINE('ELSIF l_Integer='||l_integer); + END IF; +21 ELSIF false + THEN + DBMS_OUTPUT.PUT_LINE('ELSIF l_Integer='||l_integer); +22 IF true + THEN + DBMS_OUTPUT.PUT_LINE('IF/THEN l_Integer='||l_integer); +23 ELSIF false + THEN + DBMS_OUTPUT.PUT_LINE('ELSIF l_Integer='||l_integer); + ELSE + DBMS_OUTPUT.PUT_LINE('ELSIF l_Integer='||l_integer); + END IF; + ELSE + DBMS_OUTPUT.PUT_LINE('ELSIF l_Integer='||l_integer); +24 IF true + THEN + DBMS_OUTPUT.PUT_LINE('IF/THEN l_Integer='||l_integer); +25 ELSIF false + THEN + DBMS_OUTPUT.PUT_LINE('ELSIF l_Integer='||l_integer); + ELSE + DBMS_OUTPUT.PUT_LINE('ELSIF l_Integer='||l_integer); + END IF; + END IF; + END IF; +END; + +END; +``` + +**This rule has the following properties:** + +|Name|Default Value|Description| +|----|-------------|-----------| +|showMethodsComplexity|true|Add method average violations to the report| +|showClassesComplexity|true|Add class average violations to the report| +|reportLevel|10|Cyclomatic Complexity reporting threshold| + +## ExcessiveMethodLength + +**Since:** PMD 5.1 + +**Priority:** Medium (3) + +When methods are excessively long this usually indicates that the method is doing more than its +name/signature might suggest. They also become challenging for others to digest since excessive +scrolling causes readers to lose focus. +Try to reduce the method length by creating helper methods and removing any copy/pasted code. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.plsql.rule.codesize.ExcessiveMethodLengthRule](https://github.com/pmd/pmd/blob/master/pmd-plsql/src/main/java/net/sourceforge/pmd/lang/plsql/rule/codesize/ExcessiveMethodLengthRule.java) + +**Example(s):** + +``` +CREATE OR REPLACE +PROCEDURE doSomething BEGIN + DBMS_OUTPUT.PUT_LINE("Hello world!"); + DBMS_OUTPUT.PUT_LINE("Hello world!"); + -- 98 copies omitted for brevity. +END; +``` + +**This rule has the following properties:** + +|Name|Default Value|Description| +|----|-------------|-----------| +|topscore||Top score value| +|minimum||Minimum reporting threshold| +|sigma||Sigma value| + +## ExcessiveObjectLength + +**Since:** PMD 5.1 + +**Priority:** Medium (3) + +Excessive object line lengths are usually indications that the object may be burdened with excessive +responsibilities that could be provided by other objects. In breaking these methods +apart the code becomes more managable and ripe for reuse. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.plsql.rule.codesize.ExcessiveObjectLengthRule](https://github.com/pmd/pmd/blob/master/pmd-plsql/src/main/java/net/sourceforge/pmd/lang/plsql/rule/codesize/ExcessiveObjectLengthRule.java) + +**Example(s):** + +``` +CREATE OR REPLACE +PACKAGE BODY Foo AS + PROCEDURE bar1 IS BEGIN + -- 1000 lines of code + END bar1; + PROCEDURE bar2 IS BEGIN + -- 1000 lines of code + END bar2; + PROCEDURE bar3 IS BEGIN + -- 1000 lines of code + END bar3; + + + PROCEDURE barN IS BEGIN + -- 1000 lines of code + END barn; +END; +``` + +**This rule has the following properties:** + +|Name|Default Value|Description| +|----|-------------|-----------| +|topscore||Top score value| +|minimum||Minimum reporting threshold| +|sigma||Sigma value| + +## ExcessivePackageBodyLength + +**Since:** PMD 5.1 + +**Priority:** Medium (3) + +Excessive class file lengths are usually indications that the class may be burdened with excessive +responsibilities that could be provided by external classes or functions. In breaking these methods +apart the code becomes more managable and ripe for reuse. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.plsql.rule.codesize.ExcessivePackageBodyLengthRule](https://github.com/pmd/pmd/blob/master/pmd-plsql/src/main/java/net/sourceforge/pmd/lang/plsql/rule/codesize/ExcessivePackageBodyLengthRule.java) + +**Example(s):** + +``` +CREATE OR REPLACE +PACKAGE BODY Foo AS + PROCEDURE bar1 IS BEGIN + -- 1000 lines of code + END bar1; + PROCEDURE bar2 IS BEGIN + -- 1000 lines of code + END bar2; + PROCEDURE bar3 IS BEGIN + -- 1000 lines of code + END bar3; + + + PROCEDURE barN IS BEGIN + -- 1000 lines of code + END barn; +END; +``` + +**This rule has the following properties:** + +|Name|Default Value|Description| +|----|-------------|-----------| +|topscore||Top score value| +|minimum||Minimum reporting threshold| +|sigma||Sigma value| + +## ExcessivePackageSpecificationLength + +**Since:** PMD 5.1 + +**Priority:** Medium (3) + +Excessive class file lengths are usually indications that the class may be burdened with excessive +responsibilities that could be provided by external classes or functions. In breaking these methods +apart the code becomes more managable and ripe for reuse. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.plsql.rule.codesize.ExcessivePackageSpecificationLengthRule](https://github.com/pmd/pmd/blob/master/pmd-plsql/src/main/java/net/sourceforge/pmd/lang/plsql/rule/codesize/ExcessivePackageSpecificationLengthRule.java) + +**Example(s):** + +``` +CREATE OR REPLACE +PACKAGE Foo AS + PROCEDURE bar1 ; + PROCEDURE bar2 ; + PROCEDURE bar3 ; + + .. + + PROCEDURE barN ; +END; +``` + +**This rule has the following properties:** + +|Name|Default Value|Description| +|----|-------------|-----------| +|topscore||Top score value| +|minimum||Minimum reporting threshold| +|sigma||Sigma value| + +## ExcessiveParameterList + +**Since:** PMD 5.1 + +**Priority:** Medium (3) + +Methods with numerous parameters are a challenge to maintain, especially if most of them share the +same datatype. These situations usually denote the need for new objects to wrap the numerous parameters. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.plsql.rule.codesize.ExcessiveParameterListRule](https://github.com/pmd/pmd/blob/master/pmd-plsql/src/main/java/net/sourceforge/pmd/lang/plsql/rule/codesize/ExcessiveParameterListRule.java) + +**Example(s):** + +``` +CREATE OR REPLACE +PROCEDURE addPerson( -- too many arguments liable to be mixed up + birthYear pls_integer, birthMonth pls_integer, birthDate pls_integer, height pls_integer, weight pls_integer, ssn pls_integer) { + + . . . . +END ADDPERSON; + +CREATE OR REPLACE +PROCEDURE addPerson( -- preferred approach + birthdate DATE, measurements BodyMeasurements , ssn INTEGER) BEGIN + + . . . . +END; +``` + +**This rule has the following properties:** + +|Name|Default Value|Description| +|----|-------------|-----------| +|topscore||Top score value| +|minimum||Minimum reporting threshold| +|sigma||Sigma value| + +## ExcessiveTypeLength + +**Since:** PMD 5.1 + +**Priority:** Medium (3) + +Excessive class file lengths are usually indications that the class may be burdened with excessive +responsibilities that could be provided by external classes or functions. In breaking these methods +apart the code becomes more managable and ripe for reuse. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.plsql.rule.codesize.ExcessiveTypeLengthRule](https://github.com/pmd/pmd/blob/master/pmd-plsql/src/main/java/net/sourceforge/pmd/lang/plsql/rule/codesize/ExcessiveTypeLengthRule.java) + +**Example(s):** + +``` +CREATE OR REPLACE +TYPE BODY Foo AS + MEMBER PROCEDURE bar1 IS BEGIN + -- 1000 lines of code + END bar1; + MEMBER PROCEDURE bar2 IS BEGIN + -- 1000 lines of code + END bar2; + MEMBER PROCEDURE bar3 IS BEGIN + -- 1000 lines of code + END bar3; + + + MEMBER PROCEDURE barN IS BEGIN + -- 1000 lines of code + END barn; +END; +``` + +**This rule has the following properties:** + +|Name|Default Value|Description| +|----|-------------|-----------| +|topscore||Top score value| +|minimum||Minimum reporting threshold| +|sigma||Sigma value| + +## NcssMethodCount + +**Since:** PMD 5.1 + +**Priority:** Medium (3) + +This rule uses the NCSS (Non-Commenting Source Statements) algorithm to determine the number of lines +of code for a given method. NCSS ignores comments, and counts actual statements. Using this algorithm, +lines of code that are split are counted as one. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.plsql.rule.codesize.NcssMethodCountRule](https://github.com/pmd/pmd/blob/master/pmd-plsql/src/main/java/net/sourceforge/pmd/lang/plsql/rule/codesize/NcssMethodCountRule.java) + +**Example(s):** + +``` +CREATE OR REPLACE PACKAGE BODY AS + FUNCTION methd RETURN INTEGER IS + BEGIN + RETURN 1;; + END; +END; +``` + +**This rule has the following properties:** + +|Name|Default Value|Description| +|----|-------------|-----------| +|topscore||Top score value| +|minimum||Minimum reporting threshold| +|sigma||Sigma value| + +## NcssObjectCount + +**Since:** PMD 5.1 + +**Priority:** Medium (3) + +This rule uses the NCSS (Non-Commenting Source Statements) algorithm to determine the number of lines +of code for a given Oracle object. NCSS ignores comments, and counts actual statements. Using this algorithm, +lines of code that are split are counted as one. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.plsql.rule.codesize.NcssObjectCountRule](https://github.com/pmd/pmd/blob/master/pmd-plsql/src/main/java/net/sourceforge/pmd/lang/plsql/rule/codesize/NcssObjectCountRule.java) + +**Example(s):** + +``` +CREATE OR REPLACE PACKAGE pkg_ + PROCEDURE Foo IS + BEGIN + --this class only has 6 NCSS lines + super(); + super(); + END; +} +``` + +**This rule has the following properties:** + +|Name|Default Value|Description| +|----|-------------|-----------| +|topscore||Top score value| +|minimum||Minimum reporting threshold| +|sigma||Sigma value| + +## NPathComplexity + +**Since:** PMD 5.1 + +**Priority:** Medium (3) + +The NPath complexity of a method is the number of acyclic execution paths through that method. +A threshold of 200 is generally considered the point where measures should be taken to reduce +complexity and increase readability. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.plsql.rule.codesize.NPathComplexityRule](https://github.com/pmd/pmd/blob/master/pmd-plsql/src/main/java/net/sourceforge/pmd/lang/plsql/rule/codesize/NPathComplexityRule.java) + +**Example(s):** + +``` +CREATE OR REPLACE +PROCEDURE bar AS BEGIN -- this is something more complex than it needs to be, + if (y) THEN -- it should be broken down into smaller methods or functions + for j IN 0 .. j-1 LOOP + if (j > r) THEN + doSomething; + while (f < 5 ) LOOP + anotherThing; + f := f - 27; + END LOOP; + else + tryThis(); + END IF; + END LOOP; + END IF; + if ( r - n > 45) THEN + while (doMagic) LOOP + findRabbits; + END LOOP; + END IF; + BEGIN + doSomethingDangerous(); + EXCEPTION WHEN FooException THEN + makeAmends; + BEGIN + dontDoItAgain; + EXCEPTION + WHEN OTHERS THEN + log_problem; + END; + END; +END; +``` + +**This rule has the following properties:** + +|Name|Default Value|Description| +|----|-------------|-----------| +|topscore||Top score value| +|minimum||Minimum reporting threshold| +|sigma||Sigma value| + +## TooManyFields + +**Since:** PMD 5.1 + +**Priority:** Medium (3) + +Classes that have too many fields can become unwieldy and could be redesigned to have fewer fields, +possibly through grouping related fields in new objects. For example, a class with individual +city/state/zip fields could park them within a single Address field. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.plsql.rule.codesize.TooManyFieldsRule](https://github.com/pmd/pmd/blob/master/pmd-plsql/src/main/java/net/sourceforge/pmd/lang/plsql/rule/codesize/TooManyFieldsRule.java) + +**Example(s):** + +``` +CREATE OR REPLACE PACKAGE pkg_too_many_fields AS + C_CHAR_A CONSTANT CHAR(1 CHAR) := 'A'; + C_CHAR_B CONSTANT CHAR(1 CHAR) := 'B'; + ... + C_CHAR_Z CONSTANT CHAR(1 CHAR) := 'Z'; +END pkg_too_many_fields; +``` + +**This rule has the following properties:** + +|Name|Default Value|Description| +|----|-------------|-----------| +|maxfields|15|Max allowable fields| + +## TooManyMethods + +**Since:** PMD 5.1 + +**Priority:** Medium (3) + +A package or type with too many methods is probably a good suspect for refactoring, in order to reduce its complexity and find a way to +have more fine grained objects. + +``` +//node() + [ ( + local-name(.) = 'PackageSpecification' + or + local-name(.) = 'TypeSpecification' + ) + and + ( + count(/descendant::ProgramUnit[ + not ( + starts-with(@Image,'get') + or + starts-with(@Image,'set') + or + starts-with(@Image,'is') + ) + ] + ) + + + count(/descendant::TypeMethod[ + not ( + starts-with(@Image,'get') + or + starts-with(@Image,'set') + or + starts-with(@Image,'is') + ) + ] + ) + ) > $maxmethods + ] +``` + +**This rule has the following properties:** + +|Name|Default Value|Description| +|----|-------------|-----------| +|maxmethods|1|The method count reporting threshold| + diff --git a/docs/pages/pmd/rules/plsql/dates.md b/docs/pages/pmd/rules/plsql/dates.md new file mode 100644 index 000000000..39e9a70d9 --- /dev/null +++ b/docs/pages/pmd/rules/plsql/dates.md @@ -0,0 +1,129 @@ +--- +title: PLSQL DATETIME +summary: The Dates ruleset deals with PLSQL DATETIME usages. +permalink: pmd_rules_plsql_dates.html +folder: pmd/rules/plsql +sidebaractiveurl: /pmd_rules_plsql.html +editmepath: ../pmd-plsql/src/main/resources/rulesets/plsql/dates.xml +--- +## TO_DATE_TO_CHAR + +**Since:** PMD 5.1 + +**Priority:** Medium (3) + +TO_DATE(TO_CHAR(date-variable)) used to remove time component - use TRUNC(date-veriable) + +``` +//PrimaryExpression[PrimaryPrefix/Name/@Image='TO_DATE' + and count(PrimarySuffix/Arguments/ArgumentList/Argument) = 1 + and .//PrimaryExpression[PrimaryPrefix/Name/@Image='TO_CHAR' + and count(PrimarySuffix/Arguments/ArgumentList/Argument) = 1 + ] + ] +``` + +**Example(s):** + +``` +CREATE OR REPLACE PACKAGE BODY date_utilities +IS + +-- Take single parameter, relyimg on current default NLS date format +FUNCTION strip_time (p_date IN DATE) RETURN DATE +IS +BEGIN + RETURN TO_DATE(TO_CHAR(p_date)); +END strip_time ; + + +END date_utilities ; +/ +``` + +## TO_DATEWithoutDateFormat + +**Since:** PMD 5.1 + +**Priority:** Medium (3) + +TO_DATE without date format- use TO_DATE(expression, date-format) + +``` +//PrimaryExpression[PrimaryPrefix/Name/@Image='TO_DATE' and count(PrimarySuffix/Arguments/ArgumentList/Argument) = 1 ] +``` + +**Example(s):** + +``` +CREATE OR REPLACE PACKAGE BODY date_utilities +IS + +-- Take single parameter, relyimg on current default NLS date format +FUNCTION to_date_single_parameter (p_date_string IN VARCHAR2) RETURN DATE +IS +BEGIN + RETURN TO_DATE(p_date_string); +END to_date_single_parameter ; + +-- Take 2 parameters, using an explicit date format string +FUNCTION to_date_two_parameters (p_date_string IN VARCHAR2, p_format_mask IN VARCHAR2) RETURN DATE +IS +BEGIN + TO_DATE(p_date_string, p_date_format); +END to_date_two_parameters ; + +-- Take 3 parameters, using an explicit date format string and an explicit language +FUNCTION to_date_three_parameters (p_date_string IN VARCHAR2, p_format_mask IN VARCHAR2, p_nls_language VARCHAR2 ) RETURN DATE +IS +BEGIN + TO_DATE(p_date_string, p_format_mask, p_nls_language); +END to_date_three_parameters ; + +END date_utilities ; +/ +``` + +## TO_TIMESTAMPWithoutDateFormat + +**Since:** PMD 5.1 + +**Priority:** Medium (3) + +TO_TIMESTAMP without date format- use TO_TIMESTAMP(expression, date-format) + +``` +//PrimaryExpression[PrimaryPrefix/Name/@Image='TO_TIMESTAMP' and count(PrimarySuffix/Arguments/ArgumentList/Argument) = 1 ] +``` + +**Example(s):** + +``` +CREATE OR REPLACE PACKAGE BODY date_utilities +IS + +-- Take single parameter, relyimg on current default NLS date format +FUNCTION to_timestamp_single_parameter (p_date_string IN VARCHAR2) RETURN DATE +IS +BEGIN + RETURN TO_TIMESTAMP(p_date_string); +END to_timestamp_single_parameter ; + +-- Take 2 parameters, using an explicit date format string +FUNCTION to_timestamp_two_parameters (p_date_string IN VARCHAR2, p_format_mask IN VARCHAR2) RETURN DATE +IS +BEGIN + TO_TIMESTAMP(p_date_string, p_date_format); +END to_timestamp_two_parameters ; + +-- Take 3 parameters, using an explicit date format string and an explicit language +FUNCTION to_timestamp_three_parameters (p_date_string IN VARCHAR2, p_format_mask IN VARCHAR2, p_nls_language VARCHAR2 ) RETURN DATE +IS +BEGIN + TO_TIMESTAMP(p_date_string, p_format_mask, p_nls_language); +END to_timestamp_three_parameters ; + +END date_utilities ; +/ +``` + diff --git a/docs/pages/pmd/rules/plsql/strictsyntax.md b/docs/pages/pmd/rules/plsql/strictsyntax.md new file mode 100644 index 000000000..aac900bae --- /dev/null +++ b/docs/pages/pmd/rules/plsql/strictsyntax.md @@ -0,0 +1,43 @@ +--- +title: Strict Syntax +summary: The Strict Syntax ruleset contains rules that highlight invalid plsql syntax, which works, but should be avoided. +permalink: pmd_rules_plsql_strictsyntax.html +folder: pmd/rules/plsql +sidebaractiveurl: /pmd_rules_plsql.html +editmepath: ../pmd-plsql/src/main/resources/rulesets/plsql/strictsyntax.xml +--- +## MisplacedPragma + +**Since:** PMD 5.5.2 + +**Priority:** Medium (3) + +Oracle states that the PRAQMA AUTONOMOUS_TRANSACTION must be in the declaration block, +but the code does not complain, when being compiled on the 11g DB. +https://docs.oracle.com/cd/B28359_01/appdev.111/b28370/static.htm#BABIIHBJ + +``` +//ProgramUnit/Pragma +``` + +**Example(s):** + +``` +create or replace package inline_pragma_error is + +end; +/ + +create or replace package body inline_pragma_error is + procedure do_transaction(p_input_token in varchar(200)) is + PRAGMA AUTONOMOUS_TRANSACTION; /* this is correct place for PRAGMA */ + begin + PRAGMA AUTONOMOUS_TRANSACTION; /* this is the wrong place for PRAGMA -> violation */ + /* do something */ + COMMIT; + end do_transaction; + +end inline_pragma_error; +/ +``` + diff --git a/docs/pages/pmd/rules/vf.md b/docs/pages/pmd/rules/vf.md new file mode 100644 index 000000000..95b6f0915 --- /dev/null +++ b/docs/pages/pmd/rules/vf.md @@ -0,0 +1,13 @@ +--- +title: Salesforce VisualForce Rules +permalink: pmd_rules_vf.html +folder: pmd/rules +--- +List of rulesets and rules contained in each ruleset. + +* [Basic VF](pmd_rules_vf_security.html): Rules concerning basic VF guidelines. + +## Basic VF +* [VfCsrf](pmd_rules_vf_security.html#vfcsrf): Avoid calling VF action upon page load as the action becomes vulnerable to CSRF. +* [VfUnescapeEl](pmd_rules_vf_security.html#vfunescapeel): Avoid unescaped user controlled content in EL as it results in XSS. + diff --git a/docs/pages/pmd/rules/vf/security.md b/docs/pages/pmd/rules/vf/security.md new file mode 100644 index 000000000..79d077f7b --- /dev/null +++ b/docs/pages/pmd/rules/vf/security.md @@ -0,0 +1,40 @@ +--- +title: Basic VF +summary: Rules concerning basic VF guidelines. +permalink: pmd_rules_vf_security.html +folder: pmd/rules/vf +sidebaractiveurl: /pmd_rules_vf.html +editmepath: ../pmd-visualforce/src/main/resources/rulesets/vf/security.xml +--- +## VfCsrf + +**Since:** PMD 5.6.0 + +**Priority:** Medium (3) + +Avoid calling VF action upon page load as the action becomes vulnerable to CSRF. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.vf.rule.security.VfCsrfRule](https://github.com/pmd/pmd/blob/master/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/rule/security/VfCsrfRule.java) + +**Example(s):** + +``` +<apex:page controller="AcRestActionsController" action="{!csrfInitMethod}" > +``` + +## VfUnescapeEl + +**Since:** PMD 5.6.0 + +**Priority:** Medium (3) + +Avoid unescaped user controlled content in EL as it results in XSS. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.vf.rule.security.VfUnescapeElRule](https://github.com/pmd/pmd/blob/master/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeElRule.java) + +**Example(s):** + +``` +<apex:outputText value="Potential XSS is {! here }" escape="false" /> +``` + diff --git a/docs/pages/pmd/rules/vm.md b/docs/pages/pmd/rules/vm.md index 0cc4f3a95..88f95f9be 100644 --- a/docs/pages/pmd/rules/vm.md +++ b/docs/pages/pmd/rules/vm.md @@ -1,6 +1,20 @@ --- -title: Apache Velocity Rules -sidebar: pmd_sidebar +title: VM Rules permalink: pmd_rules_vm.html folder: pmd/rules --- +List of rulesets and rules contained in each ruleset. + +* [Basic Velocity](pmd_rules_vm_basic.html): The Basic Velocity ruleset contains basic rules for Apache Velocity pages. + +## Basic Velocity +* [AvoidDeeplyNestedIfStmts](pmd_rules_vm_basic.html#avoiddeeplynestedifstmts): Avoid creating deeply nested if-then statements since they are harder to read and error-prone to ... +* [AvoidReassigningParameters](pmd_rules_vm_basic.html#avoidreassigningparameters): Reassigning values to incoming parameters is not recommended. Use temporary local variables inst... +* [CollapsibleIfStatements](pmd_rules_vm_basic.html#collapsibleifstatements): Sometimes two consecutive 'if' statements can be consolidated by separating their conditions with... +* [EmptyForeachStmt](pmd_rules_vm_basic.html#emptyforeachstmt): Empty foreach statements should be deleted. +* [EmptyIfStmt](pmd_rules_vm_basic.html#emptyifstmt): Empty if statements should be deleted. +* [ExcessiveTemplateLength](pmd_rules_vm_basic.html#excessivetemplatelength): The template is too long. It should be broken up into smaller pieces. +* [NoInlineJavaScript](pmd_rules_vm_basic.html#noinlinejavascript): Avoid inline JavaScript. Import .js files instead. +* [NoInlineStyles](pmd_rules_vm_basic.html#noinlinestyles): Avoid inline styles. Use css classes instead. +* [UnusedMacroParameter](pmd_rules_vm_basic.html#unusedmacroparameter): Avoid unused macro parameters. They should be deleted. + diff --git a/docs/pages/pmd/rules/vm/basic.md b/docs/pages/pmd/rules/vm/basic.md new file mode 100644 index 000000000..3499c6228 --- /dev/null +++ b/docs/pages/pmd/rules/vm/basic.md @@ -0,0 +1,114 @@ +--- +title: Basic Velocity +summary: The Basic Velocity ruleset contains basic rules for Apache Velocity pages. +permalink: pmd_rules_vm_basic.html +folder: pmd/rules/vm +sidebaractiveurl: /pmd_rules_vm.html +editmepath: ../pmd-vm/src/main/resources/rulesets/vm/basic.xml +--- +## AvoidDeeplyNestedIfStmts + +**Since:** PMD 5.1 + +**Priority:** Medium (3) + +Avoid creating deeply nested if-then statements since they are harder to read and error-prone to maintain. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.vm.rule.basic.AvoidDeeplyNestedIfStmtsRule](https://github.com/pmd/pmd/blob/master/pmd-vm/src/main/java/net/sourceforge/pmd/lang/vm/rule/basic/AvoidDeeplyNestedIfStmtsRule.java) + +**This rule has the following properties:** + +|Name|Default Value|Description| +|----|-------------|-----------| +|problemDepth|3|The if statement depth reporting threshold| + +## AvoidReassigningParameters + +**Since:** PMD 5.1 + +**Priority:** Medium High (2) + +Reassigning values to incoming parameters is not recommended. Use temporary local variables instead. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.vm.rule.basic.AvoidReassigningParametersRule](https://github.com/pmd/pmd/blob/master/pmd-vm/src/main/java/net/sourceforge/pmd/lang/vm/rule/basic/AvoidReassigningParametersRule.java) + +## CollapsibleIfStatements + +**Since:** PMD 5.1 + +**Priority:** Medium (3) + +Sometimes two consecutive 'if' statements can be consolidated by separating their conditions with a boolean short-circuit operator. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.vm.rule.basic.CollapsibleIfStatementsRule](https://github.com/pmd/pmd/blob/master/pmd-vm/src/main/java/net/sourceforge/pmd/lang/vm/rule/basic/CollapsibleIfStatementsRule.java) + +## EmptyForeachStmt + +**Since:** PMD 5.1 + +**Priority:** Medium High (2) + +Empty foreach statements should be deleted. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.vm.rule.basic.EmptyForeachStmtRule](https://github.com/pmd/pmd/blob/master/pmd-vm/src/main/java/net/sourceforge/pmd/lang/vm/rule/basic/EmptyForeachStmtRule.java) + +## EmptyIfStmt + +**Since:** PMD 5.1 + +**Priority:** Medium High (2) + +Empty if statements should be deleted. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.vm.rule.basic.EmptyIfStmtRule](https://github.com/pmd/pmd/blob/master/pmd-vm/src/main/java/net/sourceforge/pmd/lang/vm/rule/basic/EmptyIfStmtRule.java) + +## ExcessiveTemplateLength + +**Since:** PMD 5.1 + +**Priority:** Medium (3) + +The template is too long. It should be broken up into smaller pieces. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.vm.rule.basic.ExcessiveTemplateLengthRule](https://github.com/pmd/pmd/blob/master/pmd-vm/src/main/java/net/sourceforge/pmd/lang/vm/rule/basic/ExcessiveTemplateLengthRule.java) + +**This rule has the following properties:** + +|Name|Default Value|Description| +|----|-------------|-----------| +|topscore||Top score value| +|minimum||Minimum reporting threshold| +|sigma||Sigma value| + +## NoInlineJavaScript + +**Since:** PMD 5.1 + +**Priority:** Medium High (2) + +Avoid inline JavaScript. Import .js files instead. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.vm.rule.basic.NoInlineJavaScriptRule](https://github.com/pmd/pmd/blob/master/pmd-vm/src/main/java/net/sourceforge/pmd/lang/vm/rule/basic/NoInlineJavaScriptRule.java) + +## NoInlineStyles + +**Since:** PMD 5.1 + +**Priority:** Medium High (2) + +Avoid inline styles. Use css classes instead. + +``` +//Text[matches(@literal, "<[^>]+\s[sS][tT][yY][lL][eE]\s*=")] +``` + +## UnusedMacroParameter + +**Since:** PMD 5.1 + +**Priority:** Medium High (2) + +Avoid unused macro parameters. They should be deleted. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.vm.rule.basic.UnusedMacroParameterRule](https://github.com/pmd/pmd/blob/master/pmd-vm/src/main/java/net/sourceforge/pmd/lang/vm/rule/basic/UnusedMacroParameterRule.java) + diff --git a/docs/pages/pmd/rules/xml.md b/docs/pages/pmd/rules/xml.md index 91968c2bb..c5318a408 100644 --- a/docs/pages/pmd/rules/xml.md +++ b/docs/pages/pmd/rules/xml.md @@ -1,6 +1,17 @@ --- -title: XML and XSL Rules -sidebar: pmd_sidebar +title: XML Rules permalink: pmd_rules_xml.html folder: pmd/rules --- +List of rulesets and rules contained in each ruleset. + +* [Basic POM](pmd_rules_xml_basic.html): The Basic POM Ruleset contains a collection of good practices regarding Maven's POM files. +* [Basic XML](pmd_rules_xml_basic.html): The Basic XML Ruleset contains a collection of good practices which everyone should follow. + +## Basic POM +* [InvalidDependencyTypes](pmd_rules_xml_basic.html#invaliddependencytypes): While Maven will not failed if you use an invalid type for a dependency in the dependency managem... +* [ProjectVersionAsDependencyVersion](pmd_rules_xml_basic.html#projectversionasdependencyversion): Using that expression in dependency declarations seems like a shortcut, but it can go wrong. By f... + +## Basic XML +* [MistypedCDATASection](pmd_rules_xml_basic.html#mistypedcdatasection): An XML CDATA section begins with a <!CDATA[ marker, which has only one [, and ends with a ]]> mar... + diff --git a/docs/pages/pmd/rules/xml/basic.md b/docs/pages/pmd/rules/xml/basic.md new file mode 100644 index 000000000..c1324f053 --- /dev/null +++ b/docs/pages/pmd/rules/xml/basic.md @@ -0,0 +1,26 @@ +--- +title: Basic XML +summary: The Basic XML Ruleset contains a collection of good practices which everyone should follow. +permalink: pmd_rules_xml_basic.html +folder: pmd/rules/xml +sidebaractiveurl: /pmd_rules_xml.html +editmepath: ../pmd-xml/src/main/resources/rulesets/xml/basic.xml +--- +## MistypedCDATASection + +**Since:** PMD 5.0 + +**Priority:** Medium (3) + +An XML CDATA section begins with a <!CDATA[ marker, which has only one [, and ends with a ]]> marker, which has only two ]. + +``` +//cdata-section[starts-with(@Image,'[') or ends-with(@Image,']')] +``` + +**Example(s):** + +``` +An extra [ looks like <!CDATA[[]]>, and an extra ] looks like <!CDATA[]]]>. +``` + diff --git a/docs/pages/pmd/rules/xsl.md b/docs/pages/pmd/rules/xsl.md new file mode 100644 index 000000000..136139ca4 --- /dev/null +++ b/docs/pages/pmd/rules/xsl.md @@ -0,0 +1,13 @@ +--- +title: XSL Rules +permalink: pmd_rules_xsl.html +folder: pmd/rules +--- +List of rulesets and rules contained in each ruleset. + +* [XPath in XSL](pmd_rules_xsl_xpath.html): This ruleset regroups a collection of good practices regarding XPath querying and functions inside an XSL. + +## XPath in XSL +* [AvoidAxisNavigation](pmd_rules_xsl_xpath.html#avoidaxisnavigation): Avoid using the 'following' or 'preceeding' axes whenever possible, as these can cut through 100%... +* [UseConcatOnce](pmd_rules_xsl_xpath.html#useconcatonce): The XPath concat() functions accepts as many arguments as required so you can have "concat($a,'b'... + diff --git a/docs/pages/pmd/rules/xsl/xpath.md b/docs/pages/pmd/rules/xsl/xpath.md new file mode 100644 index 000000000..c0ca7a5c8 --- /dev/null +++ b/docs/pages/pmd/rules/xsl/xpath.md @@ -0,0 +1,64 @@ +--- +title: XPath in XSL +summary: This ruleset regroups a collection of good practices regarding XPath querying and functions inside an XSL. +permalink: pmd_rules_xsl_xpath.html +folder: pmd/rules/xsl +sidebaractiveurl: /pmd_rules_xsl.html +editmepath: ../pmd-xml/src/main/resources/rulesets/xsl/xpath.xml +--- +## AvoidAxisNavigation + +**Since:** PMD 5.0 + +**Priority:** Medium (3) + +Avoid using the 'following' or 'preceeding' axes whenever possible, as these can cut through 100% of the document in the worst case. Also, try to avoid using 'descendant' or 'descendant-self' axes, as if you're at the top of the Document, it necessarily means cutting through 100% of the document. + +``` +//node()[ + contains(@select,'preceeding::') + or + contains(@select,'following::') + or + contains(@select,'descendant::') + or + contains(@select,'descendant-self::') + or ( + ($checkSelfDescendantAbreviation = 'true' ) + and + contains(@select,'//') + ) +] +``` + +**Example(s):** + +``` +<xsl:variable name="var" select="//item/descendant::child"/> +``` + +**This rule has the following properties:** + +|Name|Default Value|Description| +|----|-------------|-----------| +|checkSelfDescendantAbreviation|false|descendant::self abreviation, '//', will also trigger this rule.| + +## UseConcatOnce + +**Since:** PMD 5.0 + +**Priority:** Medium (3) + +The XPath concat() functions accepts as many arguments as required so you can have "concat($a,'b',$c)" rather than "concat($a,concat('b',$c)". + +``` +//node()[contains(substring-after(@select,'concat'),'concat')] +``` + +**Example(s):** + +``` +<xsl:variable name="var" select="concat("Welcome",concat("to you ",$name))"/> + <xsl:variable name="var" select="concat("Welcome","to you ",$name))"> +``` + diff --git a/pmd-apex/pom.xml b/pmd-apex/pom.xml index e0da3492c..9ab8e2fa9 100644 --- a/pmd-apex/pom.xml +++ b/pmd-apex/pom.xml @@ -112,18 +112,47 @@ <scope>test</scope> </dependency> - <dependency> - <groupId>apex</groupId> - <artifactId>apex-jorje-ide</artifactId> - <version>1.0-sfdc-187-SNAPSHOT-55042bfc2e</version> - </dependency> + <dependency> + <groupId>apex</groupId> + <artifactId>apex-jorje-data</artifactId> + <version>1.0-sfdc-224-SNAPSHOT-3083815933</version> + </dependency> - <dependency> - <groupId>apex</groupId> - <artifactId>apex-jorje-semantic</artifactId> - <version>1.0-sfdc-187-SNAPSHOT-55042bfc2e</version> - <classifier>tests</classifier> - </dependency> + <dependency> + <groupId>apex</groupId> + <artifactId>apex-jorje-ide</artifactId> + <version>1.0-sfdc-224-SNAPSHOT-3083815933</version> + </dependency> + + <dependency> + <groupId>apex</groupId> + <artifactId>apex-jorje-parser</artifactId> + <version>1.0-sfdc-224-SNAPSHOT-3083815933</version> + </dependency> + + <dependency> + <groupId>apex</groupId> + <artifactId>apex-jorje-semantic</artifactId> + <version>1.0-sfdc-224-SNAPSHOT-3083815933</version> + </dependency> + + <dependency> + <groupId>apex</groupId> + <artifactId>apex-jorje-services</artifactId> + <version>1.0-sfdc-224-SNAPSHOT-3083815933</version> + </dependency> + + <dependency> + <groupId>apex</groupId> + <artifactId>apex-jorje-tools</artifactId> + <version>1.0-sfdc-224-SNAPSHOT-3083815933</version> + </dependency> + + <dependency> + <groupId>com.google.guava</groupId> + <artifactId>guava</artifactId> + <version>22.0</version> + </dependency> <dependency> <groupId>org.antlr</groupId> diff --git a/pmd-apex/repo/README.md b/pmd-apex/repo/README.md index 46b19a96e..6922be924 100644 --- a/pmd-apex/repo/README.md +++ b/pmd-apex/repo/README.md @@ -8,21 +8,49 @@ Apex Reference: In order to add a the jar files to the local repo, use the following commands: - mvn install:install-file -Dfile=path/to/apex-jorje-ide-1.0-sfdc-187-SNAPSHOT.jar \ + mvn install:install-file -Dfile=apex-jorje-data-1.0-sfdc-224-SNAPSHOT.jar \ + -DgroupId=apex \ + -DartifactId=apex-jorje-data \ + -Dversion=1.0-sfdc-224-SNAPSHOT \ + -Dpackaging=jar \ + -DlocalRepositoryPath=./repo + + mvn install:install-file -Dfile=apex-jorje-ide-1.0-sfdc-224-SNAPSHOT.jar \ -DgroupId=apex \ -DartifactId=apex-jorje-ide \ - -Dversion=1.0-sfdc-187-SNAPSHOT \ + -Dversion=1.0-sfdc-224-SNAPSHOT-3083815933 \ -Dpackaging=jar \ -DlocalRepositoryPath=./repo - mvn install:install-file -Dfile=path/to/apex-jorje-semantic-1.0-sfdc-187-SNAPSHOT-tests.jar \ + mvn install:install-file -Dfile=apex-jorje-parser-1.0-sfdc-224-SNAPSHOT.jar \ + -DgroupId=apex \ + -DartifactId=apex-jorje-parser \ + -Dversion=1.0-sfdc-224-SNAPSHOT-3083815933 \ + -Dpackaging=jar \ + -DlocalRepositoryPath=./repo + + mvn install:install-file -Dfile=apex-jorje-semantic-1.0-sfdc-224-SNAPSHOT.jar \ -DgroupId=apex \ -DartifactId=apex-jorje-semantic \ - -Dversion=1.0-sfdc-187-SNAPSHOT \ - -Dclassifier=tests \ + -Dversion=1.0-sfdc-224-SNAPSHOT-3083815933 \ -Dpackaging=jar \ -DlocalRepositoryPath=./repo + + mvn install:install-file -Dfile=apex-jorje-services-1.0-sfdc-224-SNAPSHOT.jar \ + -DgroupId=apex \ + -DartifactId=apex-jorje-services \ + -Dversion=1.0-sfdc-224-SNAPSHOT-3083815933 \ + -Dpackaging=jar \ + -DlocalRepositoryPath=./repo + + mvn install:install-file -Dfile=apex-jorje-tools-1.0-sfdc-224-SNAPSHOT.jar \ + -DgroupId=apex \ + -DartifactId=apex-jorje-tools \ + -Dversion=1.0-sfdc-224-SNAPSHOT-3083815933 \ + -Dpackaging=jar \ + -DlocalRepositoryPath=./repo + -For the PMD 5.5.0 Release, the versions from -<https://github.com/forcedotcom/idecore/tree/55042bfc2ebeb4c9f38c445b577c3bdd6a18fc5a/com.salesforce.ide.apex.core/lib> +For the PMD 6.0.0 Release, the versions from +<https://github.com/forcedotcom/idecore/tree/3083815933c2d015d03417986f57bd25786d58ce/com.salesforce.ide.apex.core/lib> have been taken. diff --git a/pmd-apex/repo/apex/apex-jorje-data/1.0-sfdc-224-SNAPSHOT-3083815933/apex-jorje-data-1.0-sfdc-224-SNAPSHOT-3083815933.jar b/pmd-apex/repo/apex/apex-jorje-data/1.0-sfdc-224-SNAPSHOT-3083815933/apex-jorje-data-1.0-sfdc-224-SNAPSHOT-3083815933.jar new file mode 100644 index 000000000..0dc242ad4 Binary files /dev/null and b/pmd-apex/repo/apex/apex-jorje-data/1.0-sfdc-224-SNAPSHOT-3083815933/apex-jorje-data-1.0-sfdc-224-SNAPSHOT-3083815933.jar differ diff --git a/pmd-apex/repo/apex/apex-jorje-data/1.0-sfdc-224-SNAPSHOT-3083815933/apex-jorje-data-1.0-sfdc-224-SNAPSHOT-3083815933.pom b/pmd-apex/repo/apex/apex-jorje-data/1.0-sfdc-224-SNAPSHOT-3083815933/apex-jorje-data-1.0-sfdc-224-SNAPSHOT-3083815933.pom new file mode 100644 index 000000000..058d3840b --- /dev/null +++ b/pmd-apex/repo/apex/apex-jorje-data/1.0-sfdc-224-SNAPSHOT-3083815933/apex-jorje-data-1.0-sfdc-224-SNAPSHOT-3083815933.pom @@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + <modelVersion>4.0.0</modelVersion> + <groupId>apex</groupId> + <artifactId>apex-jorje-data</artifactId> + <version>1.0-sfdc-224-SNAPSHOT-3083815933</version> + <description>POM was created from install:install-file</description> +</project> diff --git a/pmd-apex/repo/apex/apex-jorje-data/maven-metadata-local.xml b/pmd-apex/repo/apex/apex-jorje-data/maven-metadata-local.xml new file mode 100644 index 000000000..6422123f7 --- /dev/null +++ b/pmd-apex/repo/apex/apex-jorje-data/maven-metadata-local.xml @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="UTF-8"?> +<metadata> + <groupId>apex</groupId> + <artifactId>apex-jorje-data</artifactId> + <versioning> + <release>1.0-sfdc-224-SNAPSHOT-3083815933</release> + <versions> + <version>1.0-sfdc-224-SNAPSHOT-3083815933</version> + </versions> + <lastUpdated>20170629193627</lastUpdated> + </versioning> +</metadata> diff --git a/pmd-apex/repo/apex/apex-jorje-ide/1.0-sfdc-187-SNAPSHOT-55042bfc2e/apex-jorje-ide-1.0-sfdc-187-SNAPSHOT-55042bfc2e.jar b/pmd-apex/repo/apex/apex-jorje-ide/1.0-sfdc-187-SNAPSHOT-55042bfc2e/apex-jorje-ide-1.0-sfdc-187-SNAPSHOT-55042bfc2e.jar deleted file mode 100644 index 3f1bb3ed5..000000000 Binary files a/pmd-apex/repo/apex/apex-jorje-ide/1.0-sfdc-187-SNAPSHOT-55042bfc2e/apex-jorje-ide-1.0-sfdc-187-SNAPSHOT-55042bfc2e.jar and /dev/null differ diff --git a/pmd-apex/repo/apex/apex-jorje-ide/1.0-sfdc-224-SNAPSHOT-3083815933/apex-jorje-ide-1.0-sfdc-224-SNAPSHOT-3083815933.jar b/pmd-apex/repo/apex/apex-jorje-ide/1.0-sfdc-224-SNAPSHOT-3083815933/apex-jorje-ide-1.0-sfdc-224-SNAPSHOT-3083815933.jar new file mode 100644 index 000000000..49d659f70 Binary files /dev/null and b/pmd-apex/repo/apex/apex-jorje-ide/1.0-sfdc-224-SNAPSHOT-3083815933/apex-jorje-ide-1.0-sfdc-224-SNAPSHOT-3083815933.jar differ diff --git a/pmd-apex/repo/apex/apex-jorje-ide/1.0-sfdc-187-SNAPSHOT-55042bfc2e/apex-jorje-ide-1.0-sfdc-187-SNAPSHOT-55042bfc2e.pom b/pmd-apex/repo/apex/apex-jorje-ide/1.0-sfdc-224-SNAPSHOT-3083815933/apex-jorje-ide-1.0-sfdc-224-SNAPSHOT-3083815933.pom similarity index 88% rename from pmd-apex/repo/apex/apex-jorje-ide/1.0-sfdc-187-SNAPSHOT-55042bfc2e/apex-jorje-ide-1.0-sfdc-187-SNAPSHOT-55042bfc2e.pom rename to pmd-apex/repo/apex/apex-jorje-ide/1.0-sfdc-224-SNAPSHOT-3083815933/apex-jorje-ide-1.0-sfdc-224-SNAPSHOT-3083815933.pom index 527510bdf..7de5f2f2b 100644 --- a/pmd-apex/repo/apex/apex-jorje-ide/1.0-sfdc-187-SNAPSHOT-55042bfc2e/apex-jorje-ide-1.0-sfdc-187-SNAPSHOT-55042bfc2e.pom +++ b/pmd-apex/repo/apex/apex-jorje-ide/1.0-sfdc-224-SNAPSHOT-3083815933/apex-jorje-ide-1.0-sfdc-224-SNAPSHOT-3083815933.pom @@ -4,6 +4,6 @@ <modelVersion>4.0.0</modelVersion> <groupId>apex</groupId> <artifactId>apex-jorje-ide</artifactId> - <version>1.0-sfdc-187-SNAPSHOT-55042bfc2e</version> + <version>1.0-sfdc-224-SNAPSHOT-3083815933</version> <description>POM was created from install:install-file</description> </project> diff --git a/pmd-apex/repo/apex/apex-jorje-ide/maven-metadata-local.xml b/pmd-apex/repo/apex/apex-jorje-ide/maven-metadata-local.xml index d6fa47ac9..190fe95a1 100644 --- a/pmd-apex/repo/apex/apex-jorje-ide/maven-metadata-local.xml +++ b/pmd-apex/repo/apex/apex-jorje-ide/maven-metadata-local.xml @@ -3,10 +3,10 @@ <groupId>apex</groupId> <artifactId>apex-jorje-ide</artifactId> <versioning> - <release>1.0-sfdc-187-SNAPSHOT-55042bfc2e</release> + <release>1.0-sfdc-224-SNAPSHOT-3083815933</release> <versions> - <version>1.0-sfdc-187-SNAPSHOT-55042bfc2e</version> + <version>1.0-sfdc-224-SNAPSHOT-3083815933</version> </versions> - <lastUpdated>20160625150218</lastUpdated> + <lastUpdated>20170629193403</lastUpdated> </versioning> </metadata> diff --git a/pmd-apex/repo/apex/apex-jorje-parser/1.0-sfdc-224-SNAPSHOT-3083815933/apex-jorje-parser-1.0-sfdc-224-SNAPSHOT-3083815933.jar b/pmd-apex/repo/apex/apex-jorje-parser/1.0-sfdc-224-SNAPSHOT-3083815933/apex-jorje-parser-1.0-sfdc-224-SNAPSHOT-3083815933.jar new file mode 100644 index 000000000..04f8c8daa Binary files /dev/null and b/pmd-apex/repo/apex/apex-jorje-parser/1.0-sfdc-224-SNAPSHOT-3083815933/apex-jorje-parser-1.0-sfdc-224-SNAPSHOT-3083815933.jar differ diff --git a/pmd-apex/repo/apex/apex-jorje-parser/1.0-sfdc-224-SNAPSHOT-3083815933/apex-jorje-parser-1.0-sfdc-224-SNAPSHOT-3083815933.pom b/pmd-apex/repo/apex/apex-jorje-parser/1.0-sfdc-224-SNAPSHOT-3083815933/apex-jorje-parser-1.0-sfdc-224-SNAPSHOT-3083815933.pom new file mode 100644 index 000000000..5556dfc70 --- /dev/null +++ b/pmd-apex/repo/apex/apex-jorje-parser/1.0-sfdc-224-SNAPSHOT-3083815933/apex-jorje-parser-1.0-sfdc-224-SNAPSHOT-3083815933.pom @@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + <modelVersion>4.0.0</modelVersion> + <groupId>apex</groupId> + <artifactId>apex-jorje-parser</artifactId> + <version>1.0-sfdc-224-SNAPSHOT-3083815933</version> + <description>POM was created from install:install-file</description> +</project> diff --git a/pmd-apex/repo/apex/apex-jorje-parser/maven-metadata-local.xml b/pmd-apex/repo/apex/apex-jorje-parser/maven-metadata-local.xml new file mode 100644 index 000000000..206bde25a --- /dev/null +++ b/pmd-apex/repo/apex/apex-jorje-parser/maven-metadata-local.xml @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="UTF-8"?> +<metadata> + <groupId>apex</groupId> + <artifactId>apex-jorje-parser</artifactId> + <versioning> + <release>1.0-sfdc-224-SNAPSHOT-3083815933</release> + <versions> + <version>1.0-sfdc-224-SNAPSHOT-3083815933</version> + </versions> + <lastUpdated>20170629193645</lastUpdated> + </versioning> +</metadata> diff --git a/pmd-apex/repo/apex/apex-jorje-semantic/1.0-sfdc-187-SNAPSHOT-55042bfc2e/apex-jorje-semantic-1.0-sfdc-187-SNAPSHOT-55042bfc2e-tests.jar b/pmd-apex/repo/apex/apex-jorje-semantic/1.0-sfdc-187-SNAPSHOT-55042bfc2e/apex-jorje-semantic-1.0-sfdc-187-SNAPSHOT-55042bfc2e-tests.jar deleted file mode 100644 index f2060ac6c..000000000 Binary files a/pmd-apex/repo/apex/apex-jorje-semantic/1.0-sfdc-187-SNAPSHOT-55042bfc2e/apex-jorje-semantic-1.0-sfdc-187-SNAPSHOT-55042bfc2e-tests.jar and /dev/null differ diff --git a/pmd-apex/repo/apex/apex-jorje-semantic/1.0-sfdc-224-SNAPSHOT-3083815933/apex-jorje-semantic-1.0-sfdc-224-SNAPSHOT-3083815933.jar b/pmd-apex/repo/apex/apex-jorje-semantic/1.0-sfdc-224-SNAPSHOT-3083815933/apex-jorje-semantic-1.0-sfdc-224-SNAPSHOT-3083815933.jar new file mode 100644 index 000000000..9c047a30f Binary files /dev/null and b/pmd-apex/repo/apex/apex-jorje-semantic/1.0-sfdc-224-SNAPSHOT-3083815933/apex-jorje-semantic-1.0-sfdc-224-SNAPSHOT-3083815933.jar differ diff --git a/pmd-apex/repo/apex/apex-jorje-semantic/1.0-sfdc-187-SNAPSHOT-55042bfc2e/apex-jorje-semantic-1.0-sfdc-187-SNAPSHOT-55042bfc2e.pom b/pmd-apex/repo/apex/apex-jorje-semantic/1.0-sfdc-224-SNAPSHOT-3083815933/apex-jorje-semantic-1.0-sfdc-224-SNAPSHOT-3083815933.pom similarity index 89% rename from pmd-apex/repo/apex/apex-jorje-semantic/1.0-sfdc-187-SNAPSHOT-55042bfc2e/apex-jorje-semantic-1.0-sfdc-187-SNAPSHOT-55042bfc2e.pom rename to pmd-apex/repo/apex/apex-jorje-semantic/1.0-sfdc-224-SNAPSHOT-3083815933/apex-jorje-semantic-1.0-sfdc-224-SNAPSHOT-3083815933.pom index aecb89988..042bbb58c 100644 --- a/pmd-apex/repo/apex/apex-jorje-semantic/1.0-sfdc-187-SNAPSHOT-55042bfc2e/apex-jorje-semantic-1.0-sfdc-187-SNAPSHOT-55042bfc2e.pom +++ b/pmd-apex/repo/apex/apex-jorje-semantic/1.0-sfdc-224-SNAPSHOT-3083815933/apex-jorje-semantic-1.0-sfdc-224-SNAPSHOT-3083815933.pom @@ -4,6 +4,6 @@ <modelVersion>4.0.0</modelVersion> <groupId>apex</groupId> <artifactId>apex-jorje-semantic</artifactId> - <version>1.0-sfdc-187-SNAPSHOT-55042bfc2e</version> + <version>1.0-sfdc-224-SNAPSHOT-3083815933</version> <description>POM was created from install:install-file</description> </project> diff --git a/pmd-apex/repo/apex/apex-jorje-semantic/maven-metadata-local.xml b/pmd-apex/repo/apex/apex-jorje-semantic/maven-metadata-local.xml index 47a93a64f..512c049b0 100644 --- a/pmd-apex/repo/apex/apex-jorje-semantic/maven-metadata-local.xml +++ b/pmd-apex/repo/apex/apex-jorje-semantic/maven-metadata-local.xml @@ -3,10 +3,10 @@ <groupId>apex</groupId> <artifactId>apex-jorje-semantic</artifactId> <versioning> - <release>1.0-sfdc-187-SNAPSHOT-55042bfc2e</release> + <release>1.0-sfdc-224-SNAPSHOT-3083815933</release> <versions> - <version>1.0-sfdc-187-SNAPSHOT-55042bfc2e</version> + <version>1.0-sfdc-224-SNAPSHOT-3083815933</version> </versions> - <lastUpdated>20160625150226</lastUpdated> + <lastUpdated>20170629193659</lastUpdated> </versioning> </metadata> diff --git a/pmd-apex/repo/apex/apex-jorje-services/1.0-sfdc-224-SNAPSHOT-3083815933/apex-jorje-services-1.0-sfdc-224-SNAPSHOT-3083815933.jar b/pmd-apex/repo/apex/apex-jorje-services/1.0-sfdc-224-SNAPSHOT-3083815933/apex-jorje-services-1.0-sfdc-224-SNAPSHOT-3083815933.jar new file mode 100644 index 000000000..a4b83d818 Binary files /dev/null and b/pmd-apex/repo/apex/apex-jorje-services/1.0-sfdc-224-SNAPSHOT-3083815933/apex-jorje-services-1.0-sfdc-224-SNAPSHOT-3083815933.jar differ diff --git a/pmd-apex/repo/apex/apex-jorje-services/1.0-sfdc-224-SNAPSHOT-3083815933/apex-jorje-services-1.0-sfdc-224-SNAPSHOT-3083815933.pom b/pmd-apex/repo/apex/apex-jorje-services/1.0-sfdc-224-SNAPSHOT-3083815933/apex-jorje-services-1.0-sfdc-224-SNAPSHOT-3083815933.pom new file mode 100644 index 000000000..4f67f8b8b --- /dev/null +++ b/pmd-apex/repo/apex/apex-jorje-services/1.0-sfdc-224-SNAPSHOT-3083815933/apex-jorje-services-1.0-sfdc-224-SNAPSHOT-3083815933.pom @@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + <modelVersion>4.0.0</modelVersion> + <groupId>apex</groupId> + <artifactId>apex-jorje-services</artifactId> + <version>1.0-sfdc-224-SNAPSHOT-3083815933</version> + <description>POM was created from install:install-file</description> +</project> diff --git a/pmd-apex/repo/apex/apex-jorje-services/maven-metadata-local.xml b/pmd-apex/repo/apex/apex-jorje-services/maven-metadata-local.xml new file mode 100644 index 000000000..a8bc5aea7 --- /dev/null +++ b/pmd-apex/repo/apex/apex-jorje-services/maven-metadata-local.xml @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="UTF-8"?> +<metadata> + <groupId>apex</groupId> + <artifactId>apex-jorje-services</artifactId> + <versioning> + <release>1.0-sfdc-224-SNAPSHOT-3083815933</release> + <versions> + <version>1.0-sfdc-224-SNAPSHOT-3083815933</version> + </versions> + <lastUpdated>20170629193717</lastUpdated> + </versioning> +</metadata> diff --git a/pmd-apex/repo/apex/apex-jorje-tools/1.0-sfdc-224-SNAPSHOT-3083815933/apex-jorje-tools-1.0-sfdc-224-SNAPSHOT-3083815933.jar b/pmd-apex/repo/apex/apex-jorje-tools/1.0-sfdc-224-SNAPSHOT-3083815933/apex-jorje-tools-1.0-sfdc-224-SNAPSHOT-3083815933.jar new file mode 100644 index 000000000..3f858966c Binary files /dev/null and b/pmd-apex/repo/apex/apex-jorje-tools/1.0-sfdc-224-SNAPSHOT-3083815933/apex-jorje-tools-1.0-sfdc-224-SNAPSHOT-3083815933.jar differ diff --git a/pmd-apex/repo/apex/apex-jorje-tools/1.0-sfdc-224-SNAPSHOT-3083815933/apex-jorje-tools-1.0-sfdc-224-SNAPSHOT-3083815933.pom b/pmd-apex/repo/apex/apex-jorje-tools/1.0-sfdc-224-SNAPSHOT-3083815933/apex-jorje-tools-1.0-sfdc-224-SNAPSHOT-3083815933.pom new file mode 100644 index 000000000..383087334 --- /dev/null +++ b/pmd-apex/repo/apex/apex-jorje-tools/1.0-sfdc-224-SNAPSHOT-3083815933/apex-jorje-tools-1.0-sfdc-224-SNAPSHOT-3083815933.pom @@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + <modelVersion>4.0.0</modelVersion> + <groupId>apex</groupId> + <artifactId>apex-jorje-tools</artifactId> + <version>1.0-sfdc-224-SNAPSHOT-3083815933</version> + <description>POM was created from install:install-file</description> +</project> diff --git a/pmd-apex/repo/apex/apex-jorje-tools/maven-metadata-local.xml b/pmd-apex/repo/apex/apex-jorje-tools/maven-metadata-local.xml new file mode 100644 index 000000000..0c2c47f51 --- /dev/null +++ b/pmd-apex/repo/apex/apex-jorje-tools/maven-metadata-local.xml @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="UTF-8"?> +<metadata> + <groupId>apex</groupId> + <artifactId>apex-jorje-tools</artifactId> + <versioning> + <release>1.0-sfdc-224-SNAPSHOT-3083815933</release> + <versions> + <version>1.0-sfdc-224-SNAPSHOT-3083815933</version> + </versions> + <lastUpdated>20170629193730</lastUpdated> + </versioning> +</metadata> diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTBlockStatement.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTBlockStatement.java index a7371ad36..522d9489c 100644 --- a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTBlockStatement.java +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTBlockStatement.java @@ -7,6 +7,7 @@ import apex.jorje.semantic.ast.statement.BlockStatement; public class ASTBlockStatement extends AbstractApexNode<BlockStatement> { + private boolean curlyBrace; public ASTBlockStatement(BlockStatement blockStatement) { super(blockStatement); @@ -15,4 +16,21 @@ public ASTBlockStatement(BlockStatement blockStatement) { public Object jjtAccept(ApexParserVisitor visitor, Object data) { return visitor.visit(this, data); } + + public boolean hasCurlyBrace() { + return curlyBrace; + } + + @Override + protected void handleSourceCode(final String source) { + if (!hasRealLoc()) { + return; + } + + // check, whether the this block statement really begins with a curly brace + // unfortunately, for-loop and if-statements always contain a block statement, + // regardless whether curly braces where present or not. + char firstChar = source.charAt(node.getLoc().startIndex); + curlyBrace = firstChar == '{'; + } } diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTCastExpression.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTCastExpression.java new file mode 100644 index 000000000..74f52d243 --- /dev/null +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTCastExpression.java @@ -0,0 +1,18 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.apex.ast; + +import apex.jorje.semantic.ast.expression.CastExpression; + +public class ASTCastExpression extends AbstractApexNode<CastExpression> { + + public ASTCastExpression(CastExpression node) { + super(node); + } + + public Object jjtAccept(ApexParserVisitor visitor, Object data) { + return visitor.visit(this, data); + } +} diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTConstructorPreamble.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTConstructorPreamble.java new file mode 100644 index 000000000..41163d838 --- /dev/null +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTConstructorPreamble.java @@ -0,0 +1,18 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.apex.ast; + +import apex.jorje.semantic.ast.compilation.ConstructorPreamble; + +public class ASTConstructorPreamble extends AbstractApexNode<ConstructorPreamble> { + + public ASTConstructorPreamble(ConstructorPreamble node) { + super(node); + } + + public Object jjtAccept(ApexParserVisitor visitor, Object data) { + return visitor.visit(this, data); + } +} diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTIllegalStoreExpression.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTIllegalStoreExpression.java new file mode 100644 index 000000000..be7d283a1 --- /dev/null +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTIllegalStoreExpression.java @@ -0,0 +1,18 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.apex.ast; + +import apex.jorje.semantic.ast.expression.IllegalStoreExpression; + +public class ASTIllegalStoreExpression extends AbstractApexNode<IllegalStoreExpression> { + + public ASTIllegalStoreExpression(IllegalStoreExpression node) { + super(node); + } + + public Object jjtAccept(ApexParserVisitor visitor, Object data) { + return visitor.visit(this, data); + } +} diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTMethodBlockStatement.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTMethodBlockStatement.java new file mode 100644 index 000000000..0890667eb --- /dev/null +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTMethodBlockStatement.java @@ -0,0 +1,18 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.apex.ast; + +import apex.jorje.semantic.ast.statement.MethodBlockStatement; + +public class ASTMethodBlockStatement extends AbstractApexNode<MethodBlockStatement> { + + public ASTMethodBlockStatement(MethodBlockStatement node) { + super(node); + } + + public Object jjtAccept(ApexParserVisitor visitor, Object data) { + return visitor.visit(this, data); + } +} diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTTestNode.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTModifier.java similarity index 59% rename from pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTTestNode.java rename to pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTModifier.java index a1ebda8d3..58a788c44 100644 --- a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTTestNode.java +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTModifier.java @@ -4,12 +4,12 @@ package net.sourceforge.pmd.lang.apex.ast; -import apex.jorje.semantic.tester.TestNode; +import apex.jorje.semantic.ast.modifier.Modifier; -public class ASTTestNode extends AbstractApexNode<TestNode> { +public class ASTModifier extends AbstractApexNode<Modifier> { - public ASTTestNode(TestNode testNode) { - super(testNode); + public ASTModifier(Modifier node) { + super(node); } public Object jjtAccept(ApexParserVisitor visitor, Object data) { diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTMultiStatement.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTMultiStatement.java new file mode 100644 index 000000000..b4632dcc5 --- /dev/null +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTMultiStatement.java @@ -0,0 +1,18 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.apex.ast; + +import apex.jorje.semantic.ast.statement.MultiStatement; + +public class ASTMultiStatement extends AbstractApexNode<MultiStatement> { + + public ASTMultiStatement(MultiStatement node) { + super(node); + } + + public Object jjtAccept(ApexParserVisitor visitor, Object data) { + return visitor.visit(this, data); + } +} diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTDottedExpression.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTNestedExpression.java similarity index 55% rename from pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTDottedExpression.java rename to pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTNestedExpression.java index 718f33be0..2d07059de 100644 --- a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTDottedExpression.java +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTNestedExpression.java @@ -4,12 +4,12 @@ package net.sourceforge.pmd.lang.apex.ast; -import apex.jorje.semantic.ast.expression.DottedExpression; +import apex.jorje.semantic.ast.expression.NestedExpression; -public class ASTDottedExpression extends AbstractApexNode<DottedExpression> { +public class ASTNestedExpression extends AbstractApexNode<NestedExpression> { - public ASTDottedExpression(DottedExpression dottedExpression) { - super(dottedExpression); + public ASTNestedExpression(NestedExpression node) { + super(node); } public Object jjtAccept(ApexParserVisitor visitor, Object data) { diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTNestedStoreExpression.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTNestedStoreExpression.java new file mode 100644 index 000000000..acdaab383 --- /dev/null +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTNestedStoreExpression.java @@ -0,0 +1,18 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.apex.ast; + +import apex.jorje.semantic.ast.expression.NestedStoreExpression; + +public class ASTNestedStoreExpression extends AbstractApexNode<NestedStoreExpression> { + + public ASTNestedStoreExpression(NestedStoreExpression node) { + super(node); + } + + public Object jjtAccept(ApexParserVisitor visitor, Object data) { + return visitor.visit(this, data); + } +} diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTNewKeyValueObjectExpression.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTNewKeyValueObjectExpression.java new file mode 100644 index 000000000..6b7c51295 --- /dev/null +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTNewKeyValueObjectExpression.java @@ -0,0 +1,18 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.apex.ast; + +import apex.jorje.semantic.ast.expression.NewKeyValueObjectExpression; + +public class ASTNewKeyValueObjectExpression extends AbstractApexNode<NewKeyValueObjectExpression> { + + public ASTNewKeyValueObjectExpression(NewKeyValueObjectExpression node) { + super(node); + } + + public Object jjtAccept(ApexParserVisitor visitor, Object data) { + return visitor.visit(this, data); + } +} diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTNewNameValueObjectExpression.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTNewNameValueObjectExpression.java deleted file mode 100644 index d618df8f2..000000000 --- a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTNewNameValueObjectExpression.java +++ /dev/null @@ -1,18 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.apex.ast; - -import apex.jorje.semantic.ast.expression.NewNameValueObjectExpression; - -public class ASTNewNameValueObjectExpression extends AbstractApexNode<NewNameValueObjectExpression> { - - public ASTNewNameValueObjectExpression(NewNameValueObjectExpression newNameValueObjectExpression) { - super(newNameValueObjectExpression); - } - - public Object jjtAccept(ApexParserVisitor visitor, Object data) { - return visitor.visit(this, data); - } -} diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTStatementExecuted.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTStatementExecuted.java new file mode 100644 index 000000000..3307ddca4 --- /dev/null +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTStatementExecuted.java @@ -0,0 +1,18 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.apex.ast; + +import apex.jorje.semantic.ast.statement.StatementExecuted; + +public class ASTStatementExecuted extends AbstractApexNode<StatementExecuted> { + + public ASTStatementExecuted(StatementExecuted node) { + super(node); + } + + public Object jjtAccept(ApexParserVisitor visitor, Object data) { + return visitor.visit(this, data); + } +} diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/AbstractApexNode.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/AbstractApexNode.java index 3c729b270..d0e62947c 100644 --- a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/AbstractApexNode.java +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/AbstractApexNode.java @@ -8,8 +8,8 @@ import net.sourceforge.pmd.lang.ast.Node; import net.sourceforge.pmd.lang.ast.SourceCodePositioner; -import apex.jorje.data.Loc; -import apex.jorje.data.Loc.RealLoc; +import apex.jorje.data.Location; +import apex.jorje.data.Locations; import apex.jorje.semantic.ast.AstNode; import apex.jorje.semantic.exception.UnexpectedCodePathException; @@ -27,7 +27,7 @@ void calculateLineNumbers(SourceCodePositioner positioner) { return; } - RealLoc loc = (RealLoc) node.getLoc(); + Location loc = node.getLoc(); int startOffset = loc.startIndex; int endOffset = loc.endIndex; // end column will be interpreted as inclusive, while endOffset/endIndex @@ -44,6 +44,10 @@ void calculateLineNumbers(SourceCodePositioner positioner) { } } + protected void handleSourceCode(String source) { + // default implementation does nothing + } + @Override public int getBeginLine() { if (this.beginLine > 0) { @@ -113,8 +117,8 @@ public T getNode() { protected boolean hasRealLoc() { try { - Loc loc = node.getLoc(); - return loc instanceof RealLoc; + Location loc = node.getLoc(); + return loc != null && Locations.isReal(loc); } catch (UnexpectedCodePathException e) { return false; } catch (IndexOutOfBoundsException e) { diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ApexParserVisitor.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ApexParserVisitor.java index 0911bf3cf..6a20eb582 100644 --- a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ApexParserVisitor.java +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ApexParserVisitor.java @@ -8,6 +8,8 @@ public interface ApexParserVisitor { Object visit(ApexNode<?> node, Object data); Object visit(ASTAnnotation node, Object data); + + Object visit(ASTAnnotationParameter node, Object data); Object visit(ASTAnonymousClass node, Object data); @@ -29,10 +31,14 @@ public interface ApexParserVisitor { Object visit(ASTBridgeMethodCreator node, Object data); + Object visit(ASTCastExpression node, Object data); + Object visit(ASTCatchBlockStatement node, Object data); Object visit(ASTClassRefExpression node, Object data); + Object visit(ASTConstructorPreamble node, Object data); + Object visit(ASTConstructorPreambleStatement node, Object data); Object visit(ASTContinueStatement node, Object data); @@ -51,8 +57,6 @@ public interface ApexParserVisitor { Object visit(ASTDoLoopStatement node, Object data); - Object visit(ASTDottedExpression node, Object data); - Object visit(ASTExpression node, Object data); Object visit(ASTExpressionStatement node, Object data); @@ -71,6 +75,8 @@ public interface ApexParserVisitor { Object visit(ASTIfElseBlockStatement node, Object data); + Object visit(ASTIllegalStoreExpression node, Object data); + Object visit(ASTInstanceOfExpression node, Object data); Object visit(ASTJavaMethodCallExpression node, Object data); @@ -83,12 +89,24 @@ public interface ApexParserVisitor { Object visit(ASTMethod node, Object data); + Object visit(ASTMethodBlockStatement node, Object data); + Object visit(ASTMethodCallExpression node, Object data); + Object visit(ASTModifier node, Object data); + Object visit(ASTModifierNode node, Object data); Object visit(ASTModifierOrAnnotation node, Object data); + Object visit(ASTMultiStatement node, Object data); + + Object visit(ASTNestedExpression node, Object data); + + Object visit(ASTNestedStoreExpression node, Object data); + + Object visit(ASTNewKeyValueObjectExpression node, Object data); + Object visit(ASTNewListInitExpression node, Object data); Object visit(ASTNewListLiteralExpression node, Object data); @@ -97,8 +115,6 @@ public interface ApexParserVisitor { Object visit(ASTNewMapLiteralExpression node, Object data); - Object visit(ASTNewNameValueObjectExpression node, Object data); - Object visit(ASTNewObjectExpression node, Object data); Object visit(ASTNewSetInitExpression node, Object data); @@ -129,14 +145,14 @@ public interface ApexParserVisitor { Object visit(ASTStatement node, Object data); + Object visit(ASTStatementExecuted node, Object data); + Object visit(ASTSuperMethodCallExpression node, Object data); Object visit(ASTSuperVariableExpression node, Object data); Object visit(ASTTernaryExpression node, Object data); - Object visit(ASTTestNode node, Object data); - Object visit(ASTThisMethodCallExpression node, Object data); Object visit(ASTThisVariableExpression node, Object data); diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ApexParserVisitorAdapter.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ApexParserVisitorAdapter.java index d17eff076..639b88ec8 100644 --- a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ApexParserVisitorAdapter.java +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ApexParserVisitorAdapter.java @@ -220,11 +220,6 @@ public Object visit(ASTDmlUpsertStatement node, Object data) { return visit((ApexNode<?>) node, data); } - @Override - public Object visit(ASTDottedExpression node, Object data) { - return visit((ApexNode<?>) node, data); - } - @Override public Object visit(ASTExpression node, Object data) { return visit((ApexNode<?>) node, data); @@ -290,11 +285,6 @@ public Object visit(ASTNewMapLiteralExpression node, Object data) { return visit((ApexNode<?>) node, data); } - @Override - public Object visit(ASTNewNameValueObjectExpression node, Object data) { - return visit((ApexNode<?>) node, data); - } - @Override public Object visit(ASTNewObjectExpression node, Object data) { return visit((ApexNode<?>) node, data); @@ -365,11 +355,6 @@ public Object visit(ASTSuperVariableExpression node, Object data) { return visit((ApexNode<?>) node, data); } - @Override - public Object visit(ASTTestNode node, Object data) { - return visit((ApexNode<?>) node, data); - } - @Override public Object visit(ASTThisMethodCallExpression node, Object data) { return visit((ApexNode<?>) node, data); @@ -409,4 +394,59 @@ public Object visit(ASTVariableDeclarationStatements node, Object data) { public Object visit(ASTVariableExpression node, Object data) { return visit((ApexNode<?>) node, data); } + + @Override + public Object visit(ASTAnnotationParameter node, Object data) { + return visit((ApexNode<?>) node, data); + } + + @Override + public Object visit(ASTCastExpression node, Object data) { + return visit((ApexNode<?>) node, data); + } + + @Override + public Object visit(ASTConstructorPreamble node, Object data) { + return visit((ApexNode<?>) node, data); + } + + @Override + public Object visit(ASTIllegalStoreExpression node, Object data) { + return visit((ApexNode<?>) node, data); + } + + @Override + public Object visit(ASTMethodBlockStatement node, Object data) { + return visit((ApexNode<?>) node, data); + } + + @Override + public Object visit(ASTModifier node, Object data) { + return visit((ApexNode<?>) node, data); + } + + @Override + public Object visit(ASTMultiStatement node, Object data) { + return visit((ApexNode<?>) node, data); + } + + @Override + public Object visit(ASTNestedExpression node, Object data) { + return visit((ApexNode<?>) node, data); + } + + @Override + public Object visit(ASTNestedStoreExpression node, Object data) { + return visit((ApexNode<?>) node, data); + } + + @Override + public Object visit(ASTNewKeyValueObjectExpression node, Object data) { + return visit((ApexNode<?>) node, data); + } + + @Override + public Object visit(ASTStatementExecuted node, Object data) { + return visit((ApexNode<?>) node, data); + } } diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ApexTreeBuilder.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ApexTreeBuilder.java index 614f54c41..21e0c6e96 100644 --- a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ApexTreeBuilder.java +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ApexTreeBuilder.java @@ -14,6 +14,8 @@ import net.sourceforge.pmd.lang.ast.SourceCodePositioner; import apex.jorje.semantic.ast.AstNode; +import apex.jorje.semantic.ast.compilation.AnonymousClass; +import apex.jorje.semantic.ast.compilation.ConstructorPreamble; import apex.jorje.semantic.ast.compilation.UserClass; import apex.jorje.semantic.ast.compilation.UserClassMethods; import apex.jorje.semantic.ast.compilation.UserEnum; @@ -27,20 +29,23 @@ import apex.jorje.semantic.ast.expression.BinaryExpression; import apex.jorje.semantic.ast.expression.BindExpressions; import apex.jorje.semantic.ast.expression.BooleanExpression; +import apex.jorje.semantic.ast.expression.CastExpression; import apex.jorje.semantic.ast.expression.ClassRefExpression; -import apex.jorje.semantic.ast.expression.DottedExpression; import apex.jorje.semantic.ast.expression.Expression; +import apex.jorje.semantic.ast.expression.IllegalStoreExpression; import apex.jorje.semantic.ast.expression.InstanceOfExpression; import apex.jorje.semantic.ast.expression.JavaMethodCallExpression; import apex.jorje.semantic.ast.expression.JavaVariableExpression; import apex.jorje.semantic.ast.expression.LiteralExpression; import apex.jorje.semantic.ast.expression.MapEntryNode; import apex.jorje.semantic.ast.expression.MethodCallExpression; +import apex.jorje.semantic.ast.expression.NestedExpression; +import apex.jorje.semantic.ast.expression.NestedStoreExpression; +import apex.jorje.semantic.ast.expression.NewKeyValueObjectExpression; import apex.jorje.semantic.ast.expression.NewListInitExpression; import apex.jorje.semantic.ast.expression.NewListLiteralExpression; import apex.jorje.semantic.ast.expression.NewMapInitExpression; import apex.jorje.semantic.ast.expression.NewMapLiteralExpression; -import apex.jorje.semantic.ast.expression.NewNameValueObjectExpression; import apex.jorje.semantic.ast.expression.NewObjectExpression; import apex.jorje.semantic.ast.expression.NewSetInitExpression; import apex.jorje.semantic.ast.expression.NewSetLiteralExpression; @@ -64,11 +69,13 @@ import apex.jorje.semantic.ast.member.bridge.BridgeMethodCreator; import apex.jorje.semantic.ast.modifier.Annotation; import apex.jorje.semantic.ast.modifier.AnnotationParameter; +import apex.jorje.semantic.ast.modifier.Modifier; import apex.jorje.semantic.ast.modifier.ModifierNode; import apex.jorje.semantic.ast.modifier.ModifierOrAnnotation; import apex.jorje.semantic.ast.statement.BlockStatement; import apex.jorje.semantic.ast.statement.BreakStatement; import apex.jorje.semantic.ast.statement.CatchBlockStatement; +import apex.jorje.semantic.ast.statement.ConstructorPreambleStatement; import apex.jorje.semantic.ast.statement.ContinueStatement; import apex.jorje.semantic.ast.statement.DmlDeleteStatement; import apex.jorje.semantic.ast.statement.DmlInsertStatement; @@ -84,9 +91,12 @@ import apex.jorje.semantic.ast.statement.ForLoopStatement; import apex.jorje.semantic.ast.statement.IfBlockStatement; import apex.jorje.semantic.ast.statement.IfElseBlockStatement; +import apex.jorje.semantic.ast.statement.MethodBlockStatement; +import apex.jorje.semantic.ast.statement.MultiStatement; import apex.jorje.semantic.ast.statement.ReturnStatement; import apex.jorje.semantic.ast.statement.RunAsBlockStatement; import apex.jorje.semantic.ast.statement.Statement; +import apex.jorje.semantic.ast.statement.StatementExecuted; import apex.jorje.semantic.ast.statement.ThrowStatement; import apex.jorje.semantic.ast.statement.TryCatchFinallyBlockStatement; import apex.jorje.semantic.ast.statement.VariableDeclaration; @@ -95,15 +105,15 @@ import apex.jorje.semantic.ast.visitor.AdditionalPassScope; import apex.jorje.semantic.ast.visitor.AstVisitor; import apex.jorje.semantic.exception.Errors; -import apex.jorje.semantic.tester.TestNode; public final class ApexTreeBuilder extends AstVisitor<AdditionalPassScope> { - private static final Map<Class<? extends AstNode>, Constructor<? extends ApexNode<?>>> NODE_TYPE_TO_NODE_ADAPTER_TYPE = new HashMap<>(); + private static final Map<Class<? extends AstNode>, Constructor<? extends AbstractApexNode<?>>> NODE_TYPE_TO_NODE_ADAPTER_TYPE = new HashMap<>(); static { register(Annotation.class, ASTAnnotation.class); register(AnnotationParameter.class, ASTAnnotationParameter.class); + register(AnonymousClass.class, ASTAnonymousClass.class); register(ArrayLoadExpression.class, ASTArrayLoadExpression.class); register(ArrayStoreExpression.class, ASTArrayStoreExpression.class); register(AssignmentExpression.class, ASTAssignmentExpression.class); @@ -113,8 +123,11 @@ public final class ApexTreeBuilder extends AstVisitor<AdditionalPassScope> { register(BooleanExpression.class, ASTBooleanExpression.class); register(BreakStatement.class, ASTBreakStatement.class); register(BridgeMethodCreator.class, ASTBridgeMethodCreator.class); + register(CastExpression.class, ASTCastExpression.class); register(CatchBlockStatement.class, ASTCatchBlockStatement.class); register(ClassRefExpression.class, ASTClassRefExpression.class); + register(ConstructorPreamble.class, ASTConstructorPreamble.class); + register(ConstructorPreambleStatement.class, ASTConstructorPreambleStatement.class); register(ContinueStatement.class, ASTContinueStatement.class); register(DmlDeleteStatement.class, ASTDmlDeleteStatement.class); register(DmlInsertStatement.class, ASTDmlInsertStatement.class); @@ -123,7 +136,6 @@ public final class ApexTreeBuilder extends AstVisitor<AdditionalPassScope> { register(DmlUpdateStatement.class, ASTDmlUpdateStatement.class); register(DmlUpsertStatement.class, ASTDmlUpsertStatement.class); register(DoLoopStatement.class, ASTDoLoopStatement.class); - register(DottedExpression.class, ASTDottedExpression.class); register(Expression.class, ASTExpression.class); register(ExpressionStatement.class, ASTExpressionStatement.class); register(Field.class, ASTField.class); @@ -133,20 +145,26 @@ public final class ApexTreeBuilder extends AstVisitor<AdditionalPassScope> { register(ForLoopStatement.class, ASTForLoopStatement.class); register(IfBlockStatement.class, ASTIfBlockStatement.class); register(IfElseBlockStatement.class, ASTIfElseBlockStatement.class); + register(IllegalStoreExpression.class, ASTIllegalStoreExpression.class); register(InstanceOfExpression.class, ASTInstanceOfExpression.class); register(JavaMethodCallExpression.class, ASTJavaMethodCallExpression.class); register(JavaVariableExpression.class, ASTJavaVariableExpression.class); register(LiteralExpression.class, ASTLiteralExpression.class); register(MapEntryNode.class, ASTMapEntryNode.class); register(Method.class, ASTMethod.class); + register(MethodBlockStatement.class, ASTMethodBlockStatement.class); register(MethodCallExpression.class, ASTMethodCallExpression.class); + register(Modifier.class, ASTModifier.class); register(ModifierNode.class, ASTModifierNode.class); register(ModifierOrAnnotation.class, ASTModifierOrAnnotation.class); + register(MultiStatement.class, ASTMultiStatement.class); + register(NestedExpression.class, ASTNestedExpression.class); + register(NestedStoreExpression.class, ASTNestedStoreExpression.class); + register(NewKeyValueObjectExpression.class, ASTNewKeyValueObjectExpression.class); register(NewListInitExpression.class, ASTNewListInitExpression.class); register(NewListLiteralExpression.class, ASTNewListLiteralExpression.class); register(NewMapInitExpression.class, ASTNewMapInitExpression.class); register(NewMapLiteralExpression.class, ASTNewMapLiteralExpression.class); - register(NewNameValueObjectExpression.class, ASTNewNameValueObjectExpression.class); register(NewObjectExpression.class, ASTNewObjectExpression.class); register(NewSetInitExpression.class, ASTNewSetInitExpression.class); register(NewSetLiteralExpression.class, ASTNewSetLiteralExpression.class); @@ -162,28 +180,28 @@ public final class ApexTreeBuilder extends AstVisitor<AdditionalPassScope> { register(SoslExpression.class, ASTSoslExpression.class); register(StandardCondition.class, ASTStandardCondition.class); register(Statement.class, ASTStatement.class); + register(StatementExecuted.class, ASTStatementExecuted.class); register(SuperMethodCallExpression.class, ASTSuperMethodCallExpression.class); register(SuperVariableExpression.class, ASTSuperVariableExpression.class); register(TernaryExpression.class, ASTTernaryExpression.class); - register(TestNode.class, ASTTestNode.class); register(ThisMethodCallExpression.class, ASTThisMethodCallExpression.class); register(ThisVariableExpression.class, ASTThisVariableExpression.class); register(ThrowStatement.class, ASTThrowStatement.class); register(TriggerVariableExpression.class, ASTTriggerVariableExpression.class); register(TryCatchFinallyBlockStatement.class, ASTTryCatchFinallyBlockStatement.class); register(UserClass.class, ASTUserClass.class); - register(UserTrigger.class, ASTUserTrigger.class); register(UserClassMethods.class, ASTUserClassMethods.class); - register(UserEnum.class, ASTUserEnum.class); register(UserExceptionMethods.class, ASTUserExceptionMethods.class); + register(UserEnum.class, ASTUserEnum.class); register(UserInterface.class, ASTUserInterface.class); + register(UserTrigger.class, ASTUserTrigger.class); register(VariableDeclaration.class, ASTVariableDeclaration.class); register(VariableDeclarationStatements.class, ASTVariableDeclarationStatements.class); register(VariableExpression.class, ASTVariableExpression.class); register(WhileLoopStatement.class, ASTWhileLoopStatement.class); } - private static <T extends AstNode> void register(Class<T> nodeType, Class<? extends ApexNode<T>> nodeAdapterType) { + private static <T extends AstNode> void register(Class<T> nodeType, Class<? extends AbstractApexNode<T>> nodeAdapterType) { try { NODE_TYPE_TO_NODE_ADAPTER_TYPE.put(nodeType, nodeAdapterType.getConstructor(nodeType)); } catch (SecurityException e) { @@ -199,20 +217,22 @@ private static <T extends AstNode> void register(Class<T> nodeType, Class<? exte // The Apex nodes with children to build. private Stack<AstNode> parents = new Stack<>(); - private SourceCodePositioner sourceCodePositioner; + private final SourceCodePositioner sourceCodePositioner; + private final String sourceCode; public ApexTreeBuilder(String sourceCode) { + this.sourceCode = sourceCode; sourceCodePositioner = new SourceCodePositioner(sourceCode); } AdditionalPassScope scope = new AdditionalPassScope(new Errors()); - static <T extends AstNode> ApexNode<T> createNodeAdapter(T node) { + static <T extends AstNode> AbstractApexNode<T> createNodeAdapter(T node) { try { @SuppressWarnings("unchecked") // the register function makes sure only ApexNode<T> can be added, // where T is "T extends AstNode". - Constructor<? extends ApexNode<T>> constructor = (Constructor<? extends ApexNode<T>>) NODE_TYPE_TO_NODE_ADAPTER_TYPE + Constructor<? extends AbstractApexNode<T>> constructor = (Constructor<? extends AbstractApexNode<T>>) NODE_TYPE_TO_NODE_ADAPTER_TYPE .get(node.getClass()); if (constructor == null) { throw new IllegalArgumentException( @@ -230,8 +250,9 @@ static <T extends AstNode> ApexNode<T> createNodeAdapter(T node) { public <T extends AstNode> ApexNode<T> build(T astNode) { // Create a Node - ApexNode<T> node = createNodeAdapter(astNode); - calculateLineNumbers(node); + AbstractApexNode<T> node = createNodeAdapter(astNode); + node.calculateLineNumbers(sourceCodePositioner); + node.handleSourceCode(sourceCode); // Append to parent Node parent = nodes.isEmpty() ? null : nodes.peek(); @@ -250,11 +271,6 @@ public <T extends AstNode> ApexNode<T> build(T astNode) { return node; } - private void calculateLineNumbers(ApexNode<?> node) { - AbstractApexNode<?> apexNode = (AbstractApexNode<?>) node; - apexNode.calculateLineNumbers(sourceCodePositioner); - } - private boolean visit(AstNode node) { if (parents.peek() == node) { return true; @@ -369,11 +385,6 @@ public boolean visit(NewSetLiteralExpression node, AdditionalPassScope scope) { return visit(node); } - @Override - public boolean visit(NewNameValueObjectExpression node, AdditionalPassScope scope) { - return visit(node); - } - @Override public boolean visit(PackageVersionExpression node, AdditionalPassScope scope) { return visit(node); @@ -404,11 +415,6 @@ public boolean visit(TriggerVariableExpression node, AdditionalPassScope scope) return visit(node); } - @Override - public boolean visit(DottedExpression node, AdditionalPassScope scope) { - return visit(node); - } - @Override public boolean visit(VariableExpression node, AdditionalPassScope scope) { return visit(node); @@ -638,4 +644,19 @@ public boolean visit(UserClass node, AdditionalPassScope scope) { public boolean visit(Method node, AdditionalPassScope scope) { return visit(node); } + + @Override + public boolean visit(AnonymousClass node, AdditionalPassScope scope) { + return visit(node); + } + + @Override + public boolean visit(CastExpression node, AdditionalPassScope scope) { + return visit(node); + } + + @Override + public boolean visit(NewKeyValueObjectExpression node, AdditionalPassScope scope) { + return visit(node); + } } diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/CompilerService.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/CompilerService.java index 352079472..825fe3305 100644 --- a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/CompilerService.java +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/CompilerService.java @@ -23,9 +23,6 @@ import apex.jorje.semantic.compiler.sfdc.AccessEvaluator; import apex.jorje.semantic.compiler.sfdc.QueryValidator; import apex.jorje.semantic.compiler.sfdc.SymbolProvider; -import apex.jorje.semantic.tester.EmptySymbolProvider; -import apex.jorje.semantic.tester.TestAccessEvaluator; -import apex.jorje.semantic.tester.TestQueryValidators; import com.google.common.collect.ImmutableList; /** diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/EmptySymbolProvider.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/EmptySymbolProvider.java new file mode 100644 index 000000000..ea008d20e --- /dev/null +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/EmptySymbolProvider.java @@ -0,0 +1,79 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ +// Note: taken from https://github.com/forcedotcom/idecore/blob/3083815933c2d015d03417986f57bd25786d58ce/com.salesforce.ide.apex.core/src/com/salesforce/ide/apex/internal/core/EmptySymbolProvider.java + +/* + * Copyright 2016 salesforce.com, inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.sourceforge.pmd.lang.apex.ast; + +import apex.jorje.semantic.compiler.Namespace; +import apex.jorje.semantic.compiler.sfdc.SymbolProvider; +import apex.jorje.semantic.symbol.resolver.SymbolResolver; +import apex.jorje.semantic.symbol.type.TypeInfo; + +/** + * @author jspagnola + */ +public class EmptySymbolProvider implements SymbolProvider { + + private static final EmptySymbolProvider INSTANCE = new EmptySymbolProvider(); + + EmptySymbolProvider() { + } + + public static EmptySymbolProvider get() { + return INSTANCE; + } + + @Override + public TypeInfo find(final SymbolResolver symbols, final TypeInfo referencingType, final String lowerCaseFullName) { + return null; + } + + @Override + public TypeInfo getVfComponentType(final SymbolResolver symbols, final TypeInfo referencingType, + final Namespace namespace, final String name) { + return null; + } + + @Override + public TypeInfo getFlowInterviewType(final SymbolResolver symbols, final TypeInfo referencingType, + final Namespace namespace, final String name) { + return null; + } + + @Override + public TypeInfo getSObjectType(final TypeInfo referencingType, final String name) { + return null; + } + + @Override + public String getPageReference(final TypeInfo referencingType, final String name) { + return null; + } + + @Override + public boolean hasLabelField(final TypeInfo referencingType, final Namespace namespace, final String name) { + return false; + } + + @Override + public String getQuickAction(TypeInfo arg0, String arg1, String arg2) { + return null; + } +} diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/TestAccessEvaluator.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/TestAccessEvaluator.java new file mode 100644 index 000000000..92e8eec52 --- /dev/null +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/TestAccessEvaluator.java @@ -0,0 +1,334 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ +// Note: taken from https://github.com/forcedotcom/idecore/blob/3083815933c2d015d03417986f57bd25786d58ce/com.salesforce.ide.apex.core/src/apex/jorje/semantic/common/TestAccessEvaluator.java + +/* + * Copyright 2016 salesforce.com, inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package net.sourceforge.pmd.lang.apex.ast; + +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; + +import apex.jorje.semantic.compiler.Namespace; +import apex.jorje.semantic.compiler.StructuredVersion; +import apex.jorje.semantic.compiler.sfdc.AccessEvaluator; +import apex.jorje.semantic.compiler.sfdc.PlaceholderOrgPerm; +import apex.jorje.semantic.symbol.type.SObjectTypeInfo; +import apex.jorje.semantic.symbol.type.StandardTypeInfo; +import apex.jorje.semantic.symbol.type.StandardTypeInfoImpl; +import apex.jorje.semantic.symbol.type.TypeInfo; +import com.google.common.collect.HashMultimap; +import com.google.common.collect.Iterables; +import com.google.common.collect.SetMultimap; + +/** + * For now everything returns false. + * If you actually need to override something, it would be easier to probably mock and adjust what you needed. + * Otherwise this is simply to create a concrete representation and not force a mockito init. + * + * @author jspagnola + */ +public class TestAccessEvaluator implements AccessEvaluator { + + private final SetMultimap<Namespace, StructuredVersion> validPageVersions; + private final SetMultimap<SObjectTypeInfo, TypeInfo> visibleSetupEntitiesToTypes; + private final Set<Namespace> accessibleSystemNamespaces; + private final Set<PlaceholderOrgPerm> orgPerm; + private final Set<AllowedPermGuard> allowedPermGuards; + private final Set<Namespace> reservedNamespaces; + private final Set<String> globalComponents; + private final Set<Namespace> managedPackagesNotInstalled; + private final Set<String> typesWithConnectApiDeserializers; + private boolean hasInternalSfdc; + private boolean isRunningTests; + private boolean hasPrivateApi; + private boolean isTrustedApplication; + private boolean hasLocalizedTranslation; + private boolean isSfdc; + private boolean isReallyRunningTests; + private boolean hasApexGenericTypes; + private boolean hasRemoteActionPerm; + + public TestAccessEvaluator() { + validPageVersions = HashMultimap.create(); + visibleSetupEntitiesToTypes = HashMultimap.create(); + managedPackagesNotInstalled = new HashSet<>(); + accessibleSystemNamespaces = new HashSet<>(); + orgPerm = new HashSet<>(); + allowedPermGuards = new HashSet<>(); + reservedNamespaces = new HashSet<>(); + globalComponents = new HashSet<>(); + typesWithConnectApiDeserializers = new HashSet<>(); + hasRemoteActionPerm = true; + } + + @Override + public boolean hasPermission(final PlaceholderOrgPerm orgPerm) { + return this.orgPerm.contains(orgPerm); + } + + @Override + public boolean hasPermissionForPermGuard(final Namespace referencingNamespace, final String orgPerm) { + return allowedPermGuards.contains(new AllowedPermGuard(referencingNamespace, orgPerm)); + } + + @Override + public boolean hasPrivateApi() { + return hasPrivateApi; + } + + @Override + public boolean hasLocalizedTranslation() { + return hasLocalizedTranslation; + } + + @Override + public boolean hasInternalSfdc() { + return hasInternalSfdc; + } + + @Override + public boolean isTrustedApplication() { + return isTrustedApplication; + } + + @Override + public boolean isReservedNamespace(final Namespace namespace) { + return reservedNamespaces.contains(namespace); + } + + @Override + public boolean isReservedNamespace(final Namespace namespace, final boolean excludePackages) { + return reservedNamespaces.contains(namespace); + } + + /** + * See {@link #isAccessibleOrTrustedNamespace(Namespace)} + */ + @Override + public boolean isAccessibleSystemNamespace(final Namespace namespace) { + return accessibleSystemNamespaces.contains(namespace); + } + + /** + * Okay so this check and its partner isAccessibleSystemNamespace are used slightly differently. + * This is like a black list check, that prevents referencing code from seeing things in a reserved namespace. + * The other check allows code to see certain things if the code's namespace is a reserved namespace. + * <p> + * Hence here we return true by default, and the {@link #isAccessibleSystemNamespace(Namespace)} returns false + * by default. + */ + @Override + public boolean isAccessibleOrTrustedNamespace(final Namespace namespace) { + return true; + } + + @Override + public boolean isRunningTests() { + return isRunningTests; + } + + @Override + public boolean isReallyRunningTests() { + return isReallyRunningTests; + } + + @Override + public boolean isSfdc() { + return isSfdc; + } + + @Override + public boolean hasApexParameterizedTypes() { + return hasApexGenericTypes; + } + + @Override + public boolean isValidPackageVersion(final Namespace namespace, final StructuredVersion version) { + return validPageVersions.containsEntry(namespace, version); + } + + /** + * @return 'true' for everything EXCEPT namespaces you've added through {@link #addManagedPackageNotInstalled(Namespace)} + */ + @Override + public boolean isManagedPackageInstalled(final Namespace namespace) { + return !managedPackagesNotInstalled.contains(namespace); + } + + @Override + public boolean isSetupEntityVisibleToType(final SObjectTypeInfo type, final TypeInfo referencingType) { + final TypeInfo visibleReferencingType = Iterables.getFirst(visibleSetupEntitiesToTypes.get(type), null); + return visibleReferencingType != null + && visibleReferencingType.getBytecodeName().equals(referencingType.getBytecodeName()); + } + + @Override + public boolean hasConnectDeserializer(final TypeInfo type) { + return typesWithConnectApiDeserializers.contains(type.getApexName()); + } + + @Override + public boolean hasRemoteAction(final TypeInfo type) { + return false; + } + + @Override + public boolean hasRemoteActionPerm() { + return hasRemoteActionPerm; + } + + @Override + public boolean isGlobalComponent(final TypeInfo type) { + return globalComponents.contains(type.getApexName()); + } + + /** + * Things isManagedPackageInstalled will say 'false' to. + */ + public TestAccessEvaluator addManagedPackageNotInstalled(final Namespace namespace) { + managedPackagesNotInstalled.add(namespace); + return this; + } + + public TestAccessEvaluator addReservedNamespace(final Namespace namespace) { + reservedNamespaces.add(namespace); + return this; + } + + public TestAccessEvaluator addPermission(final PlaceholderOrgPerm orgPerm) { + this.orgPerm.add(orgPerm); + return this; + } + + public TestAccessEvaluator setHasInternalSfdc(final boolean hasInternalSfdc) { + this.hasInternalSfdc = hasInternalSfdc; + return this; + } + + public TestAccessEvaluator addValidPackageVersion(final Namespace namespace, final StructuredVersion version) { + validPageVersions.put(namespace, version); + return this; + } + + public TestAccessEvaluator addSetupEntityVisibleToType( + final SObjectTypeInfo type, + final String typeName + ) { + final StandardTypeInfo typeInfo = StandardTypeInfoImpl.builder() + .setApexName(typeName) + .setBytecodeName(typeName) + .buildResolved(); + visibleSetupEntitiesToTypes.put(type, typeInfo); + return this; + } + + public TestAccessEvaluator setIsRunningTests(final boolean isRunningTests) { + this.isRunningTests = isRunningTests; + return this; + } + + public TestAccessEvaluator setHasPrivateApi(final boolean hasPrivateApi) { + this.hasPrivateApi = hasPrivateApi; + return this; + } + + public TestAccessEvaluator setIsTrustedApplication(final boolean isTrustedApplication) { + this.isTrustedApplication = isTrustedApplication; + return this; + } + + public TestAccessEvaluator setHasLocalizedTranslation(final boolean hasLocalizedTranslation) { + this.hasLocalizedTranslation = hasLocalizedTranslation; + return this; + } + + public TestAccessEvaluator setIsSfdc(final boolean isSfdc) { + this.isSfdc = isSfdc; + return this; + } + + public TestAccessEvaluator setIsReallyRunningTests(final boolean isReallyRunningTests) { + this.isReallyRunningTests = isReallyRunningTests; + return this; + } + + public TestAccessEvaluator setAccessibleSystemNamespace(final Namespace namespace) { + accessibleSystemNamespaces.add(namespace); + return this; + } + + public TestAccessEvaluator setHasApexGenericType(final boolean hasApexGenericTypes) { + this.hasApexGenericTypes = hasApexGenericTypes; + return this; + } + + public TestAccessEvaluator allowPermGuard(final Namespace namespace, final String permGuard) { + allowedPermGuards.add(new AllowedPermGuard(namespace, permGuard)); + return this; + } + + /** + * It appears that remote action is enabled by default in most orgs, at least test orgs. + * So we will behave the same. + */ + public TestAccessEvaluator setHasRemoteActionPerm(final boolean hasRemoteActionPerm) { + this.hasRemoteActionPerm = hasRemoteActionPerm; + return this; + } + + public TestAccessEvaluator setTypeWithConnectApiDeserializer(final String typeName) { + typesWithConnectApiDeserializers.add(typeName); + return this; + } + + public void setGlobalComponent(final String globalComponent) { + globalComponents.add(globalComponent); + } + + private static class AllowedPermGuard { + private final Namespace referencingNamespace; + private final String permGuard; + + AllowedPermGuard(final Namespace namespace, final String permGuard) { + referencingNamespace = namespace; + this.permGuard = permGuard; + } + + @Override + public int hashCode() { + return Objects.hash(referencingNamespace, permGuard); + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + if (obj == null || getClass() != obj.getClass()) { + return false; + } + + final AllowedPermGuard other = (AllowedPermGuard) obj; + return Objects.equals(referencingNamespace, other.referencingNamespace) + && Objects.equals(permGuard, other.permGuard); + } + } +} diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/TestQueryValidators.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/TestQueryValidators.java new file mode 100644 index 000000000..96ee968a9 --- /dev/null +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/TestQueryValidators.java @@ -0,0 +1,83 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ +// Note: taken from https://github.com/forcedotcom/idecore/blob/3083815933c2d015d03417986f57bd25786d58ce/com.salesforce.ide.apex.core/src/apex/jorje/semantic/common/TestQueryValidators.java + +/* + * Copyright 2016 salesforce.com, inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package net.sourceforge.pmd.lang.apex.ast; + +import apex.jorje.semantic.ast.expression.SoqlExpression; +import apex.jorje.semantic.ast.expression.SoslExpression; +import apex.jorje.semantic.ast.visitor.ValidationScope; +import apex.jorje.semantic.compiler.sfdc.QueryValidator; +import apex.jorje.semantic.symbol.resolver.SymbolResolver; + +/** + * The test query validators will return back the query it was given. The real implementation actually creates its own + * query. + * + * @author jspagnola + */ +public final class TestQueryValidators { + + private TestQueryValidators() { + } + + public static class Noop implements QueryValidator { + @Override + public String validateSoql( + final SymbolResolver symbols, + final ValidationScope scope, + final SoqlExpression soql + ) { + return soql.getCanonicalQuery(); + } + + @Override + public String validateSosl( + final SymbolResolver symbols, + final ValidationScope typeInfo, + final SoslExpression sosl + ) { + return sosl.getCanonicalQuery(); + } + } + + public static class Error implements QueryValidator { + @Override + public String validateSoql( + final SymbolResolver symbols, + final ValidationScope scope, + final SoqlExpression soql + ) { + scope.getErrors().markInvalid(soql, "Bad Soql"); + return soql.getCanonicalQuery(); + } + + @Override + public String validateSosl( + final SymbolResolver symbols, + final ValidationScope scope, + final SoslExpression sosl + ) { + scope.getErrors().markInvalid(sosl, "Bad Sosl"); + return sosl.getCanonicalQuery(); + } + } +} diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/AbstractApexRule.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/AbstractApexRule.java index b51210428..0984252fb 100644 --- a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/AbstractApexRule.java +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/AbstractApexRule.java @@ -12,6 +12,7 @@ import net.sourceforge.pmd.lang.apex.ApexLanguageModule; import net.sourceforge.pmd.lang.apex.ApexParserOptions; import net.sourceforge.pmd.lang.apex.ast.ASTAnnotation; +import net.sourceforge.pmd.lang.apex.ast.ASTAnnotationParameter; import net.sourceforge.pmd.lang.apex.ast.ASTAnonymousClass; import net.sourceforge.pmd.lang.apex.ast.ASTArrayLoadExpression; import net.sourceforge.pmd.lang.apex.ast.ASTArrayStoreExpression; @@ -22,8 +23,10 @@ import net.sourceforge.pmd.lang.apex.ast.ASTBooleanExpression; import net.sourceforge.pmd.lang.apex.ast.ASTBreakStatement; import net.sourceforge.pmd.lang.apex.ast.ASTBridgeMethodCreator; +import net.sourceforge.pmd.lang.apex.ast.ASTCastExpression; import net.sourceforge.pmd.lang.apex.ast.ASTCatchBlockStatement; import net.sourceforge.pmd.lang.apex.ast.ASTClassRefExpression; +import net.sourceforge.pmd.lang.apex.ast.ASTConstructorPreamble; import net.sourceforge.pmd.lang.apex.ast.ASTConstructorPreambleStatement; import net.sourceforge.pmd.lang.apex.ast.ASTContinueStatement; import net.sourceforge.pmd.lang.apex.ast.ASTDmlDeleteStatement; @@ -33,7 +36,6 @@ import net.sourceforge.pmd.lang.apex.ast.ASTDmlUpdateStatement; import net.sourceforge.pmd.lang.apex.ast.ASTDmlUpsertStatement; import net.sourceforge.pmd.lang.apex.ast.ASTDoLoopStatement; -import net.sourceforge.pmd.lang.apex.ast.ASTDottedExpression; import net.sourceforge.pmd.lang.apex.ast.ASTExpression; import net.sourceforge.pmd.lang.apex.ast.ASTExpressionStatement; import net.sourceforge.pmd.lang.apex.ast.ASTField; @@ -43,20 +45,26 @@ import net.sourceforge.pmd.lang.apex.ast.ASTForLoopStatement; import net.sourceforge.pmd.lang.apex.ast.ASTIfBlockStatement; import net.sourceforge.pmd.lang.apex.ast.ASTIfElseBlockStatement; +import net.sourceforge.pmd.lang.apex.ast.ASTIllegalStoreExpression; import net.sourceforge.pmd.lang.apex.ast.ASTInstanceOfExpression; import net.sourceforge.pmd.lang.apex.ast.ASTJavaMethodCallExpression; import net.sourceforge.pmd.lang.apex.ast.ASTJavaVariableExpression; import net.sourceforge.pmd.lang.apex.ast.ASTLiteralExpression; import net.sourceforge.pmd.lang.apex.ast.ASTMapEntryNode; import net.sourceforge.pmd.lang.apex.ast.ASTMethod; +import net.sourceforge.pmd.lang.apex.ast.ASTMethodBlockStatement; import net.sourceforge.pmd.lang.apex.ast.ASTMethodCallExpression; +import net.sourceforge.pmd.lang.apex.ast.ASTModifier; import net.sourceforge.pmd.lang.apex.ast.ASTModifierNode; import net.sourceforge.pmd.lang.apex.ast.ASTModifierOrAnnotation; +import net.sourceforge.pmd.lang.apex.ast.ASTMultiStatement; +import net.sourceforge.pmd.lang.apex.ast.ASTNestedExpression; +import net.sourceforge.pmd.lang.apex.ast.ASTNestedStoreExpression; +import net.sourceforge.pmd.lang.apex.ast.ASTNewKeyValueObjectExpression; import net.sourceforge.pmd.lang.apex.ast.ASTNewListInitExpression; import net.sourceforge.pmd.lang.apex.ast.ASTNewListLiteralExpression; import net.sourceforge.pmd.lang.apex.ast.ASTNewMapInitExpression; import net.sourceforge.pmd.lang.apex.ast.ASTNewMapLiteralExpression; -import net.sourceforge.pmd.lang.apex.ast.ASTNewNameValueObjectExpression; import net.sourceforge.pmd.lang.apex.ast.ASTNewObjectExpression; import net.sourceforge.pmd.lang.apex.ast.ASTNewSetInitExpression; import net.sourceforge.pmd.lang.apex.ast.ASTNewSetLiteralExpression; @@ -72,10 +80,10 @@ import net.sourceforge.pmd.lang.apex.ast.ASTSoslExpression; import net.sourceforge.pmd.lang.apex.ast.ASTStandardCondition; import net.sourceforge.pmd.lang.apex.ast.ASTStatement; +import net.sourceforge.pmd.lang.apex.ast.ASTStatementExecuted; import net.sourceforge.pmd.lang.apex.ast.ASTSuperMethodCallExpression; import net.sourceforge.pmd.lang.apex.ast.ASTSuperVariableExpression; import net.sourceforge.pmd.lang.apex.ast.ASTTernaryExpression; -import net.sourceforge.pmd.lang.apex.ast.ASTTestNode; import net.sourceforge.pmd.lang.apex.ast.ASTThisMethodCallExpression; import net.sourceforge.pmd.lang.apex.ast.ASTThisVariableExpression; import net.sourceforge.pmd.lang.apex.ast.ASTThrowStatement; @@ -345,11 +353,6 @@ public Object visit(ASTDmlUpsertStatement node, Object data) { return visit((ApexNode<?>) node, data); } - @Override - public Object visit(ASTDottedExpression node, Object data) { - return visit((ApexNode<?>) node, data); - } - @Override public Object visit(ASTExpression node, Object data) { return visit((ApexNode<?>) node, data); @@ -415,11 +418,6 @@ public Object visit(ASTNewMapLiteralExpression node, Object data) { return visit((ApexNode<?>) node, data); } - @Override - public Object visit(ASTNewNameValueObjectExpression node, Object data) { - return visit((ApexNode<?>) node, data); - } - @Override public Object visit(ASTNewObjectExpression node, Object data) { return visit((ApexNode<?>) node, data); @@ -490,11 +488,6 @@ public Object visit(ASTSuperVariableExpression node, Object data) { return visit((ApexNode<?>) node, data); } - @Override - public Object visit(ASTTestNode node, Object data) { - return visit((ApexNode<?>) node, data); - } - @Override public Object visit(ASTThisMethodCallExpression node, Object data) { return visit((ApexNode<?>) node, data); @@ -534,4 +527,59 @@ public Object visit(ASTVariableDeclarationStatements node, Object data) { public Object visit(ASTVariableExpression node, Object data) { return visit((ApexNode<?>) node, data); } + + @Override + public Object visit(ASTAnnotationParameter node, Object data) { + return visit((ApexNode<?>) node, data); + } + + @Override + public Object visit(ASTCastExpression node, Object data) { + return visit((ApexNode<?>) node, data); + } + + @Override + public Object visit(ASTConstructorPreamble node, Object data) { + return visit((ApexNode<?>) node, data); + } + + @Override + public Object visit(ASTIllegalStoreExpression node, Object data) { + return visit((ApexNode<?>) node, data); + } + + @Override + public Object visit(ASTMethodBlockStatement node, Object data) { + return visit((ApexNode<?>) node, data); + } + + @Override + public Object visit(ASTModifier node, Object data) { + return visit((ApexNode<?>) node, data); + } + + @Override + public Object visit(ASTMultiStatement node, Object data) { + return visit((ApexNode<?>) node, data); + } + + @Override + public Object visit(ASTNestedExpression node, Object data) { + return visit((ApexNode<?>) node, data); + } + + @Override + public Object visit(ASTNestedStoreExpression node, Object data) { + return visit((ApexNode<?>) node, data); + } + + @Override + public Object visit(ASTNewKeyValueObjectExpression node, Object data) { + return visit((ApexNode<?>) node, data); + } + + @Override + public Object visit(ASTStatementExecuted node, Object data) { + return visit((ApexNode<?>) node, data); + } } diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/security/ApexCRUDViolationRule.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/security/ApexCRUDViolationRule.java index 959e11fb0..0cc1d4634 100644 --- a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/security/ApexCRUDViolationRule.java +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/security/ApexCRUDViolationRule.java @@ -25,14 +25,13 @@ import net.sourceforge.pmd.lang.apex.ast.ASTDmlMergeStatement; import net.sourceforge.pmd.lang.apex.ast.ASTDmlUpdateStatement; import net.sourceforge.pmd.lang.apex.ast.ASTDmlUpsertStatement; -import net.sourceforge.pmd.lang.apex.ast.ASTDottedExpression; import net.sourceforge.pmd.lang.apex.ast.ASTField; import net.sourceforge.pmd.lang.apex.ast.ASTFieldDeclaration; import net.sourceforge.pmd.lang.apex.ast.ASTFieldDeclarationStatements; import net.sourceforge.pmd.lang.apex.ast.ASTIfElseBlockStatement; import net.sourceforge.pmd.lang.apex.ast.ASTMethod; import net.sourceforge.pmd.lang.apex.ast.ASTMethodCallExpression; -import net.sourceforge.pmd.lang.apex.ast.ASTNewNameValueObjectExpression; +import net.sourceforge.pmd.lang.apex.ast.ASTNewKeyValueObjectExpression; import net.sourceforge.pmd.lang.apex.ast.ASTParameter; import net.sourceforge.pmd.lang.apex.ast.ASTProperty; import net.sourceforge.pmd.lang.apex.ast.ASTReferenceExpression; @@ -326,16 +325,13 @@ private void collectCRUDMethodLevelChecks(final ASTMethodCallExpression node) { } // see if getDescribe() - final ASTDottedExpression dottedExpr = ref.getFirstChildOfType(ASTDottedExpression.class); - if (dottedExpr != null) { - final ASTMethodCallExpression nestedMethodCall = dottedExpr - .getFirstChildOfType(ASTMethodCallExpression.class); - if (nestedMethodCall != null) { - if (isLastMethodName(nestedMethodCall, S_OBJECT_TYPE, GET_DESCRIBE)) { - String resolvedType = getType(nestedMethodCall); - if (!typeToDMLOperationMapping.get(resolvedType).contains(method)) { - typeToDMLOperationMapping.put(resolvedType, method); - } + final ASTMethodCallExpression nestedMethodCall = ref + .getFirstChildOfType(ASTMethodCallExpression.class); + if (nestedMethodCall != null) { + if (isLastMethodName(nestedMethodCall, S_OBJECT_TYPE, GET_DESCRIBE)) { + String resolvedType = getType(nestedMethodCall); + if (!typeToDMLOperationMapping.get(resolvedType).contains(method)) { + typeToDMLOperationMapping.put(resolvedType, method); } } } @@ -392,7 +388,7 @@ private void checkForCRUD(final AbstractApexNode<?> node, final Object data, fin return; } - final ASTNewNameValueObjectExpression newObj = node.getFirstChildOfType(ASTNewNameValueObjectExpression.class); + final ASTNewKeyValueObjectExpression newObj = node.getFirstChildOfType(ASTNewKeyValueObjectExpression.class); if (newObj != null) { final String type = Helper.getFQVariableName(newObj); validateCRUDCheckPresent(node, data, crudMethod, type); diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/security/Helper.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/security/Helper.java index c1a0869c8..9a266138f 100644 --- a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/security/Helper.java +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/security/Helper.java @@ -14,12 +14,11 @@ import net.sourceforge.pmd.lang.apex.ast.ASTDmlUndeleteStatement; import net.sourceforge.pmd.lang.apex.ast.ASTDmlUpdateStatement; import net.sourceforge.pmd.lang.apex.ast.ASTDmlUpsertStatement; -import net.sourceforge.pmd.lang.apex.ast.ASTDottedExpression; import net.sourceforge.pmd.lang.apex.ast.ASTField; import net.sourceforge.pmd.lang.apex.ast.ASTFieldDeclaration; import net.sourceforge.pmd.lang.apex.ast.ASTMethodCallExpression; import net.sourceforge.pmd.lang.apex.ast.ASTModifierNode; -import net.sourceforge.pmd.lang.apex.ast.ASTNewNameValueObjectExpression; +import net.sourceforge.pmd.lang.apex.ast.ASTNewKeyValueObjectExpression; import net.sourceforge.pmd.lang.apex.ast.ASTReferenceExpression; import net.sourceforge.pmd.lang.apex.ast.ASTSoqlExpression; import net.sourceforge.pmd.lang.apex.ast.ASTSoslExpression; @@ -32,7 +31,7 @@ import apex.jorje.data.ast.TypeRef; import apex.jorje.data.ast.TypeRef.ClassTypeRef; import apex.jorje.semantic.ast.expression.MethodCallExpression; -import apex.jorje.semantic.ast.expression.NewNameValueObjectExpression; +import apex.jorje.semantic.ast.expression.NewKeyValueObjectExpression; import apex.jorje.semantic.ast.expression.VariableExpression; import apex.jorje.semantic.ast.member.Field; import apex.jorje.semantic.ast.member.Parameter; @@ -132,21 +131,17 @@ static boolean isMethodCallChain(final ASTMethodCallExpression methodNode, final if (Helper.isMethodName(methodNode, methodName)) { final ASTReferenceExpression reference = methodNode.getFirstChildOfType(ASTReferenceExpression.class); if (reference != null) { - final ASTDottedExpression dottedExpression = reference.getFirstChildOfType(ASTDottedExpression.class); - if (dottedExpression != null) { - final ASTMethodCallExpression nestedMethod = dottedExpression - .getFirstChildOfType(ASTMethodCallExpression.class); - if (nestedMethod != null) { - String[] newMethodNames = Arrays.copyOf(methodNames, methodNames.length - 1); - return isMethodCallChain(nestedMethod, newMethodNames); - } else { - String[] newClassName = Arrays.copyOf(methodNames, methodNames.length - 1); - if (newClassName.length == 1) { - return Helper.isMethodName(methodNode, newClassName[0], methodName); - } + final ASTMethodCallExpression nestedMethod = reference + .getFirstChildOfType(ASTMethodCallExpression.class); + if (nestedMethod != null) { + String[] newMethodNames = Arrays.copyOf(methodNames, methodNames.length - 1); + return isMethodCallChain(nestedMethod, newMethodNames); + } else { + String[] newClassName = Arrays.copyOf(methodNames, methodNames.length - 1); + if (newClassName.length == 1) { + return Helper.isMethodName(methodNode, newClassName[0], methodName); } } - } } @@ -206,8 +201,8 @@ static String getFQVariableName(final ASTFieldDeclaration variable) { return sb.toString(); } - static String getFQVariableName(final ASTNewNameValueObjectExpression variable) { - NewNameValueObjectExpression n = variable.getNode(); + static String getFQVariableName(final ASTNewKeyValueObjectExpression variable) { + NewKeyValueObjectExpression n = variable.getNode(); String objType = ""; try { // no other way to get this field, Apex Jorje does not expose it diff --git a/pmd-apex/src/main/resources/rulesets/apex/braces.xml b/pmd-apex/src/main/resources/rulesets/apex/braces.xml index 64a126c35..1e0dd4319 100644 --- a/pmd-apex/src/main/resources/rulesets/apex/braces.xml +++ b/pmd-apex/src/main/resources/rulesets/apex/braces.xml @@ -24,7 +24,7 @@ controlled from the rest. <property name="xpath"> <value> <![CDATA[ -//IfBlockStatement/ExpressionStatement +//IfBlockStatement/BlockStatement[@CurlyBrace='false'] ]]> </value> </property> @@ -59,7 +59,7 @@ controlled from the rest. <property name="xpath"> <value> <![CDATA[ -//WhileLoopStatement/ExpressionStatement +//WhileLoopStatement/BlockStatement[@CurlyBrace='false'] ]]> </value> </property> @@ -92,11 +92,9 @@ from the rest. <property name="xpath"> <value> <![CDATA[ -//ExpressionStatement[parent::IfBlockStatement] +//IfBlockStatement/BlockStatement[@CurlyBrace='false'][count(child::*) > 0] | -//ExpressionStatement[parent::IfElseBlockStatement] -| -//IfElseBlockStatement[parent::IfBlockStatement] +//IfElseBlockStatement/BlockStatement[@CurlyBrace='false'][count(child::*) > 0] ]]> </value> </property> @@ -131,11 +129,9 @@ from the rest. <property name="xpath"> <value> <![CDATA[ -//ForLoopStatement -[child::ExpressionStatement] +//ForLoopStatement/BlockStatement[@CurlyBrace='false'] | -//ForEachStatement -[child::ExpressionStatement] +//ForEachStatement/BlockStatement[@CurlyBrace='false'] ]]> </value> </property> diff --git a/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/ast/ApexCompilerSoqlTest.java b/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/ast/ApexCompilerSoqlTest.java new file mode 100644 index 000000000..6a7c763e9 --- /dev/null +++ b/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/ast/ApexCompilerSoqlTest.java @@ -0,0 +1,30 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.apex.ast; + +import java.io.StringReader; + +import org.junit.Assert; +import org.junit.Test; + +import net.sourceforge.pmd.lang.apex.ApexParserOptions; + +import apex.jorje.semantic.ast.compilation.Compilation; + +public class ApexCompilerSoqlTest { + + private static final String CODE = "public class Foo {\n" + + " public List<SObject> test1() {\n" + + " return Database.query(\'Select Id from Account LIMIT 100\');\n" + + " }\n" + + "}\n"; + + @Test + public void testSoqlCompilation() { + ApexParser parser = new ApexParser(new ApexParserOptions()); + ApexNode<Compilation> cu = parser.parse(new StringReader(CODE)); + Assert.assertNotNull(cu); + } +} diff --git a/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/ast/ApexLexerTest.java b/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/ast/ApexLexerTest.java new file mode 100644 index 000000000..9f767b176 --- /dev/null +++ b/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/ast/ApexLexerTest.java @@ -0,0 +1,48 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.apex.ast; + +import org.antlr.runtime.ANTLRStringStream; +import org.antlr.runtime.CharStream; +import org.antlr.runtime.CommonTokenStream; +import org.antlr.runtime.Token; +import org.junit.Assert; +import org.junit.Test; + +import apex.jorje.data.ast.CompilationUnit; +import apex.jorje.parser.impl.ApexLexer; +import apex.jorje.parser.impl.ApexParser; + +public class ApexLexerTest { + + private static final String CODE = "public class Foo {\n" + + " public List<SObject> test1() {\n" + + " return Database.query(\"Select Id from Account LIMIT 100\");\n" + + " }\n" + + "}\n"; + + @Test + public void testLexer() throws Exception { + CharStream in = new ANTLRStringStream(CODE); + ApexLexer lexer = new ApexLexer(in); + + Token token = lexer.nextToken(); + int tokenCount = 0; + while (token.getType() != Token.EOF) { + tokenCount++; + token = lexer.nextToken(); + } + Assert.assertEquals(43, tokenCount); + } + + @Test + public void testParser() throws Exception { + CharStream in = new ANTLRStringStream(CODE); + ApexLexer lexer = new ApexLexer(in); + ApexParser parser = new ApexParser(new CommonTokenStream(lexer)); + CompilationUnit compilationUnit = parser.compilationUnit(); + Assert.assertNotNull(compilationUnit); + } +} diff --git a/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/ast/ApexParserTest.java b/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/ast/ApexParserTest.java index b65014a70..1e4498b82 100644 --- a/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/ast/ApexParserTest.java +++ b/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/ast/ApexParserTest.java @@ -121,7 +121,7 @@ public void parsesRealWorldClasses() throws Exception { /** * See bug #1485 * - * @see <a href="https://sourceforge.net/p/pmd/bugs/1485/">#1485</a> + * @see <a href="https://sourceforge.net/p/pmd/bugs/1485/">#1485 [apex] Analysis of some apex classes cause a stackoverflow error</a> */ @Test public void stackOverflowDuringClassParsing() throws Exception { @@ -130,7 +130,7 @@ public void stackOverflowDuringClassParsing() throws Exception { Assert.assertNotNull(rootNode); int count = visitPosition(rootNode, 0); - Assert.assertEquals(586, count); + Assert.assertEquals(489, count); } private int visitPosition(Node node, int count) { diff --git a/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/braces/xml/ForLoopsMustUseBraces.xml b/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/braces/xml/ForLoopsMustUseBraces.xml index 8354c4c2b..e71bbb5a8 100644 --- a/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/braces/xml/ForLoopsMustUseBraces.xml +++ b/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/braces/xml/ForLoopsMustUseBraces.xml @@ -31,7 +31,7 @@ public class Foo { </test-code> <test-code> <description><![CDATA[ - +for loop without increment ]]></description> <expected-problems>1</expected-problems> <code><![CDATA[ @@ -45,7 +45,7 @@ public class Foo { </test-code> <test-code> <description><![CDATA[ - +for loop without condition and increment ]]></description> <expected-problems>1</expected-problems> <code><![CDATA[ @@ -59,7 +59,7 @@ public class Foo { </test-code> <test-code> <description><![CDATA[ - +for loop without initializer, condition and increment ]]></description> <expected-problems>1</expected-problems> <code><![CDATA[ @@ -72,9 +72,7 @@ public class Foo { ]]></code> </test-code> <test-code> - <description><![CDATA[ -for-each - ]]></description> + <description>for-each, not ok</description> <expected-problems>1</expected-problems> <code><![CDATA[ public class Foo { @@ -82,6 +80,19 @@ public class Foo { for (Account a : accounts) foo(); } +} + ]]></code> + </test-code> + <test-code> + <description>for-each, ok</description> + <expected-problems>0</expected-problems> + <code><![CDATA[ +public class Foo { + void foo() { + for (Account a : accounts) { + foo(); + } + } } ]]></code> </test-code> diff --git a/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/complexity/xml/TooManyFields.xml b/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/complexity/xml/TooManyFields.xml index 4e0dee61e..6021b7356 100644 --- a/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/complexity/xml/TooManyFields.xml +++ b/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/complexity/xml/TooManyFields.xml @@ -82,11 +82,11 @@ public class Foo { Integer a4; Integer a5; interface Bar { - Integer a6; - Integer a7; - Integer a8; - Integer a9; - Integer a10; + static Integer a6; + static Integer a7; + static Integer a8; + static Integer a9; + static Integer a10; } } ]]></code> diff --git a/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/security/xml/ApexSharingViolations.xml b/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/security/xml/ApexSharingViolations.xml index 83777fd2a..fb70ffd08 100644 --- a/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/security/xml/ApexSharingViolations.xml +++ b/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/security/xml/ApexSharingViolations.xml @@ -49,7 +49,7 @@ public class Foo { <code><![CDATA[ public class Foo { public List<SObject> test1() { - return Database.query("Select Id from Account LIMIT 100"); + return Database.query('Select Id from Account LIMIT 100'); } } ]]></code> @@ -62,7 +62,7 @@ public class Foo { <code><![CDATA[ public with sharing class Foo { public List<SObject> test1() { - return Database.query("Select Id from Account LIMIT 100"); + return Database.query('Select Id from Account LIMIT 100'); } } ]]></code> diff --git a/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/style/xml/VariableNamingConventions.xml b/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/style/xml/VariableNamingConventions.xml index 929c4c808..4a058a6ae 100644 --- a/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/style/xml/VariableNamingConventions.xml +++ b/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/style/xml/VariableNamingConventions.xml @@ -280,17 +280,7 @@ public class X { } ]]></code> </test-code> - - <test-code> - <description>#1399 False positive for VariableNamingConventions with annotation @interface</description> - <expected-problems>0</expected-problems> - <code><![CDATA[ -public @interface Relation { - Integer NO_REVERSE_RELATION = -1; -} - ]]></code> - </test-code> - + <test-code> <description>Inner exception</description> <expected-problems>0</expected-problems> 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 79026d54a..599957bc3 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java @@ -567,6 +567,10 @@ private void parseSingleRuleNode(RuleSetReferenceId ruleSetReferenceId, RuleSetB rule.setRuleSetName(ruleSetBuilder.getName()); rule.setExternalInfoUrl(ruleElement.getAttribute(EXTERNAL_INFO_URL)); + if (hasAttributeSetTrue(ruleElement, "deprecated")) { + rule.setDeprecated(true); + } + if (hasAttributeSetTrue(ruleElement, "dfa")) { rule.setUsesDFA(); } 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 3623e0137..4d2452a2f 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryTest.java @@ -431,6 +431,7 @@ public void testInvertedMinimumMaximumLanugageVersions() throws RuleSetNotFoundE public void testDirectDeprecatedRule() throws RuleSetNotFoundException { Rule r = loadFirstRule(DIRECT_DEPRECATED_RULE); assertNotNull("Direct Deprecated Rule", r); + assertTrue(r.isDeprecated()); } @Test diff --git a/pmd-doc/pom.xml b/pmd-doc/pom.xml new file mode 100644 index 000000000..23c78952a --- /dev/null +++ b/pmd-doc/pom.xml @@ -0,0 +1,56 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + <artifactId>pmd-doc</artifactId> + <name>PMD Documentation Generator</name> + <packaging>jar</packaging> + + <parent> + <groupId>net.sourceforge.pmd</groupId> + <artifactId>pmd</artifactId> + <version>6.0.0-SNAPSHOT</version> + </parent> + + <properties> + <java.version>1.8</java.version> + <config.basedir>${basedir}/../pmd-core</config.basedir> + </properties> + + <build> + <plugins> + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>exec-maven-plugin</artifactId> + <version>1.6.0</version> + <executions> + <execution> + <goals> + <goal>java</goal> + </goals> + <phase>package</phase> + </execution> + </executions> + <configuration> + <mainClass>net.sourceforge.pmd.docs.GenerateRuleDocsCmd</mainClass> + <arguments> + <argument>${project.basedir}</argument> + </arguments> + </configuration> + </plugin> + </plugins> + </build> + + <dependencies> + <dependency> + <groupId>net.sourceforge.pmd</groupId> + <artifactId>pmd-dist</artifactId> + <version>${project.version}</version> + </dependency> + + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <scope>test</scope> + </dependency> + </dependencies> +</project> diff --git a/pmd-doc/src/main/java/net/sourceforge/pmd/docs/DefaultFileWriter.java b/pmd-doc/src/main/java/net/sourceforge/pmd/docs/DefaultFileWriter.java new file mode 100644 index 000000000..5c7e84ad2 --- /dev/null +++ b/pmd-doc/src/main/java/net/sourceforge/pmd/docs/DefaultFileWriter.java @@ -0,0 +1,19 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.docs; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; + +public class DefaultFileWriter implements FileWriter { + @Override + public void write(Path path, List<String> lines) throws IOException { + Files.createDirectories(path.getParent()); + Files.write(path, lines, StandardCharsets.UTF_8); + } +} diff --git a/pmd-doc/src/main/java/net/sourceforge/pmd/docs/FileWriter.java b/pmd-doc/src/main/java/net/sourceforge/pmd/docs/FileWriter.java new file mode 100644 index 000000000..d47452754 --- /dev/null +++ b/pmd-doc/src/main/java/net/sourceforge/pmd/docs/FileWriter.java @@ -0,0 +1,14 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.docs; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.List; + +public interface FileWriter { + + void write(Path path, List<String> lines) throws IOException; +} 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 new file mode 100644 index 000000000..64f04bc96 --- /dev/null +++ b/pmd-doc/src/main/java/net/sourceforge/pmd/docs/GenerateRuleDocsCmd.java @@ -0,0 +1,32 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.docs; + +import java.nio.file.FileSystems; +import java.nio.file.Path; +import java.util.Iterator; + +import net.sourceforge.pmd.RuleSet; +import net.sourceforge.pmd.RuleSetFactory; +import net.sourceforge.pmd.RuleSetNotFoundException; + +public class GenerateRuleDocsCmd { + private GenerateRuleDocsCmd() { + // Utility class + } + + public static void main(String[] args) throws RuleSetNotFoundException { + long start = System.currentTimeMillis(); + Path output = FileSystems.getDefault().getPath(args[0]).resolve("..").toAbsolutePath().normalize(); + System.out.println("Generating docs into " + output); + RuleDocGenerator generator = new RuleDocGenerator(new DefaultFileWriter(), output); + + RuleSetFactory ruleSetFactory = new RuleSetFactory(); + Iterator<RuleSet> registeredRuleSets = ruleSetFactory.getRegisteredRuleSets(); + + generator.generate(registeredRuleSets); + System.out.println("Generated docs in " + (System.currentTimeMillis() - start) + " ms"); + } +} 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 new file mode 100644 index 000000000..5eb70b5fa --- /dev/null +++ b/pmd-doc/src/main/java/net/sourceforge/pmd/docs/RuleDocGenerator.java @@ -0,0 +1,418 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.docs; + +import java.io.File; +import java.io.IOException; +import java.nio.file.FileVisitResult; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.attribute.BasicFileAttributes; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Objects; + +import org.apache.commons.io.FilenameUtils; +import org.apache.commons.lang3.StringUtils; + +import net.sourceforge.pmd.PropertyDescriptor; +import net.sourceforge.pmd.Rule; +import net.sourceforge.pmd.RuleSet; +import net.sourceforge.pmd.RuleSetNotFoundException; +import net.sourceforge.pmd.lang.Language; +import net.sourceforge.pmd.lang.rule.RuleReference; +import net.sourceforge.pmd.lang.rule.XPathRule; + +public class RuleDocGenerator { + private static final String LANGUAGE_INDEX_FILENAME_PATTERN = "docs/pages/pmd/rules/${language.tersename}.md"; + private static final String LANGUAGE_INDEX_PERMALINK_PATTERN = "pmd_rules_${language.tersename}.html"; + private static final String RULESET_INDEX_FILENAME_PATTERN = "docs/pages/pmd/rules/${language.tersename}/${ruleset.name}.md"; + private static final String RULESET_INDEX_PERMALINK_PATTERN = "pmd_rules_${language.tersename}_${ruleset.name}.html"; + + private static final String DEPRECATION_LABEL_SMALL = "<span style=\"border-radius: 0.25em; color: #fff; padding: 0.2em 0.6em 0.3em; display: inline; background-color: #d9534f; font-size: 75%;\">Deprecated</span> "; + private static final String DEPRECATION_LABEL = "<span style=\"border-radius: 0.25em; color: #fff; padding: 0.2em 0.6em 0.3em; display: inline; background-color: #d9534f;\">Deprecated</span> "; + + private static final String GITHUB_SOURCE_LINK = "https://github.com/pmd/pmd/blob/master/"; + + private final Path root; + private final FileWriter writer; + + public RuleDocGenerator(FileWriter writer, Path root) { + this.root = Objects.requireNonNull(root, "Root directory must be provided"); + this.writer = Objects.requireNonNull(writer, "A file writer must be provided"); + + Path docsDir = root.resolve("docs"); + if (!Files.exists(docsDir) || !Files.isDirectory(docsDir)) { + throw new IllegalArgumentException("Couldn't find \"docs\" subdirectory"); + } + } + + public void generate(Iterator<RuleSet> rulesets) { + Map<Language, List<RuleSet>> sortedRulesets; + try { + sortedRulesets = sortRulesets(rulesets); + generateLanguageIndex(sortedRulesets); + generateRuleSetIndex(sortedRulesets); + + } catch (RuleSetNotFoundException | IOException e) { + throw new RuntimeException(e); + } + } + + private Path getAbsoluteOutputPath(String filename) { + return root.resolve(FilenameUtils.normalize(filename)); + } + + private Map<Language, List<RuleSet>> sortRulesets(Iterator<RuleSet> rulesets) throws RuleSetNotFoundException { + Map<Language, List<RuleSet>> rulesetsByLanguage = new HashMap<>(); + + while (rulesets.hasNext()) { + RuleSet ruleset = rulesets.next(); + Language language = getRuleSetLanguage(ruleset); + + if (!rulesetsByLanguage.containsKey(language)) { + rulesetsByLanguage.put(language, new ArrayList<RuleSet>()); + } + rulesetsByLanguage.get(language).add(ruleset); + } + + for (List<RuleSet> rulesetsOfOneLanguage : rulesetsByLanguage.values()) { + Collections.sort(rulesetsOfOneLanguage, new Comparator<RuleSet>() { + @Override + public int compare(RuleSet o1, RuleSet o2) { + return o1.getName().compareToIgnoreCase(o2.getName()); + } + }); + } + return rulesetsByLanguage; + } + + /** + * Rulesets could potentially contain rules from various languages. + * But for built-in rulesets, all rules within one ruleset belong to + * one language. So we take the language of the first rule. + * @param ruleset + * @return the terse name of the ruleset's language + */ + private static Language getRuleSetLanguage(RuleSet ruleset) { + Collection<Rule> rules = ruleset.getRules(); + if (rules.isEmpty()) { + throw new RuntimeException("Ruleset " + ruleset.getFileName() + " is empty!"); + } + return rules.iterator().next().getLanguage(); + } + + /** + * Writes for each language an index file, which lists the rulesets, the rules + * and links to the ruleset pages. + * @param rulesets all rulesets + * @throws IOException + */ + private void generateLanguageIndex(Map<Language, List<RuleSet>> rulesets) throws IOException { + for (Map.Entry<Language, List<RuleSet>> entry : rulesets.entrySet()) { + String languageTersename = entry.getKey().getTerseName(); + String filename = LANGUAGE_INDEX_FILENAME_PATTERN + .replace("${language.tersename}", languageTersename); + Path path = getAbsoluteOutputPath(filename); + + List<String> lines = new LinkedList<>(); + lines.add("---"); + lines.add("title: " + entry.getKey().getName() + " Rules"); + lines.add("permalink: " + LANGUAGE_INDEX_PERMALINK_PATTERN.replace("${language.tersename}", languageTersename)); + lines.add("folder: pmd/rules"); + lines.add("---"); + + lines.add("List of rulesets and rules contained in each ruleset."); + lines.add(""); + + for (RuleSet ruleset : entry.getValue()) { + String link = RULESET_INDEX_PERMALINK_PATTERN + .replace("${language.tersename}", languageTersename) + .replace("${ruleset.name}", getRuleSetFilename(ruleset)); + lines.add("* [" + ruleset.getName() + "](" + link + "): " + getRuleSetDescriptionSingleLine(ruleset)); + } + lines.add(""); + + for (RuleSet ruleset : entry.getValue()) { + lines.add("## " + ruleset.getName()); + + for (Rule rule : getSortedRules(ruleset)) { + String link = RULESET_INDEX_PERMALINK_PATTERN + .replace("${language.tersename}", languageTersename) + .replace("${ruleset.name}", getRuleSetFilename(ruleset)); + if (rule instanceof RuleReference) { + RuleReference ref = (RuleReference) rule; + if (ruleset.getFileName().equals(ref.getRuleSetReference().getRuleSetFileName())) { + // rule renamed within same ruleset + lines.add("* [" + rule.getName() + "](" + link + "#" + rule.getName().toLowerCase(Locale.ROOT) + "): " + + DEPRECATION_LABEL_SMALL + + "The rule has been renamed. Use instead " + + "[" + ref.getRule().getName() + "](" + link + "#" + ref.getRule().getName().toLowerCase(Locale.ROOT) + ")."); + } else { + // rule moved to another ruleset... + String otherLink = RULESET_INDEX_PERMALINK_PATTERN + .replace("${language.tersename}", languageTersename) + .replace("${ruleset.name}", getRuleSetFilename(ref.getRuleSetReference().getRuleSetFileName())); + lines.add("* [" + rule.getName() + "](" + link + "#" + rule.getName().toLowerCase(Locale.ROOT) + "): " + + DEPRECATION_LABEL_SMALL + + "The rule has been moved to another ruleset. Use instead " + + "[" + ref.getRule().getName() + "](" + otherLink + "#" + ref.getRule().getName().toLowerCase(Locale.ROOT) + ")."); + } + } else { + link += "#" + rule.getName().toLowerCase(Locale.ROOT); + lines.add("* [" + rule.getName() + "](" + link + "): " + + (rule.isDeprecated() ? DEPRECATION_LABEL_SMALL : "") + + getShortRuleDescription(rule)); + } + } + lines.add(""); + } + + System.out.println("Generated " + path); + writer.write(path, lines); + } + } + + /** + * Shortens and escapes (for markdown) some special characters. Otherwise the shortened text + * could contain some unfinished sequences. + * @param rule + * @return + */ + private static String getShortRuleDescription(Rule rule) { + return StringUtils.abbreviate( + StringUtils.stripToEmpty(rule.getDescription().replaceAll("\n|\r", "") + .replaceAll("\\|", "\\\\|") + .replaceAll("`", "'") + .replaceAll("\\*", "")), 100); + } + + /** + * Gets the sanitized base name of the ruleset. + * For some reason, the filename might contain some newlines, which are removed. + * @param ruleset + * @return + */ + private static String getRuleSetFilename(RuleSet ruleset) { + return getRuleSetFilename(ruleset.getFileName()); + } + + private static String getRuleSetFilename(String rulesetFileName) { + return FilenameUtils.getBaseName(StringUtils.chomp(rulesetFileName)); + } + + private static String getRuleSetDescriptionSingleLine(RuleSet ruleset) { + String description = ruleset.getDescription(); + description = description.replaceAll("\\n|\\r", " "); + description = StringUtils.stripToEmpty(description); + return description; + } + + /** + * Generates for each ruleset a page. The page contains the details for each rule. + * + * @param rulesets all rulesets + * @throws IOException + */ + private void generateRuleSetIndex(Map<Language, List<RuleSet>> rulesets) throws IOException { + for (Map.Entry<Language, List<RuleSet>> entry : rulesets.entrySet()) { + String languageTersename = entry.getKey().getTerseName(); + for (RuleSet ruleset : entry.getValue()) { + String filename = RULESET_INDEX_FILENAME_PATTERN + .replace("${language.tersename}", languageTersename) + .replace("${ruleset.name}", getRuleSetFilename(ruleset)); + + Path path = getAbsoluteOutputPath(filename); + + String permalink = RULESET_INDEX_PERMALINK_PATTERN + .replace("${language.tersename}", languageTersename) + .replace("${ruleset.name}", getRuleSetFilename(ruleset)); + + List<String> lines = new LinkedList<>(); + lines.add("---"); + lines.add("title: " + ruleset.getName()); + lines.add("summary: " + getRuleSetDescriptionSingleLine(ruleset)); + lines.add("permalink: " + permalink); + lines.add("folder: pmd/rules/" + languageTersename); + lines.add("sidebaractiveurl: /" + LANGUAGE_INDEX_PERMALINK_PATTERN.replace("${language.tersename}", languageTersename)); + lines.add("editmepath: ../" + getRuleSetSourceFilepath(ruleset)); + lines.add("---"); + + for (Rule rule : getSortedRules(ruleset)) { + lines.add("## " + rule.getName()); + lines.add(""); + + if (rule instanceof RuleReference) { + RuleReference ref = (RuleReference) rule; + if (ruleset.getFileName().equals(ref.getRuleSetReference().getRuleSetFileName())) { + // rule renamed within same ruleset + lines.add(DEPRECATION_LABEL); + lines.add(""); + lines.add("This rule has been renamed. Use instead: [" + + ref.getRule().getName() + "](" + "#" + ref.getRule().getName().toLowerCase(Locale.ROOT) + ")"); + lines.add(""); + } else { + // rule moved to another ruleset + String otherLink = RULESET_INDEX_PERMALINK_PATTERN + .replace("${language.tersename}", languageTersename) + .replace("${ruleset.name}", getRuleSetFilename(ref.getRuleSetReference().getRuleSetFileName())); + lines.add(DEPRECATION_LABEL); + lines.add(""); + lines.add("The rule has been moved to another ruleset. Use instead: [" + + ref.getRule().getName() + "](" + otherLink + "#" + ref.getRule().getName().toLowerCase(Locale.ROOT) + ")"); + lines.add(""); + } + } + + if (rule.isDeprecated()) { + lines.add(DEPRECATION_LABEL); + lines.add(""); + } + if (rule.getSince() != null) { + lines.add("**Since:** PMD " + rule.getSince()); + lines.add(""); + } + lines.add("**Priority:** " + rule.getPriority() + " (" + rule.getPriority().getPriority() + ")"); + lines.add(""); + + if (rule.getMinimumLanguageVersion() != null) { + lines.add("**Minimum Language Version:** " + + rule.getLanguage().getName() + " " + rule.getMinimumLanguageVersion().getVersion()); + lines.add(""); + } + + lines.add(StringUtils.stripToEmpty(rule.getDescription())); + lines.add(""); + + if (rule instanceof XPathRule || rule instanceof RuleReference && ((RuleReference) rule).getRule() instanceof XPathRule) { + lines.add("```"); + lines.add(StringUtils.stripToEmpty(rule.getProperty(XPathRule.XPATH_DESCRIPTOR))); + lines.add("```"); + lines.add(""); + } else { + lines.add("**This rule is defined by the following Java class:** " + + "[" + rule.getRuleClass() + "](" + + GITHUB_SOURCE_LINK + getRuleClassSourceFilepath(rule.getRuleClass()) + + ")"); + lines.add(""); + } + + if (!rule.getExamples().isEmpty()) { + lines.add("**Example(s):**"); + lines.add(""); + for (String example : rule.getExamples()) { + lines.add("```"); + lines.add(StringUtils.stripToEmpty(example)); + lines.add("```"); + lines.add(""); + } + } + + List<PropertyDescriptor<?>> properties = new ArrayList<>(rule.getPropertyDescriptors()); + // filter out standard properties + properties.remove(Rule.VIOLATION_SUPPRESS_REGEX_DESCRIPTOR); + properties.remove(Rule.VIOLATION_SUPPRESS_XPATH_DESCRIPTOR); + properties.remove(XPathRule.XPATH_DESCRIPTOR); + properties.remove(XPathRule.VERSION_DESCRIPTOR); + + if (!properties.isEmpty()) { + lines.add("**This rule has the following properties:**"); + lines.add(""); + lines.add("|Name|Default Value|Description|"); + lines.add("|----|-------------|-----------|"); + for (PropertyDescriptor<?> propertyDescriptor : properties) { + lines.add("|" + propertyDescriptor.name() + + "|" + (propertyDescriptor.defaultValue() != null ? String.valueOf(propertyDescriptor.defaultValue()) : "") + + "|" + propertyDescriptor.description() + + "|"); + } + lines.add(""); + } + } + + writer.write(path, lines); + System.out.println("Generated " + path); + } + } + } + + private List<Rule> getSortedRules(RuleSet ruleset) { + List<Rule> sortedRules = new ArrayList<>(ruleset.getRules()); + Collections.sort(sortedRules, new Comparator<Rule>() { + @Override + public int compare(Rule o1, Rule o2) { + return o1.getName().compareToIgnoreCase(o2.getName()); + } + }); + return sortedRules; + } + + /** + * Searches for the source file of the given ruleset. This provides the information + * for the "editme" link. + * + * @param ruleset the ruleset to search for. + * @return + * @throws IOException + */ + private String getRuleSetSourceFilepath(RuleSet ruleset) throws IOException { + final String rulesetFilename = FilenameUtils.normalize(StringUtils.chomp(ruleset.getFileName())); + final List<Path> foundPathResult = new LinkedList<>(); + + Files.walkFileTree(root, new SimpleFileVisitor<Path>() { + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { + String path = file.toString(); + if (path.contains("src") && path.endsWith(rulesetFilename)) { + foundPathResult.add(file); + return FileVisitResult.TERMINATE; + } + return super.visitFile(file, attrs); + } + }); + + if (!foundPathResult.isEmpty()) { + Path foundPath = foundPathResult.get(0); + foundPath = root.relativize(foundPath); + return foundPath.toString(); + } + + return StringUtils.chomp(ruleset.getFileName()); + } + + private String getRuleClassSourceFilepath(String ruleClass) throws IOException { + final String relativeSourceFilename = ruleClass.replaceAll("\\.", File.separator) + ".java"; + final List<Path> foundPathResult = new LinkedList<>(); + + Files.walkFileTree(root, new SimpleFileVisitor<Path>() { + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { + String path = file.toString(); + if (path.contains("src") && path.endsWith(relativeSourceFilename)) { + foundPathResult.add(file); + return FileVisitResult.TERMINATE; + } + return super.visitFile(file, attrs); + } + }); + + if (!foundPathResult.isEmpty()) { + Path foundPath = foundPathResult.get(0); + foundPath = root.relativize(foundPath); + return FilenameUtils.normalize(foundPath.toString(), true); + } + + return FilenameUtils.normalize(relativeSourceFilename, true); + } +} diff --git a/pmd-doc/src/test/java/net/sourceforge/pmd/docs/MockedFileWriter.java b/pmd-doc/src/test/java/net/sourceforge/pmd/docs/MockedFileWriter.java new file mode 100644 index 000000000..a533e136f --- /dev/null +++ b/pmd-doc/src/test/java/net/sourceforge/pmd/docs/MockedFileWriter.java @@ -0,0 +1,46 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.docs; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.lang3.StringUtils; + +public class MockedFileWriter implements FileWriter { + + public static class FileEntry { + private String filename; + private String content; + + public String getFilename() { + return filename; + } + + public String getContent() { + return content; + } + } + + private List<FileEntry> data = new ArrayList<>(); + + @Override + public void write(Path path, List<String> lines) throws IOException { + FileEntry entry = new FileEntry(); + entry.filename = path.toString(); + entry.content = StringUtils.join(lines, System.getProperty("line.separator")); + data.add(entry); + } + + public List<FileEntry> getData() { + return data; + } + + public void reset() { + data.clear(); + } +} 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 new file mode 100644 index 000000000..43e64bb1d --- /dev/null +++ b/pmd-doc/src/test/java/net/sourceforge/pmd/docs/RuleDocGeneratorTest.java @@ -0,0 +1,64 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.docs; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Arrays; + +import org.apache.commons.io.IOUtils; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import net.sourceforge.pmd.RuleSet; +import net.sourceforge.pmd.RuleSetFactory; +import net.sourceforge.pmd.RuleSetNotFoundException; +import net.sourceforge.pmd.docs.MockedFileWriter.FileEntry; + +public class RuleDocGeneratorTest { + + private MockedFileWriter writer = new MockedFileWriter(); + private Path root; + + @Before + public void setup() throws IOException { + writer.reset(); + + root = Files.createTempDirectory("pmd-ruledocgenerator-test"); + Files.createDirectory(root.resolve("docs")); + } + + @After + public void cleanup() throws IOException { + Files.delete(root.resolve("docs")); + Files.delete(root); + } + + @Test + public void testSingleRuleset() throws RuleSetNotFoundException, IOException { + RuleDocGenerator generator = new RuleDocGenerator(writer, root); + + RuleSetFactory rsf = new RuleSetFactory(); + RuleSet ruleset = rsf.createRuleSet("rulesets/ruledoctest/sample.xml"); + + generator.generate(Arrays.asList(ruleset).iterator()); + + assertEquals(2, writer.getData().size()); + FileEntry languageIndex = writer.getData().get(0); + assertTrue(languageIndex.getFilename().endsWith("docs/pages/pmd/rules/java.md")); + assertEquals(IOUtils.toString(RuleDocGeneratorTest.class.getResourceAsStream("/expected/java.md")), + languageIndex.getContent()); + + FileEntry ruleSetIndex = writer.getData().get(1); + assertTrue(ruleSetIndex.getFilename().endsWith("docs/pages/pmd/rules/java/sample.md")); + assertEquals(IOUtils.toString(RuleDocGeneratorTest.class.getResourceAsStream("/expected/sample.md")), + ruleSetIndex.getContent()); + } +} diff --git a/pmd-doc/src/test/resources/expected/java.md b/pmd-doc/src/test/resources/expected/java.md new file mode 100644 index 000000000..8ddbfc08d --- /dev/null +++ b/pmd-doc/src/test/resources/expected/java.md @@ -0,0 +1,15 @@ +--- +title: Java Rules +permalink: pmd_rules_java.html +folder: pmd/rules +--- +List of rulesets and rules contained in each ruleset. + +* [Sample](pmd_rules_java_sample.html): Sample ruleset to test rule doc generation. + +## Sample +* [DeprecatedSample](pmd_rules_java_sample.html#deprecatedsample): <span style="border-radius: 0.25em; color: #fff; padding: 0.2em 0.6em 0.3em; display: inline; background-color: #d9534f; font-size: 75%;">Deprecated</span> Just some description of a deprecated rule. +* [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): <span style="border-radius: 0.25em; color: #fff; padding: 0.2em 0.6em 0.3em; display: inline; background-color: #d9534f; font-size: 75%;">Deprecated</span> The rule has been moved to another ruleset. Use instead [JumbledIncrementer](pmd_rules_java_basic.html#jumbledincrementer). +* [OverrideBothEqualsAndHashcode](pmd_rules_java_sample.html#overridebothequalsandhashcode): Override both 'public boolean Object.equals(Object other)', and 'public int Object.hashCode()', o... +* [RenamedRule](pmd_rules_java_sample.html#renamedrule): <span style="border-radius: 0.25em; color: #fff; padding: 0.2em 0.6em 0.3em; display: inline; background-color: #d9534f; font-size: 75%;">Deprecated</span> The rule has been renamed. Use instead [JumbledIncrementer](pmd_rules_java_sample.html#jumbledincrementer). diff --git a/pmd-doc/src/test/resources/expected/sample.md b/pmd-doc/src/test/resources/expected/sample.md new file mode 100644 index 000000000..72a9d3cf9 --- /dev/null +++ b/pmd-doc/src/test/resources/expected/sample.md @@ -0,0 +1,173 @@ +--- +title: Sample +summary: Sample ruleset to test rule doc generation. +permalink: pmd_rules_java_sample.html +folder: pmd/rules/java +sidebaractiveurl: /pmd_rules_java.html +editmepath: ../rulesets/ruledoctest/sample.xml +--- +## DeprecatedSample + +<span style="border-radius: 0.25em; color: #fff; padding: 0.2em 0.6em 0.3em; display: inline; background-color: #d9534f;">Deprecated</span> + +**Since:** PMD 1.0 + +**Priority:** Medium (3) + +Just some description of a deprecated rule. + +``` +//ForStatement +``` + +## JumbledIncrementer + +**Since:** PMD 1.0 + +**Priority:** Medium (3) + +Avoid jumbled loop incrementers - its usually a mistake, and is confusing even if intentional. + +``` +//ForStatement + [ + ForUpdate/StatementExpressionList/StatementExpression/PostfixExpression/PrimaryExpression/PrimaryPrefix/Name/@Image + = + ancestor::ForStatement/ForInit//VariableDeclaratorId/@Image + ] +``` + +**Example(s):** + +``` +public class JumbledIncrementerRule1 { + public void foo() { + for (int i = 0; i < 10; i++) { // only references 'i' + for (int k = 0; k < 20; i++) { // references both 'i' and 'k' + System.out.println("Hello"); + } + } + } +} +``` + +**This rule has the following properties:** + +|Name|Default Value|Description| +|----|-------------|-----------| +|sampleAdditionalProperty|the value|This is a additional property for tests| + +## MovedRule + +<span style="border-radius: 0.25em; color: #fff; padding: 0.2em 0.6em 0.3em; display: inline; background-color: #d9534f;">Deprecated</span> + +The rule has been moved to another ruleset. Use instead: [JumbledIncrementer](pmd_rules_java_basic.html#jumbledincrementer) + +**Since:** PMD 1.0 + +**Priority:** Medium (3) + +Avoid jumbled loop incrementers - its usually a mistake, and is confusing even if intentional. + +``` +//ForStatement + [ + ForUpdate/StatementExpressionList/StatementExpression/PostfixExpression/PrimaryExpression/PrimaryPrefix/Name/@Image + = + ancestor::ForStatement/ForInit//VariableDeclaratorId/@Image + ] +``` + +**Example(s):** + +``` +public class JumbledIncrementerRule1 { + public void foo() { + for (int i = 0; i < 10; i++) { // only references 'i' + for (int k = 0; k < 20; i++) { // references both 'i' and 'k' + System.out.println("Hello"); + } + } + } +} +``` + +## OverrideBothEqualsAndHashcode + +**Since:** PMD 0.4 + +**Priority:** Medium (3) + +**Minimum Language Version:** Java 1.5 + +Override both `public boolean Object.equals(Object other)`, and `public int Object.hashCode()`, or override neither. +Even if you are inheriting a `hashCode()` from a parent class, consider implementing hashCode and explicitly +delegating to your superclass. + +**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.basic.OverrideBothEqualsAndHashcodeRule](https://github.com/pmd/pmd/blob/master/net/sourceforge/pmd/lang/java/rule/basic/OverrideBothEqualsAndHashcodeRule.java) + +**Example(s):** + +``` +public class Bar { // poor, missing a hashcode() method + public boolean equals(Object o) { + // do some comparison + } +} + +public class Baz { // poor, missing an equals() method + public int hashCode() { + // return some hash value + } +} + +public class Foo { // perfect, both methods provided + public boolean equals(Object other) { + // do some comparison + } + public int hashCode() { + // return some hash value + } +} +``` + +## RenamedRule + +<span style="border-radius: 0.25em; color: #fff; padding: 0.2em 0.6em 0.3em; display: inline; background-color: #d9534f;">Deprecated</span> + +This rule has been renamed. Use instead: [JumbledIncrementer](#jumbledincrementer) + +**Since:** PMD 1.0 + +**Priority:** Medium (3) + +Avoid jumbled loop incrementers - its usually a mistake, and is confusing even if intentional. + +``` +//ForStatement + [ + ForUpdate/StatementExpressionList/StatementExpression/PostfixExpression/PrimaryExpression/PrimaryPrefix/Name/@Image + = + ancestor::ForStatement/ForInit//VariableDeclaratorId/@Image + ] +``` + +**Example(s):** + +``` +public class JumbledIncrementerRule1 { + public void foo() { + for (int i = 0; i < 10; i++) { // only references 'i' + for (int k = 0; k < 20; i++) { // references both 'i' and 'k' + System.out.println("Hello"); + } + } + } +} +``` + +**This rule has the following properties:** + +|Name|Default Value|Description| +|----|-------------|-----------| +|sampleAdditionalProperty|the value|This is a additional property for tests| diff --git a/pmd-doc/src/test/resources/rulesets/ruledoctest/sample.xml b/pmd-doc/src/test/resources/rulesets/ruledoctest/sample.xml new file mode 100644 index 000000000..2f3b5c876 --- /dev/null +++ b/pmd-doc/src/test/resources/rulesets/ruledoctest/sample.xml @@ -0,0 +1,116 @@ +<?xml version="1.0"?> + +<ruleset name="Sample" + xmlns="http://pmd.sourceforge.net/ruleset/2.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0 http://pmd.sourceforge.net/ruleset_2_0_0.xsd"> + <description> +Sample ruleset to test rule doc generation. + </description> + + <rule name="OverrideBothEqualsAndHashcode" + language="java" + since="0.4" + message="Ensure you override both equals() and hashCode()" + class="net.sourceforge.pmd.lang.java.rule.basic.OverrideBothEqualsAndHashcodeRule" + externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_sample.html#overridebothequalsandhashcode" + minimumLanguageVersion="1.5"> + <description> +Override both `public boolean Object.equals(Object other)`, and `public int Object.hashCode()`, or override neither. +Even if you are inheriting a `hashCode()` from a parent class, consider implementing hashCode and explicitly +delegating to your superclass. + </description> + <priority>3</priority> + <example> + <![CDATA[ +public class Bar { // poor, missing a hashcode() method + public boolean equals(Object o) { + // do some comparison + } +} + +public class Baz { // poor, missing an equals() method + public int hashCode() { + // return some hash value + } +} + +public class Foo { // perfect, both methods provided + public boolean equals(Object other) { + // do some comparison + } + public int hashCode() { + // return some hash value + } +} + ]]> + </example> + </rule> + + <rule name="JumbledIncrementer" + language="java" + since="1.0" + message="Avoid modifying an outer loop incrementer in an inner loop for update expression" + class="net.sourceforge.pmd.lang.rule.XPathRule" + externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_sample.html#jumbledincrementer"> + <description> +Avoid jumbled loop incrementers - its usually a mistake, and is confusing even if intentional. + </description> + <priority>3</priority> + <properties> + <property name="xpath"> + <value> + <![CDATA[ +//ForStatement + [ + ForUpdate/StatementExpressionList/StatementExpression/PostfixExpression/PrimaryExpression/PrimaryPrefix/Name/@Image + = + ancestor::ForStatement/ForInit//VariableDeclaratorId/@Image + ] + ]]> + </value> + </property> + <property name="sampleAdditionalProperty" type="String" description="This is a additional property for tests" value="the value" /> + </properties> + <example> + <![CDATA[ +public class JumbledIncrementerRule1 { + public void foo() { + for (int i = 0; i < 10; i++) { // only references 'i' + for (int k = 0; k < 20; i++) { // references both 'i' and 'k' + System.out.println("Hello"); + } + } + } +} + ]]> + </example> + </rule> + +<rule name="DeprecatedSample" + language="java" + since="1.0" + message="Sample rule, which is deprecated" + class="net.sourceforge.pmd.lang.rule.XPathRule" + externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_sample.html#deprecatedsample" + deprecated="true"> + <description> +Just some description of a deprecated rule. + </description> + <priority>3</priority> + <properties> + <property name="xpath"> + <value> +<![CDATA[ +//ForStatement +]]> + </value> + </property> + </properties> + </rule> + +<rule name="RenamedRule" ref="JumbledIncrementer"/> + +<rule name="MovedRule" ref="rulesets/java/basic.xml/JumbledIncrementer"/> + +</ruleset> diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTType.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTType.java index b398ca9a7..bdd26c4f5 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTType.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTType.java @@ -23,11 +23,11 @@ public Object jjtAccept(JavaParserVisitor visitor, Object data) { } public String getTypeImage() { - ASTPrimitiveType prim = getFirstDescendantOfType(ASTPrimitiveType.class); - if (prim != null) { - return prim.getImage(); + ASTClassOrInterfaceType refType = getFirstDescendantOfType(ASTClassOrInterfaceType.class); + if (refType != null) { + return refType.getImage(); } - return getFirstDescendantOfType(ASTClassOrInterfaceType.class).getImage(); + return getFirstDescendantOfType(ASTPrimitiveType.class).getImage(); } public int getArrayDepth() { diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/metrics/impl/visitors/DefaultNcssVisitor.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/metrics/impl/visitors/DefaultNcssVisitor.java index 082e55cea..79da9a5e2 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/metrics/impl/visitors/DefaultNcssVisitor.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/metrics/impl/visitors/DefaultNcssVisitor.java @@ -7,6 +7,7 @@ import org.apache.commons.lang3.mutable.MutableInt; import net.sourceforge.pmd.lang.java.ast.ASTAnnotationTypeDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTAssertStatement; import net.sourceforge.pmd.lang.java.ast.ASTBreakStatement; import net.sourceforge.pmd.lang.java.ast.ASTCatchStatement; import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration; @@ -220,4 +221,10 @@ public Object visit(ASTInitializer node, Object data) { return super.visit(node, data); } + @Override + public Object visit(ASTAssertStatement node, Object data) { + ((MutableInt) data).increment(); + return super.visit(node, data); + } + } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/comments/CommentDefaultAccessModifierRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/comments/CommentDefaultAccessModifierRule.java index a219be226..9ea78854e 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/comments/CommentDefaultAccessModifierRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/comments/CommentDefaultAccessModifierRule.java @@ -12,6 +12,7 @@ import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceBodyDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit; +import net.sourceforge.pmd.lang.java.ast.ASTConstructorDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTFieldDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclarator; @@ -86,6 +87,14 @@ public Object visit(final ASTClassOrInterfaceDeclaration decl, final Object data return super.visit(decl, data); } + @Override + public Object visit(final ASTConstructorDeclaration decl, Object data) { + if (shouldReport(decl)) { + addViolationWithMessage(data, decl, String.format(MESSAGE, decl.getImage(), "constructor")); + } + return super.visit(decl, data); + } + private boolean shouldReport(final AbstractJavaAccessNode decl) { List<ASTClassOrInterfaceDeclaration> parentClassOrInterface = decl .getParentsOfType(ASTClassOrInterfaceDeclaration.class); diff --git a/pmd-java/src/main/resources/rulesets/java/basic.xml b/pmd-java/src/main/resources/rulesets/java/basic.xml index f7f06a0ad..df103cd67 100644 --- a/pmd-java/src/main/resources/rulesets/java/basic.xml +++ b/pmd-java/src/main/resources/rulesets/java/basic.xml @@ -35,13 +35,13 @@ Avoid jumbled loop incrementers - its usually a mistake, and is confusing even i <example> <![CDATA[ public class JumbledIncrementerRule1 { - public void foo() { - for (int i = 0; i < 10; i++) { // only references 'i' - for (int k = 0; k < 20; i++) { // references both 'i' and 'k' - System.out.println("Hello"); - } - } - } + public void foo() { + for (int i = 0; i < 10; i++) { // only references 'i' + for (int k = 0; k < 20; i++) { // references both 'i' and 'k' + System.out.println("Hello"); + } + } + } } ]]> </example> diff --git a/pmd-java/src/main/resources/rulesets/java/comments.xml b/pmd-java/src/main/resources/rulesets/java/comments.xml index 9462a4862..42dad25e1 100755 --- a/pmd-java/src/main/resources/rulesets/java/comments.xml +++ b/pmd-java/src/main/resources/rulesets/java/comments.xml @@ -82,9 +82,9 @@ A rule for the politically correct... we don't want to offend anyone. message="Missing commented default access modifier" externalInfoUrl="${pmd.website.baseurl}/rules/java/comments.html#CommentDefaultAccessModifier"> <description> - To avoid mistakes if we want that a Method, Field or Nested class have a default access modifier - we must add a comment at the beginning of the Method, Field or Nested class. - By default the comment must be /* default */, if you want another, you have to provide a regex. + To avoid mistakes if we want that a Method, Constructor, Field or Nested class have a default access modifier + we must add a comment at the beginning of it's declaration. + By default the comment must be /* default */, if you want another, you have to provide a regexp. </description> <priority>3</priority> <properties> diff --git a/pmd-java/src/main/resources/rulesets/java/metrics.xml b/pmd-java/src/main/resources/rulesets/java/metrics.xml index 422a7e56f..5a35fb5bb 100644 --- a/pmd-java/src/main/resources/rulesets/java/metrics.xml +++ b/pmd-java/src/main/resources/rulesets/java/metrics.xml @@ -15,52 +15,47 @@ class="net.sourceforge.pmd.lang.java.metrics.rule.CyclomaticComplexityRule" metrics="true" externalInfoUrl="${pmd.website.baseurl}/rules/java/codesize.html#CyclomaticComplexity"> - <description> - <![CDATA[ -Complexity directly affects maintenance costs is determined by the number of decision points in a method -plus one for the method entry. The decision points include 'if', 'while', 'for', and 'case labels' calls. -Generally, numbers ranging from 1-4 denote low complexity, 5-7 denote moderate complexity, 8-10 denote -high complexity, and 11+ is very high complexity. - ]]> + <description><![CDATA[ + The complexity of methods directly affects maintenance costs and readability. Concentrating too much decisional logic + in a single method makes its behaviour hard to read and change. + + Cyclomatic complexity assesses the complexity of a method by counting the number of decision points in a method, + plus one for the method entry. Decision points are places where the control flow jumps to another place in the + program. As such, they include all control flow statements, such as `if`, `while`, `for`, and `case`. For more + details on the calculation, see the documentation of the [Cyclo metric](/pmd_java_metrics_index.html#cyclomatic-complexity-cyclo). + + Generally, numbers ranging from 1-4 denote low complexity, 5-7 denote moderate complexity, 8-10 denote + high complexity, and 11+ is very high complexity. + + Reported methods should be broken down into several smaller methods. Reported classes should probably be broken down + into subcomponents.]]> </description> <priority>3</priority> <example> <![CDATA[ -public class Foo { // This has a Cyclomatic Complexity = 12 -1 public void example() { -2 if (a == b) { -3 if (a1 == b1) { - fiddle(); -4 } else if a2 == b2) { - fiddle(); - } else { - fiddle(); - } -5 } else if (c == d) { -6 while (c == d) { - fiddle(); - } -7 } else if (e == f) { -8 for (int n = 0; n < h; n++) { - fiddle(); - } - } else{ - switch (z) { -9 case 1: - fiddle(); - break; -10 case 2: - fiddle(); - break; -11 case 3: - fiddle(); - break; -12 default: - fiddle(); - break; - } +class Foo { + void baseCyclo() { // Cyclo = 1 + highCyclo(); + } + + void highCyclo() { // Cyclo = 10: reported! + int x = 0, y = 2; + boolean a = false, b = true; + + if (a && (y == 1 ? b : true)) { // +3 + if (y == x) { // +1 + while (true) { // +1 + if (x++ < 20) { // +1 + break; // +1 + } } + } else if (y == t && !d) { // +2 + x = a ? y : x; // +1 + } else { + x = 2; + } } + } } ]]> </example> @@ -71,24 +66,38 @@ public class Foo { // This has a Cyclomatic Complexity = 12 since="3.9" class="net.sourceforge.pmd.lang.java.metrics.rule.NcssCountRule" metrics="true" - externalInfoUrl="${pmd.website.baseurl}/rules/java/codesize.html#NcssTypeCount"> + externalInfoUrl="${pmd.website.baseurl}/rules/java/codesize.html#NcssCount"> <description> - This rule uses the NCSS (Non-Commenting Source Statements) algorithm to determine the number of lines - of code for a given type. NCSS ignores comments, and counts actual statements. Using this algorithm, - lines of code that are split are counted as one. + This rule uses the NCSS (Non-Commenting Source Statements) metric to determine the number of lines + of code in a class, method or constructor. NCSS ignores comments, blank lines, and only counts actual + statements. For more details on the calculation, see the documentation ofthe[NCSSmetric](/pmd_java_metrics_index.html#non-commenting-source-statements-ncss). </description> <priority>3</priority> <example> <![CDATA[ -public class Foo extends Bar { - public Foo() { //this class only has 4 NCSS lines - super(); - +import java.util.Collections; // +0 +import java.io.IOException; // +0 +class Foo { // +1, total Ncss = 12 + public void bigMethod() // +1 + throws IOException { + int x = 0, y = 2; // +1 + boolean a = false, b = true; // +1 + if (a || b) { // +1 + try { // +1 + do { // +1 + x += 2; // +1 + } while (x < 12); - super.foo(); + System.exit(0); // +1 + } catch (IOException ioe) { // +1 + throw new PatheticFailException(ioe); // +1 + } + } else { + assert false; // +1 + } } } ]]> @@ -103,41 +112,51 @@ public class Foo extends Bar { externalInfoUrl="${pmd.website.baseurl}/rules/java/codesize.html#NPathComplexity"> <description> The NPath complexity of a method is the number of acyclic execution paths through that method. + While cyclomatic complexity counts the number of decision points in a method, NPath counts the number of + full paths from the beginning to the end of the block of the method. That metric grows exponentially, as + it multiplies the complexity of statements in the same block. For more details on the calculation, see the + documentation of the [NPath metric](/pmd_java_metrics_index.html#npath-complexity-npath). + A threshold of 200 is generally considered the point where measures should be taken to reduce complexity and increase readability. </description> <priority>3</priority> <example> <![CDATA[ -void bar() { // This is something more complex than it needs to be, - if (y) { // it should be broken down into smaller methods or functions - for (j = 0; j < m; j++) { - if (j > r) { - doSomething(); - while (f < 5 ) { - anotherThing(); - f -= 27; - } - } else { - tryThis(); - } +public class Foo { + public static void bar() { // Ncss = 252: reported! + boolean a, b = true; + try { // 2 * 2 + 2 = 6 + if (true) { // 2 + List buz = new ArrayList(); + } + + for(int i = 0; i < 19; i++) { // * 2 + List buz = new ArrayList(); + } + } catch(Exception e) { + if (true) { // 2 + e.printStackTrace(); } } - if ( r - n > 45) { - while (doMagic()) { - findRabbits(); - } + + while (j++ < 20) { // * 2 + List buz = new ArrayList(); } - try { - doSomethingDangerous(); - } catch (Exception ex) { - makeAmends(); - } finally { - dontDoItAgain(); + + switch(j) { // * 7 + case 1: + case 2: break; + case 3: j = 5; break; + case 4: if (b && a) { bar(); } break; + default: break; } + + do { // * 3 + List buz = new ArrayList(); + } while (a && j++ < 30); } } - ]]> </example> </rule> diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/metrics/impl/xml/NcssTest.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/metrics/impl/xml/NcssTest.xml index ecf84267f..80c26d932 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/metrics/impl/xml/NcssTest.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/metrics/impl/xml/NcssTest.xml @@ -104,6 +104,7 @@ e.printStackTrace(); } catch (ThemAll pokemon) { pokemon.train(); + assert pokemon.level > 12; } finally { // Do nothing } @@ -125,11 +126,11 @@ <rule-property name="reportClasses">true</rule-property> <expected-problems>5</expected-problems> <expected-messages> - <message>'com.company.money.Foo' has value 65 highest 20.</message> + <message>'com.company.money.Foo' has value 66 highest 21.</message> <message>'com.company.money.Foo#Foo()' has value 2.</message> <message>'com.company.money.Foo#Foo(int)' has value 13.</message> <message>'com.company.money.Foo#foo()' has value 14.</message> - <message>'com.company.money.Foo#main(String)' has value 20.</message> + <message>'com.company.money.Foo#main(String)' has value 21.</message> </expected-messages> <code-ref id="full-example"/> </test-code> @@ -140,11 +141,11 @@ <rule-property name="metricVersion">javaNcss</rule-property> <expected-problems>5</expected-problems> <expected-messages> - <message>'com.company.money.Foo' has value 68 highest 20.</message> + <message>'com.company.money.Foo' has value 69 highest 21.</message> <message>'com.company.money.Foo#Foo()' has value 2.</message> <message>'com.company.money.Foo#Foo(int)' has value 13.</message> <message>'com.company.money.Foo#foo()' has value 14.</message> - <message>'com.company.money.Foo#main(String)' has value 20.</message> + <message>'com.company.money.Foo#main(String)' has value 21.</message> </expected-messages> <code-ref id="full-example"/> </test-code> diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/comments/xml/CommentDefaultAccessModifier.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/comments/xml/CommentDefaultAccessModifier.xml index 4b8474bef..ee56ce392 100755 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/comments/xml/CommentDefaultAccessModifier.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/comments/xml/CommentDefaultAccessModifier.xml @@ -155,6 +155,28 @@ public enum TestEnum { <code><![CDATA[ public class CommentDefaultAccessModifier { @VisibleForTesting void method() {} +} + ]]></code> + </test-code> + + <test-code> + <description>#536 Constructor with default access modifier should trigger</description> + <expected-problems>1</expected-problems> + <code><![CDATA[ +public class Foo { + Foo() {} +} + ]]></code> + </test-code> + + <test-code> + <description>#536 Enum constructor with implicit private modifier should not trigger</description> + <expected-problems>0</expected-problems> + <code><![CDATA[ +public enum Bar { + ONE, TWO; + + Bar() {} } ]]></code> </test-code> diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/unusedcode/xml/UnusedPrivateMethod.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/unusedcode/xml/UnusedPrivateMethod.xml index 1b6642249..b5d810c04 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/unusedcode/xml/UnusedPrivateMethod.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/unusedcode/xml/UnusedPrivateMethod.xml @@ -1531,4 +1531,22 @@ public class Something { } ]]></code> </test-code> + + <test-code> + <description>#521 UnusedPrivateMethod returns false positives with primitive data type in map argument</description> + <expected-problems>0</expected-problems> + <code><![CDATA[ + public class Foo { + public Foo() { + Map<String, double[]> map = new LinkedHashMap<>(); + addToMap(map); + } + + private void addToMap(Map<String, double[]> map) { + map.put("foo", new double[]{0., 1.}); + } + } + ]]></code> + </test-code> + </test-data> diff --git a/pom.xml b/pom.xml index 9f3c42896..9c6b65098 100644 --- a/pom.xml +++ b/pom.xml @@ -471,7 +471,7 @@ Additionally it includes CPD, the copy-paste-detector. CPD finds duplicated code <artifactId>nexus-staging-maven-plugin</artifactId> <version>1.6.8</version> </plugin> - <!--This plugin's configuration is used to store Eclipse + <!--This plugin's configuration is used to store Eclipse m2e settings only. It has no influence on the Maven build itself. --> <plugin> <groupId>org.eclipse.m2e</groupId> @@ -945,6 +945,7 @@ Additionally it includes CPD, the copy-paste-detector. CPD finds duplicated code <module>pmd-apex</module> <module>pmd-java8</module> <module>pmd-ui</module> + <module>pmd-doc</module> </modules> </profile> diff --git a/src/site/markdown/overview/changelog.md b/src/site/markdown/overview/changelog.md index 26d98f53d..5e45ae798 100644 --- a/src/site/markdown/overview/changelog.md +++ b/src/site/markdown/overview/changelog.md @@ -13,6 +13,8 @@ This is a major release. * [Java Type Resolution](#Java_Type_Resolution) * [Metrics Framework](#Metrics_Framework) * [Configuration Error Reporting](#Configuration_Error_Reporting) + * [Java Symbol Table](#Java_Symbol_Table) + * [Apex Parser Update](#Apex_Parser_Update) * [Modified Rules](#Modified_Rules) * [Removed Rules](#Removed_Rules) * [Fixed Issues](#Fixed_Issues) @@ -79,10 +81,32 @@ and include them to such reports. * The deprecated rule `UseSingleton` has been removed from the ruleset `java-design`. The rule has been renamed long time ago to `UseUtilityClass`. + +#### Java Symbol Table + +A [bug in symbol table](https://github.com/pmd/pmd/pull/549/commits/0958621ca884a8002012fc7738308c8dfc24b97c) prevented +the symbol table analysis to properly match primitive arrays types. The issue [affected the `java-unsedcode/UnusedPrivateMethod`](https://github.com/pmd/pmd/issues/521) +rule, but other rules may now produce improved results as consequence of this fix. + +#### Apex Parser Update + +The Apex parser version was bumped, from `1.0-sfdc-187` to `1.0-sfdc-224`. This update let us take full advatange +of the latest improvements from Salesforce, but introduces some breaking changes: +* `BlockStatements` are now created for all control structures, even if no brace is used. We have therefore added + a `hasCurlyBrace` method to differentiate between both scenarios. +* New AST node types are available. In particular `CastExpression`, `ConstructorPreamble`, `IllegalStoreExpression`, + `MethodBlockStatement`, `Modifier`, `MultiStatement`, `NestedExpression`, `NestedStoreExpression`, + `NewKeyValueObjectExpression` and `StatementExecuted` +* Some nodes have been removed. Such is the case of `TestNode`, `DottedExpression` and `NewNameValueObjectExpression` + (replaced by `NewKeyValueObjectExpression`) + +Al existing rules have been updated to reflect these changes. If you have custom rules, be sure to update them. + ### Fixed Issues * apex * [#488](https://github.com/pmd/pmd/pull/488): \[apex] Use Apex lexer for CPD + * [#489](https://github.com/pmd/pmd/pull/489): \[apex] Update Apex compiler * [#500](https://github.com/pmd/pmd/issues/500): \[apex] Running through CLI shows jorje optimization messages * cpp * [#448](https://github.com/pmd/pmd/issues/448): \[cpp] Write custom CharStream to handle continuation characters @@ -91,10 +115,14 @@ and include them to such reports. * [#487](https://github.com/pmd/pmd/pull/487): \[java] Fix typeresolution for anonymous extending object * [#496](https://github.com/pmd/pmd/issues/496): \[java] processing error on generics inherited from enclosing class * [#527](https://github.com/pmd/pmd/issues/527): \[java] Lombok getter annotation on enum is not recognized correctly +* java-comments + * [#536](https://github.com/pmd/pmd/issues/536): \[java] CommentDefaultAccessModifierRule ignores constructors * java-controversial * [#408](https://github.com/pmd/pmd/issues/408): \[java] DFA not analyzing asserts * java-sunsecure * [#468](https://github.com/pmd/pmd/issues/468): \[java] ArrayIsStoredDirectly false positive +* java-unusedcode + * [#521](https://github.com/pmd/pmd/issues/521): \[java] UnusedPrivateMethod returns false positives with primitive data type in map argument * java-unnecessarycode * [#412](https://github.com/pmd/pmd/issues/412): \[java] java-unnecessarycode/UnnecessaryFinalModifier missing cases @@ -129,8 +157,9 @@ and include them to such reports. * [#524](https://github.com/pmd/pmd/pull/524): \[java] Add support for explicit type arguments with method invocation - [Bendegúz Nagy](https://github.com/WinterGrascph) * [#525](https://github.com/pmd/pmd/pull/525): \[core] Fix line ending and not ignored files issues - [Matias Comercio](https://github.com/MatiasComercio) * [#528](https://github.com/pmd/pmd/pull/528): \[core] Fix typo - [Ayoub Kaanich](https://github.com/kayoub5) +* [#529](https://github.com/pmd/pmd/pull/529): \[java] Abstracted the Java metrics framework - [Clément Fournier](https://github.com/oowekyala) * [#530](https://github.com/pmd/pmd/pull/530): \[java] Fix issue #527: Lombok getter annotation on enum is not recognized correctly - [Clément Fournier](https://github.com/oowekyala) * [#535](https://github.com/pmd/pmd/pull/535): \[apex] Fix broken Apex visitor adapter - [Clément Fournier](https://github.com/oowekyala) -* [#529](https://github.com/pmd/pmd/pull/529): \[java] Abstracted the Java metrics framework - [Clément Fournier](https://github.com/oowekyala) * [#542](https://github.com/pmd/pmd/pull/542): \[java] Metrics abstraction - [Clément Fournier](https://github.com/oowekyala) +* [#548](https://github.com/pmd/pmd/pull/548): \[java] Metrics documentation - [Clément Fournier](https://github.com/oowekyala)