00e9dc8644
Change-Id: Ice38a93e009cc9c07638ddce96e8960d986fa3c1 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/258509 Reviewed-by: Konstantin Shcheglov <scheglov@google.com> Commit-Queue: Brian Wilkerson <brianwilkerson@google.com>
68 lines
3.2 KiB
Markdown
68 lines
3.2 KiB
Markdown
# Adding a new diagnostic
|
|
|
|
This document describes the process of adding a new (non-lint) diagnostic to the
|
|
analyzer.
|
|
|
|
## Define the diagnostic code
|
|
|
|
The first step is to define the code(s) associated with the diagnostic.
|
|
|
|
The codes are defined in the file `analyzer/messages.yaml`. There's a comment at
|
|
the top of the file describing the structure of the file.
|
|
|
|
Every diagnostic has at least one code associated with it. A code defines the
|
|
problem and correction messages that will be shown to users.
|
|
|
|
For most diagnostics, a single message (code) is sufficient. But sometimes it's
|
|
useful to tailor the message based on the context in which the problem occurs or
|
|
because the message can be made more clear. For example, it's an error to
|
|
declare two or more constructors with the same name. That's true whether the
|
|
name is explicit or implicit (the default constructor). In order for the message
|
|
to match the user's model of the language, we define two messages for this one
|
|
problem: one that refers to the constructors by their name, and one that refers
|
|
to the constructors as "unnamed". The way we define two messages is by defining
|
|
two codes.
|
|
|
|
Each code has a unique name (the key used in the map in `messages.yaml`) and can
|
|
optionally define a shared name that links all the codes for a single diagnostic
|
|
together. It is the shared name that is displayed to users. If a shared name
|
|
isn't explicitly provided, it will default to being the same as the unique name.
|
|
|
|
After every edit to the `messages.yaml` file, you will need to run the utility
|
|
`analyzer/tool/messages/generate.dart` to update the generated files.
|
|
|
|
You also need to manually add the name of the code to the list of codes in two
|
|
files:
|
|
- `analyzer/lib/error/error.dart`
|
|
- `analysis_server/lib/src/services/correction/error_fix_status.yaml`
|
|
|
|
In the status file, the code should have the line
|
|
```yaml
|
|
status: needsEvaluation
|
|
```
|
|
nested under the name of the code.
|
|
|
|
## Write tests
|
|
|
|
We recommend writing the tests for a diagnostic before writing the code to
|
|
generate the diagnostic. Doing so helps you think about the specific cases that
|
|
the implementation code needs to handle, which can result in cleaner
|
|
implementation code and fewer bugs.
|
|
|
|
The tests for each diagnostic code (or set of codes that have the same shared
|
|
name) are in a separate file in the directory `analyzer/test/src/diagnostics`.
|
|
Looking at the implementation of tests in a few of the other files can help you
|
|
see the basic pattern, but all the tests essentially work by setting up the code
|
|
to be analyzed, then assert that either the expected diagnostic has been
|
|
produced in the expected locations or that there are no diagnostics being
|
|
generated. (It's often valuable to test that the diagnostic doesn't have any
|
|
false positives.)
|
|
|
|
## Report the diagnostic
|
|
|
|
The last step is to write the code to report the diagnostic. Where that code
|
|
lives depends on the kind of diagnostic you're adding. If you're adding a
|
|
diagnostic that's defined by the language specification (with a severity of
|
|
'error'), then the best place to implement it will usually be in one of the
|
|
`<node class>Resolver` classes. If you're adding a warning, then the class
|
|
`BestPracticesVerifier` is usually the best place for it. |