pmd/pmd_devdocs_major_adding_new_language.html

1865 lines
57 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="How to add a new language to PMD.">
<meta name="keywords" content="devdocsextending, ">
<title>Adding PMD support for a new language | PMD Source Code Analyzer</title>
<link rel="stylesheet" href="css/syntax.css">
<link rel="stylesheet" type="text/css" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css">
<!--<link rel="stylesheet" type="text/css" href="css/bootstrap.min.css">-->
<link rel="stylesheet" href="css/modern-business.css">
<link rel="stylesheet" href="css/lavish-bootstrap.css">
<link rel="stylesheet" href="css/customstyles.css">
<link rel="stylesheet" href="css/theme-blue.css">
<link rel="stylesheet" href="css/pmd-customstyles.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-cookie/1.4.1/jquery.cookie.min.js"></script>
<script src="js/jquery.navgoco.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/js/bootstrap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/anchor-js/2.0.0/anchor.min.js"></script>
<script src="js/toc.js"></script>
<script src="js/customscripts.js"></script>
<link rel="shortcut icon" href="images/favicon.ico" type="image/x-icon">
<link rel="icon" href="images/favicon.ico" type="image/x-icon">
<!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
<script src="https://oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js"></script>
<![endif]-->
<link rel="alternate" type="application/rss+xml" title="" href="https://pmd.github.io/pmd/feed.xml">
<script>
$(document).ready(function() {
// Initialize navgoco with default options
$("#mysidebar").navgoco({
caretHtml: '',
accordion: true,
openClass: 'active', // open
save: false, // leave false or nav highlighting doesn't work right
cookie: {
name: 'navgoco',
expires: false,
path: '/'
},
slide: {
duration: 400,
easing: 'swing'
}
});
$("#collapseAll").click(function(e) {
e.preventDefault();
$("#mysidebar").navgoco('toggle', false);
});
$("#expandAll").click(function(e) {
e.preventDefault();
$("#mysidebar").navgoco('toggle', true);
});
});
</script>
<script>
$(function () {
$('[data-toggle="tooltip"]').tooltip()
})
</script>
<script>
$(document).ready(function() {
$("#tg-sb-link").click(function() {
$("#tg-sb-sidebar").toggle();
$("#tg-sb-content").toggleClass('col-md-9');
$("#tg-sb-content").toggleClass('col-md-12');
$("#tg-sb-icon").toggleClass('fa-toggle-on');
$("#tg-sb-icon").toggleClass('fa-toggle-off');
});
});
</script>
</head>
<body>
<!-- Content is offset by the height of the topnav bar. -->
<!-- There's already a padding-top rule in modern-business.css, but it apparently doesn't work on Firefox 60 and Chrome 67 -->
<div id="topbar-content-offset">
<!-- Navigation -->
<nav class="navbar navbar-inverse navbar-fixed-top">
<div class="container topnavlinks">
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="fa fa-home fa-lg navbar-brand" href="index.html">&nbsp;<span class="projectTitle"> PMD Source Code Analyzer Project</span></a>
</div>
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav navbar-right">
<!-- toggle sidebar button -->
<li><a id="tg-sb-link" href="#"><i id="tg-sb-icon" class="fa fa-toggle-on"></i> Nav</a></li>
<!-- entries without drop-downs appear here -->
<li><a href="https://github.com/pmd/pmd/releases/latest" target="_blank">Download</a></li>
<li><a href="https://github.com/pmd/pmd" target="_blank">Fork us on github</a></li>
<!-- entries with drop-downs appear here -->
<!-- conditional logic to control which topnav appears for the audience defined in the configuration file.-->
<!--comment out this block if you want to hide search-->
<li>
<!--start search-->
<div id="search-demo-container">
<input type="text" id="search-input" placeholder="search...">
<ul id="results-container"></ul>
</div>
<script src="js/jekyll-search.js" type="text/javascript"></script>
<script type="text/javascript">
SimpleJekyllSearch.init({
searchInput: document.getElementById('search-input'),
resultsContainer: document.getElementById('results-container'),
dataSource: 'search.json',
searchResultTemplate: '<li><a href="{url}" title="Adding PMD support for a new language">{title}</a></li>',
noResultsText: 'No results found.',
limit: 10,
fuzzy: true,
})
</script>
<!--end search-->
</li>
</ul>
</div>
</div>
<!-- /.container -->
</nav>
<!-- Page Content -->
<div class="container-toc-wrapper">
<div class="container">
<div class="col-lg-12">&nbsp;</div>
<!-- Content Row -->
<div class="row">
<!-- Sidebar Column -->
<div class="col-md-3" id="tg-sb-sidebar">
<ul id="mysidebar" class="nav">
<li class="sidebarTitle">PMD 6.54.0</li>
<div class="sidebarTitleDate">Release date: 28-January-2023</div>
<li>
<a href="#">About</a>
<ul>
<li><a href="index.html">Home</a></li>
<li><a href="pmd_release_notes.html">Release notes</a></li>
<li><a href="pmd_next_major_development.html">PMD 7.0.0 development</a></li>
<li><a href="pmd_about_help.html">Getting help</a></li>
</ul>
</li>
<li>
<a href="#">User Documentation</a>
<ul>
<li><a href="pmd_userdocs_installation.html">Installation and basic CLI usage</a></li>
<li><a href="pmd_userdocs_making_rulesets.html">Making rulesets</a></li>
<li><a href="pmd_userdocs_configuring_rules.html">Configuring rules</a></li>
<li><a href="pmd_userdocs_best_practices.html">Best practices</a></li>
<li><a href="pmd_userdocs_suppressing_warnings.html">Suppressing warnings</a></li>
<li><a href="pmd_userdocs_incremental_analysis.html">Incremental analysis</a></li>
<li><a href="pmd_userdocs_cli_reference.html">PMD CLI reference</a></li>
<li><a href="pmd_userdocs_report_formats.html">PMD Report formats</a></li>
<li><a href="pmd_userdocs_3rdpartyrulesets.html">3rd party rulesets</a></li>
<li class="subfolders">
<a href="#">CPD reference</a>
<ul>
<li><a href="pmd_userdocs_cpd.html">Copy-paste detection</a></li>
<li><a href="pmd_userdocs_cpd_report_formats.html">CPD Report formats</a></li>
</ul>
</li>
<li class="subfolders">
<a href="#">Extending PMD</a>
<ul>
<li><a href="pmd_userdocs_extending_writing_rules_intro.html">Introduction to writing rules</a></li>
<li><a href="pmd_userdocs_extending_your_first_rule.html">Your first rule</a></li>
<li><a href="pmd_userdocs_extending_writing_xpath_rules.html">XPath rules</a></li>
<li><a href="pmd_userdocs_extending_writing_java_rules.html">Java rules</a></li>
<li><a href="pmd_userdocs_extending_designer_reference.html">Rule designer reference</a></li>
<li><a href="pmd_userdocs_extending_defining_properties.html">Defining rule properties</a></li>
<li><a href="pmd_userdocs_extending_metrics_howto.html">Using and defining code metrics</a></li>
<li><a href="pmd_userdocs_extending_rule_guidelines.html">Rule guidelines</a></li>
<li><a href="pmd_userdocs_extending_testing.html">Testing your rules</a></li>
</ul>
</li>
<li class="subfolders">
<a href="#">Tools / Integrations</a>
<ul>
<li><a href="pmd_userdocs_tools_maven.html">Maven PMD Plugin</a></li>
<li><a href="pmd_userdocs_tools_gradle.html">Gradle</a></li>
<li><a href="pmd_userdocs_tools_ant.html">Ant</a></li>
<li><a href="pmd_userdocs_tools_java_api.html">PMD Java API</a></li>
<li><a href="pmd_userdocs_tools_ci.html">CI integrations</a></li>
<li><a href="pmd_userdocs_tools.html">Other Tools / Integrations</a></li>
</ul>
</li>
</ul>
</li>
<li>
<a href="#">Rule Reference</a>
<ul>
<li class="subfolders">
<a href="#">Apex Rules</a>
<ul>
<li><a href="pmd_rules_apex.html">Index</a></li>
<li><a href="pmd_rules_apex_bestpractices.html">Best Practices</a></li>
<li><a href="pmd_rules_apex_codestyle.html">Code Style</a></li>
<li><a href="pmd_rules_apex_design.html">Design</a></li>
<li><a href="pmd_rules_apex_documentation.html">Documentation</a></li>
<li><a href="pmd_rules_apex_errorprone.html">Error Prone</a></li>
<li><a href="pmd_rules_apex_performance.html">Performance</a></li>
<li><a href="pmd_rules_apex_security.html">Security</a></li>
</ul>
</li>
<li class="subfolders">
<a href="#">Ecmascript Rules</a>
<ul>
<li><a href="pmd_rules_ecmascript.html">Index</a></li>
<li><a href="pmd_rules_ecmascript_bestpractices.html">Best Practices</a></li>
<li><a href="pmd_rules_ecmascript_codestyle.html">Code Style</a></li>
<li><a href="pmd_rules_ecmascript_errorprone.html">Error Prone</a></li>
</ul>
</li>
<li class="subfolders">
<a href="#">HTML Rules</a>
<ul>
<li><a href="pmd_rules_html.html">Index</a></li>
<li><a href="pmd_rules_html_bestpractices.html">Best Practices</a></li>
</ul>
</li>
<li class="subfolders">
<a href="#">Java Rules</a>
<ul>
<li><a href="pmd_rules_java.html">Index</a></li>
<li><a href="pmd_rules_java_bestpractices.html">Best Practices</a></li>
<li><a href="pmd_rules_java_codestyle.html">Code Style</a></li>
<li><a href="pmd_rules_java_design.html">Design</a></li>
<li><a href="pmd_rules_java_documentation.html">Documentation</a></li>
<li><a href="pmd_rules_java_errorprone.html">Error Prone</a></li>
<li><a href="pmd_rules_java_multithreading.html">Multithreading</a></li>
<li><a href="pmd_rules_java_performance.html">Performance</a></li>
<li><a href="pmd_rules_java_security.html">Security</a></li>
</ul>
</li>
<li class="subfolders">
<a href="#">Java Server Pages Rules</a>
<ul>
<li><a href="pmd_rules_jsp.html">Index</a></li>
<li><a href="pmd_rules_jsp_bestpractices.html">Best Practices</a></li>
<li><a href="pmd_rules_jsp_codestyle.html">Code Style</a></li>
<li><a href="pmd_rules_jsp_design.html">Design</a></li>
<li><a href="pmd_rules_jsp_errorprone.html">Error Prone</a></li>
<li><a href="pmd_rules_jsp_security.html">Security</a></li>
</ul>
</li>
<li class="subfolders">
<a href="#">Maven POM Rules</a>
<ul>
<li><a href="pmd_rules_pom.html">Index</a></li>
<li><a href="pmd_rules_pom_errorprone.html">Error Prone</a></li>
</ul>
</li>
<li class="subfolders">
<a href="#">Modelica Rules</a>
<ul>
<li><a href="pmd_rules_modelica.html">Index</a></li>
<li><a href="pmd_rules_modelica_bestpractices.html">Best Practices</a></li>
</ul>
</li>
<li class="subfolders">
<a href="#">PLSQL Rules</a>
<ul>
<li><a href="pmd_rules_plsql.html">Index</a></li>
<li><a href="pmd_rules_plsql_bestpractices.html">Best Practices</a></li>
<li><a href="pmd_rules_plsql_codestyle.html">Code Style</a></li>
<li><a href="pmd_rules_plsql_design.html">Design</a></li>
<li><a href="pmd_rules_plsql_errorprone.html">Error Prone</a></li>
</ul>
</li>
<li class="subfolders">
<a href="#">Salesforce VisualForce Rules</a>
<ul>
<li><a href="pmd_rules_vf.html">Index</a></li>
<li><a href="pmd_rules_vf_security.html">Security</a></li>
</ul>
</li>
<li class="subfolders">
<a href="#">Scala Rules</a>
<ul>
<li><a href="pmd_rules_scala.html">Index</a></li>
</ul>
</li>
<li class="subfolders">
<a href="#">VM Rules</a>
<ul>
<li><a href="pmd_rules_vm.html">Index</a></li>
<li><a href="pmd_rules_vm_bestpractices.html">Best Practices</a></li>
<li><a href="pmd_rules_vm_design.html">Design</a></li>
<li><a href="pmd_rules_vm_errorprone.html">Error Prone</a></li>
</ul>
</li>
<li class="subfolders">
<a href="#">WSDL Rules</a>
<ul>
<li><a href="pmd_rules_wsdl.html">Index</a></li>
</ul>
</li>
<li class="subfolders">
<a href="#">XML Rules</a>
<ul>
<li><a href="pmd_rules_xml.html">Index</a></li>
<li><a href="pmd_rules_xml_errorprone.html">Error Prone</a></li>
</ul>
</li>
<li class="subfolders">
<a href="#">XSL Rules</a>
<ul>
<li><a href="pmd_rules_xsl.html">Index</a></li>
<li><a href="pmd_rules_xsl_codestyle.html">Code Style</a></li>
<li><a href="pmd_rules_xsl_performance.html">Performance</a></li>
</ul>
</li>
</ul>
</li>
<li>
<a href="#">Language Specific Documentation</a>
<ul>
<li><a href="pmd_languages_jsp.html">JSP Support</a></li>
<li class="subfolders">
<a href="#">Java Support</a>
<ul>
<li><a href="pmd_languages_java.html">Java Versions</a></li>
<li><a href="pmd_java_metrics_index.html">Java Code Metrics</a></li>
</ul>
</li>
<li><a href="pmd_apex_metrics_index.html">Apex code metrics</a></li>
<li><a href="pmd_languages_plsql.html">PLSQL</a></li>
<li><a href="pmd_languages_visualforce.html">Visualforce</a></li>
<li><a href="pmd_languages_xml.html">XML and XML dialects</a></li>
<li><a href="pmd_languages_html.html">HTML</a></li>
<li><a href="pmd_languages_gherkin.html">Gherkin</a></li>
</ul>
</li>
<li>
<a href="#">Developer Documentation</a>
<ul>
<li><a href="pmd_devdocs_development.html">Developer resources</a></li>
<li><a href="pmd_devdocs_building.html">Building PMD from source</a></li>
<li><a href="https://github.com/pmd/pmd/blob/master/CONTRIBUTING.md" target="_blank">Contributing</a></li>
<li><a href="pmd_devdocs_writing_documentation.html">Writing documentation</a></li>
<li><a href="pmd_devdocs_roadmap.html">Roadmap</a></li>
<li><a href="pmd_devdocs_how_pmd_works.html">How PMD works</a></li>
<li><a href="pmd_devdocs_pmdtester.html">Pmdtester</a></li>
<li><a href="pmd_devdocs_rule_deprecation_policy.html">Rule Deprecation Policy</a></li>
<li class="subfolders">
<a href="#">Major contributions</a>
<ul>
<li><a href="pmd_devdocs_major_rule_guidelines.html">Rule Guidelines</a></li>
<li class="active"><a href="pmd_devdocs_major_adding_new_language.html">Adding a new language</a></li>
<li><a href="pmd_devdocs_major_adding_new_cpd_language.html">Adding a new CPD language</a></li>
<li><a href="pmd_devdocs_major_adding_new_metrics_framework.html">Adding metrics support to a language</a></li>
</ul>
</li>
<li class="subfolders">
<a href="#">Experimental features</a>
<ul>
<li><a href="pmd_devdocs_experimental_ast_dump.html">Creating (XML) dump of the AST</a></li>
</ul>
</li>
</ul>
</li>
<li>
<a href="#">Project documentation</a>
<ul>
<li class="subfolders">
<a href="#">Trivia about PMD</a>
<ul>
<li><a href="pmd_projectdocs_trivia_news.html">PMD in the press</a></li>
<li><a href="pmd_projectdocs_trivia_products.html">Products & books related to PMD</a></li>
<li><a href="pmd_projectdocs_trivia_similarprojects.html">Similar projects</a></li>
<li><a href="pmd_projectdocs_trivia_meaning.html">What does 'PMD' mean?</a></li>
</ul>
</li>
<li><a href="pmd_projectdocs_faq.html">FAQ</a></li>
<li><a href="license.html">License</a></li>
<li><a href="pmd_projectdocs_credits.html">Credits</a></li>
<li><a href="pmd_release_notes_old.html">Old release notes</a></li>
<li><a href="pmd_projectdocs_decisions.html">Decisions</a></li>
<li class="subfolders">
<a href="#">Project management</a>
<ul>
<li><a href="pmd_projectdocs_committers_infrastructure.html">Infrastructure</a></li>
<li><a href="pmd_projectdocs_committers_releasing.html">Release process</a></li>
<li><a href="pmd_projectdocs_committers_merging_pull_requests.html">Merging pull requests</a></li>
<li><a href="pmd_projectdocs_committers_main_landing_page.html">Main Landing page</a></li>
</ul>
</li>
</ul>
</li>
<!-- if you aren't using the accordion, uncomment this block:
<p class="external">
<a href="#" id="collapseAll">Collapse All</a> | <a href="#" id="expandAll">Expand All</a>
</p>
-->
</ul>
<!-- this highlights the active parent class in the navgoco sidebar. this is critical so that the parent expands when you're viewing a page. This must appear below the sidebar code above. Otherwise, if placed inside customscripts.js, the script runs before the sidebar code runs and the class never gets inserted.-->
<script>$("li.active").parents('li').toggleClass("active");</script>
</div>
<!-- Content Column -->
<div class="col-md-9" id="tg-sb-content">
<header>
<div class="row">
<div class="col-lg-12">
<a href="./" role="button"
><i class="fa fa-home fa-lg"></i
></a>
» Adding PMD support for a new language
<a
target="_blank"
href="https://github.com/pmd/pmd/blob/master/docs/pages/pmd/devdocs/major_contributions/adding_new_language.md"
class="pull-right"
role="button"
><i class="fa fa-github fa-lg"></i> Edit on GitHub</a
>
</div>
</div>
<hr />
</header>
<div class="post-header">
<h1 class="post-title-main">Adding PMD support for a new language</h1>
</div>
<div class="post-content" data-github-edit-url="https://github.com/pmd/pmd/blob/master/docs/pages/pmd/devdocs/major_contributions/adding_new_language.md">
<div class="summary">How to add a new language to PMD.</div>
<div id="inline-toc"><!-- empty, move TOC here when screen size too small --></div>
<h2 id="1--start-with-a-new-sub-module">1. Start with a new sub-module.</h2>
<ul>
<li>See pmd-java or pmd-vm for examples.</li>
</ul>
<h2 id="2--implement-an-ast-parser-for-your-language">2. Implement an AST parser for your language</h2>
<ul>
<li>Ideally an AST parser should be implemented as a JJT file <em>(see VmParser.jjt or Java.jjt for example)</em></li>
<li>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.</li>
<li>See this link for reference: <a href="https://javacc.java.net/doc/JJTree.html">https://javacc.java.net/doc/JJTree.html</a></li>
</ul>
<h2 id="3--create-ast-node-classes">3. Create AST node classes</h2>
<ul>
<li>For each AST node that your parser can generate, there should be a class</li>
<li>The name of the AST class should be “AST” + “whatever is the name of the node in JJT file”.
<ul>
<li>For example, if JJT contains a node called “IfStatement”, there should be a class called “ASTIfStatement”</li>
</ul>
</li>
<li>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</li>
<li>Its a good idea to create a parent AST class for all AST classes of the language. This simplifies rule creation later. <em>(see SimpleNode for Velocity and AbstractJavaNode for Java for example)</em></li>
<li>Note: These AST node classes are generated usually once by javacc/jjtree and can then be modified as needed.</li>
</ul>
<h2 id="4--compile-your-parser-if-using-jjt">4. Compile your parser (if using JJT)</h2>
<ul>
<li>An ant script is being used to compile jjt files into classes. This is in <code class="language-plaintext highlighter-rouge">pmd-&lt;lang&gt;/src/main/ant/alljavacc.xml</code> file.</li>
<li>Create <code class="language-plaintext highlighter-rouge">alljavacc.xml</code> file for your language, you can use one from <code class="language-plaintext highlighter-rouge">pmd-java</code> as an example.</li>
<li>You would probably want to adjust contents of the <code class="language-plaintext highlighter-rouge">&lt;delete&gt;</code> tag: start with an empty <code class="language-plaintext highlighter-rouge">&lt;fileset&gt;</code> and add there <code class="language-plaintext highlighter-rouge">&lt;include&gt;</code>s for those AST nodes you had to manually rewrite (moving those node classes from autogenerated directory to the regular source tree).</li>
</ul>
<h2 id="5--create-a-tokenmanager">5. Create a TokenManager</h2>
<ul>
<li>Create a new class that implements the <code class="language-plaintext highlighter-rouge">TokenManager</code> interface <em>(see VmTokenManager or JavaTokenManager for example)</em></li>
</ul>
<h2 id="6--create-a-pmd-parser-adapter">6. Create a PMD parser “adapter”</h2>
<ul>
<li>Create a new class that extends AbstractParser</li>
<li>There are two important methods to implement
<ul>
<li><code class="language-plaintext highlighter-rouge">createTokenManager</code> method should return a new instance of a token manager for your language <em>(see step #5)</em></li>
<li><code class="language-plaintext highlighter-rouge">parse</code> method should return the root node of the AST tree obtained by parsing the Reader source</li>
<li>See <code class="language-plaintext highlighter-rouge">VmParser</code> class as an example</li>
</ul>
</li>
</ul>
<h2 id="7--create-a-rule-violation-factory">7. Create a rule violation factory</h2>
<ul>
<li>Extend <code class="language-plaintext highlighter-rouge">AbstractRuleViolationFactory</code> <em>(see VmRuleViolationFactory for example)</em></li>
<li>The purpose of this class is to create a rule violation instance specific to your language</li>
</ul>
<h2 id="8--create-a-version-handler">8. Create a version handler</h2>
<ul>
<li>Extend <code class="language-plaintext highlighter-rouge">AbstractLanguageVersionHandler</code> <em>(see VmHandler for example)</em></li>
<li>This class is sort of a gateway between PMD and all parsing logic specific to your language. It has 3 purposes:
<ul>
<li><code class="language-plaintext highlighter-rouge">getRuleViolationFactory</code> method returns an instance of your rule violation factory <em>(see step #7)</em></li>
<li><code class="language-plaintext highlighter-rouge">getParser</code> returns an instance of your parser adapter <em>(see step #6)</em></li>
<li><code class="language-plaintext highlighter-rouge">getDumpFacade</code> returns a <code class="language-plaintext highlighter-rouge">VisitorStarter</code> that allows to dump a text representation of the AST into a writer <em>(likely for debugging purposes)</em></li>
</ul>
</li>
</ul>
<h2 id="9--create-a-parser-visitor-adapter">9. Create a parser visitor adapter</h2>
<ul>
<li>If you use JJT to generate your parser, it should also generate an interface for a parser visitor <em>(see VmParserVisitor for example)</em></li>
<li>Create a class that implements this auto-generated interface <em>(see VmParserVisitorAdapter for example)</em></li>
<li>The purpose of this class is to serve as a pass-through <code class="language-plaintext highlighter-rouge">visitor</code> implementation, which, for all AST types in your language, just executes visit on the base AST type</li>
</ul>
<h2 id="10-create-a-rule-chain-visitor">10. Create a rule chain visitor</h2>
<ul>
<li>Extend <code class="language-plaintext highlighter-rouge">AbstractRuleChainVisitor</code> <em>(see VmRuleChainVisitor for example)</em></li>
<li>This class should <code class="language-plaintext highlighter-rouge">implement</code> two <code class="language-plaintext highlighter-rouge">important</code> methods:
<ul>
<li><code class="language-plaintext highlighter-rouge">indexNodes</code> 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.</li>
<li><code class="language-plaintext highlighter-rouge">visit</code> 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.</li>
</ul>
</li>
</ul>
<h2 id="11-make-pmd-recognize-your-language">11. Make PMD recognize your language</h2>
<ul>
<li>Create your own subclass of <code class="language-plaintext highlighter-rouge">net.sourceforge.pmd.lang.BaseLanguageModule</code>. <em>(see VmLanguageModule or JavaLanguageModule as an example)</em></li>
<li>Youll need to refer the rule chain visitor created in step #10.</li>
<li>Add for each version of your language a call to <code class="language-plaintext highlighter-rouge">addVersion</code> in your language modules constructor.</li>
<li>Create the service registration via the text file <code class="language-plaintext highlighter-rouge">src/main/resources/META-INF/services/net.sourceforge.pmd.lang.Language</code>. Add your fully qualified class name as a single line into it.</li>
</ul>
<h2 id="12-add-ast-regression-tests">12. Add AST regression tests</h2>
<p>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.</p>
<p>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.</p>
<p>This helps to detect anything in the AST structure, that changed, maybe unexpectedly.</p>
<ul>
<li>Create a test class in the package <code class="language-plaintext highlighter-rouge">net.sourceforge.pmd.lang.$lang.ast</code> with the name <code class="language-plaintext highlighter-rouge">$langTreeDumpTest</code>.</li>
<li>This test class must extend <code class="language-plaintext highlighter-rouge">net.sourceforge.pmd.lang.ast.test.BaseTreeDumpTest</code>. Note: This class
is written in kotlin and is available in the module “lang-test”.</li>
<li>
<p>Add a default constructor, that calls the super constructor like so:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="kd">public</span> <span class="n">$langTreeDumpTest</span><span class="o">()</span> <span class="o">{</span>
<span class="kd">super</span><span class="o">(</span><span class="nc">NodePrintersKt</span><span class="o">.</span><span class="na">getSimpleNodePrinter</span><span class="o">(),</span> <span class="s">".$extension"</span><span class="o">);</span>
<span class="o">}</span>
</code></pre></div> </div>
<p>Replace “$lang” and “$extension” accordingly.</p>
</li>
<li>Implement the method <code class="language-plaintext highlighter-rouge">getParser()</code>. It must return a
subclass of <code class="language-plaintext highlighter-rouge">net.sourceforge.pmd.lang.ast.test.BaseParsingHelper</code>. See
<code class="language-plaintext highlighter-rouge">net.sourceforge.pmd.lang.ecmascript.ast.JsParsingHelper</code> for a example.
With this parser helper you can also specify, where the test files are searched, by using
the method <code class="language-plaintext highlighter-rouge">withResourceContext(Class&lt;?&gt;, String)</code>.</li>
<li>
<p>Add one or more test methods. Each test method parses one file and compares the result. The base
class has a helper method <code class="language-plaintext highlighter-rouge">doTest(String)</code> that does all the work. This method just needs to be called:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nd">@Test</span>
<span class="kd">public</span> <span class="kt">void</span> <span class="nf">myFirstAstTest</span><span class="o">()</span> <span class="o">{</span>
<span class="n">doTest</span><span class="o">(</span><span class="s">"filename-without-extension"</span><span class="o">);</span>
<span class="o">}</span>
</code></pre></div> </div>
</li>
<li>On the first test run the test fails. A text file (with the extension <code class="language-plaintext highlighter-rouge">.txt</code>) is created, that records the
current AST. On the next run, the text file is used as comparison and the test should pass. Dont forget
to commit the generated text file.</li>
</ul>
<p>A complete example can be seen in the JavaScript module: <code class="language-plaintext highlighter-rouge">net.sourceforge.pmd.lang.ecmascript.ast.JsTreeDumpTest</code>.
The test resources are in the subpackage “testdata”: <code class="language-plaintext highlighter-rouge">pmd-javascript/src/test/resources/net/sourceforge/pmd/lang/ecmascript/ast/testdata/</code>.</p>
<p>The Scala module also has a test, written in Kotlin instead of Java:
<code class="language-plaintext highlighter-rouge">net.sourceforge.pmd.lang.scala.ast.ScalaParserTests</code>.</p>
<h2 id="13-create-an-abstract-rule-class-for-the-language">13. Create an abstract rule class for the language</h2>
<ul>
<li>Extend <code class="language-plaintext highlighter-rouge">AbstractRule</code> and implement the parser visitor interface for your language <em>(see AbstractVmRule for example)</em></li>
<li>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.</li>
</ul>
<h2 id="14-create-rules">14. Create rules</h2>
<ul>
<li>Rules are created by extending the abstract rule class created in step 13 <em>(see <code class="language-plaintext highlighter-rouge">EmptyForeachStmtRule</code> for example)</em></li>
<li>Creating rules is already pretty well documented in PMD - and its no different for a new language, except you may have different AST nodes.</li>
</ul>
<h2 id="15-test-the-rules">15. Test the rules</h2>
<ul>
<li>See BasicRulesTest for example</li>
<li>You have to create a rule set for your language <em>(see vm/basic.xml for example)</em></li>
<li>For each rule in this set you want to test, call <code class="language-plaintext highlighter-rouge">addRule</code> method in setUp of the unit test
<ul>
<li>This triggers the unit test to read the corresponding XML file with rule test data <em>(see <code class="language-plaintext highlighter-rouge">EmptyForeachStmtRule.xml</code> for example)</em></li>
<li>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</li>
</ul>
</li>
<li>
<p>To verify the validity of the created ruleset, create a subclass of <code class="language-plaintext highlighter-rouge">AbstractRuleSetFactoryTest</code> (<em>see <code class="language-plaintext highlighter-rouge">RuleSetFactoryTest</code> in pmd-vm for example)</em>.
This will load all rulesets and verify, that all required attributes are provided.</p>
<p><em>Note:</em> Youll need to add your ruleset to <code class="language-plaintext highlighter-rouge">rulesets.properties</code>, so that it can be found.</p>
</li>
</ul>
<h2 id="debugging-with-rule-designer">Debugging with Rule Designer</h2>
<p>When implementing your grammar it may be very useful to see how PMD parses your example files.
This can be achieved with Rule Designer:</p>
<ul>
<li>Override the <code class="language-plaintext highlighter-rouge">getXPathNodeName</code> in your AST nodes for Designer to show node names.</li>
<li>Make sure to override both <code class="language-plaintext highlighter-rouge">jjtOpen</code> and <code class="language-plaintext highlighter-rouge">jjtClose</code> in your AST node base class so that they set both start and end line and column for proper node bound highlighting.</li>
<li><em>Not strictly required but trivial and useful:</em> implement syntax highlighting for Rule Designer:
<ul>
<li>Fork and clone the <a href="https://github.com/pmd/pmd-designer">pmd/pmd-designer</a> repository.</li>
<li>Add a syntax highlighter implementation to <code class="language-plaintext highlighter-rouge">net.sourceforge.pmd.util.fxdesigner.util.codearea.syntaxhighlighting</code> (you could use Java as an example).</li>
<li>Register it in the <code class="language-plaintext highlighter-rouge">AvailableSyntaxHighlighters</code> enumeration.</li>
<li>Now build your implementation and place the <code class="language-plaintext highlighter-rouge">target/pmd-ui-&lt;version&gt;-SNAPSHOT.jar</code> to the <code class="language-plaintext highlighter-rouge">lib</code> directory inside your <code class="language-plaintext highlighter-rouge">pmd-bin-...</code> distribution (you have to delete old <code class="language-plaintext highlighter-rouge">pmd-ui-*.jar</code> from there).</li>
</ul>
</li>
</ul>
<div class="tags">
<b>Tags: </b>
<a href="tag_devdocs.html" class="btn btn-default navbar-btn cursorNorm" role="button">devdocs</a>
<a href="tag_extending.html" class="btn btn-default navbar-btn cursorNorm" role="button">extending</a>
</div>
</div>
<footer>
<hr />
<div>
This documentation is written in markdown. <br />
If there is something missing or can be improved, edit this page on
github and create a PR:
<a
target="_blank"
href="https://github.com/pmd/pmd/blob/master/docs/pages/pmd/devdocs/major_contributions/adding_new_language.md"
role="button"
><i class="fa fa-github fa-lg"></i> Edit on GitHub</a
>
</div>
<hr />
<div class="row">
<div class="col-lg-12 footer">
&copy;2023 PMD Open Source Project. All rights
reserved. <br />
<span>Page last updated:</span>
October 5, 2019<br /> Site last generated: Jan 28, 2023 <br />
<p>
<img src="images/pmd-logo-small.png" alt="Company
logo"/>
</p>
</div>
</div>
</footer>
</div>
<!-- /.row -->
</div>
<!-- /.container -->
</div>
<!-- Sticky TOC column -->
<div class="toc-col">
<div id="toc"></div>
</div>
<!-- /.toc-container-wrapper -->
</div>
</div>
</body>
</html>