This guide will show you how to implement development rules with Query API.
The com.optimyth.qaking.highlevelapi.dsl.Query class represents a query in the syntax tree, in this case, the High-Level Tree (HLT).
Query provides a fluent interface to perform searches in the AST, specifying a sequence of operations (find, filter, navigate, visit...), which will be done starting from a given set of nodes. Each operation will be set passing primitive objects (NodePredicate, NodeVisitor, NodeSearch or Navigation) so then a call to one of the available run() methods makes to execute all the registered operations.
Once the nodes of interest are found, the report() operation will generate in the report one violation for each node reached.
- com.als.core.ast.NodeVisitor: apply some logic to the node. Execution sequence on a tree or sub-tree.
- com.als.core.ast.NodePredicate: check if a node meets a series of clauses or conditions.
- com.optimyth.qaking.highlevelapi.nodeset.NodeSearch: find some node starting from a given one. For example, find the declaration of a variable from its use, the definition of a function from its call...
- com.optimyth.qaking.highlevelapi.navigation.Navigation: navigate through the AST from a given node, possibly applying some other primitive object.
- com.optimyth.qaking.highlevelapi.dsl.QueryOperation: definition of a new operation.
The available operations to perform the queries can be classified as follows:
|custom operation||Registers a custom operation||operation(QueryOperation)|
|execute query||Executes the query, specifying initial node(s)||run(Rule, RuleContext) run(Rule, RuleContext, BaseNode...) run(Rule, RuleContext, NodeSet)|
|filter||Filter current context nodes||filter(NodePredicate) filter(Query)|
|find following a navigation||Find nodes traversed by navigation, matching predicate||find(NodePredicate, Navigation)|
|find sucessors||Find all successors matching predicate||find(NodePredicate match)|
|navigate||Traverses the given navigation from current (context) nodes||navigate(Navigation) navigate(NodeSearch) navigate(String xpath)|
|navigate & visit||Visit each node reachable via given navigation with visitor||visit(NodeVisitor, Navigation)|
|report||Emit a rule violation for each current context node or snapshot, using the ToViolation object to custimize violation to emit||report() report(String snapshot) report(ToViolation) report(String, ToViolation)|
|snapshot||Create a "snapshot" of current context nodeset, giving it a name||snapshot(String)|
|visit||Visits each node in current context||visit(NodeVisitor)|
Most of these operations return the same Query instance so that calls can be chained. Besides, available actions and primitives are typically thread-safe, so you could use the Query object as a rule instance field and make the query.run() call from the visit method of the rule to process each source file under analysis.
As a simple example, imagine you want to report the getter methods that return null. Using the Query API, your rule could have an implementation that:
The rule will be with a declarative format and coded in a few lines, leaving the thickest part of the implementation in the declaration of the appropriate primitives for each case.
Besides, we can always define those specific classes we need. To do that and specifically in the case of the predicates, it is possible to use both the own class com.als.core.ast.NodePredicate, already mentioned, and the one defined in Google Guava library (com.google.common.base.Predicate), which is included in the classpath for developing.
Let's see a complete example; in this case, a rule that detects, in Java classes, not used variables (local, class fields or parameters).
Java rule implementation example - Query API
And another one: the implementation of a rule for Cobol files that detects the use of the DISPLAY statement in arithmetical operations.
If you have knowledge of XPath, the class com.optimyth.qaking.highlevelapi.navigation.Region is available, which will provide Navigation instances for each XPath axis.
Region name XPath axis Nodes traversed
- SELF self:: context node itself
- ROOT / go to the root node
- CHILDREN child:: immediate children
- PARENT parent:: parent node
- ANCESTORS ancestor-or-self:: ancestors, including self
- SUCCESSORS descendant:: subtree nodes from node, not including itself
- LEFTSIBLINGS preceding-sibling:: Siblings of node at left, not including itself
- RIGHTSIBLINGS following-sibling:: Siblings of node at right, not including itself
- PRECEDING preceding:: Nodes appearing before node (before in code text)
- FOLLOWING following:: Nodes appearing after node (before in code text)
As you can guess, the power and potential this API provides are great, so we encourage you to examine and test the available options in the development of your own rules.
To expand and consolidate the concepts that we have presented, please consult the documentation in the development\doc directory of Kiuwan Local Analyzer distribution.
- No labels