forked from phoedos/pmd
Document XPath funs
This commit is contained in:
@ -64,6 +64,12 @@ entries:
|
||||
- title: Writing a rule
|
||||
url: /pmd_userdocs_extending_writing_pmd_rules.html
|
||||
output: web, pdf
|
||||
- title: Introduction to writing rules
|
||||
url: /pmd_userdocs_extending_writing_rules_intro.html
|
||||
output: web, pdf
|
||||
- title: Using the Rule Designer
|
||||
url: /pmd_userdocs_extending_designer_intro.html
|
||||
output: web, pdf
|
||||
- title: Writing XPath rules
|
||||
url: /pmd_userdocs_extending_writing_xpath_rules.html
|
||||
output: web, pdf
|
||||
|
29
docs/_data/xpath_funs.yml
Normal file
29
docs/_data/xpath_funs.yml
Normal file
@ -0,0 +1,29 @@
|
||||
langs:
|
||||
- name: "Java"
|
||||
ns: "pmd-java"
|
||||
funs:
|
||||
- name: typeIs
|
||||
returnType: "xs:boolean"
|
||||
description: "Returns true if the context node's static type is a subtype of the given type"
|
||||
notes: "The context node must be a {% jdoc jast::TypeNode %}"
|
||||
parameters:
|
||||
- name: javaQualifiedName
|
||||
type: "xs:string"
|
||||
description: "the qualified name of a class, possibly with pairs of brackets to indicate an array type.
|
||||
Can also be a primitive type name."
|
||||
|
||||
- name: typeIsExactly
|
||||
returnType: "xs:boolean"
|
||||
description: "Returns true if the context node's static type is exactly the given type.
|
||||
In particular, returns false if the context node's type is
|
||||
a subtype of the given type."
|
||||
notes_are_same_as: typeIs
|
||||
params_are_same_as: typeIs
|
||||
|
||||
- name: metric
|
||||
returnType: "xs:decimal?"
|
||||
description: "Returns the value of the metric as evaluated on the context node"
|
||||
notes: "The context node must be a {% jdoc jast::ASTAnyTypeDeclaration %} or a {% jdoc jast::MethodLikeNode %}"
|
||||
parameters:
|
||||
- name: "metricKey"
|
||||
type: "xs:string"
|
56
docs/_includes/custom/xpath_fun_doc.html
Normal file
56
docs/_includes/custom/xpath_fun_doc.html
Normal file
@ -0,0 +1,56 @@
|
||||
{% for lang in site.data.xpath_funs.langs %}
|
||||
|
||||
### {{ lang.name }}
|
||||
|
||||
{{ lang.name }} functions are in the namespace `{{ lang.ns }}`.
|
||||
|
||||
{% for fun in lang.funs %}
|
||||
|
||||
|
||||
|
||||
<h4><code>{{ fun.name }}</code></h4>
|
||||
|
||||
{{ fun.description }} {{ fun.notes }}
|
||||
|
||||
{% unless fun.parameters.size == 0 %}
|
||||
|
||||
<h5>Parameters</h5>
|
||||
|
||||
<table>
|
||||
|
||||
{% for param in fun.parameters %}
|
||||
|
||||
<tr>
|
||||
<td>{{ param.name }}</td>
|
||||
<td>{{ param.description }}</td>
|
||||
</tr>
|
||||
|
||||
{% endfor %}
|
||||
|
||||
</table>
|
||||
|
||||
{% endunless %}
|
||||
|
||||
{% unless fun.examples.size == 0 %}
|
||||
|
||||
<h5>Examples</h5>
|
||||
|
||||
<table>
|
||||
|
||||
{% for example in fun.examples %}
|
||||
|
||||
<tr>
|
||||
<td><code>{{ example.code }}</code></td>
|
||||
<td>{{ example.description }}</td>
|
||||
</tr>
|
||||
|
||||
{% endfor %}
|
||||
</table>
|
||||
|
||||
{% endunless %}
|
||||
|
||||
{% endfor %}
|
||||
|
||||
|
||||
|
||||
{% endfor %}
|
@ -161,7 +161,7 @@ class JavadocTag < Liquid::Tag
|
||||
end
|
||||
|
||||
def markup_link(rname, link)
|
||||
"[`#{rname}`](#{link})"
|
||||
"<a href=\"#{link}\"><code>#{rname}</code></a>"
|
||||
end
|
||||
|
||||
|
||||
|
19
docs/_plugins/render_block.rb
Normal file
19
docs/_plugins/render_block.rb
Normal file
@ -0,0 +1,19 @@
|
||||
require 'pp'
|
||||
#
|
||||
# Tags to create a complex object inline in JSON.
|
||||
#
|
||||
class RenderBlock < Liquid::Block
|
||||
|
||||
def initialize(tag_name, arg, tokens)
|
||||
super
|
||||
@body = tokens
|
||||
end
|
||||
|
||||
def render(context)
|
||||
template = @body.render(context)
|
||||
pp template
|
||||
pp Liquid::Template.parse(template).render(context)
|
||||
end
|
||||
end
|
||||
|
||||
Liquid::Template.register_tag('render', RenderBlock)
|
157
docs/pages/pmd/userdocs/extending/designer_intro.md
Normal file
157
docs/pages/pmd/userdocs/extending/designer_intro.md
Normal file
@ -0,0 +1,157 @@
|
||||
---
|
||||
title: Using the Rule Designer
|
||||
tags: [extending, userdocs]
|
||||
summary: "This page is a gentle introduction to the Rule Designer, a tool made
|
||||
to help developers write new rules."
|
||||
last_updated: July 2018 (6.6.0)
|
||||
permalink: pmd_userdocs_extending_designer_intro.html
|
||||
author: Miguel Griffa <mikkey@users.sourceforge.net>
|
||||
---
|
||||
|
||||
This page is a gentle introduction to the Rule Designer, a tool made to help
|
||||
developers write new rules. Using the designer is useful both to write Java
|
||||
rules and XPath rules, but it's more specifically geared towards XPath rules.
|
||||
This page uses a simple XPath rule to illustrate the common workflow. We assume
|
||||
here that you already know what XPath is and how to read basic XPath queries. W3C
|
||||
has a good tutorial [here](https://www.w3schools.com/xml/xpath_syntax.asp) if you don't.
|
||||
|
||||
## The Rule Designer
|
||||
|
||||
The rule designer is a tool that packs a lot of features to help you develop XPath
|
||||
rules quickly and painlessly. Basically, it allows you to examine the AST of a code
|
||||
snippet and evaluate an XPath expression against it.
|
||||
|
||||
Like for PMD and CPD, you can launch it using `run.sh designer` on Linux/Unix
|
||||
and `designer.bat` on Windows. The interface looks like the following:
|
||||
|
||||
{% include image.html file="userdocs/designer-overview-with-nums.png" alt="Designer overview" %}
|
||||
|
||||
The zone (2) is the **main editor**. When you write a code snippet in the
|
||||
code area to the left, you'll see that the tree to the right will be updated
|
||||
automatically: it's the AST of the code.
|
||||
Note that the code snippet must be a syntactically valid compilation unit for the
|
||||
language you've chosen, e.g. for Java, a compilation unit necessarily has a top-level
|
||||
type declaration.
|
||||
|
||||
If you select a node in the AST, its specific properties will also be displayed
|
||||
in the panel (1): they're the XPath attributes of the node. More on that later.
|
||||
|
||||
The zone (3) is the **XPath editor**. If you enter an XPath query in that area,
|
||||
it will be evaluated on the current AST and the results will be displayed in the
|
||||
list to the bottom right.
|
||||
|
||||
### Rule development process
|
||||
|
||||
|
||||
The basic development process is straightforward:
|
||||
|
||||
1. Write a code snippet in the main editor that features the offending code you're looking for
|
||||
2. Examine the AST and determine what node the violation should be reported on
|
||||
3. Write an XPath expression matching that node in the XPath editor
|
||||
4. Refine the XPath expression iteratively using different code snippets, so that
|
||||
it matches violation cases, but no other node
|
||||
5. Export your XPath expression to an XML rule element, and place it in your ruleset
|
||||
|
||||
In the following sections, we walk through several examples to refine your rule.
|
||||
|
||||
## A simple rule
|
||||
|
||||
Let's say you want to prevent your coding team from naming variables of type
|
||||
`short` after your boss, whose name is Bill. You try the designer on the following
|
||||
offending code snippet:
|
||||
|
||||
```java
|
||||
|
||||
public class KeepingItSerious {
|
||||
|
||||
public void method() {
|
||||
short bill; // LocalVariableDeclaration
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
Examining the AST, you find out that the LocalVariableDeclaration has a VariableDeclaratorId
|
||||
descendant, whose `Image` XPath attribute is exactly `bill`. You thus write your first attempt
|
||||
in the XPath editor:
|
||||
```xpath
|
||||
//VariableDeclaratorId[@Image = "bill"]
|
||||
```
|
||||
|
||||
You can see the XPath result list is updated with the variable declarator.
|
||||
If you try the query against the following updated snippet though, you can
|
||||
see that the field declaration id is matched even though it's not of type `short`.
|
||||
|
||||
```java
|
||||
public class KeepingItSerious {
|
||||
|
||||
Delegator bill; // FieldDeclaration
|
||||
|
||||
public void method() {
|
||||
short bill; // LocalVariableDeclaration
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
You thus refine your XPath expression with an additional predicate,
|
||||
based on your examination of the Type node of the field and local variable
|
||||
declaration nodes.
|
||||
|
||||
```xpath
|
||||
//VariableDeclaratorId[@Image = "bill" and ../../Type[@TypeImage = "short"]]
|
||||
```
|
||||
|
||||
### Exporting to XML
|
||||
|
||||
You estimate that your rule is now production ready, and you'd like to use it in your ruleset.
|
||||
The `File > Export XPath to rule...` allows you to do that in a few clicks: just enter some
|
||||
additional metadata for your rule, and the popup will generate an XML element that you can
|
||||
copy-paste into your ruleset XML. The resulting element looks like so:
|
||||
|
||||
```xml
|
||||
<rule name="DontCallBossShort"
|
||||
language="java"
|
||||
message="Boss wants to talk to you."
|
||||
class="net.sourceforge.pmd.lang.rule.XPathRule" >
|
||||
<description>
|
||||
TODO
|
||||
</description>
|
||||
<priority>3</priority>
|
||||
<properties>
|
||||
<property name="xpath">
|
||||
<value>
|
||||
<![CDATA[
|
||||
//VariableDeclaratorId[../../Type[@TypeImage="short"] and @Image = "bill"]
|
||||
]]>
|
||||
</value>
|
||||
</property>
|
||||
</properties>
|
||||
</rule>
|
||||
```
|
||||
|
||||
You can notice that your XPath expression ends up inside a [property](pmd_userdocs_configuring_rules.html#rule-properties)
|
||||
of a rule of type XPathRule, which is how XPath rules are implemented.
|
||||
|
||||
### Defining rule properties
|
||||
|
||||
Some time later, your boss' boss decides he doesn't want to be called short in Java
|
||||
too, and would like you to add him to the rule. There are several ways to do that,
|
||||
but you decide to use a rule property to make your rule extensible. Doing that
|
||||
directly in the XML is [explained on that page](pmd_userdocs_extending_defining_properties.html#for-xpath-rules),
|
||||
and we'll explain here how to do that in the designer.
|
||||
|
||||
The table to the left of the zone (3) in the screenshot above is a list of
|
||||
properties defined for your rule.
|
||||
Right-clicking the table and selecting "Add property..", you may add a property of
|
||||
type `List[String]` to represent your boss names. You can then use it in your XPath
|
||||
query with a dollar prefix, i.e.
|
||||
|
||||
```xpath
|
||||
//VariableDeclaratorId[@Image = $bossNames and ../../Type[@TypeImage = "short"]]
|
||||
```
|
||||
|
||||
|
||||
{% include note.html content="Using a property of type `List[String]` requires you to use XPath 2.0" %}
|
@ -27,11 +27,11 @@ Conceptually, PMD rules work by *matching a "pattern" against the AST* of a file
|
||||
Rules explore the AST and find nodes that satisfy some conditions that are characteristic
|
||||
of the specific thing the rule is trying to flag. Rules then report a violation on these nodes.
|
||||
|
||||
## Defining rules
|
||||
## Writing new rules
|
||||
|
||||
PMD supports two ways to define rules: using an **XPath query**, or using a
|
||||
**Java visitor**. XPath rules are much easier to set up, since they're defined
|
||||
directly in your ruleset XML, and are expressive enough for most tasks.
|
||||
directly in your ruleset XML, and are expressive enough for nearly any task.
|
||||
|
||||
On the other hand, some parts of PMD's API are only accessible from Java, e.g.
|
||||
accessing the usages of a declaration. And Java rules allow you to do some
|
||||
|
@ -1,23 +1,63 @@
|
||||
---
|
||||
title: Writing XPath rules with the Designer
|
||||
title: Writing XPath rules
|
||||
tags: [extending, userdocs]
|
||||
summary: "Writing XPath rules with the Designer"
|
||||
summary: "This page describes XPath rule support in more details"
|
||||
last_updated: July 2018 (6.6.0)
|
||||
permalink: pmd_userdocs_extending_writing_xpath_rules.html
|
||||
author: Miguel Griffa <mikkey@users.sourceforge.net>
|
||||
author: Miguel Griffa <mikkey@users.sourceforge.net>, Clément Fournier <clement.fournier76@gmail.com>
|
||||
---
|
||||
|
||||
TODO create more technical reference page for XPath rules
|
||||
|
||||
{% include note.html content="For a translation to Georgian, see [webhostinggeeks.com/science/xpath-sourceforge-ka](http://webhostinggeeks.com/science/xpath-sourceforge-ka)" %}
|
||||
{% jdoc_nspace :coremx core::lang.metrics %}
|
||||
{% jdoc_nspace :coreast core::lang.ast %}
|
||||
{% jdoc_nspace :jmx java::lang.java.metrics %}
|
||||
{% jdoc_nspace :jast java::lang.java.ast %}
|
||||
|
||||
|
||||
Since the AST is a tree, conceptually similar to a DOM, it can be queried with an
|
||||
*XPath expression* that matches the nodes your rule is looking for. XPath rules
|
||||
are defined using a single XPath expression, specified directly in the ruleset
|
||||
XML. The next section walks you through the development of an XPath rule.
|
||||
|
||||
{% include note.html content="This page assumes you already know what XPath is and how to read basic XPath queries. W3C has a good tutorial [here](https://www.w3schools.com/xml/xpath_syntax.asp) if you don't." %}
|
||||
## PMD extension functions
|
||||
|
||||
PMD provides some language-specific XPath functions to access semantic
|
||||
information from the AST.
|
||||
|
||||
On XPath 2.0, the namespace must be explicitly provided.
|
||||
|
||||
### Java
|
||||
|
||||
Java functions are in the namespace `pmd-java`.
|
||||
|
||||
| Function name | Arguments | Returns | Notes |
|
||||
|-----------------|----------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------|
|
||||
| `typeIs` | 1: the qualified name of a class, possibly with pairs of brackets to indicate an array type. Can also be a primitive type name. | True if the context node's static type is a subtype of the given type | The context node must be a {% jdoc jast::TypeNode %} |
|
||||
| `typeIsExactly` | (Same as for `typeIs`) | True if the context node's static type is exactly the given type. In particular, returns false if the context node's type is a subtype of the given type. | (Same as for `typeIs`) |
|
||||
| `metric` | 1: the name of an enum constant in {% jdoc jmx::api.JavaOperationMetricKey %} or {% jdoc jmx::api.JavaClassMetricKey %} | A decimal value representing the value of the metric as evaluated on the context node | The context node must be a {% jdoc jast::ASTAnyTypeDeclaration %} or a {% jdoc jast::MethodLikeNode %} |
|
||||
|
||||
{% render %}
|
||||
{% include custom/xpath_fun_doc.html %}
|
||||
{% endrender %}
|
||||
|
||||
There is also a `typeOf` function which is deprecated and whose usages
|
||||
should be replaced with uses of `typeIs` or `typeIsExactly`. That one will
|
||||
be removed with PMD 7.0.0.
|
||||
|
||||
## XPath version
|
||||
|
||||
PMD supports three XPath versions: 1.0, 2.0, and 1.0 compatibility mode. The
|
||||
version can be specified with the `version` property in the rule definition,
|
||||
like so:
|
||||
|
||||
```xml
|
||||
<property version="2.0" /> <!-- or "1.0", or "1.0 compatibility" -->
|
||||
```
|
||||
|
||||
As of PMD version 6.13.0, XPath versions 1.0 and the 1.0 compatibility mode are
|
||||
deprecated. It is recommended that you migrate to 2.0, which shouldn't be too
|
||||
hard.
|
||||
|
||||
### Migrating
|
||||
|
||||
TODO
|
||||
|
||||
|
||||
|
||||
|
||||
|
Reference in New Issue
Block a user