blob: 8e2f28fc09ffee4e31bc72900e1df9fd8ea21868 [file] [log] [blame] [view]
# Writing Lints
Preliminary notes on writing lints.
## Lint Criteria
Borrowing heavily from the criteria for [adding new checks to errorprone],
lints should have the following properties.
Dart lints:
* should be easy to understand. The problem should be obvious once the linter
points it out.
* should have a correspondingly easy fix. For example, "Remove this type
annotation", or "Delete these braces", not "Introduce a new subclass and
override methods A, B, and C."
* should have *few* false positives.
## Lint Properties
Every lint has a:
**Name.** A short name using [Dart package naming conventions]. Naming is
*hard* but strive to be concise and consistent. Prefer to use the _problem_ as
the name, as in the existing lints `control_flow_in_finally` and
`empty_catches`. Do not start a lint's name with "always", "avoid", or
"prefer". Where possible, use existing rules for inspiration and observe the
rules of [parallel construction].
**Description.** A short description of the lint, suitable for printing in
console output. For example:
```
[lint] DO name types using UpperCamelCase.
```
**Kind.** The first word in the description should identify the *kind* of lint
where kinds are derived from the [style guide]. In summary:
* ***DO*** guidelines describe practices that should always be followed. There
will almost never be a valid reason to stray from them.
* ***DON'T*** guidelines are the converse: things that are almost never a good
idea. You'll note there are few of these here. Guidelines like these in other
languages help to avoid the pitfalls that appear over time. Dart is new enough
that we can just fix those pitfalls directly instead of putting up ropes around
them.
* ***PREFER*** guidelines are practices that you should follow. However, there
may be circumstances where it makes sense to do otherwise. Just make sure you
understand the full implications of ignoring the guideline when you do.
* ***AVOID*** guidelines are the dual to "prefer": stuff you shouldn't do but
where there may be good reasons to on rare occasions.
* ***CONSIDER*** guidelines are practices that you might or might not want to
follow, depending on circumstances, precedents, and your own preference.
**Detailed Description.** In addition to the short description, a lint rule
should have more detailed rationale with code examples, ideally *good* and
*bad*. The [style guide] is a great source for inspiration. Many style
recommendations have been directly translated to lints as enumerated
[here][lints].
**Group.** A grouping. For example, *Style Guide* aggregates style guide
derived lints.
**Maturity.** Rules can be further distinguished by maturity. Unqualified rules
are considered stable, while others may be marked *EXPERIMENTAL* or *PROPOSED*
to indicate that they are under review.
## Mechanics
Lints live in the [lib/src/rules] directory. Corresponding unit tests live in
[test/rules].
Rule stubs can be generated with the [rule.dart] helper script:
$ dart tool/rule.dart -n my_new_lint
generates lint and test stubs in `lib/src/rules` and `test/rules`.
### Analyzer APIs
The linter has a close relationship with the `analyzer` package and at times
reaches into non-public APIs. For the most part, we have isolated these
references in an [analyzer.dart utility library]. *Wherever possible please
use this library to access analyzer internals.*
* If `analyzer.dart` is missing something please consider opening an issue
where we can discuss how best to add it.
* If you find yourself tempted to make references to analyzer
[implementation classes][implementation_imports] also consider opening an
issue so that we can see how best to manage the new dependency.
Thanks!
### Dart Language Specification
When writing lints, it can be useful to have the [Dart Language Specification]
handy. If you're working to support bleeding edge language features, you'll
want the [latest draft][draft language spec].
### Writing Tests that Depend on Dart SDK Details
**Important:** when writing tests that use standard `dart:` libraries, it's
important to keep in mind that linter tests use a mocked SDK that has only a
small subset of the real one. We do this for performance reasons as it's FAR
faster to load a mock SDK into memory than read the real one from disk. If you
are writing tests that depend on something in the Dart SDK (for example, an
interface such as `Iterable`), you may need to update SDK mock content located
in the `package:analyzer` [test utilities `mock_sdk.dart`][mock_sdk.dart].
### Running Tests
The test suite run during the linter's CI, can be run locally like so:
$ dart test/all.dart
alternatively, tests can be run using `pub`:
$ dart test
Running a single test can be done using the `rule_debug` helper:
$ dart test/util/rule_debug.dart always_declare_return_types
would only test `always_declare_return_types`. (This can be very handy if you
want to run tests in the VM debugger).
If you simply want to verify a test, you can run it solo in `pub`:
$ dart test -N always_declare_return_types
### Utilities
You'll notice when authoring a new rule that failures cause the AST of the test
case to be displayed to `stdout`. If you simply want to dump the AST of a given
compilation unit, you can use the `spelunk` helper directly. For example:
$ dart tool/spelunk.dart lib/src/rules.dart
would dump the AST of `rules.dart`.
### Performance
For performance reasons rules should prefer implementing `NodeLintRule` and
registering interest in specific AST node types using
`registry.addXYZ(this, visitor)`. Avoid overriding `visitCompilationUnit()` and
performing your own full `CompilationUnit` visits.
# Feedback is Welcome!
Details are under active development. Feedback is most [welcome][issues]!
[adding new checks to errorprone]: https://github.com/google/error-prone/wiki/Criteria-for-new-checks
[Dart package naming conventions]: https://dart.dev/tools/pub/pubspec#name
[parallel construction]: https://en.wikipedia.org/wiki/Parallelism_(grammar)
[style guide]: https://dart.dev/effective-dart/style/
[lints]: https://dart.dev/lints
[lib/src/rules]: https://github.com/dart-lang/linter/tree/main/lib/src/rules
[test_data/rules]: https://github.com/dart-lang/linter/tree/main/test_data/rules
[rule.dart]: https://github.com/dart-lang/linter/blob/main/tool/rule.dart
[analyzer.dart utility library]: https://github.com/dart-lang/linter/blob/main/lib/src/analyzer.dart
[implementation_imports]: https://dart.dev/lints/implementation_imports
[Dart Language Specification]: https://dart.dev/guides/language/spec
[draft language spec]: https://spec.dart.dev/DartLangSpecDraft.pdf
[mock_sdk.dart]: https://github.com/dart-lang/sdk/blob/main/pkg/analyzer/lib/src/test_utilities/mock_sdk.dart
[issues]: https://github.com/dart-lang/linter/issues