blob: 6de24cecd23d0f6990fd732f0b9867efa05b3518 [file] [log] [blame] [view]
# SV Style Linter Developer Guide
<!--*
freshness: { owner: 'hzeller' reviewed: '2020-10-28' }
*-->
This document describes how to implement style lint rules.
Before you begin, familiarize yourself with:
* [General contribution guidelines](../CONTRIBUTING.md)
* [General developer resources](development.md)
## Communication
* Join the developer's mailing list: verible-dev@googlegroups.com
([join](https://groups.google.com/forum/#!forum/verible-dev/join))
* Use the github issue tracker, discuss and comment:
* [existing lint rule issues](https://github.com/chipsalliance/verible/issues?q=is%3Aissue+is%3Aopen+label%3Astyle-linter)
* [file new issue about existing rule](https://github.com/chipsalliance/verible/issues/new?assignees=&labels=style-linter&template=style-linter-bug.md&title=)
* [file new issue about a new rule](https://github.com/chipsalliance/verible/issues/new?assignees=&labels=enhancement%2C+style-linter&template=style-linter-feature-request.md&title=)
## Whose Style Guide?
Whose style guide serves as a reference for lint rules? Everyone and anyone's.
Every team may set its own guidelines on what constitutes correct style. The
style linter hosts an ever-growing library of rules, but _you_ decide
[which rules or configurations](../verible/verilog/tools/lint.md#usage) best suit your
project.
### Traits of good lint rules
* Identify error-prone constructs, including those that may lead to production
bugs
* Have few exceptions, low risk of false positives, and low frequency of lint
waivers
* Reduces the number of choices for ways-to-express-a-concept
* Rules that enforce [SV-LRM] conformance should be implemented in actual
compilers, but sometimes can be enforced in style linters as well.
## Types of Analyses
The major classes of text analyses available today are:
* [LineLintRule] analyzes text one line at a time. Examples:
* [no_trailing_spaces_rule](https://cs.opensource.google/verible/verible/+/master:verible/verilog/analysis/checkers/no-trailing-spaces-rule.h)
* [no_tabs_rule](https://cs.opensource.google/verible/verible/+/master:verible/verilog/analysis/checkers/no-tabs-rule.h)
* [TokenStreamLintRule] scans one token at a time. Examples:
* [endif_comment_rule](https://cs.opensource.google/verible/verible/+/master:verible/verilog/analysis/checkers/endif-comment-rule.h)
and
* [macro_name_style_rule](https://cs.opensource.google/verible/verible/+/master:verible/verilog/analysis/checkers/macro-name-style-rule.h)
* [SyntaxTreeLintRule] analyzes the syntax trees, examining tree nodes and
leaves. The
[vast majority of SystemVerilog lint rules](https://cs.opensource.google/verible/verible/+/master:verible/verilog/analysis/checkers)
fall under this category. The [Analysis Tools](#syntax-tree-analysis-tools)
section describes various syntax tree analysis tools.
* [TextStructureLintRule] analyzes an entire [TextStructureView] in any
manner. This is the most flexible analyzer that can access all of the
structured views of the analyzed text. It is best suited for rules that:
* need access to **more than one** of the following forms: lines, tokens,
syntax-tree
* can be implemented efficiently without having to traverse any of the
aforementioned forms.
Examples:
[module_filename_rule](https://cs.opensource.google/verible/verible/+/master:verible/verilog/analysis/checkers/module-filename-rule.h?q=class:%5CbModuleFilenameRule),
[line_length_rule](https://cs.opensource.google/verible/verible/+/master:verible/verilog/analysis/checkers/line-length-rule.h?q=class:%5CbLineLengthRule),
and
[posix_eof_rule](https://cs.opensource.google/verible/verible/+/master:verible/verilog/analysis/checkers/posix-eof-rule.h?q=class:%5CbPosixEOFRule).
For complete links to examples of each of the above lint rule classes, click on
the class definition and navigate to "Extended By" inside the "Cross References"
panel in the code search viewer.
## Syntax Tree Analysis Tools
This section describes various tools and libraries for analyzing and querying
syntax trees.
### Syntax Tree Examiner
Use [`verible-verilog-syntax --printtree`](../verible/verilog/tools/syntax) to examine
the syntax structure of examples of code of interest.
### Syntax Tree Visitors
[TreeContextVisitor] is a syntax tree visitor that maintains a stack of ancestor
nodes in a stack as it traverses nodes and leaves. This is useful for being able
to query the stack to determine the context at any node.
### Syntax Tree Direct Substructure Access
The [SV concrete syntax tree (CST) is described here](../verible/verilog/CST). The CST
library contains a number of useful `GetXFromY`-type accessor functions. These
functions offer the most direct way of extracting information from syntax tree
nodes. Accessor functions are useful when you've already narrowed down your
search to one or a few specific types (enums) of syntax tree nodes.
Pros:
* Fast, because no search is involved.
Cons:
* You may have to write some new CST accessor functions if what you need
doesn't already exist.
### Syntax Tree Searching
[SearchSyntaxTree](https://cs.opensource.google/verible/verible/+/master:verible/common/analysis/syntax-tree-search.h)
is a generic search function for identifying all syntax tree nodes that satisfy
a given predicate. Searching with this function yields TreeSearchMatch objects
that point to syntax tree nodes/leaves and include the context in which the node
matched.
Pros:
* Good for finding nodes when you don't know (or don't care) where in a
subtree they could appear.
* More resilient (but not immune) to CST restructuring.
Cons:
* Slower than direct access because a search always visits every subnode and
leaf.
### Syntax Tree Pattern Matching
The
[CST Matcher library](https://cs.opensource.google/verible/verible/+/master:verible/common/analysis/matcher/)
provides a convenient way to create matcher objects that describe certain
syntactic patterns.
The Syntax Tree Matcher library uses some principles from
[Clang's ASTMatcher Library](http://clang.llvm.org/docs/LibASTMatchersReference.html).
Pros:
* Expressive and composeable.
* More resilient to CST positional substructure changes such as changing child
ranks.
Cons:
* Can be expensive due to searching.
### Single Node Type Matchers
For every [SystemVerilog CST node enum](../verible/verilog/CST/verilog_nonterminals.h),
we produce a corresponding node-matcher in [verilog_matchers.h] that finds that
node type. For example, `NodekFunctionDeclaration` matches nodes tagged
`kFunctionDeclaration`. These are defined using [TagMatchBuilder].
### Path-based Matchers
Path matchers are a shorthand for expressing a match on an ancestral chain of
node types. For example, `MakePathMatcher({X, Y, Z})`, where `X`, `Y`, and `Z`
are CST tags for specific nodes types, creates a matcher that will find nodes of
type `X` that directly contain a `Y` child that directly contains a `Z` child.
[verilog_matchers.h] contains several examples.
### Composition
Every matcher object can accept inner matchers that can refine matching
conditions and narrow search results. Composing matchers looks like
`OuterMatcher(InnerMatcher(...), ...)`, which would return a positive match on a
node that matches `OuterMatcher`, whose subtree also satisfies `InnerMatcher`.
Matcher _operators_ are functions described in [core_matchers.h].
Summary:
* `AllOf(...)` matches positively if _all_ of its inner matchers positively.
* `AnyOf(...)` matches positively if _any_ of its inner matchers positively.
* `Unless(...)` matches positively if its inner matcher does **not** to match.
[TagMatchBuilder]s by default combines its inner matchers with `AllOf`, so can
write `NodekFoo(InnerMatcher1(), InnerMatcher2())` instead of the equivalent
`NodekFoo(AllOf(InnerMatcher1(), InnerMatcher2()))`.
The order of the inner matchers to the above functions is inconsequential to the
match result; they are fully commutative.
### Named Binding of Symbols
Many matchers support _binding_ to user-provided names called
[BindableMatchers]. This lets you save interesting subtree positions found
during the match and retrieve them from a [BoundSymbolManager].
[Example using `.Bind()`](../verible/verilog/analysis/checkers/undersized_binary_literal_rule.h).
## Reporting Positive Findings
When you've determined that the code being analyzed matches a pattern of
interest, record a [LintViolation] object.
Narrow down the location of the offending substring of text as much as possible
so that users can see precisely what is wrong. You can select a whole block of
text, a syntax node subtree, a single token, or even a substring within a token.
Include a diagnostic message that describes the problem, citing a passage from a
style guide. Recommend a corrective action where you can.
Each lint rule that analyzes code produces a [LintRuleStatus] that contains a
set of [LintViolations].
## Lint Rule Configuration
## Testing
A typical set of lint rule tests follow this template:
```c++
TEST(LintRuleNameTest, Various) {
const std::initializer_list<LintTestCase> kTestCases = {
...
};
RunLintTestCases<VerilogAnalyzer, LintRuleName>(kTestCases);
}
```
Each [LintTestCase] is built from mix of plain string literals and tagged string
literals that markup where findings are to be expected.
```c++
{"uninteresting text ",
{kSymbolType, "interesting text"}, // expect a finding over this substring
"; uninteresting text"},
```
The test driver converts this into input code to analyze and a set of expected
findings. A test will fail if the actual findings do not match the expected ones
_exactly_ (down to their locations).
Make sure to include negative tests that expect no lint violations.
## Exercises
<!-- reference links -->
[SV-LRM]: https://ieeexplore.ieee.org/document/8299595
[LineLintRule]: https://cs.opensource.google/verible/verible/+/master:verible/common/analysis/line-lint-rule.h
[TokenStreamLintRule]: https://cs.opensource.google/verible/verible/+/master:verible/common/analysis/token-stream-lint-rule.h
[SyntaxTreeLintRule]: https://cs.opensource.google/verible/verible/+/master:verible/common/analysis/syntax-tree-lint-rule.h
[TextStructureLintRule]: https://cs.opensource.google/verible/verible/+/master:verible/common/analysis/text-structure-lint-rule.h
[TextStructureView]: https://cs.opensource.google/verible/verible/+/master:verible/common/text/text-structure.h
[TreeContextVisitor]: https://cs.opensource.google/verible/verible/+/master:verible/common/text/tree-context-visitor.h
[LintViolation]: https://cs.opensource.google/verible/verible/+/master:verible/common/analysis/lint-rule-status.h?q=class:%5CbLintViolation%5Cb&ss=verible%2Fverible
[LintRuleStatus]: https://cs.opensource.google/verible/verible/+/master:verible/common/analysis/lint-rule-status.h?q=class:%5CbLintRuleStatus%5Cb&ss=verible%2Fverible
[LintTestCase]: https://cs.opensource.google/verible/verible/+/master:verible/common/analysis/linter-test-utils.h?q=class:%5CbLintTestCase%5Cb&ss=verible%2Fverible
[core_matchers.h]: https://cs.opensource.google/verible/verible/+/master:verible/common/analysis/matcher/core-matchers.h
[verilog_matchers.h]: https://cs.opensource.google/verible/verible/+/master:verible/verilog/CST/verilog-matchers.h
[TagMatchBuilder]: https://cs.opensource.google/verible/verible/+/master:verible/common/analysis/matcher/matcher-builders.h?q=class:%5CbTagMatchBuilder%5Cb%20&ss=verible%2Fverible
[BindableMatcher]: https://cs.opensource.google/verible/verible/+/master:verible/common/analysis/matcher/matcher.h?q=class:%5CbBindableMatcher%5Cb%20&ss=verible%2Fverible
[BoundSymbolManager]: https://cs.opensource.google/verible/verible/+/master:verible/common/analysis/matcher/bound-symbol-manager.h?q=class:BoundSymbolManager&ss=verible%2Fverible