+
+A lot of effort has been put into making rustc
have great error messages.
+This chapter is about how to emit compile errors and lints from the compiler.
+
+The main parts of a diagnostic error are the following:
+error[E0000]: main error message
+ --> file.rs:LL:CC
+ |
+LL | <code>
+ | -^^^^- secondary label
+ | |
+ | primary label
+ |
+ = note: note without a `Span`, created with `.note`
+note: sub-diagnostic message for `.span_note`
+ --> file.rs:LL:CC
+ |
+LL | more code
+ | ^^^^
+
+
+- Description (
error
, warning
, etc.).
+- Code (for example, for "mismatched types", it is
E0308
). It helps
+users get more information about the current error through an extended
+description of the problem in the error code index.
+- Message. It is the main description of the problem. It should be general and
+able to stand on its own, so that it can make sense even in isolation.
+- Diagnostic window. This contains several things:
+
+- The path, line number and column of the beginning of the primary span.
+- The users' affected code and its surroundings.
+- Primary and secondary spans underlying the users' code. These spans can
+optionally contain one or more labels.
+
+- Primary spans should have enough text to describe the problem in such a
+way that if it where the only thing being displayed (for example, in an
+IDE) it would still make sense. Because it is "spatially aware" (it
+points at the code), it can generally be more succinct than the error
+message.
+- If cluttered output can be foreseen in cases when multiple span labels
+overlap, it is a good idea to tweak the output appropriately. For
+example, the
if/else arms have incompatible types
error uses different
+spans depending on whether the arms are all in the same line, if one of
+the arms is empty and if none of those cases applies.
+
+
+
+
+- Sub-diagnostics. Any error can have multiple sub-diagnostics that look
+similar to the main part of the error. These are used for cases where the
+order of the explanation might not correspond with the order of the code. If
+the order of the explanation can be "order free", leveraging secondary labels
+in the main diagnostic is preferred, as it is typically less verbose.
+
+The text should be matter of fact and avoid capitalization and periods, unless
+multiple sentences are needed:
+error: the fobrulator needs to be krontrificated
+
+When code or an identifier must appear in an message or label, it should be
+surrounded with single acute accents `.
+
+Some errors include long form descriptions. They may be viewed with the
+--explain
flag, or via the error index. Each explanation comes with an
+example of how to trigger it and advice on how to fix it.
+Please read RFC 1567 for details on how to format and write long error
+codes.
+The descriptions are written in markdown, and all of them are linked in the
+librustc_error_codes
crate.
+TODO: When should an error use an error code, and when shouldn't it?
+
+Some messages are emitted via lints, where the user can control the
+level. Most diagnostics are hard-coded such that the user cannot control the
+level.
+Usually it is obvious whether a diagnostic should be "fixed" or a lint, but
+there are some grey areas.
+Here are a few examples:
+
+- Borrow checker errors: these are fixed errors. The user cannot adjust the
+level of these diagnostics to silence the borrow checker.
+- Dead code: this is a lint. While the user probably doesn't want dead code in
+their crate, making this a hard error would make refactoring and development
+very painful.
+- safe_packed_borrows future compatibility warning:
+this is a silencable lint related to safety. It was judged that the making
+this a hard (fixed) error would cause too much breakage, so instead a
+warning is emitted that eventually will be turned into a hard error.
+
+Hard-coded warnings (those using the span_warn
methods) should be avoided
+for normal code, preferring to use lints instead. Some cases, such as warnings
+with CLI flags, will require the use of hard-coded warnings.
+See the deny
lint level below for guidelines when to
+use an error-level lint instead of a fixed error.
+
+
+- Write in plain simple English. If your message, when shown on a – possibly
+small – screen (which hasn't been cleaned for a while), cannot be understood
+by a normal programmer, who just came out of bed after a night partying,
+it's too complex.
+Error
, Warning
, Note
, and Help
messages start with a lowercase
+letter and do not end with punctuation.
+- Error messages should be succinct. Users will see these error messages many
+times, and more verbose descriptions can be viewed with the
--explain
+flag. That said, don't make it so terse that it's hard to understand.
+- The word "illegal" is illegal. Prefer "invalid" or a more specific word
+instead.
+- Errors should document the span of code where they occur – the
+
rustc_errors::diagnostic_builder::DiagnosticBuilder
span_*
+methods allow to easily do this. Also note
other spans that have
+contributed to the error if the span isn't too large.
+- When emitting a message with span, try to reduce the span to the smallest
+amount possible that still signifies the issue
+- Try not to emit multiple error messages for the same error. This may require
+detecting duplicates.
+- When the compiler has too little information for a specific error message,
+consult with the compiler team to add new attributes for library code that
+allow adding more information. For example see
+
#[rustc_on_unimplemented]
. Use these
+annotations when available!
+- Keep in mind that Rust's learning curve is rather steep, and that the
+compiler messages are an important learning tool.
+- When talking about the compiler, call it
the compiler
, not Rust
or
+rustc
.
+
+
+From RFC 0344, lint names should be consistent, with the following
+guidelines:
+The basic rule is: the lint name should make sense when read as "allow
+lint-name" or "allow lint-name items". For example, "allow
+deprecated
items" and "allow dead_code
" makes sense, while "allow
+unsafe_block
" is ungrammatical (should be plural).
+
+-
+
Lint names should state the bad thing being checked for, e.g. deprecated
,
+so that #[allow(deprecated)]
(items) reads correctly. Thus ctypes
is not
+an appropriate name; improper_ctypes
is.
+
+-
+
Lints that apply to arbitrary items (like the stability lints) should just
+mention what they check for: use deprecated
rather than
+deprecated_items
. This keeps lint names short. (Again, think "allow
+lint-name items".)
+
+-
+
If a lint applies to a specific grammatical class, mention that class and
+use the plural form: use unused_variables
rather than unused_variable
.
+This makes #[allow(unused_variables)]
read correctly.
+
+-
+
Lints that catch unnecessary, unused, or useless aspects of code should use
+the term unused
, e.g. unused_imports
, unused_typecasts
.
+
+-
+
Use snake case in the same way you would for function names.
+
+
+
+Guidelines for different diagnostic levels:
+
+-
+
error
: emitted when the compiler detects a problem that makes it unable to
+compile the program, either because the program is invalid or the programmer
+has decided to make a specific warning
into an error.
+
+-
+
warning
: emitted when the compiler detects something odd about a program.
+Care should be taken when adding warnings to avoid warning fatigue, and
+avoid false-positives where there really isn't a problem with the code. Some
+examples of when it is appropriate to issue a warning:
+
+- A situation where the user should take action, such as swap out a
+deprecated item, or use a
Result
, but otherwise doesn't prevent
+compilation.
+- Unnecessary syntax that can be removed without affecting the semantics of
+the code. For example, unused code, or unnecessary
unsafe
.
+- Code that is very likely to be incorrect, dangerous, or confusing, but the
+language technically allows, and is not ready or confident enough to make
+an error. For example
unused_comparisons
(out of bounds comparisons) or
+bindings_with_variant_name
(the user likely did not intend to create a
+binding in a pattern).
+- Future-incompatible lints, where something was
+accidentally or erroneously accepted in the past, but rejecting would
+cause excessive breakage in the ecosystem.
+- Stylistic choices. For example, camel or snake case, or the
dyn
trait
+warning in the 2018 edition. These have a high bar to be added, and should
+only be used in exceptional circumstances. Other stylistic choices should
+either be allow-by-default lints, or part of other tools like Clippy or
+rustfmt.
+
+
+-
+
help
: emitted following an error
or warning
to give additional
+information to the user about how to solve their problem. These messages
+often include a suggestion string and rustc_errors::Applicability
+confidence level to guide automated source fixes by tools. See the
+Suggestions section for more details.
+The error or warning portion should not suggest how to fix the problem,
+only the "help" sub-diagnostic should.
+
+-
+
note
: emitted to identify additional circumstances and parts of the code
+that caused the warning or error. For example, the borrow checker will note
+any previous conflicting borrows.
+
+
+Not to be confused with lint levels, whose guidelines are:
+
+-
+
forbid
: Lints should never default to forbid
.
+
+-
+
deny
: Equivalent to error
diagnostic level. Some examples:
+
+- A future-incompatible or edition-based lint that has graduated from the
+warning level.
+- Something that has an extremely high confidence that is incorrect, but
+still want an escape hatch to allow it to pass.
+
+
+-
+
warn
: Equivalent to the warning
diagnostic level. See warning
above
+for guidelines.
+
+-
+
allow
: Examples of the kinds of lints that should default to allow
:
+
+- The lint has a too high false positive rate.
+- The lint is too opinionated.
+- The lint is experimental.
+- The lint is used for enforcing something that is not normally enforced.
+For example, the
unsafe_code
lint can be used to prevent usage of unsafe
+code.
+
+
+
+More information about lint levels can be found in the rustc
+book and the reference.
+
+
+There are two main ways to find where a given error is emitted:
+
+grep
for either a sub-part of the error message/label or error code. This
+usually works well and is straightforward, but there are some cases where
+the error emitting code is removed from the code where the error is
+constructed behind a relatively deep call-stack. Even then, it is a good way
+to get your bearings.
+- Invoking
rustc
with the nightly-only flag -Ztreat-err-as-bug=1
, which
+will treat the first error being emitted as an Internal Compiler Error, which
+allows you to use the environment variable RUST_BACKTRACE=full
to get a
+stack trace at the point the error has been emitted. Change the 1
to
+something else if you whish to trigger on a later error. Some limitations
+with this approach is that some calls get elided from the stack trace because
+they get inlined in the compiled rustc
, and the same problem we faced with
+the prior approach, where the construction of the error is far away from
+where it is emitted. In some cases we buffer multiple errors in order to
+emit them in order.
+
+The regular development practices apply: judicious use of debug!()
statements
+and use of a debugger to trigger break points in order to figure out in what
+order things are happening.
+
+Span
is the primary data structure in rustc
used to represent a
+location in the code being compiled. Span
s are attached to most constructs in
+HIR and MIR, allowing for more informative error reporting.
+A Span
can be looked up in a SourceMap
to get a "snippet"
+useful for displaying errors with span_to_snippet
and other
+similar methods on the SourceMap
.
+
+The rustc_errors
crate defines most of the utilities used for
+reporting errors.
+Session
and ParseSess
have
+methods (or fields with methods) that allow reporting errors. These methods
+usually have names like span_err
or struct_span_err
or span_warn
, etc...
+There are lots of them; they emit different types of "errors", such as
+warnings, errors, fatal errors, suggestions, etc.
+In general, there are two classes of such methods: ones that emit an error
+directly and ones that allow finer control over what to emit. For example,
+span_err
emits the given error message at the given Span
, but
+struct_span_err
instead returns a
+DiagnosticBuilder
.
+DiagnosticBuilder
allows you to add related notes and suggestions to an error
+before emitting it by calling the emit
method. (Failing to either
+emit or cancel a DiagnosticBuilder
will result in an ICE.) See the
+docs for more info on what you can do.
+// Get a DiagnosticBuilder. This does _not_ emit an error yet.
+let mut err = sess.struct_span_err(sp, "oh no! this is an error!");
+
+// In some cases, you might need to check if `sp` is generated by a macro to
+// avoid printing weird errors about macro-generated code.
+
+if let Ok(snippet) = sess.source_map().span_to_snippet(sp) {
+ // Use the snippet to generate a suggested fix
+ err.span_suggestion(suggestion_sp, "try using a qux here", format!("qux {}", snippet));
+} else {
+ // If we weren't able to generate a snippet, then emit a "help" message
+ // instead of a concrete "suggestion". In practice this is unlikely to be
+ // reached.
+ err.span_help(suggestion_sp, "you could use a qux here instead");
+}
+
+// emit the error
+err.emit();
+
+
+In addition to telling the user exactly why their code is wrong, it's
+oftentimes furthermore possible to tell them how to fix it. To this end,
+DiagnosticBuilder
offers a structured suggestions API, which formats code
+suggestions pleasingly in the terminal, or (when the --error-format json
flag
+is passed) as JSON for consumption by tools, most notably the Rust Language
+Server and rustfix
.
+Not all suggestions should be applied mechanically, they have a degree of
+confidence in the suggested code, from high
+(Applicability::MachineApplicable
) to low (Applicability::MaybeIncorrect
).
+Be conservative when choosing the level. Use the
+span_suggestion
method of DiagnosticBuilder
to
+make a suggestion. The last argument provides a hint to tools whether
+the suggestion is mechanically applicable or not.
+Suggestions point to one or more spans with corresponding code that will
+replace their current content.
+The message that accompanies them should be understandable in the following
+contexts:
+
+- shown as an independent sub-diagnostic (this is the default output)
+- shown as a label pointing at the affected span (this is done automatically if
+some heuristics for verbosity are met)
+- shown as a
help
sub-diagnostic with no content (used for cases where the
+suggestion is obvious from the text, but we still want to let tools to apply
+them))
+- not shown (used for very obvious cases, but we still want to allow tools to
+apply them)
+
+For example, to make our qux
suggestion machine-applicable, we would do:
+let mut err = sess.struct_span_err(sp, "oh no! this is an error!");
+
+if let Ok(snippet) = sess.source_map().span_to_snippet(sp) {
+ err.span_suggestion(
+ suggestion_sp,
+ "try using a qux here",
+ format!("qux {}", snippet),
+ Applicability::MachineApplicable,
+ );
+} else {
+ err.span_help(suggestion_sp, "you could use a qux here instead");
+}
+
+err.emit();
+
+This might emit an error like
+$ rustc mycode.rs
+error[E0999]: oh no! this is an error!
+ --> mycode.rs:3:5
+ |
+3 | sad()
+ | ^ help: try using a qux here: `qux sad()`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0999`.
+
+In some cases, like when the suggestion spans multiple lines or when there are
+multiple suggestions, the suggestions are displayed on their own:
+error[E0999]: oh no! this is an error!
+ --> mycode.rs:3:5
+ |
+3 | sad()
+ | ^
+help: try using a qux here:
+ |
+3 | qux sad()
+ | ^^^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0999`.
+
+The possible values of Applicability
are:
+
+MachineApplicable
: Can be applied mechanically.
+HasPlaceholders
: Cannot be applied mechanically because it has placeholder
+text in the suggestions. For example, "Try adding a type: `let x:
+<type>`".
+MaybeIncorrect
: Cannot be applied mechanically because the suggestion may
+or may not be a good one.
+Unspecified
: Cannot be applied mechanically because we don't know which
+of the above cases it falls into.
+
+
+The compiler linting infrastructure is defined in the rustc::lint
+module.
+
+The built-in compiler lints are defined in the rustc_lint
+crate.
+Every lint is implemented via a struct
that implements the LintPass
trait
+(you also implement one of the more specific lint pass traits, either
+EarlyLintPass
or LateLintPass
). The trait implementation allows you to
+check certain syntactic constructs as the linter walks the source code. You can
+then choose to emit lints in a very similar way to compile errors.
+You also declare the metadata of a particular lint via the declare_lint!
+macro. This includes the name, the default level, a short description, and some
+more details.
+Note that the lint and the lint pass must be registered with the compiler.
+For example, the following lint checks for uses
+of while true { ... }
and suggests using loop { ... }
instead.
+// Declare a lint called `WHILE_TRUE`
+declare_lint! {
+ WHILE_TRUE,
+
+ // warn-by-default
+ Warn,
+
+ // This string is the lint description
+ "suggest using `loop { }` instead of `while true { }`"
+}
+
+// This declares a struct and a lint pass, providing a list of associated lints. The
+// compiler currently doesn't use the associated lints directly (e.g., to not
+// run the pass or otherwise check that the pass emits the appropriate set of
+// lints). However, it's good to be accurate here as it's possible that we're
+// going to register the lints via the get_lints method on our lint pass (that
+// this macro generates).
+declare_lint_pass!(WhileTrue => [WHILE_TRUE]);
+
+// Helper function for `WhileTrue` lint.
+// Traverse through any amount of parenthesis and return the first non-parens expression.
+fn pierce_parens(mut expr: &ast::Expr) -> &ast::Expr {
+ while let ast::ExprKind::Paren(sub) = &expr.kind {
+ expr = sub;
+ }
+ expr
+}
+
+// `EarlyLintPass` has lots of methods. We only override the definition of
+// `check_expr` for this lint because that's all we need, but you could
+// override other methods for your own lint. See the rustc docs for a full
+// list of methods.
+impl EarlyLintPass for WhileTrue {
+ fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) {
+ if let ast::ExprKind::While(cond, ..) = &e.kind {
+ if let ast::ExprKind::Lit(ref lit) = pierce_parens(cond).kind {
+ if let ast::LitKind::Bool(true) = lit.kind {
+ if !lit.span.from_expansion() {
+ let msg = "denote infinite loops with `loop { ... }`";
+ let condition_span = cx.sess.source_map().guess_head_span(e.span);
+ cx.struct_span_lint(WHILE_TRUE, condition_span, |lint| {
+ lint.build(msg)
+ .span_suggestion_short(
+ condition_span,
+ "use `loop`",
+ "loop".to_owned(),
+ Applicability::MachineApplicable,
+ )
+ .emit();
+ })
+ }
+ }
+ }
+ }
+ }
+}
+
+
+Sometimes we want to change the behavior of a lint in a new edition. To do this,
+we just add the transition to our invocation of declare_lint!
:
+declare_lint! {
+ pub ANONYMOUS_PARAMETERS,
+ Allow,
+ "detects anonymous parameters",
+ Edition::Edition2018 => Warn,
+}
+
+This makes the ANONYMOUS_PARAMETERS
lint allow-by-default in the 2015 edition
+but warn-by-default in the 2018 edition.
+A future-incompatible lint should be declared with the @future_incompatible
+additional "field":
+declare_lint! {
+ pub ANONYMOUS_PARAMETERS,
+ Allow,
+ "detects anonymous parameters",
+ @future_incompatible = FutureIncompatibleInfo {
+ reference: "issue #41686 <https://github.com/rust-lang/rust/issues/41686>",
+ edition: Some(Edition::Edition2018),
+ };
+}
+
+If you need a combination of options that's not supported by the
+declare_lint!
macro, you can always define your own static with a type of
+&Lint
but this is currently linted against in the compiler tree.
+
+
+
+- Create a lint defaulting to warn as normal, with ideally the same error
+message you would normally give.
+- Add a suitable reference, typically an RFC or tracking issue. Go ahead
+and include the full URL, sort items in ascending order of issue numbers.
+- Later, change lint to error.
+- Eventually, remove lint.
+
+
+Lints can be turned on in groups. These groups are declared in the
+register_builtins
function in rustc_lint::lib
. The
+add_lint_group!
macro is used to declare a new group.
+For example,
+add_lint_group!(sess,
+ "nonstandard_style",
+ NON_CAMEL_CASE_TYPES,
+ NON_SNAKE_CASE,
+ NON_UPPER_CASE_GLOBALS);
+
+This defines the nonstandard_style
group which turns on the listed lints. A
+user can turn on these lints with a !#[warn(nonstandard_style)]
attribute in
+the source code, or by passing -W nonstandard-style
on the command line.
+
+On occasion, you may need to define a lint that runs before the linting system
+has been initialized (e.g. during parsing or macro expansion). This is
+problematic because we need to have computed lint levels to know whether we
+should emit a warning or an error or nothing at all.
+To solve this problem, we buffer the lints until the linting system is
+processed. Session
and ParseSess
both have
+buffer_lint
methods that allow you to buffer a lint for later. The linting
+system automatically takes care of handling buffered lints later.
+Thus, to define a lint that runs early in the compilation, one defines a lint
+like normal but invokes the lint with buffer_lint
.
+
+The parser (librustc_ast
) is interesting in that it cannot have dependencies on
+any of the other librustc*
crates. In particular, it cannot depend on
+librustc_middle::lint
or librustc_lint
, where all of the compiler linting
+infrastructure is defined. That's troublesome!
+To solve this, librustc_ast
defines its own buffered lint type, which
+ParseSess::buffer_lint
uses. After macro expansion, these buffered lints are
+then dumped into the Session::buffered_lints
used by the rest of the compiler.
+
+The compiler accepts an --error-format json
flag to output
+diagnostics as JSON objects (for the benefit of tools such as cargo fix
or the RLS). It looks like this—
+$ rustc json_error_demo.rs --error-format json
+{"message":"cannot add `&str` to `{integer}`","code":{"code":"E0277","explanation":"\nYou tried to use a type which doesn't implement some trait in a place which\nexpected that trait. Erroneous code example:\n\n```compile_fail,E0277\n// here we declare the Foo trait with a bar method\ntrait Foo {\n fn bar(&self);\n}\n\n// we now declare a function which takes an object implementing the Foo trait\nfn some_func<T: Foo>(foo: T) {\n foo.bar();\n}\n\nfn main() {\n // we now call the method with the i32 type, which doesn't implement\n // the Foo trait\n some_func(5i32); // error: the trait bound `i32 : Foo` is not satisfied\n}\n```\n\nIn order to fix this error, verify that the type you're using does implement\nthe trait. Example:\n\n```\ntrait Foo {\n fn bar(&self);\n}\n\nfn some_func<T: Foo>(foo: T) {\n foo.bar(); // we can now use this method since i32 implements the\n // Foo trait\n}\n\n// we implement the trait on the i32 type\nimpl Foo for i32 {\n fn bar(&self) {}\n}\n\nfn main() {\n some_func(5i32); // ok!\n}\n```\n\nOr in a generic context, an erroneous code example would look like:\n\n```compile_fail,E0277\nfn some_func<T>(foo: T) {\n println!(\"{:?}\", foo); // error: the trait `core::fmt::Debug` is not\n // implemented for the type `T`\n}\n\nfn main() {\n // We now call the method with the i32 type,\n // which *does* implement the Debug trait.\n some_func(5i32);\n}\n```\n\nNote that the error here is in the definition of the generic function: Although\nwe only call it with a parameter that does implement `Debug`, the compiler\nstill rejects the function: It must work with all possible input types. In\norder to make this example compile, we need to restrict the generic type we're\naccepting:\n\n```\nuse std::fmt;\n\n// Restrict the input type to types that implement Debug.\nfn some_func<T: fmt::Debug>(foo: T) {\n println!(\"{:?}\", foo);\n}\n\nfn main() {\n // Calling the method is still fine, as i32 implements Debug.\n some_func(5i32);\n\n // This would fail to compile now:\n // struct WithoutDebug;\n // some_func(WithoutDebug);\n}\n```\n\nRust only looks at the signature of the called function, as such it must\nalready specify all requirements that will be used for every type parameter.\n"},"level":"error","spans":[{"file_name":"json_error_demo.rs","byte_start":50,"byte_end":51,"line_start":4,"line_end":4,"column_start":7,"column_end":8,"is_primary":true,"text":[{"text":" a + b","highlight_start":7,"highlight_end":8}],"label":"no implementation for `{integer} + &str`","suggested_replacement":null,"suggestion_applicability":null,"expansion":null}],"children":[{"message":"the trait `std::ops::Add<&str>` is not implemented for `{integer}`","code":null,"level":"help","spans":[],"children":[],"rendered":null}],"rendered":"error[E0277]: cannot add `&str` to `{integer}`\n --> json_error_demo.rs:4:7\n |\n4 | a + b\n | ^ no implementation for `{integer} + &str`\n |\n = help: the trait `std::ops::Add<&str>` is not implemented for `{integer}`\n\n"}
+{"message":"aborting due to previous error","code":null,"level":"error","spans":[],"children":[],"rendered":"error: aborting due to previous error\n\n"}
+{"message":"For more information about this error, try `rustc --explain E0277`.","code":null,"level":"","spans":[],"children":[],"rendered":"For more information about this error, try `rustc --explain E0277`.\n"}
+
+Note that the output is a series of lines, each of which is a JSON
+object, but the series of lines taken together is, unfortunately, not
+valid JSON, thwarting tools and tricks (such as piping to python3 -m json.tool
)
+that require such. (One speculates that this was intentional for LSP
+performance purposes, so that each line/object can be sent to RLS as
+it is flushed?)
+Also note the "rendered" field, which contains the "human" output as a
+string; this was introduced so that UI tests could both make use of
+the structured JSON and see the "human" output (well, sans colors)
+without having to compile everything twice.
+The "human" readable and the json format emitter can be found under
+librustc_errors, both were moved from the librustc_ast
crate to the
+librustc_errors crate.
+The JSON emitter defines its own Diagnostic
+struct
+(and sub-structs) for the JSON serialization. Don't confuse this with
+errors::Diagnostic
!
+
+The #[rustc_on_unimplemented]
attribute allows trait definitions to add specialized
+notes to error messages when an implementation was expected but not found.
+You can refer to the trait's generic arguments by name and to the resolved type using Self
.
+For example:
+#![feature(rustc_attrs)]
+
+#[rustc_on_unimplemented="an iterator over elements of type `{A}` \
+ cannot be built from a collection of type `{Self}`"]
+trait MyIterator<A> {
+ fn next(&mut self) -> A;
+}
+
+fn iterate_chars<I: MyIterator<char>>(i: I) {
+ // ...
+}
+
+fn main() {
+ iterate_chars(&[1, 2, 3][..]);
+}
+
+When the user compiles this, they will see the following;
+error[E0277]: the trait bound `&[{integer}]: MyIterator<char>` is not satisfied
+ --> <anon>:14:5
+ |
+14 | iterate_chars(&[1, 2, 3][..]);
+ | ^^^^^^^^^^^^^ an iterator over elements of type `char` cannot be built from a collection of type `&[{integer}]`
+ |
+ = help: the trait `MyIterator<char>` is not implemented for `&[{integer}]`
+ = note: required by `iterate_chars`
+
+rustc_on_unimplemented
also supports advanced filtering for better targeting
+of messages, as well as modifying specific parts of the error message. You
+target the text of:
+
+- the main error message (
message
)
+- the label (
label
)
+- an extra note (
note
)
+
+For example, the following attribute
+#[rustc_on_unimplemented(
+ message="message",
+ label="label",
+ note="note"
+)]
+trait MyIterator<A> {
+ fn next(&mut self) -> A;
+}
+
+Would generate the following output:
+error[E0277]: message
+ --> <anon>:14:5
+ |
+14 | iterate_chars(&[1, 2, 3][..]);
+ | ^^^^^^^^^^^^^ label
+ |
+ = note: note
+ = help: the trait `MyIterator<char>` is not implemented for `&[{integer}]`
+ = note: required by `iterate_chars`
+
+To allow more targeted error messages, it is possible to filter the
+application of these fields based on a variety of attributes when using
+on
:
+
+crate_local
: whether the code causing the trait bound to not be
+fulfilled is part of the user's crate. This is used to avoid suggesting
+code changes that would require modifying a dependency.
+- Any of the generic arguments that can be substituted in the text can be
+referred by name as well for filtering, like
Rhs="i32"
, except for
+Self
.
+_Self
: to filter only on a particular calculated trait resolution, like
+Self="std::iter::Iterator<char>"
. This is needed because Self
is a
+keyword which cannot appear in attributes.
+direct
: user-specified rather than derived obligation.
+from_method
: usable both as boolean (whether the flag is present, like
+crate_local
) or matching against a particular method. Currently used
+for try
.
+from_desugaring
: usable both as boolean (whether the flag is present)
+or matching against a particular desugaring. The desugaring is identified
+with its variant name in the DesugaringKind
enum.
+
+For example, the Iterator
trait can be annotated in the following way:
+#[rustc_on_unimplemented(
+ on(
+ _Self="&str",
+ note="call `.chars()` or `.as_bytes()` on `{Self}"
+ ),
+ message="`{Self}` is not an iterator",
+ label="`{Self}` is not an iterator",
+ note="maybe try calling `.iter()` or a similar method"
+)]
+pub trait Iterator {}
+
+Which would produce the following outputs:
+error[E0277]: `Foo` is not an iterator
+ --> src/main.rs:4:16
+ |
+4 | for foo in Foo {}
+ | ^^^ `Foo` is not an iterator
+ |
+ = note: maybe try calling `.iter()` or a similar method
+ = help: the trait `std::iter::Iterator` is not implemented for `Foo`
+ = note: required by `std::iter::IntoIterator::into_iter`
+
+error[E0277]: `&str` is not an iterator
+ --> src/main.rs:5:16
+ |
+5 | for foo in "" {}
+ | ^^ `&str` is not an iterator
+ |
+ = note: call `.chars()` or `.bytes() on `&str`
+ = help: the trait `std::iter::Iterator` is not implemented for `&str`
+ = note: required by `std::iter::IntoIterator::into_iter`
+
+If you need to filter on multiple attributes, you can use all
, any
or
+not
in the following way:
+#[rustc_on_unimplemented(
+ on(
+ all(_Self="&str", T="std::string::String"),
+ note="you can coerce a `{T}` into a `{Self}` by writing `&*variable`"
+ )
+)]
+pub trait From<T>: Sized { /* ... */ }
+
+
+