Document XPath funs
This commit is contained in:
@ -64,6 +64,12 @@ entries:
|
|||||||
- title: Writing a rule
|
- title: Writing a rule
|
||||||
url: /pmd_userdocs_extending_writing_pmd_rules.html
|
url: /pmd_userdocs_extending_writing_pmd_rules.html
|
||||||
output: web, pdf
|
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
|
- title: Writing XPath rules
|
||||||
url: /pmd_userdocs_extending_writing_xpath_rules.html
|
url: /pmd_userdocs_extending_writing_xpath_rules.html
|
||||||
output: web, pdf
|
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
|
end
|
||||||
|
|
||||||
def markup_link(rname, link)
|
def markup_link(rname, link)
|
||||||
"[`#{rname}`](#{link})"
|
"<a href=\"#{link}\"><code>#{rname}</code></a>"
|
||||||
end
|
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
|
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.
|
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
|
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
|
**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.
|
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
|
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]
|
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)
|
last_updated: July 2018 (6.6.0)
|
||||||
permalink: pmd_userdocs_extending_writing_xpath_rules.html
|
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