-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge 1a0b1fe into dev
- Loading branch information
Showing
74 changed files
with
1,647 additions
and
803 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,147 @@ | ||
# Writing Rules | ||
|
||
This package gives analyzer plugin authors the ability to write static rules | ||
for source code. This document describes briefly how to write such a rule, and | ||
how to register it in an analyzer plugin. | ||
|
||
## Declaring an analysis rule | ||
|
||
Every analysis rule is declared in two parts: a rule class that extends | ||
`AnalysisRule`, and a visitor class that extends `SimpleAstVisitor`. | ||
|
||
### The rule class | ||
|
||
The rule class contains some general information about the rule, like its name | ||
and the diagnostic or diagnostics that the rule reports. It also registers the | ||
various syntax tree nodes that the visitor class needs to visit. Let's see an | ||
example: | ||
|
||
```dart | ||
class MyRule extends AnalysisRule { | ||
static const LintCode _code = LintCode( | ||
'my_rule', | ||
'No await expressions', | ||
correctionMessage: "Try removing 'await'.", | ||
); | ||
MyRule() | ||
: super( | ||
name: LintNames.prefer_void_to_null, | ||
description: 'A longer description of the rule.', | ||
); | ||
@override | ||
LintCode get lintCode => _code; | ||
@override | ||
void registerNodeProcessors( | ||
NodeLintRegistry registry, LinterContext context) { | ||
var visitor = _Visitor(this, context); | ||
registry.addAwaitExpression(this, visitor); | ||
} | ||
} | ||
``` | ||
|
||
Let's look at each declaration individually: | ||
|
||
* `class MyRule extends AnalysisRule` - The rule class must extend | ||
`AnalysisRule`. | ||
|
||
* `static const LintCode _code` and `LintCode get lintCode` - Each rule class | ||
must implement either `LintCode get lintCode` or `List<LintCode> get | ||
lintCodes`, depending on whether there is only one diagnostic that it can | ||
report, or multiple. | ||
|
||
A `LintCode` is the template for each diagnostic that is to be reported. It | ||
contains the diagnostic name, problem message, and optionally the correction | ||
message. We instantiate a `LintCode` as a static field so that it can also be | ||
made const. If the rule needs to report more than one `LintCode`, with | ||
different problem messages, then multiple static fields can be declared. | ||
|
||
* `MyRule()` - The rule class must have a constructor that calls `super()`, | ||
passing along the name of the rule, and a description. Typically this | ||
constructor has zero parameters. | ||
|
||
* `void registerNodeProcessors(...)` - An analysis rule uses a visitor to walk | ||
a [Dart syntax tree][] (we see how the visitor is defined in "The visitor | ||
class," below). This visitor is typically named `_Visitor`. This visitor | ||
class must be instantiated once in this method. Typically, the instance of | ||
the rule class (`this`) and a `LinterContext` object (described below) are | ||
passed to the visitor constructor. | ||
|
||
In order for such a visitor's various 'visit' methods to be called, we need | ||
to register them, in a `NodeLintRegistry`. Each 'visit' method found on | ||
`SimpleAstVisitor` has a corresponding 'add' method in the `NodeLintRegistry` | ||
class. | ||
|
||
[Dart syntax tree]: https://github.com/dart-lang/sdk/blob/main/pkg/analyzer/doc/tutorial/ast.md | ||
|
||
### The visitor class | ||
|
||
The visitor class contains the code that examines syntax nodes and reports | ||
diagnostics. See the [API documentation][SimpleAstVisitor docs] for the | ||
`SimpleAstVisitor` class to find the various 'visit' methods available for | ||
implementation. Let's look at a quick example: | ||
|
||
[SimpleAstVisitor docs]: https://github.com/dart-lang/sdk/blob/main/pkg/analyzer/lib/dart/ast/visitor.dart#L1841 | ||
|
||
```dart | ||
class _Visitor extends SimpleAstVisitor<void> { | ||
final LintRule rule; | ||
final LinterContext context; | ||
_Visitor(this.rule, this.context); | ||
@override | ||
void visitAwaitExpression(AwaitExpression node) { | ||
if (context.isInLibDir) { | ||
rule.reportLint(node); | ||
} | ||
} | ||
} | ||
``` | ||
|
||
Let's look at each declaration individually: | ||
|
||
* `class _Visitor extends SimpleAstVisitor<void>` - Each visitor must extend | ||
`SimpleAstVisitor`. While the analyzer package provides other Dart syntax | ||
tree visitors, using one directly in a rule can result in poor performance | ||
and unexpected behavior. The type argument on `SimpleAstVisitor` is not | ||
important, as 'visit' return values are not used, so `void` is appropriate. | ||
* `final LintRule rule` - The rule is the object to which we can report | ||
diagnostics (lints or warnings). Several methods are provided, all starting | ||
with `reportLint`. The different methods allow for different ranges of text | ||
to be highlighted. | ||
* `final LinterContext context` - The LinterContext object provides various | ||
information about the library being analyzed. In this example, we make use of | ||
a `isInLibDir` utility. | ||
* `_Visitor(...)` - Often the constructor just initializes the LintRule and | ||
LinterContext fields. Other information can be initialized as well. | ||
* `void visitAwaitExpression(AwaitExpression node)` - The main component of the | ||
`_Visitor` class is the 'visit' methods. In this case, `visitAwaitExpression` | ||
is invoked for each 'await expression' found in the source code under | ||
analysis. Typically, a 'visit' method like this is where we perform some | ||
analysis and maybe report lint(s) or warning(s). | ||
|
||
## Registering an analysis rule | ||
|
||
In order for an analysis rule to be used in an analyzer plugin, it must be | ||
registered. Register an instance of an analysis rule inside a plugin's | ||
`register` method: | ||
|
||
```dart | ||
class SimplePlugin extends Plugin { | ||
@override | ||
void register(PluginRegistry registry) { | ||
registry.registerWarningRule(MyRule()); | ||
} | ||
} | ||
``` | ||
|
||
Here, the instance of MyRule is registered as a "warning rule," so that it is | ||
enabled by default. To register an analysis rule as a "lint rule," such that it | ||
must be specifically enabled from analysis options, use `registerLintRule` | ||
instead. | ||
|
||
TODO(srawlins): Write up and link documentation for this Plugin subclass. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.