<module> entry, so that it is built alongside theother languages.src/main/javacc.int id.javacc-wrapper.xml file in thetop-level pmd sources.maven-antrun-plugin. Add this plugin to yourpom.xml file and configureit the language name. You can usepmd-java/pom.xml as an example.generate-sources whenever the whole project is built. But you cancall./mvnw generate-sources directly for your module if you want your parser to be generated.JjtreeParserAdapter.tokenBehavior method should return a new instance ofTokenDocumentBehavior constructed with the listof tokes in your language. The compile step #4 will generate a class$langTokenKinds which hasall the available tokens in the fieldTOKEN_NAMES.parseImpl method should return the root node of the AST tree obtained by parsing the CharStream sourceVmParser class as an exampleAbstractPmdLanguageVersionHandler(see VmHandler for example)ViolationDecorators, to add additional language specific information to thecreated violations. TheJava language module uses this toprovide the method name or class name, where the violation occurred.VmHandler class as an exampleAstVisitor.VmVisitor.VmVisitorBase as an example.net.sourceforge.pmd.lang.impl.SimpleLanguageModuleBase.(see VmLanguageModule orJavaLanguageModule as an example)addVersion in your language module’s constructor.UseaddDefaultVersion for defining the default version.src/main/resources/META-INF/services/net.sourceforge.pmd.lang.Language.Add your fully qualified class name as a single line into it.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 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 comparedagainst a previously recorded version. If there are differences, the test fails.
This helps to detect anything in the AST structure that changed, maybe unexpectedly.
net.sourceforge.pmd.lang.$lang.ast with the name$langTreeDumpTest.net.sourceforge.pmd.lang.test.ast.BaseTreeDumpTest. Note: This classis written in kotlin and is available in the module “lang-test”.Add a default constructor, that calls the super constructor like so:
public$langTreeDumpTest(){super(NodePrintersKt.getSimpleNodePrinter(),".$extension");}Replace “$lang” and “$extension” accordingly.
getParser(). It must return asubclass ofnet.sourceforge.pmd.lang.test.ast.BaseParsingHelper. Seenet.sourceforge.pmd.lang.ecmascript.ast.JsParsingHelper for an example.With this parser helper you can also specify, where the test files are searched, by usingthe methodwithResourceContext(Class<?>, String).Add one or more test methods. Each test method parses one file and compares the result. The baseclass has a helper methoddoTest(String) that does all the work. This method just needs to be called:
@TestpublicvoidmyFirstAstTest(){doTest("filename-without-extension");}.txt) is created, that records thecurrent AST. On the next run, the text file is used as comparison and the test should pass. Don’t forgetto commit the generated text file.A complete example can be seen in the JavaScript module:net.sourceforge.pmd.lang.ecmascript.ast.JsTreeDumpTest.The test resources are in the subpackage “testdata”:pmd-javascript/src/test/resources/net/sourceforge/pmd/lang/ecmascript/ast/testdata/.
The Scala module also has a test, written in Kotlin instead of Java:net.sourceforge.pmd.lang.scala.ast.ScalaParserTests.
AbstractRule and implement the parser visitor interface for your language(see AbstractVmRule for example)EmptyForeachStmtRule for example)PmdRuleTst(see AvoidReassigningParametersTest in pmd-vm for example)AvoidReassigningParameters.xml for example)To verify the validity of the created ruleset, create a subclass ofAbstractRuleSetFactoryTest(seeRuleSetFactoryTest 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 category ruleset tocategories.properties, so that it can be found.
Finishing up your new language module by adding a page in the documentation. Create a new markdown file<langId>.md indocs/pages/pmd/languages/. This file should have the following frontmatter:
---title: <Language Name>permalink: pmd_languages_<langId>.htmllast_updated: <Month> <Year> (<PMD Version>)tags: [languages, PmdCapableLanguage, CpdCapableLanguage]---On this page, language specifics can be documented, e.g. when the language was first supported by PMD.There is also the following Jekyll Include, that creates summary box for the language:
{% include language_info.html name='<Language Name>' id='<langId>' implementation='<langId>::lang.<langId>.<langId>LanguageModule' supports_cpd=true supports_pmd=true %}PMD exposes the AST nodes for use by XPath based rules (seeDOM representation of ASTs).Most Java getters in the AST classes are made available by default. These getters constitute the API of the language.If a getter method is renamed, then every XPath rule that uses this getter also needs to be adjusted. In order tohave more control over this, there are two annotations that can be used for AST classes and their methods:
DeprecatedAttribute: Getters might be annotated with that indicating, thatthis getter method should not be used in XPath rules. When a XPath rule uses such a method, a warning isissued. If the method additionally has the standard Java@Deprecated annotation, then the getter is alsodeprecated for java usage. Otherwise, the getter is only deprecated for usage in XPath rules.
When a getter is deprecated and there is a different getter to be used instead, then theattributereplaceWith should be used.
NoAttribute: This annotation can be used on an AST node type or on individualmethods in order to filter out which methods are available for XPath rules.When used on a type, either all methods can be filtered or only inherited methods (see attributescope).When used directly on an individual method, then only this method will be filtered out.That way methods can be added in AST nodes, that should only be used in Java rules, e.g. as auxiliary methods.
Note:
Not all getters are available for XPath rules. It depends on the result type.EspeciallyLists or Collections in general arenot supported.
Only the following Java result types are supported:
When implementing your grammar it may be very useful to see how PMD parses your example files.This can be achieved with Rule Designer:
getXPathNodeName in your AST nodes for Designer to show node names.jjtOpen andjjtClose in your AST node base class so that they set both start and end line and column for proper node bound highlighting.net.sourceforge.pmd.util.fxdesigner.util.codearea.syntaxhighlighting (you could use Java as an example).AvailableSyntaxHighlighters enumeration.target/pmd-designer-<version>-SNAPSHOT.jar to thelib directory inside yourpmd-bin-... distribution (you have to delete oldpmd-designer-*.jar from there).If you want to add support for computing metrics:
lang.<langname>.metrics<langname>MetricsgetLanguageMetricsProvider, to make the metrics available in the designer.SeeJavaMetrics for an example.
A symbol table keeps track of variables and their usages. It is part of semantic analysis and wouldbe executed in your parser adapter as an additional pass after you got the initial AST.
There is no general language independent API in PMD core. For now, each language will need to implementits own solution. The symbol information that has been resolved in the additional parser passcan be made available on the AST nodes via extra methods, e.g.getSymbolTable(),getSymbol(), orgetUsages().
Currently only Java provides an implementation for symbol table,seeJava-specific features and guidance.
Note:
With PMD 7.0.0 the symbol table and type resolution implementation has beenrewritten from scratch. There is still an old API for symbol table support, that is used by PLSQL,seenet.sourceforge.pmd.lang.symboltable. This has been deprecated and should not be used.
For typed languages like Java type information can be useful for writing rules, that trigger only onspecific types. Resolving types of expressions and variables would be done after in your parseradapter as yet another additional pass, potentially after resolving the symbol table.
Type resolution tries to find the actual class type of each used type, following along method calls(including overloaded and overwritten methods), allowing to query subtypes and type hierarchy.This might require additional configuration for the language, e.g. in Java you needto configure an auxiliary classpath.
There is no general language independent API in PMD core. For now, each language will need to implementits own solution. The type information can be made available on the AST nodes via extra methods,e.g.getType().
Currently only Java provides an implementation for type resolution,seeJava-specific features and guidance.
Call and data flow analysis keep track of the data as it is moving through different execution pathsa program has. This would be yet another analysis pass.
There is no general language independent API in PMD core. For now, each language will need to implementits own solution.
Currently Java has some limited support for data flow analysis,seeJava-specific features and guidance.