|
| 1 | +# Adding a new diagnostic |
| 2 | + |
| 3 | +This document describes the process of adding a new (non-lint) diagnostic to the |
| 4 | +analyzer. |
| 5 | + |
| 6 | +## Define the diagnostic code |
| 7 | + |
| 8 | +The first step is to define the code(s) associated with the diagnostic. |
| 9 | + |
| 10 | +The codes are defined in the file `analyzer/messages.yaml`. There's a comment at |
| 11 | +the top of the file describing the structure of the file. |
| 12 | + |
| 13 | +Every diagnostic has at least one code associated with it. A code defines the |
| 14 | +problem and correction messages that will be shown to users. |
| 15 | + |
| 16 | +For most diagnostics, a single message (code) is sufficient. But sometimes it's |
| 17 | +useful to tailor the message based on the context in which the problem occurs or |
| 18 | +because the message can be made more clear. For example, it's an error to |
| 19 | +declare two or more constructors with the same name. That's true whether the |
| 20 | +name is explicit or implicit (the default constructor). In order for the message |
| 21 | +to match the user's model of the language, we define two messages for this one |
| 22 | +problem: one that refers to the constructors by their name, and one that refers |
| 23 | +to the constructors as "unnamed". The way we define two messages is by defining |
| 24 | +two codes. |
| 25 | + |
| 26 | +Each code has a unique name (the key used in the map in `messages.yaml`) and can |
| 27 | +optionally define a shared name that links all the codes for a single diagnostic |
| 28 | +together. It is the shared name that is displayed to users. If a shared name |
| 29 | +isn't explicitly provided, it will default to being the same as the unique name. |
| 30 | + |
| 31 | +After every edit to the `messages.yaml` file, you will need to run the utility |
| 32 | +`analyzer/tool/messages/generate.dart` to update the generated files. |
| 33 | + |
| 34 | +You also need to manually add the name of the code to the list of codes in two |
| 35 | +files: |
| 36 | +- `analyzer/lib/error/error.dart` |
| 37 | +- `analysis_server/lib/src/services/correction/error_fix_status.yaml` |
| 38 | + |
| 39 | +In the status file, the code should have the line |
| 40 | +```yaml |
| 41 | + status: needsEvaluation |
| 42 | +``` |
| 43 | +nested under the name of the code. |
| 44 | +
|
| 45 | +## Write tests |
| 46 | +
|
| 47 | +We recommend writing the tests for a diagnostic before writing the code to |
| 48 | +generate the diagnostic. Doing so helps you think about the specific cases that |
| 49 | +the implementation code needs to handle, which can result in cleaner |
| 50 | +implementation code and fewer bugs. |
| 51 | +
|
| 52 | +The tests for each diagnostic code (or set of codes that have the same shared |
| 53 | +name) are in a separate file in the directory `analyzer/test/src/diagnostics`. |
| 54 | +Looking at the implementation of tests in a few of the other files can help you |
| 55 | +see the basic pattern, but all the tests essentially work by setting up the code |
| 56 | +to be analyzed, then assert that either the expected diagnostic has been |
| 57 | +produced in the expected locations or that there are no diagnostics being |
| 58 | +generated. (It's often valuable to test that the diagnostic doesn't have any |
| 59 | +false positives.) |
| 60 | + |
| 61 | +## Report the diagnostic |
| 62 | + |
| 63 | +The last step is to write the code to report the diagnostic. Where that code |
| 64 | +lives depends on the kind of diagnostic you're adding. If you're adding a |
| 65 | +diagnostic that's defined by the language specification (with a severity of |
| 66 | +'error'), then the best place to implement it will usually be in one of the |
| 67 | +`<node class>Resolver` classes. If you're adding a warning, then the class |
| 68 | +`BestPracticesVerifier` is usually the best place for it. |
0 commit comments