diff --git a/docs/pages/pmd/rules/apex.md b/docs/pages/pmd/rules/apex.md
index f58e1edb33..9dcfda90a5 100644
--- a/docs/pages/pmd/rules/apex.md
+++ b/docs/pages/pmd/rules/apex.md
@@ -22,13 +22,17 @@ folder: pmd/rules
{% include callout.html content="Rules which enforce a specific coding style." %}
-* [ClassNamingConventions](pmd_rules_apex_codestyle.html#classnamingconventions): Class names should always begin with an upper case character.
+* [ClassNamingConventions](pmd_rules_apex_codestyle.html#classnamingconventions): Configurable naming conventions for type declarations. This rule reports type declarat...
+* [FieldNamingConventions](pmd_rules_apex_codestyle.html#fieldnamingconventions): Configurable naming conventions for field declarations. This rule reports variable declarations ...
* [ForLoopsMustUseBraces](pmd_rules_apex_codestyle.html#forloopsmustusebraces): Avoid using 'for' statements without using surrounding braces. If the code formatting orindentati...
+* [FormalParameterNamingConventions](pmd_rules_apex_codestyle.html#formalparameternamingconventions): Configurable naming conventions for formal parameters of methods. This rule reports fo...
* [IfElseStmtsMustUseBraces](pmd_rules_apex_codestyle.html#ifelsestmtsmustusebraces): Avoid using if..else statements without using surrounding braces. If the code formattingor indent...
* [IfStmtsMustUseBraces](pmd_rules_apex_codestyle.html#ifstmtsmustusebraces): Avoid using if statements without using braces to surround the code block. If the codeformatting ...
-* [MethodNamingConventions](pmd_rules_apex_codestyle.html#methodnamingconventions): Method names should always begin with a lower case character, and should not contain underscores.
+* [LocalVariableNamingConventions](pmd_rules_apex_codestyle.html#localvariablenamingconventions): Configurable naming conventions for local variable declarations. This rule reports var...
+* [MethodNamingConventions](pmd_rules_apex_codestyle.html#methodnamingconventions): Configurable naming conventions for method declarations. This rule reports method decl...
* [OneDeclarationPerLine](pmd_rules_apex_codestyle.html#onedeclarationperline): Apex allows the use of several variables declaration of the same type on one line. However, itcan...
-* [VariableNamingConventions](pmd_rules_apex_codestyle.html#variablenamingconventions): A variable naming conventions rule - customize this to your liking. Currently, itchecks for fina...
+* [PropertyNamingConventions](pmd_rules_apex_codestyle.html#propertynamingconventions): Configurable naming conventions for property declarations. This rule reports property ...
+* [VariableNamingConventions](pmd_rules_apex_codestyle.html#variablenamingconventions): Deprecated A variable naming conventions rule - customize this to your liking. Currently, itchecks for fina...
* [WhileLoopsMustUseBraces](pmd_rules_apex_codestyle.html#whileloopsmustusebraces): Avoid using 'while' statements without using braces to surround the code block. If the codeformat...
## Design
diff --git a/docs/pages/pmd/rules/apex/codestyle.md b/docs/pages/pmd/rules/apex/codestyle.md
index f70bfb0b78..26712b16a8 100644
--- a/docs/pages/pmd/rules/apex/codestyle.md
+++ b/docs/pages/pmd/rules/apex/codestyle.md
@@ -5,7 +5,7 @@ permalink: pmd_rules_apex_codestyle.html
folder: pmd/rules/apex
sidebaractiveurl: /pmd_rules_apex.html
editmepath: ../pmd-apex/src/main/resources/category/apex/codestyle.xml
-keywords: Code Style, ClassNamingConventions, IfElseStmtsMustUseBraces, IfStmtsMustUseBraces, ForLoopsMustUseBraces, MethodNamingConventions, OneDeclarationPerLine, VariableNamingConventions, WhileLoopsMustUseBraces
+keywords: Code Style, ClassNamingConventions, IfElseStmtsMustUseBraces, IfStmtsMustUseBraces, FieldNamingConventions, ForLoopsMustUseBraces, FormalParameterNamingConventions, LocalVariableNamingConventions, MethodNamingConventions, OneDeclarationPerLine, PropertyNamingConventions, VariableNamingConventions, WhileLoopsMustUseBraces
language: Apex
---
@@ -15,21 +15,103 @@ language: Apex
**Priority:** High (1)
-Class names should always begin with an upper case character.
+Configurable naming conventions for type declarations. This rule reports
+type declarations which do not match the regex that applies to their
+specific kind (e.g. enum or interface). Each regex can be configured through
+properties.
+
+By default this rule uses the standard Apex naming convention (Pascal case).
**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.apex.rule.codestyle.ClassNamingConventionsRule](https://github.com/pmd/pmd/blob/master/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/codestyle/ClassNamingConventionsRule.java)
**Example(s):**
``` java
-public class Foo {}
+public class FooClass { } // This is in pascal case, so it's ok
+
+public class fooClass { } // This will be reported unless you change the regex
```
-**Use this rule by referencing it:**
+**This rule has the following properties:**
+
+|Name|Default Value|Description|Multivalued|
+|----|-------------|-----------|-----------|
+|testClassPattern|\[A-Z\]\[a-zA-Z0-9\_\]\*|Regex which applies to test class names|no|
+|abstractClassPattern|\[A-Z\]\[a-zA-Z0-9\_\]\*|Regex which applies to abstract class names|no|
+|classPattern|\[A-Z\]\[a-zA-Z0-9\_\]\*|Regex which applies to class names|no|
+|interfacePattern|\[A-Z\]\[a-zA-Z0-9\_\]\*|Regex which applies to interface names|no|
+|enumPattern|\[A-Z\]\[a-zA-Z0-9\_\]\*|Regex which applies to enum names|no|
+
+**Use this rule with the default properties by just referencing it:**
``` xml
```
+**Use this rule and customize it:**
+``` xml
+
+
+
+
+
+
+
+
+
+```
+
+## FieldNamingConventions
+
+**Since:** PMD 6.15.0
+
+**Priority:** High (1)
+
+Configurable naming conventions for field declarations. This rule reports variable declarations
+which do not match the regex that applies to their specific kind ---e.g. constants (static final),
+static field, final field. Each regex can be configured through properties.
+
+By default this rule uses the standard Apex naming convention (Camel case).
+
+**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.apex.rule.codestyle.FieldNamingConventionsRule](https://github.com/pmd/pmd/blob/master/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/codestyle/FieldNamingConventionsRule.java)
+
+**Example(s):**
+
+``` java
+public class Foo {
+ Integer instanceField; // This is in camel case, so it's ok
+
+ Integer INSTANCE_FIELD; // This will be reported unless you change the regex
+}
+```
+
+**This rule has the following properties:**
+
+|Name|Default Value|Description|Multivalued|
+|----|-------------|-----------|-----------|
+|enumConstantPattern|\[A-Z\]\[A-Z0-9\_\]\*|Regex which applies to enum constant field names|no|
+|constantPattern|\[A-Z\]\[A-Z0-9\_\]\*|Regex which applies to constant field names|no|
+|finalPattern|\[a-z\]\[a-zA-Z0-9\]\*|Regex which applies to final field names|no|
+|staticPattern|\[a-z\]\[a-zA-Z0-9\]\*|Regex which applies to static field names|no|
+|instancePattern|\[a-z\]\[a-zA-Z0-9\]\*|Regex which applies to instance field names|no|
+
+**Use this rule with the default properties by just referencing it:**
+``` xml
+
+```
+
+**Use this rule and customize it:**
+``` xml
+
+
+
+
+
+
+
+
+
+```
+
## ForLoopsMustUseBraces
**Since:** PMD 5.6.0
@@ -63,6 +145,53 @@ for (int i = 0; i < 42; i++) { // preferred approach
```
+## FormalParameterNamingConventions
+
+**Since:** PMD 6.15.0
+
+**Priority:** High (1)
+
+Configurable naming conventions for formal parameters of methods.
+This rule reports formal parameters which do not match the regex that applies to their
+specific kind (e.g. method parameter, or final method parameter). Each regex can be
+configured through properties.
+
+By default this rule uses the standard Apex naming convention (Camel case).
+
+**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.apex.rule.codestyle.FormalParameterNamingConventionsRule](https://github.com/pmd/pmd/blob/master/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/codestyle/FormalParameterNamingConventionsRule.java)
+
+**Example(s):**
+
+``` java
+public class Foo {
+ public bar(Integer methodParameter) { } // This is in camel case, so it's ok
+
+ public baz(Integer METHOD_PARAMETER) { } // This will be reported unless you change the regex
+}
+```
+
+**This rule has the following properties:**
+
+|Name|Default Value|Description|Multivalued|
+|----|-------------|-----------|-----------|
+|finalMethodParameterPattern|\[a-z\]\[a-zA-Z0-9\]\*|Regex which applies to final method parameter names|no|
+|methodParameterPattern|\[a-z\]\[a-zA-Z0-9\]\*|Regex which applies to method parameter names|no|
+
+**Use this rule with the default properties by just referencing it:**
+``` xml
+
+```
+
+**Use this rule and customize it:**
+``` xml
+
+
+
+
+
+
+```
+
## IfElseStmtsMustUseBraces
**Since:** PMD 5.6.0
@@ -129,21 +258,29 @@ if (foo) { // preferred approach
```
-## MethodNamingConventions
+## LocalVariableNamingConventions
-**Since:** PMD 5.5.0
+**Since:** PMD 6.15.0
**Priority:** High (1)
-Method names should always begin with a lower case character, and should not contain underscores.
+Configurable naming conventions for local variable declarations.
+This rule reports variable declarations which do not match the regex that applies to their
+specific kind (e.g. local variable, or final local variable). Each regex can be configured through
+properties.
-**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.apex.rule.codestyle.MethodNamingConventionsRule](https://github.com/pmd/pmd/blob/master/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/codestyle/MethodNamingConventionsRule.java)
+By default this rule uses the standard Apex naming convention (Camel case).
+
+**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.apex.rule.codestyle.LocalVariableNamingConventionsRule](https://github.com/pmd/pmd/blob/master/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/codestyle/LocalVariableNamingConventionsRule.java)
**Example(s):**
``` java
public class Foo {
- public void fooStuff() {
+ public Foo() {
+ Integer localVariable; // This is in camel case, so it's ok
+
+ Integer LOCAL_VARIABLE; // This will be reported unless you change the regex
}
}
```
@@ -152,7 +289,56 @@ public class Foo {
|Name|Default Value|Description|Multivalued|
|----|-------------|-----------|-----------|
-|skipTestMethodUnderscores|false|Skip underscores in test methods|no|
+|finalLocalPattern|\[a-z\]\[a-zA-Z0-9\]\*|Regex which applies to final local variable names|no|
+|localPattern|\[a-z\]\[a-zA-Z0-9\]\*|Regex which applies to local variable names|no|
+
+**Use this rule with the default properties by just referencing it:**
+``` xml
+
+```
+
+**Use this rule and customize it:**
+``` xml
+
+
+
+
+
+
+```
+
+## MethodNamingConventions
+
+**Since:** PMD 5.5.0
+
+**Priority:** High (1)
+
+Configurable naming conventions for method declarations. This rule reports
+method declarations which do not match the regex that applies to their
+specific kind (e.g. static method, or test method). Each regex can be
+configured through properties.
+
+By default this rule uses the standard Apex naming convention (Camel case).
+
+**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.apex.rule.codestyle.MethodNamingConventionsRule](https://github.com/pmd/pmd/blob/master/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/codestyle/MethodNamingConventionsRule.java)
+
+**Example(s):**
+
+``` java
+public class Foo {
+ public void instanceMethod() { } // This is in camel case, so it's ok
+
+ public void INSTANCE_METHOD() { } // This will be reported unless you change the regex
+```
+
+**This rule has the following properties:**
+
+|Name|Default Value|Description|Multivalued|
+|----|-------------|-----------|-----------|
+|skipTestMethodUnderscores|false|Deprecated Skip underscores in test methods|no|
+|testPattern|\[a-z\]\[a-zA-Z0-9\]\*|Regex which applies to test method names|no|
+|staticPattern|\[a-z\]\[a-zA-Z0-9\]\*|Regex which applies to static method names|no|
+|instancePattern|\[a-z\]\[a-zA-Z0-9\]\*|Regex which applies to instance method names|no|
**Use this rule with the default properties by just referencing it:**
``` xml
@@ -163,7 +349,9 @@ public class Foo {
``` xml
-
+
+
+
```
@@ -220,8 +408,57 @@ Integer b;
```
+## PropertyNamingConventions
+
+**Since:** PMD 6.15.0
+
+**Priority:** High (1)
+
+Configurable naming conventions for property declarations. This rule reports
+property declarations which do not match the regex that applies to their
+specific kind (e.g. static property, or instance property). Each regex can be
+configured through properties.
+
+By default this rule uses the standard Apex naming convention (Camel case).
+
+**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.apex.rule.codestyle.PropertyNamingConventionsRule](https://github.com/pmd/pmd/blob/master/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/codestyle/PropertyNamingConventionsRule.java)
+
+**Example(s):**
+
+``` java
+public class Foo {
+ public Integer instanceProperty { get; set; } // This is in camel case, so it's ok
+
+ public Integer INSTANCE_PROPERTY { get; set; } // This will be reported unless you change the regex
+}
+```
+
+**This rule has the following properties:**
+
+|Name|Default Value|Description|Multivalued|
+|----|-------------|-----------|-----------|
+|staticPattern|\[a-z\]\[a-zA-Z0-9\]\*|Regex which applies to static property names|no|
+|instancePattern|\[a-z\]\[a-zA-Z0-9\]\*|Regex which applies to instance property names|no|
+
+**Use this rule with the default properties by just referencing it:**
+``` xml
+
+```
+
+**Use this rule and customize it:**
+``` xml
+
+
+
+
+
+
+```
+
## VariableNamingConventions
+Deprecated
+
**Since:** PMD 5.5.0
**Priority:** High (1)
@@ -230,6 +467,12 @@ A variable naming conventions rule - customize this to your liking. Currently,
checks for final variables that should be fully capitalized and non-final variables
that should not include underscores.
+This rule is deprecated and will be removed with PMD 7.0.0. The rule is replaced
+by the more general rules {% rule "apex/codestyle/FieldNamingConventions" %},
+{% rule "apex/codestyle/FormalParameterNamingConventions" %},
+{% rule "apex/codestyle/LocalVariableNamingConventions" %}, and
+{% rule "apex/codestyle/PropertyNamingConventions" %}.
+
**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.apex.rule.codestyle.VariableNamingConventionsRule](https://github.com/pmd/pmd/blob/master/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/codestyle/VariableNamingConventionsRule.java)
**Example(s):**
diff --git a/docs/pages/release_notes.md b/docs/pages/release_notes.md
index de9a686162..268048a8fc 100644
--- a/docs/pages/release_notes.md
+++ b/docs/pages/release_notes.md
@@ -19,8 +19,77 @@ This is a {{ site.pmd.release_type }} release.
### New and noteworthy
+#### Enhanced Matlab support
+
+Thanks to the contributions from [Maikel Steneker](https://github.com/maikelsteneker) CPD for Matlab can
+now parse Matlab programs which use the question mark operator to specify access to
+class members:
+
+```
+classdef Class1
+properties (SetAccess = ?Class2)
+```
+
+CPD also understands now double quoted strings, which are supported since version R2017a of Matlab:
+
+```
+str = "This is a string"
+```
+
+#### Enhanced C++ support
+
+CPD now supports digit separators in C++ (language module "cpp"). This is a C++14 feature.
+
+Example: `auto integer_literal = 1'000'000;`
+
+The single quotes can be used to add some structure to large numbers.
+
+CPD also parses raw string literals now correctly (see [#1784](https://github.com/pmd/pmd/issues/1784)).
+
+#### New Rules
+
+* The new Apex rule {% rule "apex/codestyle/FieldNamingConventions" %} (`apex-codestyle`) checks the naming
+ conventions for field declarations. By default this rule uses the standard Apex naming convention (Camel case),
+ but it can be configured through properties.
+
+* The new Apex rule {% rule "apex/codestyle/FormalParameterNamingConventions" %} (`apex-codestyle`) checks the
+ naming conventions for formal parameters of methods. By default this rule uses the standard Apex naming
+ convention (Camel case), but it can be configured through properties.
+
+* The new Apex rule {% rule "apex/codestyle/LocalVariableNamingConventions" %} (`apex-codestyle`) checks the
+ naming conventions for local variable declarations. By default this rule uses the standard Apex naming
+ convention (Camel case), but it can be configured through properties.
+
+* The new Apex rule {% rule "apex/codestyle/PropertyNamingConventions" %} (`apex-codestyle`) checks the naming
+ conventions for property declarations. By default this rule uses the standard Apex naming convention (Camel case),
+ but it can be configured through properties.
+
+#### Modified Rules
+
+* The Apex rule {% rule "apex/codestyle/ClassNamingConventions" %} (`apex-codestyle`) can now be configured
+ using various properties for the specific kind of type declarations (e.g. class, interface, enum).
+ As before, this rule uses by default the standard Apex naming convention (Pascal case).
+
+* The Apex rule {% rule "apex/codestyle/MethodNamingConventions" %} (`apex-codestyle`) can now be configured
+ using various properties to differenciate e.g. static methods and test methods.
+ As before, this rule uses by default the standard Apex naming convention (Camel case).
+
+#### Deprecated Rules
+
+* The Apex rule {% rule "apex/codestyle/VariableNamingConventions" %} (`apex-codestyle`) has been deprecated and
+ will be removed with PMD 7.0.0. The rule is replaced by the more general rules
+ {% rule "apex/codestyle/FieldNamingConventions" %},
+ {% rule "apex/codestyle/FormalParameterNamingConventions" %},
+ {% rule "apex/codestyle/LocalVariableNamingConventions" %}, and
+ {% rule "apex/codestyle/PropertyNamingConventions" %}.
+
### Fixed Issues
+* apex
+ * [#1321](https://github.com/pmd/pmd/issues/1321): \[apex] Should VariableNamingConventions require properties to start with a lowercase letter?
+ * [#1783](https://github.com/pmd/pmd/issues/1783): \[apex] comments on constructor not recognized when the Class has inner class
+* cpp
+ * [#1784](https://github.com/pmd/pmd/issues/1784): \[cpp] Improve support for raw string literals
* dart
* [#1809](https://github.com/pmd/pmd/issues/1809): \[dart] \[cpd] Parse error with escape sequences
* java-bestpractices
@@ -29,6 +98,8 @@ This is a {{ site.pmd.release_type }} release.
* [#1804](https://github.com/pmd/pmd/issues/1804): \[java] NPE in UnnecessaryLocalBeforeReturnRule
* python
* [#1810](https://github.com/pmd/pmd/issues/1810): \[python] \[cpd] Parse error when using Python 2 backticks
+* matlab
+ * [#1830](https://github.com/pmd/pmd/issues/1830): \[matlab] \[cpd] Parse error with comments
### API Changes
@@ -38,6 +109,13 @@ This is a {{ site.pmd.release_type }} release.
* [#1802](https://github.com/pmd/pmd/pull/1802): \[python] \[cpd] Add support for Python 2 backticks - [Maikel Steneker](https://github.com/maikelsteneker)
* [#1803](https://github.com/pmd/pmd/pull/1803): \[dart] \[cpd] Dart escape sequences - [Maikel Steneker](https://github.com/maikelsteneker)
* [#1807](https://github.com/pmd/pmd/pull/1807): \[ci] Fix missing local branch issues when executing pmd-regression-tester - [BBG](https://github.com/djydewang)
+* [#1813](https://github.com/pmd/pmd/pull/1813): \[matlab] \[cpd] Matlab comments - [Maikel Steneker](https://github.com/maikelsteneker)
+* [#1816](https://github.com/pmd/pmd/pull/1816): \[apex] Fix ApexDoc handling with inner classes - [Jeff Hube](https://github.com/jeffhube)
+* [#1817](https://github.com/pmd/pmd/pull/1817): \[apex] Add configurable naming convention rules - [Jeff Hube](https://github.com/jeffhube)
+* [#1819](https://github.com/pmd/pmd/pull/1819): \[cpp] \[cpd] Add support for digit separators - [Maikel Steneker](https://github.com/maikelsteneker)
+* [#1820](https://github.com/pmd/pmd/pull/1820): \[cpp] \[cpd] Improve support for raw string literals - [Maikel Steneker](https://github.com/maikelsteneker)
+* [#1821](https://github.com/pmd/pmd/pull/1821): \[matlab] \[cpd] Matlab question mark token - [Maikel Steneker](https://github.com/maikelsteneker)
+* [#1822](https://github.com/pmd/pmd/pull/1822): \[matlab] \[cpd] Double quoted string - [Maikel Steneker](https://github.com/maikelsteneker)
{% endtocmaker %}
diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTUserClass.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTUserClass.java
index 87f94bc3ce..08a7eb2857 100644
--- a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTUserClass.java
+++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTUserClass.java
@@ -4,11 +4,8 @@
package net.sourceforge.pmd.lang.apex.ast;
-import java.lang.reflect.Field;
-
import net.sourceforge.pmd.Rule;
-import apex.jorje.data.Identifier;
import apex.jorje.semantic.ast.compilation.UserClass;
public class ASTUserClass extends ApexRootNode implements ASTUserClassOrInterface,
@@ -29,14 +26,8 @@ public class ASTUserClass extends ApexRootNode implements ASTUserClas
@Override
public String getImage() {
- try {
- Field field = node.getClass().getDeclaredField("name");
- field.setAccessible(true);
- Identifier name = (Identifier) field.get(node);
- return name.getValue();
- } catch (NoSuchFieldException | IllegalArgumentException | IllegalAccessException e) {
- throw new RuntimeException(e);
- }
+ String apexName = node.getDefiningType().getApexName();
+ return apexName.substring(apexName.lastIndexOf('.') + 1);
}
@Override
diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTUserEnum.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTUserEnum.java
index 7249cba5ce..54e5034fdc 100644
--- a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTUserEnum.java
+++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTUserEnum.java
@@ -19,7 +19,8 @@ public class ASTUserEnum extends ApexRootNode {
@Override
public String getImage() {
- return node.getClass().getName();
+ String apexName = node.getDefiningType().getApexName();
+ return apexName.substring(apexName.lastIndexOf('.') + 1);
}
public ASTModifierNode getModifiers() {
diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTUserInterface.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTUserInterface.java
index af6ccb8100..645b6e02aa 100644
--- a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTUserInterface.java
+++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTUserInterface.java
@@ -4,11 +4,8 @@
package net.sourceforge.pmd.lang.apex.ast;
-import java.lang.reflect.Field;
-
import net.sourceforge.pmd.Rule;
-import apex.jorje.data.Identifier;
import apex.jorje.semantic.ast.compilation.UserInterface;
public class ASTUserInterface extends ApexRootNode implements ASTUserClassOrInterface,
@@ -27,14 +24,8 @@ public class ASTUserInterface extends ApexRootNode implements AST
@Override
public String getImage() {
- try {
- Field field = node.getClass().getDeclaredField("name");
- field.setAccessible(true);
- Identifier name = (Identifier) field.get(node);
- return name.getValue();
- } catch (NoSuchFieldException | IllegalArgumentException | IllegalAccessException e) {
- throw new RuntimeException(e);
- }
+ String apexName = node.getDefiningType().getApexName();
+ return apexName.substring(apexName.lastIndexOf('.') + 1);
}
@Override
diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTUserTrigger.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTUserTrigger.java
index 3360750889..66c9b32004 100644
--- a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTUserTrigger.java
+++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTUserTrigger.java
@@ -4,9 +4,6 @@
package net.sourceforge.pmd.lang.apex.ast;
-import java.lang.reflect.Field;
-
-import apex.jorje.data.Identifier;
import apex.jorje.semantic.ast.compilation.UserTrigger;
public class ASTUserTrigger extends ApexRootNode {
@@ -22,14 +19,7 @@ public class ASTUserTrigger extends ApexRootNode {
@Override
public String getImage() {
- try {
- Field field = node.getClass().getDeclaredField("name");
- field.setAccessible(true);
- Identifier name = (Identifier) field.get(node);
- return name.getValue();
- } catch (NoSuchFieldException | IllegalArgumentException | IllegalAccessException e) {
- throw new RuntimeException(e);
- }
+ return node.getDefiningType().getApexName();
}
public ASTModifierNode getModifiers() {
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 fc44166cb6..75a5fc0904 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
@@ -6,10 +6,9 @@ package net.sourceforge.pmd.lang.apex.ast;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
-import java.util.ArrayList;
import java.util.HashMap;
+import java.util.LinkedList;
import java.util.List;
-import java.util.ListIterator;
import java.util.Map;
import java.util.Stack;
@@ -19,6 +18,8 @@ import org.antlr.runtime.Token;
import net.sourceforge.pmd.lang.ast.Node;
import net.sourceforge.pmd.lang.ast.SourceCodePositioner;
+import apex.jorje.data.Location;
+import apex.jorje.data.Locations;
import apex.jorje.parser.impl.ApexLexer;
import apex.jorje.semantic.ast.AstNode;
import apex.jorje.semantic.ast.compilation.AnonymousClass;
@@ -221,17 +222,17 @@ public final class ApexTreeBuilder extends AstVisitor {
// The Apex nodes with children to build.
private Stack parents = new Stack<>();
-
+
private AdditionalPassScope scope = new AdditionalPassScope(Errors.createErrors());
private final SourceCodePositioner sourceCodePositioner;
private final String sourceCode;
- private ListIterator apexDocTokenLocations;
+ private List apexDocTokenLocations;
public ApexTreeBuilder(String sourceCode) {
this.sourceCode = sourceCode;
sourceCodePositioner = new SourceCodePositioner(sourceCode);
- apexDocTokenLocations = buildApexDocTokenLocations(sourceCode).listIterator();
+ apexDocTokenLocations = buildApexDocTokenLocations(sourceCode);
}
static AbstractApexNode createNodeAdapter(T node) {
@@ -273,60 +274,87 @@ public final class ApexTreeBuilder extends AstVisitor {
nodes.pop();
parents.pop();
+ if (nodes.isEmpty()) {
+ // add the comments only at the end of the processing as the last step
+ addFormalComments();
+ }
+
return node;
}
- private void buildFormalComment(AstNode node) {
- if (parents.peek() == node) {
- ApexNode> parent = (ApexNode>) nodes.peek();
- TokenLocation tokenLocation = getApexDocTokenLocation(getApexDocIndex(parent));
- if (tokenLocation != null) {
+ private void addFormalComments() {
+ for (ApexDocTokenLocation tokenLocation : apexDocTokenLocations) {
+ ApexNode> parent = tokenLocation.nearestNode;
+ if (parent != null) {
ASTFormalComment comment = new ASTFormalComment(tokenLocation.token);
comment.calculateLineNumbers(sourceCodePositioner, tokenLocation.index,
tokenLocation.index + tokenLocation.token.length());
+
+ // move existing nodes so that we can insert the comment as the first node
+ for (int i = parent.jjtGetNumChildren(); i > 0; i--) {
+ parent.jjtAddChild(parent.jjtGetChild(i - 1), i);
+ }
+
parent.jjtAddChild(comment, 0);
comment.jjtSetParent(parent);
}
}
}
- private int getApexDocIndex(ApexNode> node) {
- final int index = node.getNode().getLoc().getStartIndex();
- return sourceCode.lastIndexOf('\n', index);
- }
-
- private TokenLocation getApexDocTokenLocation(int index) {
- TokenLocation last = null;
- while (apexDocTokenLocations.hasNext()) {
- final TokenLocation location = apexDocTokenLocations.next();
- if (location.index >= index) {
- // rollback, the next token corresponds to a different node
- apexDocTokenLocations.previous();
-
- if (last != null) {
- return last;
- }
- return null;
- }
- last = location;
+ private void buildFormalComment(AstNode node) {
+ if (parents.peek() == node) {
+ ApexNode> parent = (ApexNode>) nodes.peek();
+ assignApexDocTokenToNode(node, parent);
}
- return last;
}
- private static List buildApexDocTokenLocations(String source) {
+ /**
+ * Only remembers the node, to which the comment could belong.
+ * Since the visiting order of the nodes does not match the source order,
+ * the nodes appearing later in the source might be visiting first.
+ * The correct node will then be visited afterwards, and since the distance
+ * to the comment is smaller, it overrides the remembered node.
+ * @param jorjeNode the original node
+ * @param node the potential parent node, to which the comment could belong
+ */
+ private void assignApexDocTokenToNode(AstNode jorjeNode, ApexNode> node) {
+ Location loc = jorjeNode.getLoc();
+ if (!Locations.isReal(loc)) {
+ // Synthetic nodes such as "" don't have a location in the
+ // source code, since they are generated by the compiler
+ return;
+ }
+ // find the token, that appears as close as possible before the node
+ int nodeStart = loc.getStartIndex();
+ for (ApexDocTokenLocation tokenLocation : apexDocTokenLocations) {
+ if (tokenLocation.index > nodeStart) {
+ // this and all remaining tokens are after the node
+ // so no need to check the remaining tokens.
+ break;
+ }
+
+ int distance = nodeStart - tokenLocation.index;
+ if (tokenLocation.nearestNode == null || distance < tokenLocation.nearestNodeDistance) {
+ tokenLocation.nearestNode = node;
+ tokenLocation.nearestNodeDistance = distance;
+ }
+ }
+ }
+
+ private static List buildApexDocTokenLocations(String source) {
ANTLRStringStream stream = new ANTLRStringStream(source);
ApexLexer lexer = new ApexLexer(stream);
- ArrayList tokenLocations = new ArrayList<>();
+ List tokenLocations = new LinkedList<>();
int startIndex = 0;
Token token = lexer.nextToken();
int endIndex = lexer.getCharIndex();
+
while (token.getType() != Token.EOF) {
if (token.getType() == ApexLexer.BLOCK_COMMENT) {
-
// Filter only block comments starting with "/**"
if (token.getText().startsWith("/**")) {
- tokenLocations.add(new TokenLocation(startIndex, token.getText()));
+ tokenLocations.add(new ApexDocTokenLocation(startIndex, token.getText()));
}
}
// TODO : Check other non-doc comments and tokens of type ApexLexer.EOL_COMMENT for "NOPMD" suppressions
@@ -338,11 +366,13 @@ public final class ApexTreeBuilder extends AstVisitor {
return tokenLocations;
}
- private static class TokenLocation {
+ private static class ApexDocTokenLocation {
int index;
String token;
+ ApexNode> nearestNode;
+ int nearestNodeDistance;
- TokenLocation(int index, String token) {
+ ApexDocTokenLocation(int index, String token) {
this.index = index;
this.token = token;
}
diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/codestyle/AbstractNamingConventionsRule.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/codestyle/AbstractNamingConventionsRule.java
new file mode 100644
index 0000000000..53e6fa6894
--- /dev/null
+++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/codestyle/AbstractNamingConventionsRule.java
@@ -0,0 +1,46 @@
+/**
+ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
+ */
+
+package net.sourceforge.pmd.lang.apex.rule.codestyle;
+
+import static net.sourceforge.pmd.properties.PropertyFactory.regexProperty;
+
+import java.util.Map;
+import java.util.regex.Pattern;
+
+import net.sourceforge.pmd.lang.apex.ast.ApexNode;
+import net.sourceforge.pmd.lang.apex.rule.AbstractApexRule;
+import net.sourceforge.pmd.properties.PropertyBuilder;
+import net.sourceforge.pmd.properties.PropertyDescriptor;
+
+abstract class AbstractNamingConventionsRule extends AbstractApexRule {
+ protected static final Pattern CAMEL_CASE = Pattern.compile("[a-z][a-zA-Z0-9]*");
+ protected static final Pattern CAMEL_CASE_WITH_UNDERSCORES = Pattern.compile("[a-z][a-zA-Z0-9_]*");
+ protected static final Pattern PASCAL_CASE_WITH_UNDERSCORES = Pattern.compile("[A-Z][a-zA-Z0-9_]*");
+ protected static final Pattern ALL_CAPS = Pattern.compile("[A-Z][A-Z0-9_]*");
+
+ abstract String displayName(String name);
+
+ protected void checkMatches(PropertyDescriptor propertyDescriptor, ApexNode> node, Object data) {
+ Pattern regex = getProperty(propertyDescriptor);
+ String name = node.getImage();
+ if (!regex.matcher(name).matches()) {
+ String displayName = displayName(propertyDescriptor.name());
+ addViolation(data, node, new Object[] { displayName, name, regex.toString() });
+ }
+ }
+
+ protected void checkMatches(PropertyDescriptor propertyDescriptor, Pattern overridePattern, ApexNode> node, Object data) {
+ String name = node.getImage();
+ if (!overridePattern.matcher(name).matches()) {
+ String displayName = displayName(propertyDescriptor.name());
+ addViolation(data, node, new Object[] { displayName, name, overridePattern.toString() });
+ }
+ }
+
+ protected static PropertyBuilder.RegexPropertyBuilder prop(String name, String displayName, Map descriptorToDisplayNames) {
+ descriptorToDisplayNames.put(name, displayName);
+ return regexProperty(name).desc("Regex which applies to " + displayName + " names");
+ }
+}
diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/codestyle/ClassNamingConventionsRule.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/codestyle/ClassNamingConventionsRule.java
index 1d1529793e..a2e235f127 100644
--- a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/codestyle/ClassNamingConventionsRule.java
+++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/codestyle/ClassNamingConventionsRule.java
@@ -4,25 +4,74 @@
package net.sourceforge.pmd.lang.apex.rule.codestyle;
-import net.sourceforge.pmd.lang.apex.ast.ASTUserClass;
-import net.sourceforge.pmd.lang.apex.ast.ASTUserInterface;
-import net.sourceforge.pmd.lang.apex.rule.AbstractApexRule;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.regex.Pattern;
-public class ClassNamingConventionsRule extends AbstractApexRule {
+import net.sourceforge.pmd.lang.apex.ast.ASTUserClass;
+import net.sourceforge.pmd.lang.apex.ast.ASTUserEnum;
+import net.sourceforge.pmd.lang.apex.ast.ASTUserInterface;
+import net.sourceforge.pmd.properties.PropertyDescriptor;
+
+public class ClassNamingConventionsRule extends AbstractNamingConventionsRule {
+ private static final Map DESCRIPTOR_TO_DISPLAY_NAME = new HashMap<>();
+
+ private static final PropertyDescriptor TEST_CLASS_REGEX = prop("testClassPattern", "test class",
+ DESCRIPTOR_TO_DISPLAY_NAME).defaultValue(PASCAL_CASE_WITH_UNDERSCORES).build();
+
+ private static final PropertyDescriptor ABSTRACT_CLASS_REGEX = prop("abstractClassPattern", "abstract class",
+ DESCRIPTOR_TO_DISPLAY_NAME).defaultValue(PASCAL_CASE_WITH_UNDERSCORES).build();
+
+ private static final PropertyDescriptor CLASS_REGEX = prop("classPattern", "class",
+ DESCRIPTOR_TO_DISPLAY_NAME).defaultValue(PASCAL_CASE_WITH_UNDERSCORES).build();
+
+ private static final PropertyDescriptor INTERFACE_REGEX = prop("interfacePattern", "interface",
+ DESCRIPTOR_TO_DISPLAY_NAME).defaultValue(PASCAL_CASE_WITH_UNDERSCORES).build();
+
+ private static final PropertyDescriptor ENUM_REGEX = prop("enumPattern", "enum",
+ DESCRIPTOR_TO_DISPLAY_NAME).defaultValue(PASCAL_CASE_WITH_UNDERSCORES).build();
+
+ public ClassNamingConventionsRule() {
+ definePropertyDescriptor(TEST_CLASS_REGEX);
+ definePropertyDescriptor(ABSTRACT_CLASS_REGEX);
+ definePropertyDescriptor(CLASS_REGEX);
+ definePropertyDescriptor(INTERFACE_REGEX);
+ definePropertyDescriptor(ENUM_REGEX);
+
+ addRuleChainVisit(ASTUserClass.class);
+ addRuleChainVisit(ASTUserInterface.class);
+ addRuleChainVisit(ASTUserEnum.class);
+ }
@Override
public Object visit(ASTUserClass node, Object data) {
- if (Character.isLowerCase(node.getImage().charAt(0))) {
- addViolation(data, node);
+ if (node.getModifiers().isTest()) {
+ checkMatches(TEST_CLASS_REGEX, node, data);
+ } else if (node.getModifiers().isAbstract()) {
+ checkMatches(ABSTRACT_CLASS_REGEX, node, data);
+ } else {
+ checkMatches(CLASS_REGEX, node, data);
}
+
return data;
}
@Override
public Object visit(ASTUserInterface node, Object data) {
- if (Character.isLowerCase(node.getImage().charAt(0))) {
- addViolation(data, node);
- }
+ checkMatches(INTERFACE_REGEX, node, data);
+
return data;
}
+
+ @Override
+ public Object visit(ASTUserEnum node, Object data) {
+ checkMatches(ENUM_REGEX, node, data);
+
+ return data;
+ }
+
+ @Override
+ protected String displayName(String name) {
+ return DESCRIPTOR_TO_DISPLAY_NAME.get(name);
+ }
}
diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/codestyle/FieldNamingConventionsRule.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/codestyle/FieldNamingConventionsRule.java
new file mode 100644
index 0000000000..d5c19968dc
--- /dev/null
+++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/codestyle/FieldNamingConventionsRule.java
@@ -0,0 +1,77 @@
+/**
+ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
+ */
+
+package net.sourceforge.pmd.lang.apex.rule.codestyle;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.regex.Pattern;
+
+import net.sourceforge.pmd.lang.apex.ast.ASTField;
+import net.sourceforge.pmd.lang.apex.ast.ASTModifierNode;
+import net.sourceforge.pmd.lang.apex.ast.ASTProperty;
+import net.sourceforge.pmd.lang.apex.ast.ASTUserEnum;
+import net.sourceforge.pmd.properties.PropertyDescriptor;
+
+public class FieldNamingConventionsRule extends AbstractNamingConventionsRule {
+ private static final Map DESCRIPTOR_TO_DISPLAY_NAME = new HashMap<>();
+
+ private static final PropertyDescriptor ENUM_CONSTANT_REGEX = prop("enumConstantPattern", "enum constant field",
+ DESCRIPTOR_TO_DISPLAY_NAME).defaultValue(ALL_CAPS).build();
+
+ private static final PropertyDescriptor CONSTANT_REGEX = prop("constantPattern", "constant field",
+ DESCRIPTOR_TO_DISPLAY_NAME).defaultValue(ALL_CAPS).build();
+
+ private static final PropertyDescriptor FINAL_REGEX = prop("finalPattern", "final field",
+ DESCRIPTOR_TO_DISPLAY_NAME).defaultValue(CAMEL_CASE).build();
+
+ private static final PropertyDescriptor STATIC_REGEX = prop("staticPattern", "static field",
+ DESCRIPTOR_TO_DISPLAY_NAME).defaultValue(CAMEL_CASE).build();
+
+ private static final PropertyDescriptor INSTANCE_REGEX = prop("instancePattern", "instance field",
+ DESCRIPTOR_TO_DISPLAY_NAME).defaultValue(CAMEL_CASE).build();
+
+ public FieldNamingConventionsRule() {
+ definePropertyDescriptor(ENUM_CONSTANT_REGEX);
+ definePropertyDescriptor(CONSTANT_REGEX);
+ definePropertyDescriptor(FINAL_REGEX);
+ definePropertyDescriptor(STATIC_REGEX);
+ definePropertyDescriptor(INSTANCE_REGEX);
+
+ setProperty(CODECLIMATE_CATEGORIES, "Style");
+ // Note: x10 as Apex has not automatic refactoring
+ setProperty(CODECLIMATE_REMEDIATION_MULTIPLIER, 1);
+ setProperty(CODECLIMATE_BLOCK_HIGHLIGHTING, false);
+
+ addRuleChainVisit(ASTField.class);
+ }
+
+ @Override
+ public Object visit(ASTField node, Object data) {
+ if (node.getFirstParentOfType(ASTProperty.class) != null) {
+ return data;
+ }
+
+ ASTModifierNode modifiers = node.getModifiers();
+
+ if (node.getFirstParentOfType(ASTUserEnum.class) != null) {
+ checkMatches(ENUM_CONSTANT_REGEX, node, data);
+ } else if (modifiers.isFinal() && modifiers.isStatic()) {
+ checkMatches(CONSTANT_REGEX, node, data);
+ } else if (modifiers.isFinal()) {
+ checkMatches(FINAL_REGEX, node, data);
+ } else if (modifiers.isStatic()) {
+ checkMatches(STATIC_REGEX, node, data);
+ } else {
+ checkMatches(INSTANCE_REGEX, node, data);
+ }
+
+ return data;
+ }
+
+ @Override
+ protected String displayName(String name) {
+ return DESCRIPTOR_TO_DISPLAY_NAME.get(name);
+ }
+}
diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/codestyle/FormalParameterNamingConventionsRule.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/codestyle/FormalParameterNamingConventionsRule.java
new file mode 100644
index 0000000000..8c611cb2e3
--- /dev/null
+++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/codestyle/FormalParameterNamingConventionsRule.java
@@ -0,0 +1,55 @@
+/**
+ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
+ */
+
+package net.sourceforge.pmd.lang.apex.rule.codestyle;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.regex.Pattern;
+
+import net.sourceforge.pmd.lang.apex.ast.ASTParameter;
+import net.sourceforge.pmd.properties.PropertyDescriptor;
+
+public class FormalParameterNamingConventionsRule extends AbstractNamingConventionsRule {
+ private static final Map DESCRIPTOR_TO_DISPLAY_NAME = new HashMap<>();
+
+ private static final PropertyDescriptor FINAL_METHOD_PARAMETER_REGEX = prop("finalMethodParameterPattern", "final method parameter",
+ DESCRIPTOR_TO_DISPLAY_NAME).defaultValue(CAMEL_CASE).build();
+
+ private static final PropertyDescriptor METHOD_PARAMETER_REGEX = prop("methodParameterPattern", "method parameter",
+ DESCRIPTOR_TO_DISPLAY_NAME).defaultValue(CAMEL_CASE).build();
+
+ public FormalParameterNamingConventionsRule() {
+ definePropertyDescriptor(FINAL_METHOD_PARAMETER_REGEX);
+ definePropertyDescriptor(METHOD_PARAMETER_REGEX);
+
+ setProperty(CODECLIMATE_CATEGORIES, "Style");
+ // Note: x10 as Apex has not automatic refactoring
+ setProperty(CODECLIMATE_REMEDIATION_MULTIPLIER, 1);
+ setProperty(CODECLIMATE_BLOCK_HIGHLIGHTING, false);
+
+ addRuleChainVisit(ASTParameter.class);
+ }
+
+ @Override
+ public Object visit(ASTParameter node, Object data) {
+ // classes that extend Exception will contains methods that have parameters with null names
+ if (node.getImage() == null) {
+ return data;
+ }
+
+ if (node.getModifiers().isFinal()) {
+ checkMatches(FINAL_METHOD_PARAMETER_REGEX, node, data);
+ } else {
+ checkMatches(METHOD_PARAMETER_REGEX, node, data);
+ }
+
+ return data;
+ }
+
+ @Override
+ protected String displayName(String name) {
+ return DESCRIPTOR_TO_DISPLAY_NAME.get(name);
+ }
+}
diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/codestyle/LocalVariableNamingConventionsRule.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/codestyle/LocalVariableNamingConventionsRule.java
new file mode 100644
index 0000000000..ee19980719
--- /dev/null
+++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/codestyle/LocalVariableNamingConventionsRule.java
@@ -0,0 +1,51 @@
+/**
+ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
+ */
+
+package net.sourceforge.pmd.lang.apex.rule.codestyle;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.regex.Pattern;
+
+import net.sourceforge.pmd.lang.apex.ast.ASTVariableDeclaration;
+import net.sourceforge.pmd.lang.apex.ast.ASTVariableDeclarationStatements;
+import net.sourceforge.pmd.properties.PropertyDescriptor;
+
+public class LocalVariableNamingConventionsRule extends AbstractNamingConventionsRule {
+ private static final Map DESCRIPTOR_TO_DISPLAY_NAME = new HashMap<>();
+
+ private static final PropertyDescriptor FINAL_REGEX = prop("finalLocalPattern", "final local variable",
+ DESCRIPTOR_TO_DISPLAY_NAME).defaultValue(CAMEL_CASE).build();
+
+ private static final PropertyDescriptor LOCAL_REGEX = prop("localPattern", "local variable",
+ DESCRIPTOR_TO_DISPLAY_NAME).defaultValue(CAMEL_CASE).build();
+
+ public LocalVariableNamingConventionsRule() {
+ definePropertyDescriptor(FINAL_REGEX);
+ definePropertyDescriptor(LOCAL_REGEX);
+
+ setProperty(CODECLIMATE_CATEGORIES, "Style");
+ // Note: x10 as Apex has not automatic refactoring
+ setProperty(CODECLIMATE_REMEDIATION_MULTIPLIER, 1);
+ setProperty(CODECLIMATE_BLOCK_HIGHLIGHTING, false);
+
+ addRuleChainVisit(ASTVariableDeclaration.class);
+ }
+
+ @Override
+ public Object visit(ASTVariableDeclaration node, Object data) {
+ if (node.getFirstParentOfType(ASTVariableDeclarationStatements.class).getModifiers().isFinal()) {
+ checkMatches(FINAL_REGEX, node, data);
+ } else {
+ checkMatches(LOCAL_REGEX, node, data);
+ }
+
+ return data;
+ }
+
+ @Override
+ protected String displayName(String name) {
+ return DESCRIPTOR_TO_DISPLAY_NAME.get(name);
+ }
+}
diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/codestyle/MethodNamingConventionsRule.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/codestyle/MethodNamingConventionsRule.java
index 23d2d8ec63..9c551f951d 100644
--- a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/codestyle/MethodNamingConventionsRule.java
+++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/codestyle/MethodNamingConventionsRule.java
@@ -6,21 +6,39 @@ package net.sourceforge.pmd.lang.apex.rule.codestyle;
import static net.sourceforge.pmd.properties.PropertyFactory.booleanProperty;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.regex.Pattern;
+
import net.sourceforge.pmd.lang.apex.ast.ASTMethod;
import net.sourceforge.pmd.lang.apex.ast.ASTProperty;
-import net.sourceforge.pmd.lang.apex.rule.AbstractApexRule;
+import net.sourceforge.pmd.lang.apex.ast.ASTUserEnum;
import net.sourceforge.pmd.properties.PropertyDescriptor;
-public class MethodNamingConventionsRule extends AbstractApexRule {
+public class MethodNamingConventionsRule extends AbstractNamingConventionsRule {
+ private static final Map DESCRIPTOR_TO_DISPLAY_NAME = new HashMap<>();
+
+ private static final PropertyDescriptor TEST_REGEX = prop("testPattern", "test method",
+ DESCRIPTOR_TO_DISPLAY_NAME).defaultValue(CAMEL_CASE).build();
+
+ private static final PropertyDescriptor STATIC_REGEX = prop("staticPattern", "static method",
+ DESCRIPTOR_TO_DISPLAY_NAME).defaultValue(CAMEL_CASE).build();
+
+ private static final PropertyDescriptor INSTANCE_REGEX = prop("instancePattern", "instance method",
+ DESCRIPTOR_TO_DISPLAY_NAME).defaultValue(CAMEL_CASE).build();
private static final PropertyDescriptor SKIP_TEST_METHOD_UNDERSCORES_DESCRIPTOR
= booleanProperty("skipTestMethodUnderscores")
- .desc("Skip underscores in test methods")
+ .desc("deprecated! Skip underscores in test methods")
.defaultValue(false)
.build();
public MethodNamingConventionsRule() {
definePropertyDescriptor(SKIP_TEST_METHOD_UNDERSCORES_DESCRIPTOR);
+ definePropertyDescriptor(TEST_REGEX);
+ definePropertyDescriptor(STATIC_REGEX);
+ definePropertyDescriptor(INSTANCE_REGEX);
+
addRuleChainVisit(ASTMethod.class);
}
@@ -29,21 +47,35 @@ public class MethodNamingConventionsRule extends AbstractApexRule {
if (isOverriddenMethod(node) || isPropertyAccessor(node) || isConstructor(node)) {
return data;
}
- if (isTestMethod(node) && getProperty(SKIP_TEST_METHOD_UNDERSCORES_DESCRIPTOR)) {
+
+ if ("".equals(node.getImage()) || "clone".equals(node.getImage())) {
return data;
}
- String methodName = node.getImage();
+ if (node.getFirstParentOfType(ASTUserEnum.class) != null) {
+ return data;
+ }
- if (Character.isUpperCase(methodName.charAt(0))) {
- addViolationWithMessage(data, node, "Method names should not start with capital letters");
- }
- if (methodName.indexOf('_') >= 0) {
- addViolationWithMessage(data, node, "Method names should not contain underscores");
+ if (node.getModifiers().isTest()) {
+ if (getProperty(SKIP_TEST_METHOD_UNDERSCORES_DESCRIPTOR)) {
+ checkMatches(TEST_REGEX, CAMEL_CASE_WITH_UNDERSCORES, node, data);
+ } else {
+ checkMatches(TEST_REGEX, node, data);
+ }
+ } else if (node.getModifiers().isStatic()) {
+ checkMatches(STATIC_REGEX, node, data);
+ } else {
+ checkMatches(INSTANCE_REGEX, node, data);
}
+
return data;
}
+ @Override
+ protected String displayName(String name) {
+ return DESCRIPTOR_TO_DISPLAY_NAME.get(name);
+ }
+
private boolean isOverriddenMethod(ASTMethod node) {
return node.getModifiers().isOverride();
}
@@ -55,8 +87,4 @@ public class MethodNamingConventionsRule extends AbstractApexRule {
private boolean isConstructor(ASTMethod node) {
return node.isConstructor();
}
-
- private boolean isTestMethod(ASTMethod node) {
- return node.getModifiers().isTest();
- }
}
diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/codestyle/PropertyNamingConventionsRule.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/codestyle/PropertyNamingConventionsRule.java
new file mode 100644
index 0000000000..70ad5594e1
--- /dev/null
+++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/codestyle/PropertyNamingConventionsRule.java
@@ -0,0 +1,55 @@
+/**
+ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
+ */
+
+package net.sourceforge.pmd.lang.apex.rule.codestyle;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.regex.Pattern;
+
+import net.sourceforge.pmd.lang.apex.ast.ASTField;
+import net.sourceforge.pmd.lang.apex.ast.ASTProperty;
+import net.sourceforge.pmd.properties.PropertyDescriptor;
+
+public class PropertyNamingConventionsRule extends AbstractNamingConventionsRule {
+ private static final Map DESCRIPTOR_TO_DISPLAY_NAME = new HashMap<>();
+
+ private static final PropertyDescriptor STATIC_REGEX = prop("staticPattern", "static property",
+ DESCRIPTOR_TO_DISPLAY_NAME).defaultValue(CAMEL_CASE).build();
+
+ private static final PropertyDescriptor INSTANCE_REGEX = prop("instancePattern", "instance property",
+ DESCRIPTOR_TO_DISPLAY_NAME).defaultValue(CAMEL_CASE).build();
+
+ public PropertyNamingConventionsRule() {
+ definePropertyDescriptor(STATIC_REGEX);
+ definePropertyDescriptor(INSTANCE_REGEX);
+
+ setProperty(CODECLIMATE_CATEGORIES, "Style");
+ // Note: x10 as Apex has not automatic refactoring
+ setProperty(CODECLIMATE_REMEDIATION_MULTIPLIER, 1);
+ setProperty(CODECLIMATE_BLOCK_HIGHLIGHTING, false);
+
+ addRuleChainVisit(ASTField.class);
+ }
+
+ @Override
+ public Object visit(ASTField node, Object data) {
+ if (node.getFirstParentOfType(ASTProperty.class) == null) {
+ return data;
+ }
+
+ if (node.getModifiers().isStatic()) {
+ checkMatches(STATIC_REGEX, node, data);
+ } else {
+ checkMatches(INSTANCE_REGEX, node, data);
+ }
+
+ return data;
+ }
+
+ @Override
+ protected String displayName(String name) {
+ return DESCRIPTOR_TO_DISPLAY_NAME.get(name);
+ }
+}
diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/codestyle/VariableNamingConventionsRule.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/codestyle/VariableNamingConventionsRule.java
index 830403adaf..b5ba653d29 100644
--- a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/codestyle/VariableNamingConventionsRule.java
+++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/codestyle/VariableNamingConventionsRule.java
@@ -21,6 +21,7 @@ import net.sourceforge.pmd.lang.apex.ast.ApexNode;
import net.sourceforge.pmd.lang.apex.rule.AbstractApexRule;
import net.sourceforge.pmd.properties.PropertyDescriptor;
+@Deprecated
public class VariableNamingConventionsRule extends AbstractApexRule {
private boolean checkMembers;
diff --git a/pmd-apex/src/main/resources/category/apex/codestyle.xml b/pmd-apex/src/main/resources/category/apex/codestyle.xml
index ef53ac90d0..262b6f9757 100644
--- a/pmd-apex/src/main/resources/category/apex/codestyle.xml
+++ b/pmd-apex/src/main/resources/category/apex/codestyle.xml
@@ -11,16 +11,23 @@ Rules which enforce a specific coding style.
-Class names should always begin with an upper case character.
+ Configurable naming conventions for type declarations. This rule reports
+ type declarations which do not match the regex that applies to their
+ specific kind (e.g. enum or interface). Each regex can be configured through
+ properties.
+
+ By default this rule uses the standard Apex naming convention (Pascal case).
1
-
@@ -95,6 +102,30 @@ if (foo) { // preferred approach
+
+
+ Configurable naming conventions for field declarations. This rule reports variable declarations
+ which do not match the regex that applies to their specific kind ---e.g. constants (static final),
+ static field, final field. Each regex can be configured through properties.
+
+ By default this rule uses the standard Apex naming convention (Camel case).
+
+ 1
+
+
+
+
+
-
+
-Method names should always begin with a lower case character, and should not contain underscores.
+ Configurable naming conventions for formal parameters of methods.
+ This rule reports formal parameters which do not match the regex that applies to their
+ specific kind (e.g. method parameter, or final method parameter). Each regex can be
+ configured through properties.
+
+ By default this rule uses the standard Apex naming convention (Camel case).
1
+
+
+
+
+
+ Configurable naming conventions for local variable declarations.
+ This rule reports variable declarations which do not match the regex that applies to their
+ specific kind (e.g. local variable, or final local variable). Each regex can be configured through
+ properties.
+
+ By default this rule uses the standard Apex naming convention (Camel case).
+
+ 1
+
+
+
+
+ Configurable naming conventions for method declarations. This rule reports
+ method declarations which do not match the regex that applies to their
+ specific kind (e.g. static method, or test method). Each regex can be
+ configured through properties.
+
+ By default this rule uses the standard Apex naming convention (Camel case).
+
+ 1
+
+
+
+
+
+
+
+ Configurable naming conventions for property declarations. This rule reports
+ property declarations which do not match the regex that applies to their
+ specific kind (e.g. static property, or instance property). Each regex can be
+ configured through properties.
+
+ By default this rule uses the standard Apex naming convention (Camel case).
+
+ 1
+
+
+
+
+
@@ -199,6 +313,12 @@ Integer b;
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 deprecated and will be removed with PMD 7.0.0. The rule is replaced
+by the more general rules {% rule "apex/codestyle/FieldNamingConventions" %},
+{% rule "apex/codestyle/FormalParameterNamingConventions" %},
+{% rule "apex/codestyle/LocalVariableNamingConventions" %}, and
+{% rule "apex/codestyle/PropertyNamingConventions" %}.
1
diff --git a/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/ast/ASTUserClassTest.java b/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/ast/ASTUserClassTest.java
new file mode 100644
index 0000000000..7e2ea41201
--- /dev/null
+++ b/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/ast/ASTUserClassTest.java
@@ -0,0 +1,31 @@
+/**
+ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
+ */
+
+package net.sourceforge.pmd.lang.apex.ast;
+
+import static net.sourceforge.pmd.lang.apex.ast.ApexParserTestHelpers.parse;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import apex.jorje.semantic.ast.compilation.Compilation;
+
+public class ASTUserClassTest {
+
+ @Test
+ public void testClassName() {
+ ApexNode node = parse("class Foo { }");
+ Assert.assertSame(ASTUserClass.class, node.getClass());
+ Assert.assertEquals("Foo", node.getImage());
+ }
+
+ @Test
+ public void testInnerClassName() {
+ ApexNode node = parse("class Foo { class Bar { } }");
+ Assert.assertSame(ASTUserClass.class, node.getClass());
+ ASTUserClass innerNode = node.getFirstDescendantOfType(ASTUserClass.class);
+ Assert.assertNotNull(innerNode);
+ Assert.assertEquals("Bar", innerNode.getImage());
+ }
+}
diff --git a/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/ast/ASTUserEnumTest.java b/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/ast/ASTUserEnumTest.java
new file mode 100644
index 0000000000..827833e4bd
--- /dev/null
+++ b/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/ast/ASTUserEnumTest.java
@@ -0,0 +1,24 @@
+/**
+ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
+ */
+
+package net.sourceforge.pmd.lang.apex.ast;
+
+import static net.sourceforge.pmd.lang.apex.ast.ApexParserTestHelpers.parse;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import apex.jorje.semantic.ast.compilation.Compilation;
+
+public class ASTUserEnumTest {
+
+ @Test
+ public void testEnumName() {
+ ApexNode node = parse("class Foo { enum Bar { } }");
+ Assert.assertSame(ASTUserClass.class, node.getClass());
+ ASTUserEnum enumNode = node.getFirstDescendantOfType(ASTUserEnum.class);
+ Assert.assertNotNull(enumNode);
+ Assert.assertEquals("Bar", enumNode.getImage());
+ }
+}
diff --git a/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/ast/ASTUserInterfaceTest.java b/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/ast/ASTUserInterfaceTest.java
new file mode 100644
index 0000000000..f7944db734
--- /dev/null
+++ b/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/ast/ASTUserInterfaceTest.java
@@ -0,0 +1,31 @@
+/**
+ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
+ */
+
+package net.sourceforge.pmd.lang.apex.ast;
+
+import static net.sourceforge.pmd.lang.apex.ast.ApexParserTestHelpers.parse;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import apex.jorje.semantic.ast.compilation.Compilation;
+
+public class ASTUserInterfaceTest {
+
+ @Test
+ public void testInterfaceName() {
+ ApexNode node = parse("interface Foo { }");
+ Assert.assertSame(ASTUserInterface.class, node.getClass());
+ Assert.assertEquals("Foo", node.getImage());
+ }
+
+ @Test
+ public void testInnerInterfaceName() {
+ ApexNode node = parse("class Foo { interface Bar { } }");
+ Assert.assertSame(ASTUserClass.class, node.getClass());
+ ASTUserInterface innerNode = node.getFirstDescendantOfType(ASTUserInterface.class);
+ Assert.assertNotNull(innerNode);
+ Assert.assertEquals("Bar", innerNode.getImage());
+ }
+}
diff --git a/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/rule/codestyle/FieldNamingConventionsTest.java b/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/rule/codestyle/FieldNamingConventionsTest.java
new file mode 100644
index 0000000000..823a7f32fc
--- /dev/null
+++ b/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/rule/codestyle/FieldNamingConventionsTest.java
@@ -0,0 +1,11 @@
+/**
+ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
+ */
+
+package net.sourceforge.pmd.lang.apex.rule.codestyle;
+
+import net.sourceforge.pmd.testframework.PmdRuleTst;
+
+public class FieldNamingConventionsTest extends PmdRuleTst {
+ // no additional unit tests
+}
diff --git a/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/rule/codestyle/FormalParameterNamingConventionsTest.java b/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/rule/codestyle/FormalParameterNamingConventionsTest.java
new file mode 100644
index 0000000000..45dba2964e
--- /dev/null
+++ b/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/rule/codestyle/FormalParameterNamingConventionsTest.java
@@ -0,0 +1,11 @@
+/**
+ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
+ */
+
+package net.sourceforge.pmd.lang.apex.rule.codestyle;
+
+import net.sourceforge.pmd.testframework.PmdRuleTst;
+
+public class FormalParameterNamingConventionsTest extends PmdRuleTst {
+ // no additional unit tests
+}
diff --git a/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/rule/codestyle/LocalVariableNamingConventionsTest.java b/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/rule/codestyle/LocalVariableNamingConventionsTest.java
new file mode 100644
index 0000000000..6ddf91a354
--- /dev/null
+++ b/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/rule/codestyle/LocalVariableNamingConventionsTest.java
@@ -0,0 +1,11 @@
+/**
+ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
+ */
+
+package net.sourceforge.pmd.lang.apex.rule.codestyle;
+
+import net.sourceforge.pmd.testframework.PmdRuleTst;
+
+public class LocalVariableNamingConventionsTest extends PmdRuleTst {
+ // no additional unit tests
+}
diff --git a/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/rule/codestyle/PropertyNamingConventionsTest.java b/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/rule/codestyle/PropertyNamingConventionsTest.java
new file mode 100644
index 0000000000..413e80ed09
--- /dev/null
+++ b/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/rule/codestyle/PropertyNamingConventionsTest.java
@@ -0,0 +1,11 @@
+/**
+ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
+ */
+
+package net.sourceforge.pmd.lang.apex.rule.codestyle;
+
+import net.sourceforge.pmd.testframework.PmdRuleTst;
+
+public class PropertyNamingConventionsTest extends PmdRuleTst {
+ // no additional unit tests
+}
diff --git a/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/codestyle/xml/ClassNamingConventions.xml b/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/codestyle/xml/ClassNamingConventions.xml
index 4f9f93b79f..8817e4f30b 100644
--- a/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/codestyle/xml/ClassNamingConventions.xml
+++ b/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/codestyle/xml/ClassNamingConventions.xml
@@ -20,5 +20,143 @@ public class foo {};
public class FooBar {};
]]>
+
+
+ test class all is well
+ 0
+
+
+
+
+ abstract class all is well
+ 0
+
+
+
+
+ class all is well
+ 0
+
+
+
+
+ interface all is well
+ 0
+
+
+
+
+ enum all is well
+ 0
+
+
+
+
+ test class default is title case
+ 1
+
+ The test class name 'testClass' doesn't match '[A-Z][a-zA-Z0-9_]*'
+
+
+
+
+
+ abstract class default is title case
+ 1
+
+ The abstract class name 'abstractClass' doesn't match '[A-Z][a-zA-Z0-9_]*'
+
+
+
+
+
+ class default is title case
+ 1
+
+ The class name 'fooClass' doesn't match '[A-Z][a-zA-Z0-9_]*'
+
+
+
+
+
+ interface default is title case
+ 1
+
+ The interface name 'fooInterface' doesn't match '[A-Z][a-zA-Z0-9_]*'
+
+
+
+
+
+ enum default is title case
+ 1
+
+ The enum name 'fooEnum' doesn't match '[A-Z][a-zA-Z0-9_]*'
+
+
+
+
+
+ custom test class pattern
+ [a-zA-Z0-9_]+
+ 0
+
+
+
+
+ custom abstract class pattern
+ [a-zA-Z0-9_]+
+ 0
+
+
+
+
+ custom class pattern
+ [a-zA-Z0-9_]+
+ 0
+
+
+
+
+ custom interface pattern
+ [a-zA-Z0-9_]+
+ 0
+
+
diff --git a/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/codestyle/xml/FieldNamingConventions.xml b/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/codestyle/xml/FieldNamingConventions.xml
new file mode 100644
index 0000000000..b5de65a6b6
--- /dev/null
+++ b/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/codestyle/xml/FieldNamingConventions.xml
@@ -0,0 +1,157 @@
+
+
+
+
+
+ all is well
+ 0
+
+
+
+
+ default is all caps for constants, camel case for others
+ 4
+
+ The constant field name 'constantField' doesn't match '[A-Z][A-Z0-9_]*'
+ The final field name 'FINAL_FIELD' doesn't match '[a-z][a-zA-Z0-9]*'
+ The static field name 'STATIC_FIELD' doesn't match '[a-z][a-zA-Z0-9]*'
+ The instance field name 'INSTANCE_FIELD' doesn't match '[a-z][a-zA-Z0-9]*'
+
+
+
+
+
+ enum default is all caps and underscores
+ 1
+
+ The enum constant field name 'default' doesn't match '[A-Z][A-Z0-9_]*'
+
+
+
+
+
+ ignores properties
+ 0
+
+
+
+
+ custom enum constant pattern
+ [a-zA-Z0-9]+
+ 0
+
+
+
+
+ custom constant pattern
+ [a-zA-Z0-9_]+
+ 3
+
+ The final field name 'FINAL_FIELD' doesn't match '[a-z][a-zA-Z0-9]*'
+ The static field name 'STATIC_FIELD' doesn't match '[a-z][a-zA-Z0-9]*'
+ The instance field name 'INSTANCE_FIELD' doesn't match '[a-z][a-zA-Z0-9]*'
+
+
+
+
+
+ custom final pattern
+ [a-zA-Z0-9_]+
+ 3
+
+ The constant field name 'constantField' doesn't match '[A-Z][A-Z0-9_]*'
+ The static field name 'STATIC_FIELD' doesn't match '[a-z][a-zA-Z0-9]*'
+ The instance field name 'INSTANCE_FIELD' doesn't match '[a-z][a-zA-Z0-9]*'
+
+
+
+
+
+ custom static pattern
+ [a-zA-Z0-9_]+
+ 3
+
+ The constant field name 'constantField' doesn't match '[A-Z][A-Z0-9_]*'
+ The final field name 'FINAL_FIELD' doesn't match '[a-z][a-zA-Z0-9]*'
+ The instance field name 'INSTANCE_FIELD' doesn't match '[a-z][a-zA-Z0-9]*'
+
+
+
+
+
+ custom instance pattern
+ [a-zA-Z0-9_]+
+ 3
+
+ The constant field name 'constantField' doesn't match '[A-Z][A-Z0-9_]*'
+ The final field name 'FINAL_FIELD' doesn't match '[a-z][a-zA-Z0-9]*'
+ The static field name 'STATIC_FIELD' doesn't match '[a-z][a-zA-Z0-9]*'
+
+
+
+
+
diff --git a/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/codestyle/xml/FormalParameterNamingConventions.xml b/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/codestyle/xml/FormalParameterNamingConventions.xml
new file mode 100644
index 0000000000..7cc3fc943d
--- /dev/null
+++ b/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/codestyle/xml/FormalParameterNamingConventions.xml
@@ -0,0 +1,68 @@
+
+
+
+
+
+ all is well
+ 0
+
+
+
+
+ default is camel case
+ 2
+
+ The final method parameter name 'FINAL_METHOD_PARAMETER' doesn't match '[a-z][a-zA-Z0-9]*'
+ The method parameter name 'METHOD_PARAMETER' doesn't match '[a-z][a-zA-Z0-9]*'
+
+
+
+
+
+ custom final method parameter pattern
+ [a-zA-Z0-9_]+
+ 1
+
+ The method parameter name 'METHOD_PARAMETER' doesn't match '[a-z][a-zA-Z0-9]*'
+
+
+
+
+
+ custom method parameter pattern
+ [a-zA-Z0-9_]+
+ 1
+
+ The final method parameter name 'FINAL_METHOD_PARAMETER' doesn't match '[a-z][a-zA-Z0-9]*'
+
+
+
+
+
+ ignores null parameter names
+ 0
+
+
+
+
diff --git a/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/codestyle/xml/LocalVariableNamingConventions.xml b/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/codestyle/xml/LocalVariableNamingConventions.xml
new file mode 100644
index 0000000000..ff5098dfd2
--- /dev/null
+++ b/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/codestyle/xml/LocalVariableNamingConventions.xml
@@ -0,0 +1,72 @@
+
+
+
+
+
+ all is well
+ 0
+
+
+
+
+ default is camel case
+ 2
+
+ The final local variable name 'FINAL_LOCAL_VARIABLE' doesn't match '[a-z][a-zA-Z0-9]*'
+ The local variable name 'LOCAL_VARIABLE' doesn't match '[a-z][a-zA-Z0-9]*'
+
+
+
+
+
+ custom final local pattern
+ [a-zA-Z0-9_]+
+ 1
+
+ The local variable name 'LOCAL_VARIABLE' doesn't match '[a-z][a-zA-Z0-9]*'
+
+
+
+
+
+ custom local pattern
+ [a-zA-Z0-9_]+
+ 1
+
+ The final local variable name 'FINAL_LOCAL_VARIABLE' doesn't match '[a-z][a-zA-Z0-9]*'
+
+
+
+
+
diff --git a/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/codestyle/xml/MethodNamingConventions.xml b/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/codestyle/xml/MethodNamingConventions.xml
index 0a1798e969..ea9db4a8d0 100644
--- a/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/codestyle/xml/MethodNamingConventions.xml
+++ b/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/codestyle/xml/MethodNamingConventions.xml
@@ -100,4 +100,131 @@ public class Foo {
}
]]>
+
+
+ all is well
+ 0
+
+
+
+
+ default is camel case
+ 3
+
+ The test method name 'TEST_METHOD' doesn't match '[a-z][a-zA-Z0-9]*'
+ The static method name 'STATIC_METHOD' doesn't match '[a-z][a-zA-Z0-9]*'
+ The instance method name 'INSTANCE_METHOD' doesn't match '[a-z][a-zA-Z0-9]*'
+
+
+
+
+
+ ignores overrides
+ 0
+
+
+
+
+ ignores properties
+ 0
+
+
+
+
+ ignores constructors
+ 0
+
+
+
+
+ ignores enum methods
+ Z
+ 0
+
+
+
+
+ custom test method pattern
+ [a-zA-Z0-9_]+
+ 2
+
+ The static method name 'STATIC_METHOD' doesn't match '[a-z][a-zA-Z0-9]*'
+ The instance method name 'INSTANCE_METHOD' doesn't match '[a-z][a-zA-Z0-9]*'
+
+
+
+
+
+ custom static method pattern
+ [a-zA-Z0-9_]+
+ 2
+
+ The test method name 'TEST_METHOD' doesn't match '[a-z][a-zA-Z0-9]*'
+ The instance method name 'INSTANCE_METHOD' doesn't match '[a-z][a-zA-Z0-9]*'
+
+
+
+
+
+ custom instance method pattern
+ [a-zA-Z0-9_]+
+ 2
+
+ The test method name 'TEST_METHOD' doesn't match '[a-z][a-zA-Z0-9]*'
+ The static method name 'STATIC_METHOD' doesn't match '[a-z][a-zA-Z0-9]*'
+
+
+
diff --git a/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/codestyle/xml/PropertyNamingConventions.xml b/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/codestyle/xml/PropertyNamingConventions.xml
new file mode 100644
index 0000000000..4fdd885597
--- /dev/null
+++ b/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/codestyle/xml/PropertyNamingConventions.xml
@@ -0,0 +1,74 @@
+
+
+
+
+
+ all is well
+ 0
+
+
+
+
+ default is camel case
+ 2
+
+ The static property name 'STATIC_PROPERTY' doesn't match '[a-z][a-zA-Z0-9]*'
+ The instance property name 'INSTANCE_PROPERTY' doesn't match '[a-z][a-zA-Z0-9]*'
+
+
+
+
+
+ ignores methods
+ 0
+
+
+
+
+ custom static property pattern
+ [a-zA-Z0-9_]+
+ 1
+
+ The instance property name 'INSTANCE_PROPERTY' doesn't match '[a-z][a-zA-Z0-9]*'
+
+
+
+
+
+ custom instance property pattern
+ [a-zA-Z0-9_]+
+ 1
+
+ The static property name 'STATIC_PROPERTY' doesn't match '[a-z][a-zA-Z0-9]*'
+
+
+
+
+
diff --git a/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/documentation/xml/ApexDoc.xml b/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/documentation/xml/ApexDoc.xml
index 0e890cd274..28946ce929 100644
--- a/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/documentation/xml/ApexDoc.xml
+++ b/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/documentation/xml/ApexDoc.xml
@@ -52,6 +52,10 @@ private class Foo { }
class comment should have description
1
+ 3
+
+ Missing ApexDoc @description
+
+
+ #1783 correct comments for constructor and inner class
+ 0
+
+
+
+
+ #1783 correct comments for constructor and inner class - false negative
+ 1
+ 12
+
+
diff --git a/pmd-core/src/main/resources/rulesets/releases/6150.xml b/pmd-core/src/main/resources/rulesets/releases/6150.xml
new file mode 100644
index 0000000000..25c9b94dad
--- /dev/null
+++ b/pmd-core/src/main/resources/rulesets/releases/6150.xml
@@ -0,0 +1,17 @@
+
+
+
+
+This ruleset contains links to rules that are new in PMD v6.15.0
+
+
+
+
+
+
+
+
+
diff --git a/pmd-cpp/etc/grammar/cpp.jj b/pmd-cpp/etc/grammar/cpp.jj
index a39effe982..4e9c4586c0 100644
--- a/pmd-cpp/etc/grammar/cpp.jj
+++ b/pmd-cpp/etc/grammar/cpp.jj
@@ -96,7 +96,7 @@ public final class CppParser {
if (t.kind != ID && t.kind != SCOPE)
return null;
- StringBuffer s = new StringBuffer();
+ StringBuilder s = new StringBuilder();
int i;
if (t.kind != SCOPE)
@@ -284,37 +284,100 @@ TOKEN :
TOKEN [IGNORE_CASE] :
{
- < OCTALINT : "0" (["0"-"7"])* >
+ < OCTALINT : "0" (["'", "0"-"7"])* >
| < OCTALLONG : "l" >
| < UNSIGNED_OCTALINT : "u" >
| < UNSIGNED_OCTALLONG : ("ul" | "lu") >
-| < DECIMALINT : ["1"-"9"] (["0"-"9"])* >
+| < #DECIMALDIGIT : ["'", "0"-"9"] >
+
+| < DECIMALINT : ["1"-"9"] ()* >
| < DECIMALLONG : ["u","l"] >
| < UNSIGNED_DECIMALINT : "u" >
| < UNSIGNED_DECIMALLONG : ("ul" | "lu") >
-| < HEXADECIMALINT : "0x" (["0"-"9","a"-"f"])+ >
+| < HEXADECIMALINT : "0x" ( | ["a"-"f"])+ >
| < HEXADECIMALLONG : (["u","l"])? >
| < UNSIGNED_HEXADECIMALINT : "u" >
| < UNSIGNED_HEXADECIMALLONG : ("ul" | "lu") >
-| < FLOATONE : ((["0"-"9"])+ "." (["0"-"9"])* | (["0"-"9"])* "." (["0"-"9"])+)
- ("e" (["-","+"])? (["0"-"9"])+)? (["f","l"])? >
+| < FLOATONE : (["0"-"9"]()* "."
+ | "." ()+
+ | ["0"-"9"]()* "." ()+)
+ ("e" (["-","+"])? ()+)? (["f","l"])? >
-| < FLOATTWO : (["0"-"9"])+ "e" (["-","+"])? (["0"-"9"])+ (["f","l"])? >
+| < FLOATTWO : ["0"-"9"]()* "e" (["-","+"])? ()+ (["f","l"])? >
}
TOKEN :
{
+ < #CHRPREF : >
+| < CHARACTER :
+ "'" ( ( ~["'","\\","\r","\n"] ) | ( "\\" ( ~["\n","\r"] ) ) )* "'" >
- < CHARACTER : ("L")? "'" ( ( ~["'","\\","\r","\n"] ) | ( "\\" ( ~["\n","\r"] ) ) )* "'" >
+| < #STRPREF : ("L" | "u" | "U" | "u8")? >
+| < STRING :
+ "\"" ( ( ~["\"","\\","\r","\n"] ) | ( "\\" ( ~["\n","\r"] | "\n" | "\r\n" ) ) )* "\"" >
-| < STRING : ("L")? "\"" ( ( ~["\"","\\","\r","\n"] ) | ( "\\" ( ~["\n","\r"] | "\n" | "\r\n" ) ) )* "\"" >
+}
-| < RSTRING : "R\"(" ( ~[")"] | ( ")" ~["\""] ) )* ")\"" >
+// Raw C++11 string literal support
+// https://en.cppreference.com/w/cpp/language/string_literal
+TOKEN :
+{
+ < RSTRING : "R\"" >
+ {
+ StringBuilder sb = new StringBuilder(16);
+
+ // delim ------+
+ // vvv
+ // Matching R"...(...)..."
+ // ^
+ for (;;) {
+ try { curChar = input_stream.readChar(); }
+ catch(java.io.IOException e) { return; }
+ if (curChar == '(') break;
+ sb.append(curChar);
+ }
+ final String delim = sb.toString();
+
+rstringbody:
+ // Matching R"...(...)..."
+ // ^
+ for (;;) {
+ try { curChar = input_stream.readChar(); }
+ catch(java.io.IOException e) { return; }
+ if (curChar == ')') {
+ // delim --------------+
+ // vvv
+ // Matching R"...(...)..."
+ // ^^^
+ for (int i = 0; i < delim.length(); i++) {
+ try { curChar = input_stream.readChar(); }
+ catch(java.io.IOException e) { return; }
+ if (delim.charAt(i) != curChar) {
+ input_stream.backup(1);
+ continue rstringbody;
+ }
+ }
+ // Matching R"...(...)..."
+ // ^
+ try { curChar = input_stream.readChar(); }
+ catch(java.io.IOException e) { return; }
+ if (curChar != '"') {
+ input_stream.backup(1);
+ continue rstringbody;
+ }
+ break;
+ }
+ }
+ // Setting final token image
+ matchedToken.image = input_stream.GetImage();
+ matchedToken.endLine = input_stream.getEndLine();
+ matchedToken.endColumn = input_stream.getEndColumn();
+ }
}
TOKEN :
diff --git a/pmd-cpp/src/test/java/net/sourceforge/pmd/cpd/CPPTokenizerTest.java b/pmd-cpp/src/test/java/net/sourceforge/pmd/cpd/CPPTokenizerTest.java
index 4bffb5208d..c8caeb354e 100644
--- a/pmd-cpp/src/test/java/net/sourceforge/pmd/cpd/CPPTokenizerTest.java
+++ b/pmd-cpp/src/test/java/net/sourceforge/pmd/cpd/CPPTokenizerTest.java
@@ -159,6 +159,86 @@ public class CPPTokenizerTest {
tokenizer.tokenize(code, new Tokens());
}
+ public void testStringPrefix(String code, String expToken, int tokenIndex, int expNoTokens) {
+ final Tokens tokens = parse(code);
+ final TokenEntry token = tokens.getTokens().get(tokenIndex);
+ assertEquals(expNoTokens, tokens.size());
+ assertEquals(expToken, token.toString());
+ }
+
+ public void testCharacterPrefix(String code, String expToken) {
+ testStringPrefix(code, expToken, 3, 6);
+ }
+
+ public void testStringPrefix(String code, String expToken) {
+ testStringPrefix(code, expToken, 5, 8);
+ }
+
+ @Test
+ public void testCharacterPrefixNoPrefix() {
+ testCharacterPrefix("char a = '\\x30';", "'\\x30'");
+ }
+
+ @Test
+ public void testCharacterPrefixWideCharacter() {
+ testCharacterPrefix("wchar_t b = L'\\xFFEF';", "L'\\xFFEF'");
+ }
+
+ @Test
+ public void testCharacterPrefixChar16() {
+ testCharacterPrefix("char16_t c = u'\\u00F6';", "u'\\u00F6'");
+ }
+
+ @Test
+ public void testCharacterPrefixChar32() {
+ testCharacterPrefix("char32_t d = U'\\U0010FFFF';", "U'\\U0010FFFF'");
+ }
+
+ @Test
+ public void testStringPrefixNoPrefix() {
+ testStringPrefix("char A[] = \"Hello\\x0A\";", "\"Hello\\x0A\"");
+ }
+
+ @Test
+ public void testStringPrefixWideString() {
+ testStringPrefix("wchar_t B[] = L\"Hell\\xF6\\x0A\";", "L\"Hell\\xF6\\x0A\"");
+ }
+
+ @Test
+ public void testStringPrefixChar16() {
+ testStringPrefix("char16_t C[] = u\"Hell\\u00F6\";", "u\"Hell\\u00F6\"");
+ }
+
+ @Test
+ public void testStringPrefixChar32() {
+ testStringPrefix("char32_t D[] = U\"Hell\\U000000F6\\U0010FFFF\";", "U\"Hell\\U000000F6\\U0010FFFF\"");
+ }
+
+ @Test
+ public void testStringPrefixUtf8() {
+ testStringPrefix("auto E[] = u8\"\\u00F6\\U0010FFFF\";", "u8\"\\u00F6\\U0010FFFF\"");
+ }
+
+ @Test
+ public void testRawStringLiterals() throws IOException {
+ final String code = IOUtils.toString(CPPTokenizerTest.class.getResourceAsStream("cpp/issue-1784.cpp"), StandardCharsets.UTF_8);
+ Tokens tokens = parse(code);
+ assertTrue(TokenEntry.getEOF() != tokens.getTokens().get(0));
+ assertEquals(16, tokens.size());
+ }
+
+ @Test
+ public void testDigitSeparators() {
+ final String code = "auto integer_literal = 1'000'000;" + PMD.EOL
+ + "auto floating_point_literal = 0.000'015'3;" + PMD.EOL
+ + "auto hex_literal = 0x0F00'abcd'6f3d;" + PMD.EOL
+ + "auto silly_example = 1'0'0'000'00;";
+ Tokens tokens = parse(code);
+ assertTrue(TokenEntry.getEOF() != tokens.getTokens().get(0));
+ assertEquals("1'000'000", tokens.getTokens().get(3).toString());
+ assertEquals(21, tokens.size());
+ }
+
private Tokens parse(String snippet) {
try {
return parse(snippet, false, new Tokens());
diff --git a/pmd-cpp/src/test/resources/net/sourceforge/pmd/cpd/cpp/issue-1559.cpp b/pmd-cpp/src/test/resources/net/sourceforge/pmd/cpd/cpp/issue-1559.cpp
index 010ec09fc6..cdf47c53e6 100644
--- a/pmd-cpp/src/test/resources/net/sourceforge/pmd/cpd/cpp/issue-1559.cpp
+++ b/pmd-cpp/src/test/resources/net/sourceforge/pmd/cpd/cpp/issue-1559.cpp
@@ -4,7 +4,7 @@ namespace ABC
{
#ifdef USE_QT
- const char* perPixelQml = R"QML(
+ const char* perPixelQml = "QML( // provoking a parser error
)QML";
}
}
diff --git a/pmd-cpp/src/test/resources/net/sourceforge/pmd/cpd/cpp/issue-1784.cpp b/pmd-cpp/src/test/resources/net/sourceforge/pmd/cpd/cpp/issue-1784.cpp
new file mode 100644
index 0000000000..29d644f449
--- /dev/null
+++ b/pmd-cpp/src/test/resources/net/sourceforge/pmd/cpd/cpp/issue-1784.cpp
@@ -0,0 +1,12 @@
+namespace ABC
+{
+ namespace DEF
+ {
+
+#ifdef USE_QT
+ const char* perPixelQml = R"QML(
+ )NOTTHEND";
+)QML";
+ }
+}
+#endif // USE_QT
diff --git a/pmd-matlab/etc/grammar/matlab.jj b/pmd-matlab/etc/grammar/matlab.jj
index 6f61dd0d42..31aca621e7 100644
--- a/pmd-matlab/etc/grammar/matlab.jj
+++ b/pmd-matlab/etc/grammar/matlab.jj
@@ -41,11 +41,11 @@ PARSER_END(MatlabParser)
"\n" : DEFAULT
}
-MORE:
+ MORE:
{ "%{": IN_COMMENT }
-SPECIAL_TOKEN:
-{ }
+ SPECIAL_TOKEN:
+{ }
SPECIAL_TOKEN:
{ : DEFAULT }
@@ -65,6 +65,7 @@ SPECIAL_TOKEN:
| < AT: "@" > : DEFAULT
| < DOT: "." > : TRANSPOSE
| < COMMA: "," > : DEFAULT
+| < QUESTIONMARK: "?" > : DEFAULT
}
TOKEN : /* OPERATORS AND ASSIGNMENTS */
@@ -139,6 +140,7 @@ SPECIAL_TOKEN:
TOKEN :
{
< STRING: "'" ( | "'" "'" | ~["\\","'","\n"] )* "'" >
+| < DSTRING: "\"" ( "\\" | "\"" "\"" | ~["\\","\"","\n"] )* "\"" >
| < #ESC_SEQ:
"\\" ( "b" | "t" | "n" | "f" | "r" | "\"" | "'" | "\\" )
|
diff --git a/pmd-matlab/src/test/java/net/sourceforge/pmd/cpd/MatlabTokenizerTest.java b/pmd-matlab/src/test/java/net/sourceforge/pmd/cpd/MatlabTokenizerTest.java
index 50fdb1832e..cc45bfb48f 100644
--- a/pmd-matlab/src/test/java/net/sourceforge/pmd/cpd/MatlabTokenizerTest.java
+++ b/pmd-matlab/src/test/java/net/sourceforge/pmd/cpd/MatlabTokenizerTest.java
@@ -51,7 +51,55 @@ public class MatlabTokenizerTest extends AbstractTokenizerTest {
));
Tokens tokens = new Tokens();
tokenizer.tokenize(sourceCode, tokens);
- TokenEntry.getEOF();
assertEquals(2, tokens.size()); // 2 tokens: "end" + EOF
}
+
+ @Test
+ public void testComments() throws IOException {
+ SourceCode sourceCode = new SourceCode(new SourceCode.StringCodeLoader("classdef LC" + PMD.EOL
+ + " methods" + PMD.EOL
+ + " function [obj, c,t, s ] = Classification( obj,m,t, cm )%#codegen" + PMD.EOL
+ + " end" + PMD.EOL
+ + " end" + PMD.EOL
+ + "end"));
+ Tokens tokens = new Tokens();
+ tokenizer.tokenize(sourceCode, tokens); // should not result in parse error
+ assertEquals(28, tokens.size());
+ }
+
+ @Test
+ public void testBlockComments() throws IOException {
+ SourceCode sourceCode = new SourceCode(new SourceCode.StringCodeLoader("%{" + PMD.EOL
+ + " Name: helloworld.m\n" + PMD.EOL
+ + " Purpose: Say \"Hello World!\" in two different ways" + PMD.EOL
+ + "%}" + PMD.EOL
+ + PMD.EOL
+ + "% Do it the good ol' fashioned way...command window" + PMD.EOL
+ + "disp('Hello World!');\n" + PMD.EOL
+ + "%" + PMD.EOL
+ + "% Do it the new hip GUI way...with a message box" + PMD.EOL
+ + "msgbox('Hello World!','Hello World!');"));
+ Tokens tokens = new Tokens();
+ tokenizer.tokenize(sourceCode, tokens); // should not result in parse error
+ assertEquals(13, tokens.size());
+ }
+
+ @Test
+ public void testQuestionMark() throws IOException {
+ SourceCode sourceCode = new SourceCode(new SourceCode.StringCodeLoader("classdef Class1" + PMD.EOL
+ + "properties (SetAccess = ?Class2)"));
+ Tokens tokens = new Tokens();
+ tokenizer.tokenize(sourceCode, tokens);
+ assertEquals(10, tokens.size());
+ }
+
+ @Test
+ public void testDoubleQuotedStrings() throws IOException {
+ SourceCode sourceCode = new SourceCode(new SourceCode.StringCodeLoader(
+ "error(\"This is a double-quoted string\");"));
+ Tokens tokens = new Tokens();
+ tokenizer.tokenize(sourceCode, tokens);
+ assertEquals("\"This is a double-quoted string\"", tokens.getTokens().get(2).toString());
+ assertEquals(6, tokens.size());
+ }
}