Document XPath funs

This commit is contained in:
Clément Fournier
2019-03-06 01:29:53 +01:00
parent 45291fb6d8
commit 787a6a4a4d
8 changed files with 320 additions and 13 deletions

View File

@ -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
View 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"

View 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 %}

View File

@ -161,7 +161,7 @@ class JavadocTag < Liquid::Tag
end
def markup_link(rname, link)
"[`#{rname}`](#{link})"
"<a href=\"#{link}\"><code>#{rname}</code></a>"
end

View 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)

View 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" %}

View File

@ -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

View File

@ -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