### Background ###

Swift's diagnostic style is inherited from [Clang][], which itself was loosely based on [GCC][]'s. These diagnostics aim to fit a terse but clear description of an issue into a single line, ideally suitable for display both on the command line and in IDEs. Additional information is provided via "notes", which are always attached to a primary diagnostic.

The Clang site has [an older comparison][comparison] between Clang's diagnostics and GCC's that shows where the attention to detail has been in that compiler.

This document describes several *current* guidelines for diagnostics in the Swift compiler and accompanying tools. It does not discuss potential future directions for diagnostics, such as following the examples of the [Elm][] or [Rust][] compilers to provide more information.

  [Clang]: http://clang.llvm.org
  [GCC]: https://gcc.gnu.org
  [comparison]: http://clang.llvm.org/diagnostics.html
  [Elm]: http://elm-lang.org/blog/compiler-errors-for-humans
  [Rust]: https://blog.rust-lang.org/2016/08/10/Shape-of-errors-to-come.html


### Errors vs. Warnings ###

Swift diagnostics are classified into errors, warnings, and notes. Notes are always considered "attached" to the immediately preceding error or warning, so the primary distinction is between errors and warnings. In the Swift compiler, a particular diagnostic should be a warning if the intent of the code is clear *and* it won't immediately result in a crash. This allows users to continue to build during refactoring without having to clean up all issues first.

For a warning, consider whether there might be a legitimate need to write the code in question, even if it's rare. In this case, there should be a way to silence the warning, and a note with a fix-it suggesting this. For example, the "unused function result" warning suggests assigning to `_` to make it clear that this is intentional.

Unlike Clang and many other compilers, Swift compiler warnings cannot be disabled as part of the invocation or through a standard construct in the source. This was a deliberate choice in order to keep the language from fracturing into dialects, but has been controversial throughout Swift's history.

Clang also has a kind of diagnostic called a "remark", which represents information about the build that does not indicate an issue. Swift does not currently have remarks, but there's no reason why it couldn't.


### Grammar and Phrasing ###

- Swift diagnostics should be a single phrase or sentence, with no period at the end. If it's important to include a second idea in the diagnostic, use a semicolon to separate the two parts.

- Swift diagnostics are written in a terse abbreviated style similar to English newspaper headlines or recipes. Omit words that make the diagnostic longer without adding information, such as grammatical words like "the".

- *Do* include information that shows that the compiler understands the code. For example, referring to "instance method 'foo(bar:)'" is usually unnecessary, but may increase implicit trust in the compiler and decrease the developer's mental load.

  - *Don't* include information that might show that the compiler *doesn't* understand the code. For example, don't *assume* that this particular value is an instance method when it might be a property with a function type. However, *do* include details that might show how the compiler is *misunderstanding* the code.

- If there is a plausible fix or likely intended meaning, include that in the diagnostic whether or not there's a fix-it.

- When applicable, phrase diagnostics as rules rather than reporting that the compiler failed to do something. Example:

  - Normal: `cannot call 'super.init' outside of an initializer`
  - Better: `'super.init' cannot be called outside of an initializer`

- Use `'` to quote attributes, symbol names, and any other text that is
  referred to as code or a token.
  Some idiomatic Swift tokens, such as `protocol`, by themselves are or appear
  in Swift terms of art.
  Terms of art are written in plain text and must not be quoted.
  If you are not certain whether a Swift token has an associated term of art,
  consult the [book].
  For example:
  - `'mutating' is only valid on methods`, but
    `cannot call mutating method on immutable value`.
  - `'@autoclosure' only applies to function types`.
  - `subscript access can throw but is not marked with 'try'`.
  - `expected '{' after 'defer'`.
  - `type 'S' does not conform to protocol 'Sequence'`.

- When referring to attributes by name, use *either* `attribute 'foo'` or 
  `'@foo'`, rather than `attribute '@foo'`.

- Match the tone and phrasing of other diagnostics. Some common phrases:

  - "\<noun> '\<name>'" (e.g. "type 'Set<Int>'")
  - "value of type \<type>"
  - "did you mean...?"
  - "...to silence this warning"
  - "...here" (for a purely locational note)

- If possible, it is best to include the name of the type or function that has the error, e.g. "non-actor type 'Nope' cannot ..." is better than "non-actor type cannot ...". It helps developers relate the error message to the specific type the error is about, even if the error would highlight the appropriate line / function in other ways. 

[book]: https://docs.swift.org/swift-book/documentation/the-swift-programming-language

### Locations and Highlights ###

- Diagnostics are always emitted at a particular line and column. Try to be specific.

- Use highlights to build on the single location of the diagnostic, but use them sparingly. A diagnostic with *everything* on the line highlighted is no better than a diagnostic with nothing highlighted.


### Fix-its ###

- Fix-its can span multiple lines, but may not be displayed in all contexts if they do. (In particular, the command-line display of a diagnostic currently only shows the line the diagnostic is on.)

- Fix-its must be in the same file as the diagnostic they are attached to. (However, a note can be in a different file than the primary error or warning.)

- If a fix-it is placed on an error or warning, it must be the single, obvious, and very likely correct way to fix the issue.

  - Ideally, the compiler or other tool will recover as if the user had applied the fix-it.

- Conversely, if a note has a fix-it, the note should describe the action the fix-it is taking and why.

- If a diagnostic has multiple notes with fix-its, the different notes should be treated as alternatives. In general, the first option should be the safest one. (It's also okay to have additional notes that do not provide fix-its, but don't drown the developer in notes.)

- If a warning or error has a fix-it, its notes should not have fix-its.

- Try to find something better than "add parentheses" to indicate that a warning should be silenced. Parentheses don't have anything to do with the actual problem, and if too many warnings do this the developer may end up silencing more than they meant to.

- Use Xcode-syntax placeholders in fix-its as necessary: `<#placeholder#>`. It's better to offer a fix-it with a placeholder in it than no fix-it at all.

The correct spelling of this feature is "fix-it" rather than "fixit". In [camelcased][] contexts, use "FixIt" or "fixIt".

  [camelcased]: https://en.wikipedia.org/wiki/Camel_case

### Diagnostic Groups ###

Diagnostic groups collect some number of diagnostics together under a common group name with short-form documentation to help explain relevant language concepts. For warnings, diagnostic groups also provide a way to control whether the warnings within that group are escalated to errors or not wit compiler flags such as `-Werror <groupname>` and `-Wwarning <groupname>`. They are intended to further Swift's goal of progressive disclosure by providing a learning resource at the point of use when encountering an error message for the first time. In very limited circumstances, they also allow the main diagnostic message to use precise terminology (e.g. nominal types) which would otherwise be too unfriendly for beginners.

When outputting diagnostics on the command line, diagnostic group names will be printed as `[#GroupName]` at the end of the main diagnostic, with a footnote that links to the corresponding documentation. When presented in an IDE, it's expected they will be collapsed under a disclosure arrow, info button, or similar to avoid cluttering output.

Diagnostic group documentation should:
- Cover a single language concept. This helps keep them clear, concise, and easy to understand.
- Be written in unabbreviated English. These are longer-form messages compared to the main diagnostic, so there's no need to omit needless words and punctuation.
- Not generally exceed 3-4 paragraphs. Diagnostic group documentation should be clear and easily digestible. Messages which are too long also have the potential to create UX issues on the command line.
- Be accessible. Diagnostic group documentation should be beginner friendly and avoid assuming unnecessary prior knowledge. The goal is not only to help users understand what a diagnostic is telling them, but also to turn errors and warnings into "teachable moments".
- Include references to relevant chapters of _The Swift Programming Language_.
- Be written in Markdown, but avoid excessive markup which negatively impacts the terminal UX.

The diagnostic group documentation supports generating a DocC bundle for hosting. You can currently find the compiler diagnostics documentation at https://docs.swift.org/compiler/documentation/diagnostics/

To generate this documentation locally, run the following command from the root of the repository:
```
docc preview --allow-arbitrary-catalog-directories userdocs/diagnostics
```


### Quick-Start Guide for Contributing New Diagnostic Groups ###

Adding new diagnostic groups is a great way to get familiar with the process of contributing to Swift, while also making a big impact!

To add a new diagnostics group:
1. Follow the [directions in the README](https://github.com/swiftlang/swift#getting-sources-for-swift-and-related-projects) to checkout the Swift sources locally. Being able to build the Swift compiler is recommended, but not required, when contributing a new note.
2. Identify a diagnostic to collect into a group. To find the diagnostic, you'll need to know its internal identifier. The easiest way to do this is to write a small program which triggers the diagnostic, and run it using the `-debug-diagnostic-names` compiler flag. This flag will cause the internal diagnostic identifier to be printed after the diagnostic message in square brackets.
3. Find any closely related diagnostics. Sometimes, what appears to be one diagnostic from a user's perspective may have multiple variations internally. After determining a diagnostic's internal identifier, run a search for it in the compiler source. You should find:
    - An entry in a `Diagnostics*.def` file describing the diagnostic. If there are any closely related diagnostics the note should also be attached to, they can usually be found nearby.
    - Each point in the compiler source where the diagnostic is emitted. This can be helpful in determining the exact circumstances which cause it to be emitted.
4. Add a new Markdown file in the `userdocs/diagnostics/` directory in the swift repository containing the documentation. When writing a note, keep the writing guidelines from the section above in mind. The existing notes in the directory are another useful guide.
5. Create a new entry in `DiagnosticGroups.def` that provides the name for your new diagnostic group along with the name of the file you added in step (4), without an extension.
6. Find each diagnostic you want to make part of this group in the various `Diagnostics*.def` files. For each diagnostic, replace the `ERROR` or `WARNING` with `GROUPED_ERROR` or `GROUPED_WARNING`, respectively, and put the diagnostic group name after the string literal for the diagnostic message.
7. If possible, rebuild the compiler and try recompiling your test program. Your new diagnostic group should appear as `[#<group name>]` at the end of the diagnostic, with a link to the diagnostic file.
8. If possible, generate updated doc indexes by running `./utils/generate-doc-index.swift .`. Otherwise, the `userdoc_indices` test will fail, since your new diagnostic group won't be in `diagnostic-groups.md` yet.
9. That's it! The new diagnostic group is now ready to be submitted as a pull request on GitHub.

If you run into any issues or have questions while following the steps above, feel free to post a question on the Swift forums or open a work-in-progress pull request on GitHub.

### Format Specifiers ###

(This section is specific to the Swift compiler's diagnostic engine.)

- `%0`, `%1`, etc - Formats the specified diagnostic argument based on its type.

- `%select{a|b|c}0` - Chooses from a list of alternatives, separated by vertical bars, based on the value of the given argument. In this example, a value of 2 in diagnostic argument 0 would result in "c" being output. The argument to the %select may be an integer, enum, StringRef, Identifier, or Decl. If it's a StringRef or Identifier, the specifier acts as an emptiness check; if it's a Decl, it acts as a null check.

- `%s0` - Produces an "s" if the given argument is anything other than 1, as meant for an English plural. This isn't particularly localizable without a more general `%plural` form, but most diagnostics try to avoid cases where a plural/singular distinction would be necessary in the first place.

- `%error` - Represents a branch in a `%select` that should never be taken. In debug builds of the compiler this produces an assertion failure.

- `%%` - Emits a literal percent sign.

The following subsections describe format specifiers that are specific to 
diagnostic arguments of a given type.

#### `Decl`

- `%kind0` - Prefixes the declaration's name with its descriptive decl kind (e.g. `instance method 'foo(x:)'`).

- `%kindonly0` - Inserts only the descriptive decl kind (e.g. `instance method`).

- `%base0` - Inserts only the base name, removing any argument labels (e.g. `'foo'`).

- `%kindbase0` - Combines `kind` and `base` (e.g. `instance method 'foo'`).

#### `TypeAttribute`

- `%kind0` - Replaced with `attribute 'foo'`, whereas `%0` is replaced with
  `'@foo'`.

Note: If your diagnostic could apply to accessors, be careful how you format the declaration's name; accessors have an empty name, so you need to display their accessor kind and the name of their storage decl instead. Inserting the name with a `Decl *` parameter will handle these complications automatically; if you want to use `DeclName` or `Identifier` instead, you'll probably need a separate version of the diagnostic for accessors. 

### Diagnostic Verifier ###

(This section is specific to the Swift compiler's diagnostic engine.)

If the `-verify` frontend flag is used, the Swift compiler will check emitted diagnostics against specially formatted comments in the source. This feature is used extensively throughout the test suite to ensure diagnostics are emitted with the correct message and source location.
  
`-verify` parses all ordinary source files passed as inputs to the compiler to look for expectation comments. If you'd like to check for diagnostics in additional files, like swiftinterfaces or even Objective-C headers, specify them with `-verify-additional-file <filename>`. By default, `-verify` considers any diagnostic at `<unknown>:0` (that is, any diagnostic emitted with an invalid source location) to be unexpected; you can disable this by passing `-verify-ignore-unknown`.

An expected diagnostic is denoted by a comment which begins with `expected-error`, `expected-warning`, `expected-note`, or `expected-remark`. (You can use `-verify-additional-prefix <string>` to add expectations with additional prefixes, e.g. `-verify-additional-prefix foo-` will pick up both `expected-error` and `expected-foo-error`.) It is followed by:

- (Optional) Location information. By default, the comment will match any diagnostic emitted on the same line. However, it's possible to override this behavior and/or specify column information as well. `// expected-error@-1 ...` looks for an error on the previous line, `// expected-warning@+1:3 ...` looks for a warning on the next line at the third column, and `// expected-note@:7 ...` looks for a note on the same line at the seventh column.

  Named location markers can also be used instead of line offsets. A marker is defined by placing `// #name` in a comment (where `name` consists of alphanumeric characters, hyphens, or underscores, and must be the only content in the comment). The marker can then be referenced with `@#name`:

  ```swift
  let x: Int = "hello" // #myMarker
  // expected-error@#myMarker {{cannot convert value}}
  ```

  Markers are especially useful when the diagnostic and its expectation are far apart, when lines are frequently added or removed nearby, or when referring to diagnostics in a different file. An optional column can also be specified: `// expected-error@#myMarker:14 ...`.

- (Optional) A match count which specifies how many times the diagnostic is expected to appear. This may be a positive integer or `*`, which allows for zero or more matches. The match count must be surrounded by whitespace if present. For example, `// expected-error 2 ...` looks for two matching errors, and `// expected-warning * ...` looks for any number of matching warnings.

- (Required) The expected error message. The message should be enclosed in double curly braces and should not include the `error:`/`warning:`/`note:`/`remark:` prefix. For example, `// expected-error {{invalid redeclaration of 'y'}}` would match an error with that message on the same line. The expected message does not need to match the emitted message verbatim. As long as the expected message is a substring of the original message, they will match.

- (Optional) Expected fix-its. These are each enclosed in double curly braces and appear after the expected message. An expected fix-it consists of a column range followed by the text it's expected to be replaced with. For example, `let r : Int i = j // expected-error{{consecutive statements}} {{12-12=;}}` will match a fix-it attached to the consecutive statements error which inserts a semicolon at column 12, just after the 't' in 'Int'.
  
  * Insertions are represented by identical start and end locations: `{{3-3=@objc }}`. Deletions are represented by empty replacement text: `{{3-9=}}`.
  
  * Line offsets are also permitted; for instance, `{{-1:12-+1:42=}}` would specify a fix-it that deleted everything between column 12 on the previous line and column 42 on the next line. (If the sign is omitted, it specifies an absolute line number, not an offset.)
  
  * By default, the verifier ignores any fix-its that are *not* expected; the special `{{none}}` specifier tells it to verify that the diagnostic it's attached to has *only* the fix-its specified and no others.

  * If two (or more) expected fix-its are juxtaposed with nothing (or whitespace) between them, then both must be present for the verifier to match. If two (or more) expected fix-its have `||` between them, then one of them must be present for the verifier to match. `||` binds more tightly than juxtaposition: `{{1-1=a}} {{2-2=b}} || {{2-2=c}} {{3-3=d}} {{none}}` will only match if there is either a set of three fix-its that insert `a`, `b`, and `d`, or a set of three fix-its that insert `a`, `c`, and `d`. (Without the `{{none}}`, it would also permit all four fix-its, but only because one of the four would be unmatched and ignored.)

- (Optional) Expected documentation file. These is the name of the documentation file, enclosed in double curly braces and prefixed by 'documentation-file='. For example, `{{documentation-file=some-file}}` will verify the documentation group with filename `some-note` appears. Do not include the file extension when specifying the name.
