Merge remote-tracking branch 'origin/pmd7-language-lifecycle' into pmd7-language-lifecycle

This commit is contained in:
Clément Fournier committed 2023-02-04 19:54:48 +01:00
commit 374150b592
8 files changed
+204 -129

No files matched your search

+3
View File
@@ -400,6 +400,9 @@ entries:
- title: Language-Specific Documentation
output: web, pdf
folderitems:
- title: Language configuration
url: /pmd_languages_configuration.html
output: web, pdf
- title: Apex
url: /pmd_languages_apex.html
output: web, pdf
+19
View File
@@ -248,8 +248,10 @@ The following previously deprecated rules have been finally removed:
* [#4080](https://github.com/pmd/pmd/issues/4080): \[ant] Split off Ant integration into a new submodule
* core
* [#2234](https://github.com/pmd/pmd/issues/2234): \[core] Consolidate PMD CLI into a single command
* [#2518](https://github.com/pmd/pmd/issues/2518): \[core] Language properties
* [#2873](https://github.com/pmd/pmd/issues/2873): \[core] Utility classes in pmd 7
* [#3203](https://github.com/pmd/pmd/issues/3203): \[core] Replace RuleViolationFactory implementations with ViolationDecorator
* [#3782](https://github.com/pmd/pmd/issues/3782): \[core] Language lifecycle
* [#3902](https://github.com/pmd/pmd/issues/3902): \[core] Violation decorators
* [#4035](https://github.com/pmd/pmd/issues/4035): \[core] ConcurrentModificationException in DefaultRuleViolationFactory
* cli
@@ -399,6 +401,23 @@ The metrics framework has been made simpler and more general.
* Rule tests, that use {% jdoc test::testframework.SimpleAggregatorTst %} or {% jdoc test::testframework.PmdRuleTst %} work as before without change, but use
now JUnit5 under the hood. If you added additional JUnit4 tests to your rule test classes, then you'll need to upgrade them to use JUnit5.
#### Language Modules
In order to support language properties and provide a proper lifecycle for languages, there were some changes needed
in this area:
* The class `BaseLanguageModule` has been removed.
* Individual language modules should now extend {% jdoc core::lang.impl.SimpleLanguageModuleBase %}. Like before
this class is registered via the service loader mechanism via `META-INF/services/net.sourceforge.pmd.lang.Language`.
* The implementation of a language version handler has been simplified by providing default implementations for
most aspects. The minimum requirement is now to provide an own parser for the language version handler.
* Language modules can define [custom language properties](pmd_languages_configuration.html)
which can be set via environment variables. This allows
to add and use language specific configuration options without the need to change pmd-core.
* For each PMD analysis run a new `LanguageProcessor` instance is created and destroyed afterwards. This allows
to store global information without using static fields. This enables the implementation of multifile analysis.
* Rules have access to this language processor instance during initialization.
### External Contributions
* [#1658](https://github.com/pmd/pmd/pull/1658): \[core] Node support for Antlr-based languages - [Matías Fraga](https://github.com/matifraga)
@@ -3,7 +3,7 @@ title: Adding PMD support for a new ANTLR grammar based language
short_title: Adding a new language with ANTLR
tags: [devdocs, extending]
summary: "How to add a new language to PMD using ANTLR grammar."
last_updated: October 2021
last_updated: February 2023 (7.0.0)
sidebar: pmd_sidebar
permalink: pmd_devdocs_major_adding_new_language_antlr.html
folder: pmd/devdocs
@@ -21,7 +21,7 @@ folder: pmd/devdocs
This is really a big contribution and can't be done with a drive by contribution. It requires dedicated passion
and long commitment to implement support for a new language.<br><br>
This step by step guide is just a small intro to get the basics started and it's also not necessarily up-to-date
This step-by-step guide is just a small intro to get the basics started and it's also not necessarily up-to-date
or complete and you have to be able to fill in the blanks.<br><br>
Currently the Antlr integration has some basic limitations compared to JavaCC: The output of the
@@ -86,7 +86,7 @@ definitely don't come for free. It is much effort and requires perseverance to i
* You can add additional methods in your "InnerNode" (e.g. `SwiftInnerNode`) that are available on all nodes.
But on most cases you won't need to do anything.
## 4. Generate your parser
## 4. Generate your parser (using ANTLR)
* Make sure, you have the property `<antlr4.visitor>true</antlr4.visitor>` in your `pom.xml` file.
* This is just a matter of building the language module. ANTLR is called via ant, and this step is added
to the phase `generate-sources`. So you can just call e.g. `./mvnw generate-sources -pl pmd-swift` to
@@ -116,23 +116,17 @@ definitely don't come for free. It is much effort and requires perseverance to i
implementation that you need to extend to create your own adapter as we do with
[`PmdSwiftParser`](https://github.com/pmd/pmd/blob/pmd/7.0.x/pmd-swift/src/main/java/net/sourceforge/pmd/lang/swift/ast/PmdSwiftParser.java).
## 7. Create a rule violation factory
* This is an optional step. Most like, the default implementation will do what you need.
The default implementation is [`DefaultRuleViolationFactory`](https://github.com/pmd/pmd/blob/pmd/7.0.x/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/impl/DefaultRuleViolationFactory.java).
* The purpose of a rule violation factory is to create a rule violation instance for your handler (spoiler).
In case you want to provide additional data in your rule violation, you can create a custom one. However,
adding additional date here is discouraged, as you would need a custom renderer to actually use this
additional data. Such extensions are not language agnostic.
## 8. Create a version handler
## 7. Create a language version handler
* Now you need to create your version handler, as we did with [`SwiftHandler`](https://github.com/pmd/pmd/blob/pmd/7.0.x/pmd-swift/src/main/java/net/sourceforge/pmd/lang/swift/SwiftHandler.java).
* This class is sort of a gateway between PMD and all parsing logic specific to your language. It has 2 purposes:
* `getRuleViolationFactory` method returns an instance of your rule violation factory *(see step #7)*.
By default, this returns the default rule violation factory.
* `getParser` returns an instance of your parser adapter *(see step #6)*.
That's the only method, that needs to be implemented here.
* This class is sort of a gateway between PMD and all parsing logic specific to your language.
* For a minimal implementation, it just needs to return a parser *(see step #6)*.
* It can be used to provide other features for your language like
* violation suppression logic
* violation decorators, to add additional language specific information to the created violations
* metrics
* custom XPath functions
## 9. Create a parser visitor adapter
## 8. Create a base visitor
* A parser visitor adapter is not needed anymore with PMD 7. The visitor interface now provides a default
implementation.
* The visitor for ANTLR based AST is generated along the parser from the ANTLR grammar file. The
@@ -142,21 +136,16 @@ definitely don't come for free. It is much effort and requires perseverance to i
See [`SwiftVisitorBase`](https://github.com/pmd/pmd/blob/pmd/7.0.x/pmd-swift/src/main/java/net/sourceforge/pmd/lang/swift/ast/SwiftVisitorBase.java)
as an example.
## 10. Create a rule chain visitor
* This step is not needed anymore. For using rule chain, there is no additional adjustment necessary anymore
in the languages.
* This feature has been merged into AbstractRule via the overridable method
{% jdoc !!core::lang.rule.AbstractRule#buildTargetSelector() %}. Individual rules can make use of this optimization
by overriding this method and return an appropriate RuleTargetSelector.
## 11. Make PMD recognize your language
* Create your own subclass of `net.sourceforge.pmd.lang.BaseLanguageModule`, see Swift as an example:
## 9. Make PMD recognize your language
* Create your own subclass of `net.sourceforge.pmd.lang.impl.SimpleLanguageModuleBase`, see Swift as an example:
[`SwiftLanguageModule`](https://github.com/pmd/pmd/blob/pmd/7.0.x/pmd-swift/src/main/java/net/sourceforge/pmd/lang/swift/SwiftLanguageModule.java).
* Add your default version with `addDefaultVersion` in your language module's constructor.
* Add for each additional version of your language a call to `addVersion` as well.
* Create the service registration via the text file `src/main/resources/META-INF/services/net.sourceforge.pmd.lang.Language`. Add your fully qualified class name as a single line into it.
* Add for each version of your language a call to `addVersion` in your language modules constructor.
Use `addDefaultVersion` for defining the default version.
* Youll need to refer the language version handler created in step #7.
* Create the service registration via the text file `src/main/resources/META-INF/services/net.sourceforge.pmd.lang.Language`.
Add your fully qualified class name as a single line into it.
## 12. Create an abstract rule class for the language
## 10. Create an abstract rule class for the language
* You need to create your own `AbstractRule` in order to interface your language with PMD's generic rule
execution.
* See [`AbstractSwiftRule`](https://github.com/pmd/pmd/blob/pmd/7.0.x/pmd-swift/src/main/java/net/sourceforge/pmd/lang/swift/AbstractSwiftRule.java) as an example.
@@ -167,7 +156,7 @@ definitely don't come for free. It is much effort and requires perseverance to i
via the method `buildVisitor()` for analyzing the AST. The provided visitor only implements the visit methods
for specific AST nodes. The other node types use the default behavior and you don't need to care about them.
## 13. Create rules
## 11. Create rules
* Creating rules is already pretty well documented in PMD - and its no different for a new language, except you
may have different AST nodes.
* PMD supports 2 types of rules, through visitors or XPath.
@@ -179,15 +168,19 @@ definitely don't come for free. It is much effort and requires perseverance to i
* To add an XPath rule you can follow our guide [Writing XPath Rules](pmd_userdocs_extending_writing_xpath_rules.html).
## 14. Test the rules
* See UnavailableFunctionRuleTest for example. Each rule has it's own test class.
* You have to create the category rule set for your language *(see pmd-swift/src/main/resources/bestpractices.xml for example)*
* When executing the test class
* this triggers the unit test to read the corresponding XML file with the rule test data
*(see `UnavailableFunctionRule.xml` for example)*
* This test XML file contains sample pieces of code which should trigger a specified number of
violations of this rule. The unit test will execute the rule on this piece of code, and verify
that the number of violations matches.
* To verify the validity of all the created rulesets, create a subclass of `AbstractRuleSetFactoryTest` (*see `RuleSetFactoryTest` in pmd-swift for example)*.
* Testing rules is described in depth in [Testing your rules](pmd_userdocs_extending_testing.html).
* Each rule has its own test class: Create a test class for your rule extending `PmdRuleTst`
*(see UnavailableFunctionTest for example)*
* Create a category rule set for your language *(see pmd-swift/src/main/resources/bestpractices.xml for example)*
* Place the test XML file with the test cases in the correct location
* When executing the test class
* this triggers the unit test to read the corresponding XML file with the rule test data
*(see `UnavailableFunction.xml` for example)*
* This test XML file contains sample pieces of code which should trigger a specified number of
violations of this rule. The unit test will execute the rule on this piece of code, and verify
that the number of violations matches.
* To verify the validity of all the created rulesets, create a subclass of `AbstractRuleSetFactoryTest`
(*see `RuleSetFactoryTest` in pmd-swift for example)*.
This will load all rulesets and verify, that all required attributes are provided.
*Note:* You'll need to add your ruleset to `categories.properties`, so that it can be found.
@@ -3,7 +3,7 @@ title: Adding PMD support for a new JavaCC grammar based language
short_title: Adding a new language with JavaCC
tags: [devdocs, extending]
summary: "How to add a new language to PMD using JavaCC grammar."
last_updated: October 2021
last_updated: February 2023 (7.0.0)
sidebar: pmd_sidebar
permalink: pmd_devdocs_major_adding_new_language_javacc.html
folder: pmd/devdocs
@@ -16,7 +16,7 @@ folder: pmd/devdocs
This is really a big contribution and can't be done with a drive by contribution. It requires dedicated passion
and long commitment to implement support for a new language.<br><br>
This step by step guide is just a small intro to get the basics started and it's also not necessarily up-to-date
This step-by-step guide is just a small intro to get the basics started and it's also not necessarily up-to-date
or complete and you have to be able to fill in the blanks.<br><br>
After the basic support for a language is there, there are lots of missing features left. Typical features
@@ -39,68 +39,75 @@ definitely don't come for free. It is much effort and requires perseverance to i
## 2. Implement an AST parser for your language
* Ideally an AST parser should be implemented as a JJT file *(see VmParser.jjt or Java.jjt for example)*
* There is nothing preventing any other parser implementation, as long as you have some way to convert an input stream into an AST tree. Doing it as a JJT simplifies maintenance down the road.
* There is nothing preventing any other parser implementation, as long as you have some way to convert an input
stream into an AST tree. Doing it as a JJT simplifies maintenance down the road.
* See this link for reference: [https://javacc.java.net/doc/JJTree.html](https://javacc.java.net/doc/JJTree.html)
## 3. Create AST node classes
* For each AST node that your parser can generate, there should be a class
* The name of the AST class should be “AST” + “whatever is the name of the node in JJT file”.
* For example, if JJT contains a node called “IfStatement”, there should be a class called “ASTIfStatement”
* Each AST class should have two constructors: one that takes an int id; and one that takes an instance of the parser, and an int id
* Its a good idea to create a parent AST class for all AST classes of the language. This simplifies rule creation later. *(see SimpleNode for Velocity and AbstractJavaNode for Java for example)*
* Each AST class should have one package-private constructor, that takes an `int id`.
* Its a good idea to create a parent AST class for all AST classes of the language. This simplifies rule
creation later. *(see SimpleNode for Velocity and AbstractJavaNode for Java for example)*
* Note: These AST node classes are generated usually once by javacc/jjtree and can then be modified as needed.
## 4. Compile your parser (if using JJT)
* An ant script is being used to compile jjt files into classes. This is in `pmd-<lang>/src/main/ant/alljavacc.xml` file.
* Create `alljavacc.xml` file for your language, you can use one from `pmd-java` as an example.
* You would probably want to adjust contents of the `<delete>` tag: start with an empty `<fileset>` and add there `<include>`s for those AST nodes you had to manually rewrite (moving those node classes from autogenerated directory to the regular source tree).
## 4. Generate your parser (using JJT)
* An ant script is being used to compile jjt files into classes. This is in `javacc-wrapper.xml` file in the
top-level pmd sources.
* The ant script is executed via the `maven-antrun-plugin`. Add this plugin to your `pom.xml` file and configure
it the language name. You can use `pmd-java/pom.xml` as an example.
* The ant script is called in the phase `generate-sources` whenever the whole project is built. But you can
call `./mvnw generate-sources` directly for your module if you want your parser to be generated.
## 5. Create a TokenManager
* Create a new class that implements the `TokenManager` interface *(see VmTokenManager or JavaTokenManager for example)*
## 6. Create a PMD parser “adapter”
* Create a new class that extends AbstractParser
## 5. Create a PMD parser “adapter
* Create a new class that extends `JjtreeParserAdapter`.
* This is a generic class, and you need to declare the root AST node.
* There are two important methods to implement
* `createTokenManager` method should return a new instance of a token manager for your language *(see step #5)*
* `parse` method should return the root node of the AST tree obtained by parsing the Reader source
* `tokenBehavior` method should return a new instance of `TokenDocumentBehavior` constructed with the list
of tokes in your language. The compile step #4 will generate a class `$langTokenKinds` which has
all the available tokens in the field `TOKEN_NAMES`.
* `parseImpl` method should return the root node of the AST tree obtained by parsing the CharStream source
* See `VmParser` class as an example
## 7. Create a rule violation factory
* Extend `AbstractRuleViolationFactory` *(see VmRuleViolationFactory for example)*
* The purpose of this class is to create a rule violation instance specific to your language
## 6. Create a language version handler
* Extend `AbstractPmdLanguageVersionHandler` *(see VmHandler for example)*
* This class is sort of a gateway between PMD and all parsing logic specific to your language.
* For a minimal implementation, it just needs to return a parser *(see step #5)*.
* It can be used to provide other features for your language like
* violation suppression logic
* violation decorators, to add additional language specific information to the created violations
* metrics (see below "Optional features")
* custom XPath functions
* See `VmHandler` class as an example
## 8. Create a version handler
* Extend `AbstractLanguageVersionHandler` *(see VmHandler for example)*
* This class is sort of a gateway between PMD and all parsing logic specific to your language. It has 2 purposes:
* `getRuleViolationFactory` method returns an instance of your rule violation factory *(see step #7)*
* `getParser` returns an instance of your parser adapter *(see step #6)*
## 7. Create a base visitor
* A parser visitor adapter is not needed anymore with PMD 7. The visitor interface now provides a default
implementation.
* The visitor for JavaCC based AST is generated along the parser from the grammar file. The
base interface for a visitor is [`AstVisitor`](https://github.com/pmd/pmd/blob/pmd/7.0.x/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/AstVisitor.java).
* The generated visitor class for VM is called `VmVisitor`.
* In order to help use this visitor later on, a base visitor class should be created.
See `VmVisitorBase` as an example.
## 9. Create a parser visitor adapter
* If you use JJT to generate your parser, it should also generate an interface for a parser visitor *(see VmParserVisitor for example)*
* Create a class that implements this auto-generated interface *(see VmParserVisitorAdapter for example)*
* The purpose of this class is to serve as a pass-through `visitor` implementation, which, for all AST types in your language, just executes visit on the base AST type
## 10. Create a rule chain visitor
* Extend `AbstractRuleChainVisitor` *(see VmRuleChainVisitor for example)*
* This class should `implement` two `important` methods:
* `indexNodes` generates a map of "node type" to "list of nodes of that type". This is used to visit all applicable nodes when a rule is applied.
* `visit` method should evaluate what kind of rule is being applied, and execute appropriate logic. Usually it will just check if the rule is a "parser visitor" kind of rule specific to your language, then execute the visitor. If its an XPath rule, then we just need to execute evaluate on that.
## 11. Make PMD recognize your language
* Create your own subclass of `net.sourceforge.pmd.lang.BaseLanguageModule`. *(see VmLanguageModule or JavaLanguageModule as an example)*
* Youll need to refer the rule chain visitor created in step #10.
## 8. Make PMD recognize your language
* Create your own subclass of `net.sourceforge.pmd.lang.impl.SimpleLanguageModuleBase`. *(see VmLanguageModule or
JavaLanguageModule as an example)*
* Add for each version of your language a call to `addVersion` in your language modules constructor.
* Create the service registration via the text file `src/main/resources/META-INF/services/net.sourceforge.pmd.lang.Language`. Add your fully qualified class name as a single line into it.
Use `addDefaultVersion` for defining the default version.
* Youll need to refer the language version handler created in step #6.
* Create the service registration via the text file `src/main/resources/META-INF/services/net.sourceforge.pmd.lang.Language`.
Add your fully qualified class name as a single line into it.
## 12. Add AST regression tests
## 9. Add AST regression tests
For languages, that use an external library for parsing, the AST can easily change when upgrading the library.
Also for languages, where we have the grammar under our control, it useful to have such tests.
Also for languages, where we have the grammar under our control, it is useful to have such tests.
The tests parse one or more source files and generate a textual representation of the AST. This text is compared
against a previously recorded version. If there are differences, the test fails.
This helps to detect anything in the AST structure, that changed, maybe unexpectedly.
This helps to detect anything in the AST structure that changed, maybe unexpectedly.
* Create a test class in the package `net.sourceforge.pmd.lang.$lang.ast` with the name `$langTreeDumpTest`.
* This test class must extend `net.sourceforge.pmd.lang.ast.test.BaseTreeDumpTest`. Note: This class
@@ -116,7 +123,7 @@ This helps to detect anything in the AST structure, that changed, maybe unexpect
Replace "$lang" and "$extension" accordingly.
* Implement the method `getParser()`. It must return a
subclass of `net.sourceforge.pmd.lang.ast.test.BaseParsingHelper`. See
`net.sourceforge.pmd.lang.ecmascript.ast.JsParsingHelper` for a example.
`net.sourceforge.pmd.lang.ecmascript.ast.JsParsingHelper` for an example.
With this parser helper you can also specify, where the test files are searched, by using
the method `withResourceContext(Class<?>, String)`.
* Add one or more test methods. Each test method parses one file and compares the result. The base
@@ -139,24 +146,35 @@ The Scala module also has a test, written in Kotlin instead of Java:
`net.sourceforge.pmd.lang.scala.ast.ScalaParserTests`.
## 13. Create an abstract rule class for the language
## 10. Create an abstract rule class for the language
* Extend `AbstractRule` and implement the parser visitor interface for your language *(see AbstractVmRule for example)*
* All other rules for your language should extend this class. The purpose of this class is to implement visit methods for all AST types to simply delegate to default behavior. This is useful because most rules care only about specific AST nodes, but PMD needs to know what to do with each node - so this just lets you use default behavior for nodes you dont care about.
* All other rules for your language should extend this class. The purpose of this class is to implement visit
methods for all AST types to simply delegate to default behavior. This is useful because most rules care only
about specific AST nodes, but PMD needs to know what to do with each node - so this just lets you use default
behavior for nodes you dont care about.
## 14. Create rules
* Rules are created by extending the abstract rule class created in step 13 *(see `EmptyForeachStmtRule` for example)*
* Creating rules is already pretty well documented in PMD - and its no different for a new language, except you may have different AST nodes.
## 11. Create rules
* Rules are created by extending the abstract rule class created in step 9 *(see `EmptyForeachStmtRule` for example)*
* Creating rules is already pretty well documented in PMD - and its no different for a new language,
except you may have different AST nodes.
## 15. Test the rules
* See BasicRulesTest for example
* You have to create a rule set for your language *(see vm/basic.xml for example)*
* For each rule in this set you want to test, call `addRule` method in setUp of the unit test
* This triggers the unit test to read the corresponding XML file with rule test data *(see `EmptyForeachStmtRule.xml` for example)*
* This test XML file contains sample pieces of code which should trigger a specified number of violations of this rule. The unit test will execute the rule on this piece of code, and verify that the number of violations matches
* To verify the validity of the created ruleset, create a subclass of `AbstractRuleSetFactoryTest` (*see `RuleSetFactoryTest` in pmd-vm for example)*.
## 12. Test the rules
* Testing rules is described in depth in [Testing your rules](pmd_userdocs_extending_testing.html).
* Each rule has its own test class: Create a test class for your rule extending `PmdRuleTst`
*(see AvoidReassigningParametersTest in pmd-vm for example)*
* Create a category rule set for your language *(see category/vm/bestpractices.xml for example)*
* Place the test XML file with the test cases in the correct location
* When executing the test class
* this triggers the unit test to read the corresponding XML file with the rule test data
*(see `AvoidReassigningParameters.xml` for example)*
* This test XML file contains sample pieces of code which should trigger a specified number of
violations of this rule. The unit test will execute the rule on this piece of code, and verify
that the number of violations matches.
* To verify the validity of the created ruleset, create a subclass of `AbstractRuleSetFactoryTest`
(*see `RuleSetFactoryTest` in pmd-vm for example)*.
This will load all rulesets and verify, that all required attributes are provided.
*Note:* You'll need to add your ruleset to `rulesets.properties`, so that it can be found.
*Note:* You'll need to add your category ruleset to `categories.properties`, so that it can be found.
## Debugging with Rule Designer
+55 -13
View File
@@ -11,35 +11,77 @@ summary: "Summary of language configuration options and properties"
# Language properties
Since PMD 7.0.0, languages may be directly configured via properties.
The properties can be specified via environment variables or programmatically.
TODO describe CLI syntax
TODO describe env var syntax
The name of the environment variables follow the following pattern,
completely in uppercase:
As a convention, properties whose name start with an *x* are internal and may be
removed or changed without notice.
PMD_<LanguageId>_<PropertyName>
LanguageId is the short name of the language, which is being configured. This is e.g. "JAVA" or "APEX".
PropertyName is the uppercase name of the property, that is set to a specific value, e.g. "SUPPRESSMARKER".
As a convention, properties whose name start with an *x* are internal and may be removed or changed without notice.
Programmatically, the language properties can be set on `PMDConfiguration` before using the PmdAnalyzer instance
to start the analysis:
```java
PMDConfiguration configuration = new PMDConfiguration();
LanguagePropertyBundle properties = configuration.getLanguageProperties(LanguageRegistry.PMD.getLanguageById("java"));
properties.setProperty(LanguagePropertyBundle.SUPPRESS_MARKER, "PMD");
```
## Common language properties
All languages support the following properties:
- `suppressMarker`: A string to detect suppression comments. The default is `NOPMD`,
so e.g. in Java, a comment `// NOPMD` will suppress warnings on the same line.
- `suppressMarker`: A string to detect suppression comments. The default is `NOPMD`, so e.g. in Java, a
comment `// NOPMD` will suppress warnings on the same line.
This property can also be set via the CLI option `--suppress-marker`. The CLI option applies for all languages
and overrides any language property.
- `version`: The language version PMD should use when parsing source code. If not specified, the default
version of the language will be used.
This property can also be set via the CLI option `--use-version`.
## Java language properties
The Java language can be configured with the following properties:
- `auxClasspath`: Classpath on which to find compiled classes for the language
- `xTypeInferenceLogging`: Verbosity of type inference logging, possible values `DISABLED`, `SIMPLE`, `VERBOSE`
This property can also be set via the CLI option `--aux-classpath`.
Environment variable: `PMD_JAVA_AUXCLASSPATH`
- `xTypeInferenceLogging`: Verbosity of type inference logging, possible values `DISABLED`, `SIMPLE`, `VERBOSE`.
Environment variable: `PMD_JAVA_XTYPEINFERENCELOGGING`
## Apex language properties
- `rootDirectory`:
- `rootDirectory`: With this property the root directory of the Salesforce metadata, where `sfdx-project.json`
resides, is specified. [ApexLink](https://github.com/nawforce/ApexLink) can then load all the classes
in the project and figure out, whether a method is used or not.
This property is needed for {% rule apex/design/UnusedMethod %}.
Environment variable: `PMD_APEX_ROOTDIRECTORY`
## VisualForce language properties
- `apexDirectories`: Comma separated list of directories for Apex classes. Absolute
or relative to the Visualforce directory. Default is `../classes`. Specifying an
empty string will disable data type resolution for Apex Controller properties.
- `objectsDirectories`: Comma separated list of directories for Custom Objects.
Absolute or relative to the Visualforce directory. Default is `../objects`.
Specifying an empty string will disable data type resolution for Custom Object fields.
or relative to the Visualforce directory. Default is `../classes`. Specifying an
empty string will disable data type resolution for Apex Controller properties.
Environment variable: `PMD_VF_APEXDIRECTORIES`
- `objectsDirectories`: Comma separated list of directories for Custom Objects.
Absolute or relative to the Visualforce directory. Default is `../objects`.
Specifying an empty string will disable data type resolution for Custom Object fields.
Environment variable: `PMD_VF_OBJECTSDIRECTORIES`
@@ -46,7 +46,7 @@ import net.sourceforge.pmd.util.log.MessageReporter;
import com.github.stefanbirkner.systemlambda.SystemLambda;
public class PmdRunnableTest {
class PmdRunnableTest {
public static final String TEST_MESSAGE_SEMANTIC_ERROR = "An error occurred!";
private static final String PARSER_REPORTS_SEMANTIC_ERROR = "1.9-semantic_error";
@@ -59,7 +59,7 @@ public class PmdRunnableTest {
@BeforeEach
public void prepare() {
void prepare() {
// reset data
rule = spy(new RuleThatThrows());
configuration = new PMDConfiguration(LanguageRegistry.singleton(ThrowingLanguageModule.INSTANCE));
@@ -82,7 +82,7 @@ public class PmdRunnableTest {
}
@Test
public void inErrorRecoveryModeErrorsShouldBeLoggedByParser() throws Exception {
void inErrorRecoveryModeErrorsShouldBeLoggedByParser() throws Exception {
SystemLambda.restoreSystemProperties(() -> {
System.setProperty(SystemProps.PMD_ERROR_RECOVERY, "");
@@ -93,7 +93,7 @@ public class PmdRunnableTest {
}
@Test
public void inErrorRecoveryModeErrorsShouldBeLoggedByRule() throws Exception {
void inErrorRecoveryModeErrorsShouldBeLoggedByRule() throws Exception {
SystemLambda.restoreSystemProperties(() -> {
System.setProperty(SystemProps.PMD_ERROR_RECOVERY, "");
@@ -107,7 +107,7 @@ public class PmdRunnableTest {
}
@Test
public void withoutErrorRecoveryModeProcessingShouldBeAbortedByParser() throws Exception {
void withoutErrorRecoveryModeProcessingShouldBeAbortedByParser() throws Exception {
SystemLambda.restoreSystemProperties(() -> {
System.clearProperty(SystemProps.PMD_ERROR_RECOVERY);
assertThrows(AssertionError.class, () -> process(versionWithParserThatThrowsAssertionError()));
@@ -115,7 +115,7 @@ public class PmdRunnableTest {
}
@Test
public void withoutErrorRecoveryModeProcessingShouldBeAbortedByRule() throws Exception {
void withoutErrorRecoveryModeProcessingShouldBeAbortedByRule() throws Exception {
SystemLambda.restoreSystemProperties(() -> {
System.clearProperty(SystemProps.PMD_ERROR_RECOVERY);
assertThrows(AssertionError.class, () -> process(ThrowingLanguageModule.INSTANCE.getDefaultVersion()));
@@ -124,7 +124,7 @@ public class PmdRunnableTest {
@Test
public void semanticErrorShouldAbortTheRun() {
void semanticErrorShouldAbortTheRun() {
Report report = process(versionWithParserThatReportsSemanticError());
verify(reporter, times(1))
@@ -135,7 +135,7 @@ public class PmdRunnableTest {
}
@Test
public void semanticErrorThrownShouldAbortTheRun() {
void semanticErrorThrownShouldAbortTheRun() {
Report report = process(getVersionWithParserThatThrowsSemanticError());
verify(reporter, times(1)).log(eq(Level.ERROR), contains(TEST_MESSAGE_SEMANTIC_ERROR));
@@ -144,23 +144,23 @@ public class PmdRunnableTest {
assertEquals(1, report.getProcessingErrors().size());
}
public static LanguageVersion versionWithParserThatThrowsAssertionError() {
private static LanguageVersion versionWithParserThatThrowsAssertionError() {
return ThrowingLanguageModule.INSTANCE.getVersion(THROWS_ASSERTION_ERROR);
}
public static LanguageVersion getVersionWithParserThatThrowsSemanticError() {
private static LanguageVersion getVersionWithParserThatThrowsSemanticError() {
return ThrowingLanguageModule.INSTANCE.getVersion(THROWS_SEMANTIC_ERROR);
}
public static LanguageVersion versionWithParserThatReportsSemanticError() {
private static LanguageVersion versionWithParserThatReportsSemanticError() {
return ThrowingLanguageModule.INSTANCE.getVersion(PARSER_REPORTS_SEMANTIC_ERROR);
}
public static class ThrowingLanguageModule extends SimpleLanguageModuleBase {
private static class ThrowingLanguageModule extends SimpleLanguageModuleBase {
public static final ThrowingLanguageModule INSTANCE = new ThrowingLanguageModule();
static final ThrowingLanguageModule INSTANCE = new ThrowingLanguageModule();
public ThrowingLanguageModule() {
ThrowingLanguageModule() {
super(LanguageMetadata.withId("foo").name("Foo").extensions("foo")
.addVersion(THROWS_ASSERTION_ERROR)
.addVersion(THROWS_SEMANTIC_ERROR)
@@ -35,10 +35,10 @@ class BinaryDistributionIT extends AbstractBinaryDistributionTest {
+ " java-11, java-12, java-13, java-14, java-15," + System.lineSeparator()
+ " java-16, java-17, java-18, java-18-preview," + System.lineSeparator()
+ " java-19, java-19-preview, java-5, java-6, java-7," + System.lineSeparator()
+ " java-8, java-9, jsp-, kotlin-, kotlin-1.6," + System.lineSeparator()
+ " kotlin-1.6-rfc+0.1, modelica-, plsql-, pom-," + System.lineSeparator()
+ " scala-2.10, scala-2.11, scala-2.12, scala-2.13," + System.lineSeparator()
+ " swift-, vf-, vm-, wsdl-, xml-, xsl-";
+ " java-8, java-9, jsp-, kotlin-1.6, kotlin-1." + System.lineSeparator()
+ " 6-rfc+0.1, modelica-, plsql-, pom-, scala-2.10," + System.lineSeparator()
+ " scala-2.11, scala-2.12, scala-2.13, swift-, vf-," + System.lineSeparator()
+ " vm-, wsdl-, xml-, xsl-";
}
private final String srcDir = new File(".", "src/test/resources/sample-source/java/").getAbsolutePath();
@@ -13,7 +13,7 @@ class LanguageVersionTest extends AbstractLanguageVersionTest {
static Collection<TestDescriptor> data() {
return Arrays.asList(
new TestDescriptor(KotlinLanguageModule.NAME, KotlinLanguageModule.TERSE_NAME, "",
new TestDescriptor(KotlinLanguageModule.NAME, KotlinLanguageModule.TERSE_NAME, "1.6-rfc+0.1",
getLanguage(KotlinLanguageModule.NAME).getDefaultVersion()),
new TestDescriptor(KotlinLanguageModule.NAME, KotlinLanguageModule.TERSE_NAME, "1.6",
getLanguage(KotlinLanguageModule.NAME).getDefaultVersion()));