Migrate most of rustc_builtin_macros to diagnostic impls

Co-authored-by: Joe ST <joe@fbstj.net>
Co-authored-by: Michael Goulet <michael@errs.io>
This commit is contained in:
clubby789 2023-04-08 20:37:41 +01:00
parent b6f6104a1f
commit 64f7597776
31 changed files with 978 additions and 349 deletions

View File

@ -3,3 +3,149 @@ builtin_macros_requires_cfg_pattern =
.label = cfg-pattern required .label = cfg-pattern required
builtin_macros_expected_one_cfg_pattern = expected 1 cfg-pattern builtin_macros_expected_one_cfg_pattern = expected 1 cfg-pattern
builtin_macros_alloc_error_must_be_fn = alloc_error_handler must be a function
builtin_macros_assert_requires_boolean = macro requires a boolean expression as an argument
.label = boolean expression required
builtin_macros_assert_requires_expression = macro requires an expression as an argument
.suggestion = try removing semicolon
builtin_macros_assert_missing_comma = unexpected string literal
.suggestion = try adding a comma
builtin_macros_cfg_accessible_unspecified_path = `cfg_accessible` path is not specified
builtin_macros_cfg_accessible_multiple_paths = multiple `cfg_accessible` paths are specified
builtin_macros_cfg_accessible_literal_path = `cfg_accessible` path cannot be a literal
builtin_macros_cfg_accessible_has_args = `cfg_accessible` path cannot accept arguments
builtin_macros_cfg_accessible_indeterminate = cannot determine whether the path is accessible or not
builtin_macros_concat_bytestr = cannot concatenate a byte string literal
builtin_macros_concat_missing_literal = expected a literal
.note = only literals (like `"foo"`, `-42` and `3.14`) can be passed to `concat!()`
builtin_macros_concat_bytes_missing_literal = expected a byte literal
.note = only byte literals (like `b"foo"`, `b's'` and `[3, 4, 5]`) can be passed to `concat_bytes!()`
builtin_macros_concat_bytes_invalid = cannot concatenate {$lit_kind} literals
.byte_char = try using a byte character
.byte_str = try using a byte string
.number_array = try wrapping the number in an array
builtin_macros_concat_bytes_oob = numeric literal is out of bounds
builtin_macros_concat_bytes_non_u8 = numeric literal is not a `u8`
builtin_macros_concat_bytes_array = cannot concatenate doubly nested array
.note = byte strings are treated as arrays of bytes
.help = try flattening the array
builtin_macros_concat_bytes_bad_repeat = repeat count is not a positive number
builtin_macros_concat_idents_missing_args = `concat_idents!()` takes 1 or more arguments
builtin_macros_concat_idents_missing_comma = `concat_idents!()` expecting comma
builtin_macros_concat_idents_ident_args = `concat_idents!()` requires ident args
builtin_macros_bad_derive_target = `derive` may only be applied to `struct`s, `enum`s and `union`s
.label = not applicable here
.label2 = not a `struct`, `enum` or `union`
builtin_macros_unexpected_lit = expected path to a trait, found literal
.label = not a trait
.str_lit = try using `#[derive({$sym})]`
.other = for example, write `#[derive(Debug)]` for `Debug`
builtin_macros_derive_path_args_list = traits in `#[derive(...)]` don't accept arguments
.suggestion = remove the arguments
builtin_macros_derive_path_args_value = traits in `#[derive(...)]` don't accept values
.suggestion = remove the value
builtin_macros_derive_macro_call = `derive` cannot be used on items with type macros
builtin_macros_cannot_derive_union = this trait cannot be derived for unions
builtin_macros_no_default_variant = no default declared
.help = make a unit variant default by placing `#[default]` above it
.suggestion = make `{$ident}` default
builtin_macros_multiple_defaults = multiple declared defaults
.label = first default
.additional = additional default
.note = only one variant can be default
.suggestion = make `{$ident}` default
builtin_macros_non_unit_default = the `#[default]` attribute may only be used on unit enum variants
.help = consider a manual implementation of `Default`
builtin_macros_non_exhaustive_default = default variant must be exhaustive
.label = declared `#[non_exhaustive]` here
.help = consider a manual implementation of `Default`
builtin_macros_multiple_default_attrs = multiple `#[default]` attributes
.note = only one `#[default]` attribute is needed
.label = `#[default]` used here
.label_again = `#[default]` used again here
.help = try removing {$only_one ->
[true] this
*[false] these
}
builtin_macros_default_arg = `#[default]` attribute does not accept a value
.suggestion = try using `#[default]`
builtin_macros_env_takes_args = `env!()` takes 1 or 2 arguments
builtin_macros_env_not_defined = environment variable `{$var}` not defined at compile time
.cargo = Cargo sets build script variables at run time. Use `std::env::var("{$var}")` instead
.other = use `std::env::var("{$var}")` to read the variable at run time
builtin_macros_format_requires_string = requires at least a format string argument
builtin_macros_format_duplicate_arg = duplicate argument named `{$ident}`
.label1 = previously here
.label2 = duplicate argument
builtin_macros_format_positional_after_named = positional arguments cannot follow named arguments
.label = positional arguments must be before named arguments
.named_args = named argument
builtin_macros_format_string_invalid = invalid format string: {$desc}
.label = {$label1} in format string
.note = {$note}
.second_label = {$label}
builtin_macros_sugg = consider using a positional formatting argument instead
builtin_macros_format_no_arg_named = there is no argument named `{$name}`
.note = did you intend to capture a variable `{$name}` from the surrounding scope?
.note2 = to avoid ambiguity, `format_args!` cannot capture variables when the format string is expanded from a macro
builtin_macros_format_unknown_trait = unknown format trait `{$ty}`
.note = the only appropriate formatting traits are:
- ``, which uses the `Display` trait
- `?`, which uses the `Debug` trait
- `e`, which uses the `LowerExp` trait
- `E`, which uses the `UpperExp` trait
- `o`, which uses the `Octal` trait
- `p`, which uses the `Pointer` trait
- `b`, which uses the `Binary` trait
- `x`, which uses the `LowerHex` trait
- `X`, which uses the `UpperHex` trait
.suggestion = use the `{$trait_name}` trait
builtin_macros_format_unused_arg = {$named ->
[true] named argument
*[false] argument
} never used
builtin_macros_format_unused_args = multiple unused formatting arguments
.label = multiple missing formatting specifiers
builtin_macros_format_pos_mismatch = {$n} positional {$n ->
[one] argument
*[more] arguments
} in format string, but {$desc}

View File

@ -1,3 +1,4 @@
use crate::errors;
use crate::util::check_builtin_macro_attribute; use crate::util::check_builtin_macro_attribute;
use rustc_ast::ptr::P; use rustc_ast::ptr::P;
@ -31,7 +32,7 @@ pub fn expand(
{ {
(item, true, ecx.with_def_site_ctxt(fn_kind.sig.span)) (item, true, ecx.with_def_site_ctxt(fn_kind.sig.span))
} else { } else {
ecx.sess.parse_sess.span_diagnostic.span_err(item.span(), "alloc_error_handler must be a function"); ecx.sess.parse_sess.span_diagnostic.emit_err(errors::AllocErrorMustBeFn {span: item.span() });
return vec![orig_item]; return vec![orig_item];
}; };

View File

@ -1,12 +1,13 @@
mod context; mod context;
use crate::edition_panic::use_panic_2021; use crate::edition_panic::use_panic_2021;
use crate::errors;
use rustc_ast::ptr::P; use rustc_ast::ptr::P;
use rustc_ast::token; use rustc_ast::token;
use rustc_ast::tokenstream::{DelimSpan, TokenStream}; use rustc_ast::tokenstream::{DelimSpan, TokenStream};
use rustc_ast::{DelimArgs, Expr, ExprKind, MacCall, MacDelimiter, Path, PathSegment, UnOp}; use rustc_ast::{DelimArgs, Expr, ExprKind, MacCall, MacDelimiter, Path, PathSegment, UnOp};
use rustc_ast_pretty::pprust; use rustc_ast_pretty::pprust;
use rustc_errors::{Applicability, PResult}; use rustc_errors::PResult;
use rustc_expand::base::{DummyResult, ExtCtxt, MacEager, MacResult}; use rustc_expand::base::{DummyResult, ExtCtxt, MacEager, MacResult};
use rustc_parse::parser::Parser; use rustc_parse::parser::Parser;
use rustc_span::symbol::{sym, Ident, Symbol}; use rustc_span::symbol::{sym, Ident, Symbol};
@ -114,9 +115,7 @@ fn parse_assert<'a>(cx: &mut ExtCtxt<'a>, sp: Span, stream: TokenStream) -> PRes
let mut parser = cx.new_parser_from_tts(stream); let mut parser = cx.new_parser_from_tts(stream);
if parser.token == token::Eof { if parser.token == token::Eof {
let mut err = cx.struct_span_err(sp, "macro requires a boolean expression as an argument"); return Err(cx.create_err(errors::AssertRequiresBoolean { span: sp }));
err.span_label(sp, "boolean expression required");
return Err(err);
} }
let cond_expr = parser.parse_expr()?; let cond_expr = parser.parse_expr()?;
@ -129,15 +128,7 @@ fn parse_assert<'a>(cx: &mut ExtCtxt<'a>, sp: Span, stream: TokenStream) -> PRes
// //
// Emit an error about semicolon and suggest removing it. // Emit an error about semicolon and suggest removing it.
if parser.token == token::Semi { if parser.token == token::Semi {
let mut err = cx.struct_span_err(sp, "macro requires an expression as an argument"); cx.emit_err(errors::AssertRequiresExpression { span: sp, token: parser.token.span });
err.span_suggestion(
parser.token.span,
"try removing semicolon",
"",
Applicability::MaybeIncorrect,
);
err.emit();
parser.bump(); parser.bump();
} }
@ -149,15 +140,8 @@ fn parse_assert<'a>(cx: &mut ExtCtxt<'a>, sp: Span, stream: TokenStream) -> PRes
// Emit an error and suggest inserting a comma. // Emit an error and suggest inserting a comma.
let custom_message = let custom_message =
if let token::Literal(token::Lit { kind: token::Str, .. }) = parser.token.kind { if let token::Literal(token::Lit { kind: token::Str, .. }) = parser.token.kind {
let mut err = cx.struct_span_err(parser.token.span, "unexpected string literal"); let comma = parser.prev_token.span.shrink_to_hi();
let comma_span = parser.prev_token.span.shrink_to_hi(); cx.emit_err(errors::AssertMissingComma { span: parser.token.span, comma });
err.span_suggestion_short(
comma_span,
"try adding a comma",
", ",
Applicability::MaybeIncorrect,
);
err.emit();
parse_custom_message(&mut parser) parse_custom_message(&mut parser)
} else if parser.eat(&token::Comma) { } else if parser.eat(&token::Comma) {

View File

@ -2,13 +2,13 @@
//! a literal `true` or `false` based on whether the given cfg matches the //! a literal `true` or `false` based on whether the given cfg matches the
//! current compilation environment. //! current compilation environment.
use crate::errors;
use rustc_ast as ast; use rustc_ast as ast;
use rustc_ast::token; use rustc_ast::token;
use rustc_ast::tokenstream::TokenStream; use rustc_ast::tokenstream::TokenStream;
use rustc_attr as attr; use rustc_attr as attr;
use rustc_errors::PResult; use rustc_errors::PResult;
use rustc_expand::base::{self, *}; use rustc_expand::base::{self, *};
use rustc_macros::Diagnostic;
use rustc_span::Span; use rustc_span::Span;
pub fn expand_cfg( pub fn expand_cfg(
@ -35,26 +35,11 @@ pub fn expand_cfg(
} }
} }
#[derive(Diagnostic)]
#[diag(builtin_macros_requires_cfg_pattern)]
struct RequiresCfgPattern {
#[primary_span]
#[label]
span: Span,
}
#[derive(Diagnostic)]
#[diag(builtin_macros_expected_one_cfg_pattern)]
struct OneCfgPattern {
#[primary_span]
span: Span,
}
fn parse_cfg<'a>(cx: &mut ExtCtxt<'a>, span: Span, tts: TokenStream) -> PResult<'a, ast::MetaItem> { fn parse_cfg<'a>(cx: &mut ExtCtxt<'a>, span: Span, tts: TokenStream) -> PResult<'a, ast::MetaItem> {
let mut p = cx.new_parser_from_tts(tts); let mut p = cx.new_parser_from_tts(tts);
if p.token == token::Eof { if p.token == token::Eof {
return Err(cx.create_err(RequiresCfgPattern { span })); return Err(cx.create_err(errors::RequiresCfgPattern { span }));
} }
let cfg = p.parse_meta_item()?; let cfg = p.parse_meta_item()?;
@ -62,7 +47,7 @@ fn parse_cfg<'a>(cx: &mut ExtCtxt<'a>, span: Span, tts: TokenStream) -> PResult<
let _ = p.eat(&token::Comma); let _ = p.eat(&token::Comma);
if !p.eat(&token::Eof) { if !p.eat(&token::Eof) {
return Err(cx.create_err(OneCfgPattern { span })); return Err(cx.create_err(errors::OneCfgPattern { span }));
} }
Ok(cfg) Ok(cfg)

View File

@ -1,5 +1,6 @@
//! Implementation of the `#[cfg_accessible(path)]` attribute macro. //! Implementation of the `#[cfg_accessible(path)]` attribute macro.
use crate::errors;
use rustc_ast as ast; use rustc_ast as ast;
use rustc_expand::base::{Annotatable, ExpandResult, ExtCtxt, Indeterminate, MultiItemModifier}; use rustc_expand::base::{Annotatable, ExpandResult, ExtCtxt, Indeterminate, MultiItemModifier};
use rustc_feature::AttributeTemplate; use rustc_feature::AttributeTemplate;
@ -10,15 +11,22 @@ use rustc_span::Span;
pub(crate) struct Expander; pub(crate) struct Expander;
fn validate_input<'a>(ecx: &mut ExtCtxt<'_>, mi: &'a ast::MetaItem) -> Option<&'a ast::Path> { fn validate_input<'a>(ecx: &mut ExtCtxt<'_>, mi: &'a ast::MetaItem) -> Option<&'a ast::Path> {
use errors::CfgAccessibleInvalid::*;
match mi.meta_item_list() { match mi.meta_item_list() {
None => {} None => {}
Some([]) => ecx.span_err(mi.span, "`cfg_accessible` path is not specified"), Some([]) => {
Some([_, .., l]) => ecx.span_err(l.span(), "multiple `cfg_accessible` paths are specified"), ecx.emit_err(UnspecifiedPath(mi.span));
}
Some([_, .., l]) => {
ecx.emit_err(MultiplePaths(l.span()));
}
Some([nmi]) => match nmi.meta_item() { Some([nmi]) => match nmi.meta_item() {
None => ecx.span_err(nmi.span(), "`cfg_accessible` path cannot be a literal"), None => {
ecx.emit_err(LiteralPath(nmi.span()));
}
Some(mi) => { Some(mi) => {
if !mi.is_word() { if !mi.is_word() {
ecx.span_err(mi.span, "`cfg_accessible` path cannot accept arguments"); ecx.emit_err(HasArguments(mi.span));
} }
return Some(&mi.path); return Some(&mi.path);
} }
@ -53,7 +61,7 @@ impl MultiItemModifier for Expander {
Ok(true) => ExpandResult::Ready(vec![item]), Ok(true) => ExpandResult::Ready(vec![item]),
Ok(false) => ExpandResult::Ready(Vec::new()), Ok(false) => ExpandResult::Ready(Vec::new()),
Err(Indeterminate) if ecx.force_mode => { Err(Indeterminate) if ecx.force_mode => {
ecx.span_err(span, "cannot determine whether the path is accessible or not"); ecx.emit_err(errors::CfgAccessibleIndeterminate { span });
ExpandResult::Ready(vec![item]) ExpandResult::Ready(vec![item])
} }
Err(Indeterminate) => ExpandResult::Retry(item), Err(Indeterminate) => ExpandResult::Retry(item),

View File

@ -13,6 +13,11 @@ pub fn expand_compile_error<'cx>(
return DummyResult::any(sp); return DummyResult::any(sp);
}; };
#[expect(
rustc::diagnostic_outside_of_impl,
reason = "diagnostic message is specified by user"
)]
#[expect(rustc::untranslatable_diagnostic, reason = "diagnostic message is specified by user")]
cx.span_err(sp, var.as_str()); cx.span_err(sp, var.as_str());
DummyResult::any(sp) DummyResult::any(sp)

View File

@ -4,6 +4,8 @@ use rustc_expand::base::{self, DummyResult};
use rustc_session::errors::report_lit_error; use rustc_session::errors::report_lit_error;
use rustc_span::symbol::Symbol; use rustc_span::symbol::Symbol;
use crate::errors;
pub fn expand_concat( pub fn expand_concat(
cx: &mut base::ExtCtxt<'_>, cx: &mut base::ExtCtxt<'_>,
sp: rustc_span::Span, sp: rustc_span::Span,
@ -31,7 +33,7 @@ pub fn expand_concat(
accumulator.push_str(&b.to_string()); accumulator.push_str(&b.to_string());
} }
Ok(ast::LitKind::Byte(..) | ast::LitKind::ByteStr(..)) => { Ok(ast::LitKind::Byte(..) | ast::LitKind::ByteStr(..)) => {
cx.span_err(e.span, "cannot concatenate a byte string literal"); cx.emit_err(errors::ConcatBytestr { span: e.span });
has_errors = true; has_errors = true;
} }
Ok(ast::LitKind::Err) => { Ok(ast::LitKind::Err) => {
@ -55,7 +57,7 @@ pub fn expand_concat(
} }
} }
ast::ExprKind::IncludedBytes(..) => { ast::ExprKind::IncludedBytes(..) => {
cx.span_err(e.span, "cannot concatenate a byte string literal") cx.emit_err(errors::ConcatBytestr { span: e.span });
} }
ast::ExprKind::Err => { ast::ExprKind::Err => {
has_errors = true; has_errors = true;
@ -67,9 +69,7 @@ pub fn expand_concat(
} }
if !missing_literal.is_empty() { if !missing_literal.is_empty() {
let mut err = cx.struct_span_err(missing_literal, "expected a literal"); cx.emit_err(errors::ConcatMissingLiteral { spans: missing_literal });
err.note("only literals (like `\"foo\"`, `-42` and `3.14`) can be passed to `concat!()`");
err.emit();
return DummyResult::any(sp); return DummyResult::any(sp);
} else if has_errors { } else if has_errors {
return DummyResult::any(sp); return DummyResult::any(sp);

View File

@ -1,10 +1,11 @@
use rustc_ast as ast; use rustc_ast as ast;
use rustc_ast::{ptr::P, tokenstream::TokenStream}; use rustc_ast::{ptr::P, tokenstream::TokenStream};
use rustc_errors::Applicability;
use rustc_expand::base::{self, DummyResult}; use rustc_expand::base::{self, DummyResult};
use rustc_session::errors::report_lit_error; use rustc_session::errors::report_lit_error;
use rustc_span::Span; use rustc_span::Span;
use crate::errors;
/// Emits errors for literal expressions that are invalid inside and outside of an array. /// Emits errors for literal expressions that are invalid inside and outside of an array.
fn invalid_type_err( fn invalid_type_err(
cx: &mut base::ExtCtxt<'_>, cx: &mut base::ExtCtxt<'_>,
@ -12,62 +13,46 @@ fn invalid_type_err(
span: Span, span: Span,
is_nested: bool, is_nested: bool,
) { ) {
use errors::{
ConcatBytesInvalid, ConcatBytesInvalidSuggestion, ConcatBytesNonU8, ConcatBytesOob,
};
let snippet = cx.sess.source_map().span_to_snippet(span).ok();
match ast::LitKind::from_token_lit(token_lit) { match ast::LitKind::from_token_lit(token_lit) {
Ok(ast::LitKind::Char(_)) => { Ok(ast::LitKind::Char(_)) => {
let mut err = cx.struct_span_err(span, "cannot concatenate character literals"); let sugg =
if let Ok(snippet) = cx.sess.source_map().span_to_snippet(span) { snippet.map(|snippet| ConcatBytesInvalidSuggestion::CharLit { span, snippet });
err.span_suggestion( cx.sess.emit_err(ConcatBytesInvalid { span, lit_kind: "character", sugg });
span,
"try using a byte character",
format!("b{}", snippet),
Applicability::MachineApplicable,
)
.emit();
}
} }
Ok(ast::LitKind::Str(_, _)) => { Ok(ast::LitKind::Str(_, _)) => {
let mut err = cx.struct_span_err(span, "cannot concatenate string literals");
// suggestion would be invalid if we are nested // suggestion would be invalid if we are nested
if !is_nested { let sugg = if !is_nested {
if let Ok(snippet) = cx.sess.source_map().span_to_snippet(span) { snippet.map(|snippet| ConcatBytesInvalidSuggestion::StrLit { span, snippet })
err.span_suggestion( } else {
span, None
"try using a byte string", };
format!("b{}", snippet), cx.emit_err(ConcatBytesInvalid { span, lit_kind: "string", sugg });
Applicability::MachineApplicable,
);
}
}
err.emit();
} }
Ok(ast::LitKind::Float(_, _)) => { Ok(ast::LitKind::Float(_, _)) => {
cx.span_err(span, "cannot concatenate float literals"); cx.emit_err(ConcatBytesInvalid { span, lit_kind: "float", sugg: None });
} }
Ok(ast::LitKind::Bool(_)) => { Ok(ast::LitKind::Bool(_)) => {
cx.span_err(span, "cannot concatenate boolean literals"); cx.emit_err(ConcatBytesInvalid { span, lit_kind: "boolean", sugg: None });
} }
Ok(ast::LitKind::Err) => {} Ok(ast::LitKind::Err) => {}
Ok(ast::LitKind::Int(_, _)) if !is_nested => { Ok(ast::LitKind::Int(_, _)) if !is_nested => {
let mut err = cx.struct_span_err(span, "cannot concatenate numeric literals"); let sugg =
if let Ok(snippet) = cx.sess.source_map().span_to_snippet(span) { snippet.map(|snippet| ConcatBytesInvalidSuggestion::IntLit { span: span, snippet });
err.span_suggestion( cx.emit_err(ConcatBytesInvalid { span, lit_kind: "numeric", sugg });
span,
"try wrapping the number in an array",
format!("[{}]", snippet),
Applicability::MachineApplicable,
);
}
err.emit();
} }
Ok(ast::LitKind::Int( Ok(ast::LitKind::Int(
val, val,
ast::LitIntType::Unsuffixed | ast::LitIntType::Unsigned(ast::UintTy::U8), ast::LitIntType::Unsuffixed | ast::LitIntType::Unsigned(ast::UintTy::U8),
)) => { )) => {
assert!(val > u8::MAX.into()); // must be an error assert!(val > u8::MAX.into()); // must be an error
cx.span_err(span, "numeric literal is out of bounds"); cx.emit_err(ConcatBytesOob { span });
} }
Ok(ast::LitKind::Int(_, _)) => { Ok(ast::LitKind::Int(_, _)) => {
cx.span_err(span, "numeric literal is not a `u8`"); cx.emit_err(ConcatBytesNonU8 { span });
} }
Ok(ast::LitKind::ByteStr(..) | ast::LitKind::Byte(_)) => unreachable!(), Ok(ast::LitKind::ByteStr(..) | ast::LitKind::Byte(_)) => unreachable!(),
Err(err) => { Err(err) => {
@ -85,7 +70,7 @@ fn handle_array_element(
match expr.kind { match expr.kind {
ast::ExprKind::Array(_) | ast::ExprKind::Repeat(_, _) => { ast::ExprKind::Array(_) | ast::ExprKind::Repeat(_, _) => {
if !*has_errors { if !*has_errors {
cx.span_err(expr.span, "cannot concatenate doubly nested array"); cx.emit_err(errors::ConcatBytesArray { span: expr.span, bytestr: false });
} }
*has_errors = true; *has_errors = true;
None None
@ -99,10 +84,7 @@ fn handle_array_element(
Ok(ast::LitKind::Byte(val)) => Some(val), Ok(ast::LitKind::Byte(val)) => Some(val),
Ok(ast::LitKind::ByteStr(..)) => { Ok(ast::LitKind::ByteStr(..)) => {
if !*has_errors { if !*has_errors {
cx.struct_span_err(expr.span, "cannot concatenate doubly nested array") cx.emit_err(errors::ConcatBytesArray { span: expr.span, bytestr: true });
.note("byte strings are treated as arrays of bytes")
.help("try flattening the array")
.emit();
} }
*has_errors = true; *has_errors = true;
None None
@ -117,10 +99,7 @@ fn handle_array_element(
}, },
ast::ExprKind::IncludedBytes(..) => { ast::ExprKind::IncludedBytes(..) => {
if !*has_errors { if !*has_errors {
cx.struct_span_err(expr.span, "cannot concatenate doubly nested array") cx.emit_err(errors::ConcatBytesArray { span: expr.span, bytestr: false });
.note("byte strings are treated as arrays of bytes")
.help("try flattening the array")
.emit();
} }
*has_errors = true; *has_errors = true;
None None
@ -167,7 +146,7 @@ pub fn expand_concat_bytes(
} }
} }
} else { } else {
cx.span_err(count.value.span, "repeat count is not a positive number"); cx.emit_err(errors::ConcatBytesBadRepeat {span: count.value.span });
} }
} }
&ast::ExprKind::Lit(token_lit) => match ast::LitKind::from_token_lit(token_lit) { &ast::ExprKind::Lit(token_lit) => match ast::LitKind::from_token_lit(token_lit) {
@ -196,9 +175,7 @@ pub fn expand_concat_bytes(
} }
} }
if !missing_literals.is_empty() { if !missing_literals.is_empty() {
let mut err = cx.struct_span_err(missing_literals, "expected a byte literal"); cx.emit_err(errors::ConcatBytesMissingLiteral { spans: missing_literals });
err.note("only byte literals (like `b\"foo\"`, `b's'`, and `[3, 4, 5]`) can be passed to `concat_bytes!()`");
err.emit();
return base::MacEager::expr(DummyResult::raw_expr(sp, true)); return base::MacEager::expr(DummyResult::raw_expr(sp, true));
} else if has_errors { } else if has_errors {
return base::MacEager::expr(DummyResult::raw_expr(sp, true)); return base::MacEager::expr(DummyResult::raw_expr(sp, true));

View File

@ -6,13 +6,15 @@ use rustc_expand::base::{self, *};
use rustc_span::symbol::{Ident, Symbol}; use rustc_span::symbol::{Ident, Symbol};
use rustc_span::Span; use rustc_span::Span;
use crate::errors;
pub fn expand_concat_idents<'cx>( pub fn expand_concat_idents<'cx>(
cx: &'cx mut ExtCtxt<'_>, cx: &'cx mut ExtCtxt<'_>,
sp: Span, sp: Span,
tts: TokenStream, tts: TokenStream,
) -> Box<dyn base::MacResult + 'cx> { ) -> Box<dyn base::MacResult + 'cx> {
if tts.is_empty() { if tts.is_empty() {
cx.span_err(sp, "concat_idents! takes 1 or more arguments"); cx.emit_err(errors::ConcatIdentsMissingArgs { span: sp });
return DummyResult::any(sp); return DummyResult::any(sp);
} }
@ -22,7 +24,7 @@ pub fn expand_concat_idents<'cx>(
match e { match e {
TokenTree::Token(Token { kind: token::Comma, .. }, _) => {} TokenTree::Token(Token { kind: token::Comma, .. }, _) => {}
_ => { _ => {
cx.span_err(sp, "concat_idents! expecting comma"); cx.emit_err(errors::ConcatIdentsMissingComma { span: sp });
return DummyResult::any(sp); return DummyResult::any(sp);
} }
} }
@ -34,7 +36,7 @@ pub fn expand_concat_idents<'cx>(
} }
} }
cx.span_err(sp, "concat_idents! requires ident args"); cx.emit_err(errors::ConcatIdentsIdentArgs { span: sp });
return DummyResult::any(sp); return DummyResult::any(sp);
} }
} }

View File

@ -1,8 +1,8 @@
use crate::cfg_eval::cfg_eval; use crate::cfg_eval::cfg_eval;
use crate::errors;
use rustc_ast as ast; use rustc_ast as ast;
use rustc_ast::{GenericParamKind, ItemKind, MetaItemKind, NestedMetaItem, StmtKind}; use rustc_ast::{GenericParamKind, ItemKind, MetaItemKind, NestedMetaItem, StmtKind};
use rustc_errors::{struct_span_err, Applicability};
use rustc_expand::base::{Annotatable, ExpandResult, ExtCtxt, Indeterminate, MultiItemModifier}; use rustc_expand::base::{Annotatable, ExpandResult, ExtCtxt, Indeterminate, MultiItemModifier};
use rustc_feature::AttributeTemplate; use rustc_feature::AttributeTemplate;
use rustc_parse::validate_attr; use rustc_parse::validate_attr;
@ -116,49 +116,33 @@ fn report_bad_target(sess: &Session, item: &Annotatable, span: Span) -> bool {
let bad_target = let bad_target =
!matches!(item_kind, Some(ItemKind::Struct(..) | ItemKind::Enum(..) | ItemKind::Union(..))); !matches!(item_kind, Some(ItemKind::Struct(..) | ItemKind::Enum(..) | ItemKind::Union(..)));
if bad_target { if bad_target {
struct_span_err!( sess.emit_err(errors::BadDeriveTarget { span, item: item.span() });
sess,
span,
E0774,
"`derive` may only be applied to `struct`s, `enum`s and `union`s",
)
.span_label(span, "not applicable here")
.span_label(item.span(), "not a `struct`, `enum` or `union`")
.emit();
} }
bad_target bad_target
} }
fn report_unexpected_meta_item_lit(sess: &Session, lit: &ast::MetaItemLit) { fn report_unexpected_meta_item_lit(sess: &Session, lit: &ast::MetaItemLit) {
let help_msg = match lit.kind { let help = match lit.kind {
ast::LitKind::Str(_, ast::StrStyle::Cooked) ast::LitKind::Str(_, ast::StrStyle::Cooked)
if rustc_lexer::is_ident(lit.symbol.as_str()) => if rustc_lexer::is_ident(lit.symbol.as_str()) =>
{ {
format!("try using `#[derive({})]`", lit.symbol) errors::BadDeriveLitHelp::StrLit { sym: lit.symbol }
} }
_ => "for example, write `#[derive(Debug)]` for `Debug`".to_string(), _ => errors::BadDeriveLitHelp::Other,
}; };
struct_span_err!(sess, lit.span, E0777, "expected path to a trait, found literal",) sess.emit_err(errors::BadDeriveLit { span: lit.span, help });
.span_label(lit.span, "not a trait")
.help(&help_msg)
.emit();
} }
fn report_path_args(sess: &Session, meta: &ast::MetaItem) { fn report_path_args(sess: &Session, meta: &ast::MetaItem) {
let report_error = |title, action| { let span = meta.span.with_lo(meta.path.span.hi());
let span = meta.span.with_lo(meta.path.span.hi());
sess.struct_span_err(span, title)
.span_suggestion(span, action, "", Applicability::MachineApplicable)
.emit();
};
match meta.kind { match meta.kind {
MetaItemKind::Word => {} MetaItemKind::Word => {}
MetaItemKind::List(..) => report_error( MetaItemKind::List(..) => {
"traits in `#[derive(...)]` don't accept arguments", sess.emit_err(errors::DerivePathArgsList { span });
"remove the arguments", }
),
MetaItemKind::NameValue(..) => { MetaItemKind::NameValue(..) => {
report_error("traits in `#[derive(...)]` don't accept values", "remove the value") sess.emit_err(errors::DerivePathArgsValue { span });
} }
} }
} }

View File

@ -1,8 +1,8 @@
use crate::deriving::generic::ty::*; use crate::deriving::generic::ty::*;
use crate::deriving::generic::*; use crate::deriving::generic::*;
use crate::errors;
use rustc_ast as ast; use rustc_ast as ast;
use rustc_ast::{attr, walk_list, EnumDef, VariantData}; use rustc_ast::{attr, walk_list, EnumDef, VariantData};
use rustc_errors::Applicability;
use rustc_expand::base::{Annotatable, DummyResult, ExtCtxt}; use rustc_expand::base::{Annotatable, DummyResult, ExtCtxt};
use rustc_span::symbol::Ident; use rustc_span::symbol::Ident;
use rustc_span::symbol::{kw, sym}; use rustc_span::symbol::{kw, sym};
@ -118,67 +118,50 @@ fn extract_default_variant<'a>(
.filter(|variant| matches!(variant.data, VariantData::Unit(..))) .filter(|variant| matches!(variant.data, VariantData::Unit(..)))
.filter(|variant| !attr::contains_name(&variant.attrs, sym::non_exhaustive)); .filter(|variant| !attr::contains_name(&variant.attrs, sym::non_exhaustive));
let mut diag = cx.struct_span_err(trait_span, "no default declared"); let suggs = possible_defaults
diag.help("make a unit variant default by placing `#[default]` above it"); .map(|v| errors::NoDefaultVariantSugg { span: v.span, ident: v.ident })
for variant in possible_defaults { .collect();
// Suggest making each unit variant default. cx.emit_err(errors::NoDefaultVariant { span: trait_span, suggs });
diag.tool_only_span_suggestion(
variant.span,
&format!("make `{}` default", variant.ident),
format!("#[default] {}", variant.ident),
Applicability::MaybeIncorrect,
);
}
diag.emit();
return Err(()); return Err(());
} }
[first, rest @ ..] => { [first, rest @ ..] => {
let mut diag = cx.struct_span_err(trait_span, "multiple declared defaults"); let suggs = default_variants
diag.span_label(first.span, "first default"); .iter()
diag.span_labels(rest.iter().map(|variant| variant.span), "additional default"); .map(|variant| {
diag.note("only one variant can be default"); let spans = default_variants
for variant in &default_variants { .iter()
// Suggest making each variant already tagged default. .filter_map(|v| {
let suggestion = default_variants if v.span == variant.span {
.iter() None
.filter_map(|v| { } else {
if v.span == variant.span { Some(attr::find_by_name(&v.attrs, kw::Default)?.span)
None }
} else { })
Some((attr::find_by_name(&v.attrs, kw::Default)?.span, String::new())) .collect();
} errors::MultipleDefaultsSugg { spans, ident: variant.ident }
}) })
.collect(); .collect();
cx.emit_err(errors::MultipleDefaults {
diag.tool_only_multipart_suggestion( span: trait_span,
&format!("make `{}` default", variant.ident), first: first.span,
suggestion, additional: rest.iter().map(|v| v.span).collect(),
Applicability::MaybeIncorrect, suggs,
); });
}
diag.emit();
return Err(()); return Err(());
} }
}; };
if !matches!(variant.data, VariantData::Unit(..)) { if !matches!(variant.data, VariantData::Unit(..)) {
cx.struct_span_err( cx.emit_err(errors::NonUnitDefault { span: variant.ident.span });
variant.ident.span,
"the `#[default]` attribute may only be used on unit enum variants",
)
.help("consider a manual implementation of `Default`")
.emit();
return Err(()); return Err(());
} }
if let Some(non_exhaustive_attr) = attr::find_by_name(&variant.attrs, sym::non_exhaustive) { if let Some(non_exhaustive_attr) = attr::find_by_name(&variant.attrs, sym::non_exhaustive) {
cx.struct_span_err(variant.ident.span, "default variant must be exhaustive") cx.emit_err(errors::NonExhaustiveDefault {
.span_label(non_exhaustive_attr.span, "declared `#[non_exhaustive]` here") span: variant.ident.span,
.help("consider a manual implementation of `Default`") non_exhaustive: non_exhaustive_attr.span,
.emit(); });
return Err(()); return Err(());
} }
@ -199,35 +182,23 @@ fn validate_default_attribute(
"this method must only be called with a variant that has a `#[default]` attribute", "this method must only be called with a variant that has a `#[default]` attribute",
), ),
[first, rest @ ..] => { [first, rest @ ..] => {
let suggestion_text = let sugg = errors::MultipleDefaultAttrsSugg {
if rest.len() == 1 { "try removing this" } else { "try removing these" }; spans: rest.iter().map(|attr| attr.span).collect(),
};
cx.struct_span_err(default_variant.ident.span, "multiple `#[default]` attributes") cx.emit_err(errors::MultipleDefaultAttrs {
.note("only one `#[default]` attribute is needed") span: default_variant.ident.span,
.span_label(first.span, "`#[default]` used here") first: first.span,
.span_label(rest[0].span, "`#[default]` used again here") first_rest: rest[0].span,
.span_help(rest.iter().map(|attr| attr.span).collect::<Vec<_>>(), suggestion_text) rest: rest.iter().map(|attr| attr.span).collect::<Vec<_>>().into(),
// This would otherwise display the empty replacement, hence the otherwise only_one: rest.len() == 1,
// repetitive `.span_help` call above. sugg,
.tool_only_multipart_suggestion( });
suggestion_text,
rest.iter().map(|attr| (attr.span, String::new())).collect(),
Applicability::MachineApplicable,
)
.emit();
return Err(()); return Err(());
} }
}; };
if !attr.is_word() { if !attr.is_word() {
cx.struct_span_err(attr.span, "`#[default]` attribute does not accept a value") cx.emit_err(errors::DefaultHasArg { span: attr.span });
.span_suggestion_hidden(
attr.span,
"try using `#[default]`",
"#[default]",
Applicability::MaybeIncorrect,
)
.emit();
return Err(()); return Err(());
} }
@ -241,12 +212,7 @@ struct DetectNonVariantDefaultAttr<'a, 'b> {
impl<'a, 'b> rustc_ast::visit::Visitor<'a> for DetectNonVariantDefaultAttr<'a, 'b> { impl<'a, 'b> rustc_ast::visit::Visitor<'a> for DetectNonVariantDefaultAttr<'a, 'b> {
fn visit_attribute(&mut self, attr: &'a rustc_ast::Attribute) { fn visit_attribute(&mut self, attr: &'a rustc_ast::Attribute) {
if attr.has_name(kw::Default) { if attr.has_name(kw::Default) {
self.cx self.cx.emit_err(errors::NonUnitDefault { span: attr.span });
.struct_span_err(
attr.span,
"the `#[default]` attribute may only be used on unit enum variants",
)
.emit();
} }
rustc_ast::visit::walk_attribute(self, attr); rustc_ast::visit::walk_attribute(self, attr);

View File

@ -162,7 +162,7 @@
pub use StaticFields::*; pub use StaticFields::*;
pub use SubstructureFields::*; pub use SubstructureFields::*;
use crate::deriving; use crate::{deriving, errors};
use rustc_ast::ptr::P; use rustc_ast::ptr::P;
use rustc_ast::{ use rustc_ast::{
self as ast, BindingAnnotation, ByRef, EnumDef, Expr, GenericArg, GenericParamKind, Generics, self as ast, BindingAnnotation, ByRef, EnumDef, Expr, GenericArg, GenericParamKind, Generics,
@ -415,7 +415,7 @@ fn find_type_parameters(
} }
fn visit_mac_call(&mut self, mac: &ast::MacCall) { fn visit_mac_call(&mut self, mac: &ast::MacCall) {
self.cx.span_err(mac.span(), "`derive` cannot be used on items with type macros"); self.cx.emit_err(errors::DeriveMacroCall { span: mac.span() });
} }
} }
@ -488,7 +488,7 @@ impl<'a> TraitDef<'a> {
is_packed, is_packed,
) )
} else { } else {
cx.span_err(mitem.span, "this trait cannot be derived for unions"); cx.emit_err(errors::DeriveUnion { span: mitem.span });
return; return;
} }
} }

View File

@ -11,6 +11,8 @@ use rustc_span::Span;
use std::env; use std::env;
use thin_vec::thin_vec; use thin_vec::thin_vec;
use crate::errors;
pub fn expand_option_env<'cx>( pub fn expand_option_env<'cx>(
cx: &'cx mut ExtCtxt<'_>, cx: &'cx mut ExtCtxt<'_>,
sp: Span, sp: Span,
@ -54,7 +56,7 @@ pub fn expand_env<'cx>(
) -> Box<dyn base::MacResult + 'cx> { ) -> Box<dyn base::MacResult + 'cx> {
let mut exprs = match get_exprs_from_tts(cx, tts) { let mut exprs = match get_exprs_from_tts(cx, tts) {
Some(exprs) if exprs.is_empty() || exprs.len() > 2 => { Some(exprs) if exprs.is_empty() || exprs.len() > 2 => {
cx.span_err(sp, "env! takes 1 or 2 arguments"); cx.emit_err(errors::EnvTakesArgs { span: sp });
return DummyResult::any(sp); return DummyResult::any(sp);
} }
None => return DummyResult::any(sp), None => return DummyResult::any(sp),
@ -78,18 +80,12 @@ pub fn expand_env<'cx>(
cx.sess.parse_sess.env_depinfo.borrow_mut().insert((var, value)); cx.sess.parse_sess.env_depinfo.borrow_mut().insert((var, value));
let e = match value { let e = match value {
None => { None => {
let (msg, help) = match custom_msg { cx.emit_err(errors::EnvNotDefined {
None => ( span: sp,
format!("environment variable `{var}` not defined at compile time"), msg: custom_msg,
Some(help_for_missing_env_var(var.as_str())), var,
), help: custom_msg.is_none().then(|| help_for_missing_env_var(var.as_str())),
Some(s) => (s.to_string(), None), });
};
let mut diag = cx.struct_span_err(sp, &msg);
if let Some(help) = help {
diag.help(help);
}
diag.emit();
return DummyResult::any(sp); return DummyResult::any(sp);
} }
Some(value) => cx.expr_str(sp, value), Some(value) => cx.expr_str(sp, value),
@ -97,15 +93,13 @@ pub fn expand_env<'cx>(
MacEager::expr(e) MacEager::expr(e)
} }
fn help_for_missing_env_var(var: &str) -> String { fn help_for_missing_env_var(var: &str) -> errors::EnvNotDefinedHelp {
if var.starts_with("CARGO_") if var.starts_with("CARGO_")
|| var.starts_with("DEP_") || var.starts_with("DEP_")
|| matches!(var, "OUT_DIR" | "OPT_LEVEL" | "PROFILE" | "HOST" | "TARGET") || matches!(var, "OUT_DIR" | "OPT_LEVEL" | "PROFILE" | "HOST" | "TARGET")
{ {
format!( errors::EnvNotDefinedHelp::CargoVar
"Cargo sets build script variables at run time. Use `std::env::var(\"{var}\")` instead"
)
} else { } else {
format!("Use `std::env::var(\"{var}\")` to read the variable at run time") errors::EnvNotDefinedHelp::Other
} }
} }

View File

@ -0,0 +1,553 @@
use rustc_errors::{
AddToDiagnostic, EmissionGuarantee, IntoDiagnostic, MultiSpan, SingleLabelManySpans,
};
use rustc_macros::{Diagnostic, Subdiagnostic};
use rustc_span::{symbol::Ident, Span, Symbol};
#[derive(Diagnostic)]
#[diag(builtin_macros_requires_cfg_pattern)]
pub(crate) struct RequiresCfgPattern {
#[primary_span]
#[label]
pub(crate) span: Span,
}
#[derive(Diagnostic)]
#[diag(builtin_macros_expected_one_cfg_pattern)]
pub(crate) struct OneCfgPattern {
#[primary_span]
pub(crate) span: Span,
}
#[derive(Diagnostic)]
#[diag(builtin_macros_alloc_error_must_be_fn)]
pub(crate) struct AllocErrorMustBeFn {
#[primary_span]
pub(crate) span: Span,
}
#[derive(Diagnostic)]
#[diag(builtin_macros_assert_requires_boolean)]
pub(crate) struct AssertRequiresBoolean {
#[primary_span]
#[label]
pub(crate) span: Span,
}
#[derive(Diagnostic)]
#[diag(builtin_macros_assert_requires_expression)]
pub(crate) struct AssertRequiresExpression {
#[primary_span]
pub(crate) span: Span,
#[suggestion(code = "", applicability = "maybe-incorrect")]
pub(crate) token: Span,
}
#[derive(Diagnostic)]
#[diag(builtin_macros_assert_missing_comma)]
pub(crate) struct AssertMissingComma {
#[primary_span]
pub(crate) span: Span,
#[suggestion(code = ", ", applicability = "maybe-incorrect", style = "short")]
pub(crate) comma: Span,
}
#[derive(Diagnostic)]
pub(crate) enum CfgAccessibleInvalid {
#[diag(builtin_macros_cfg_accessible_unspecified_path)]
UnspecifiedPath(#[primary_span] Span),
#[diag(builtin_macros_cfg_accessible_multiple_paths)]
MultiplePaths(#[primary_span] Span),
#[diag(builtin_macros_cfg_accessible_literal_path)]
LiteralPath(#[primary_span] Span),
#[diag(builtin_macros_cfg_accessible_has_args)]
HasArguments(#[primary_span] Span),
}
#[derive(Diagnostic)]
#[diag(builtin_macros_cfg_accessible_indeterminate)]
pub(crate) struct CfgAccessibleIndeterminate {
#[primary_span]
pub(crate) span: Span,
}
#[derive(Diagnostic)]
#[diag(builtin_macros_concat_missing_literal)]
#[note]
pub(crate) struct ConcatMissingLiteral {
#[primary_span]
pub(crate) spans: Vec<Span>,
}
#[derive(Diagnostic)]
#[diag(builtin_macros_concat_bytestr)]
pub(crate) struct ConcatBytestr {
#[primary_span]
pub(crate) span: Span,
}
#[derive(Diagnostic)]
#[diag(builtin_macros_concat_bytes_invalid)]
pub(crate) struct ConcatBytesInvalid {
#[primary_span]
pub(crate) span: Span,
pub(crate) lit_kind: &'static str,
#[subdiagnostic]
pub(crate) sugg: Option<ConcatBytesInvalidSuggestion>,
}
#[derive(Subdiagnostic)]
pub(crate) enum ConcatBytesInvalidSuggestion {
#[suggestion(
builtin_macros_byte_char,
code = "b{snippet}",
applicability = "machine-applicable"
)]
CharLit {
#[primary_span]
span: Span,
snippet: String,
},
#[suggestion(
builtin_macros_byte_str,
code = "b{snippet}",
applicability = "machine-applicable"
)]
StrLit {
#[primary_span]
span: Span,
snippet: String,
},
#[suggestion(
builtin_macros_number_array,
code = "[{snippet}]",
applicability = "machine-applicable"
)]
IntLit {
#[primary_span]
span: Span,
snippet: String,
},
}
#[derive(Diagnostic)]
#[diag(builtin_macros_concat_bytes_oob)]
pub(crate) struct ConcatBytesOob {
#[primary_span]
pub(crate) span: Span,
}
#[derive(Diagnostic)]
#[diag(builtin_macros_concat_bytes_non_u8)]
pub(crate) struct ConcatBytesNonU8 {
#[primary_span]
pub(crate) span: Span,
}
#[derive(Diagnostic)]
#[diag(builtin_macros_concat_bytes_missing_literal)]
#[note]
pub(crate) struct ConcatBytesMissingLiteral {
#[primary_span]
pub(crate) spans: Vec<Span>,
}
#[derive(Diagnostic)]
#[diag(builtin_macros_concat_bytes_array)]
pub(crate) struct ConcatBytesArray {
#[primary_span]
pub(crate) span: Span,
#[note]
#[help]
pub(crate) bytestr: bool,
}
#[derive(Diagnostic)]
#[diag(builtin_macros_concat_bytes_bad_repeat)]
pub(crate) struct ConcatBytesBadRepeat {
#[primary_span]
pub(crate) span: Span,
}
#[derive(Diagnostic)]
#[diag(builtin_macros_concat_idents_missing_args)]
pub(crate) struct ConcatIdentsMissingArgs {
#[primary_span]
pub(crate) span: Span,
}
#[derive(Diagnostic)]
#[diag(builtin_macros_concat_idents_missing_comma)]
pub(crate) struct ConcatIdentsMissingComma {
#[primary_span]
pub(crate) span: Span,
}
#[derive(Diagnostic)]
#[diag(builtin_macros_concat_idents_ident_args)]
pub(crate) struct ConcatIdentsIdentArgs {
#[primary_span]
pub(crate) span: Span,
}
#[derive(Diagnostic)]
#[diag(builtin_macros_bad_derive_target, code = "E0774")]
pub(crate) struct BadDeriveTarget {
#[primary_span]
#[label]
pub(crate) span: Span,
#[label(builtin_macros_label2)]
pub(crate) item: Span,
}
#[derive(Diagnostic)]
#[diag(builtin_macros_unexpected_lit, code = "E0777")]
pub(crate) struct BadDeriveLit {
#[primary_span]
#[label]
pub(crate) span: Span,
#[subdiagnostic]
pub help: BadDeriveLitHelp,
}
#[derive(Subdiagnostic)]
pub(crate) enum BadDeriveLitHelp {
#[help(builtin_macros_str_lit)]
StrLit { sym: Symbol },
#[help(builtin_macros_other)]
Other,
}
#[derive(Diagnostic)]
#[diag(builtin_macros_derive_path_args_list)]
pub(crate) struct DerivePathArgsList {
#[suggestion(code = "", applicability = "machine-applicable")]
#[primary_span]
pub(crate) span: Span,
}
#[derive(Diagnostic)]
#[diag(builtin_macros_derive_path_args_value)]
pub(crate) struct DerivePathArgsValue {
#[suggestion(code = "", applicability = "machine-applicable")]
#[primary_span]
pub(crate) span: Span,
}
#[derive(Diagnostic)]
#[diag(builtin_macros_no_default_variant)]
#[help]
pub(crate) struct NoDefaultVariant {
#[primary_span]
pub(crate) span: Span,
#[subdiagnostic]
pub(crate) suggs: Vec<NoDefaultVariantSugg>,
}
#[derive(Subdiagnostic)]
#[suggestion(
builtin_macros_suggestion,
code = "#[default] {ident}",
applicability = "maybe-incorrect",
style = "tool-only"
)]
pub(crate) struct NoDefaultVariantSugg {
#[primary_span]
pub(crate) span: Span,
pub(crate) ident: Ident,
}
#[derive(Diagnostic)]
#[diag(builtin_macros_multiple_defaults)]
#[note]
pub(crate) struct MultipleDefaults {
#[primary_span]
pub(crate) span: Span,
#[label]
pub(crate) first: Span,
#[label(builtin_macros_additional)]
pub additional: Vec<Span>,
#[subdiagnostic]
pub suggs: Vec<MultipleDefaultsSugg>,
}
#[derive(Subdiagnostic)]
#[multipart_suggestion(
builtin_macros_suggestion,
applicability = "maybe-incorrect",
style = "tool-only"
)]
pub(crate) struct MultipleDefaultsSugg {
#[suggestion_part(code = "")]
pub(crate) spans: Vec<Span>,
pub(crate) ident: Ident,
}
#[derive(Diagnostic)]
#[diag(builtin_macros_non_unit_default)]
#[help]
pub(crate) struct NonUnitDefault {
#[primary_span]
pub(crate) span: Span,
}
#[derive(Diagnostic)]
#[diag(builtin_macros_non_exhaustive_default)]
#[help]
pub(crate) struct NonExhaustiveDefault {
#[primary_span]
pub(crate) span: Span,
#[label]
pub(crate) non_exhaustive: Span,
}
#[derive(Diagnostic)]
#[diag(builtin_macros_multiple_default_attrs)]
#[note]
pub(crate) struct MultipleDefaultAttrs {
#[primary_span]
pub(crate) span: Span,
#[label]
pub(crate) first: Span,
#[label(builtin_macros_label_again)]
pub(crate) first_rest: Span,
#[help]
pub(crate) rest: MultiSpan,
pub(crate) only_one: bool,
#[subdiagnostic]
pub(crate) sugg: MultipleDefaultAttrsSugg,
}
#[derive(Subdiagnostic)]
#[multipart_suggestion(
builtin_macros_help,
applicability = "machine-applicable",
style = "tool-only"
)]
pub(crate) struct MultipleDefaultAttrsSugg {
#[suggestion_part(code = "")]
pub(crate) spans: Vec<Span>,
}
#[derive(Diagnostic)]
#[diag(builtin_macros_default_arg)]
pub(crate) struct DefaultHasArg {
#[primary_span]
#[suggestion(code = "#[default]", style = "hidden", applicability = "maybe-incorrect")]
pub(crate) span: Span,
}
#[derive(Diagnostic)]
#[diag(builtin_macros_derive_macro_call)]
pub(crate) struct DeriveMacroCall {
#[primary_span]
pub(crate) span: Span,
}
#[derive(Diagnostic)]
#[diag(builtin_macros_cannot_derive_union)]
pub(crate) struct DeriveUnion {
#[primary_span]
pub(crate) span: Span,
}
#[derive(Diagnostic)]
#[diag(builtin_macros_env_takes_args)]
pub(crate) struct EnvTakesArgs {
#[primary_span]
pub(crate) span: Span,
}
//#[derive(Diagnostic)]
//#[diag(builtin_macros_env_not_defined)]
pub(crate) struct EnvNotDefined {
pub(crate) span: Span,
pub(crate) msg: Option<Symbol>,
pub(crate) var: Symbol,
pub(crate) help: Option<EnvNotDefinedHelp>,
}
// Hand-written implementation to support custom user messages
impl<'a, G: EmissionGuarantee> IntoDiagnostic<'a, G> for EnvNotDefined {
#[track_caller]
fn into_diagnostic(
self,
handler: &'a rustc_errors::Handler,
) -> rustc_errors::DiagnosticBuilder<'a, G> {
let mut diag = if let Some(msg) = self.msg {
handler.struct_diagnostic(msg.as_str())
} else {
handler.struct_diagnostic(crate::fluent_generated::builtin_macros_env_not_defined)
};
diag.set_arg("var", self.var);
diag.set_span(self.span);
if let Some(help) = self.help {
diag.subdiagnostic(help);
}
diag
}
}
#[derive(Subdiagnostic)]
pub(crate) enum EnvNotDefinedHelp {
#[help(builtin_macros_cargo)]
CargoVar,
#[help(builtin_macros_other)]
Other,
}
#[derive(Diagnostic)]
#[diag(builtin_macros_format_requires_string)]
pub(crate) struct FormatRequiresString {
#[primary_span]
pub(crate) span: Span,
}
#[derive(Diagnostic)]
#[diag(builtin_macros_format_duplicate_arg)]
pub(crate) struct FormatDuplicateArg {
#[primary_span]
pub(crate) span: Span,
#[label(builtin_macros_label1)]
pub(crate) prev: Span,
#[label(builtin_macros_label2)]
pub(crate) duplicate: Span,
pub(crate) ident: Ident,
}
#[derive(Diagnostic)]
#[diag(builtin_macros_format_positional_after_named)]
pub(crate) struct PositionalAfterNamed {
#[primary_span]
#[label]
pub(crate) span: Span,
#[label(builtin_macros_named_args)]
pub(crate) args: Vec<Span>,
}
#[derive(Diagnostic)]
#[diag(builtin_macros_format_string_invalid)]
pub(crate) struct InvalidFormatString {
#[primary_span]
#[label]
pub(crate) span: Span,
pub(crate) desc: String,
pub(crate) label1: String,
#[subdiagnostic]
pub(crate) note_: Option<InvalidFormatStringNote>,
#[subdiagnostic]
pub(crate) label_: Option<InvalidFormatStringLabel>,
#[subdiagnostic]
pub(crate) sugg_: Option<InvalidFormatStringSuggestion>,
}
#[derive(Subdiagnostic)]
#[note(builtin_macros_note)]
pub(crate) struct InvalidFormatStringNote {
pub(crate) note: String,
}
#[derive(Subdiagnostic)]
#[label(builtin_macros_second_label)]
pub(crate) struct InvalidFormatStringLabel {
#[primary_span]
pub(crate) span: Span,
pub(crate) label: String,
}
#[derive(Subdiagnostic)]
#[multipart_suggestion(
builtin_macros_sugg,
style = "verbose",
applicability = "machine-applicable"
)]
pub(crate) struct InvalidFormatStringSuggestion {
#[suggestion_part(code = "{len}")]
pub(crate) captured: Span,
pub(crate) len: String,
#[suggestion_part(code = ", {arg}")]
pub(crate) span: Span,
pub(crate) arg: String,
}
#[derive(Diagnostic)]
#[diag(builtin_macros_format_no_arg_named)]
#[note]
#[note(builtin_macros_note2)]
pub(crate) struct FormatNoArgNamed {
#[primary_span]
pub(crate) span: Span,
pub(crate) name: Symbol,
}
#[derive(Diagnostic)]
#[diag(builtin_macros_format_unknown_trait)]
#[note]
pub(crate) struct FormatUnknownTrait<'a> {
#[primary_span]
pub(crate) span: Span,
pub(crate) ty: &'a str,
#[subdiagnostic]
pub(crate) suggs: Vec<FormatUnknownTraitSugg>,
}
#[derive(Subdiagnostic)]
#[suggestion(
builtin_macros_suggestion,
code = "{fmt}",
style = "tool-only",
applicability = "maybe-incorrect"
)]
pub struct FormatUnknownTraitSugg {
#[primary_span]
pub span: Span,
pub fmt: &'static str,
pub trait_name: &'static str,
}
#[derive(Diagnostic)]
#[diag(builtin_macros_format_unused_arg)]
pub(crate) struct FormatUnusedArg {
#[primary_span]
#[label(builtin_macros_format_unused_arg)]
pub(crate) span: Span,
pub(crate) named: bool,
}
// Allow the singular form to be a subdiagnostic of the multiple-unused
// form of diagnostic.
impl AddToDiagnostic for FormatUnusedArg {
fn add_to_diagnostic_with<F>(self, diag: &mut rustc_errors::Diagnostic, f: F)
where
F: Fn(
&mut rustc_errors::Diagnostic,
rustc_errors::SubdiagnosticMessage,
) -> rustc_errors::SubdiagnosticMessage,
{
diag.set_arg("named", self.named);
let msg = f(diag, crate::fluent_generated::builtin_macros_format_unused_arg.into());
diag.span_label(self.span, msg);
}
}
#[derive(Diagnostic)]
#[diag(builtin_macros_format_unused_args)]
pub(crate) struct FormatUnusedArgs {
#[primary_span]
pub(crate) unused: Vec<Span>,
#[label]
pub(crate) fmt: Span,
#[subdiagnostic]
pub(crate) unused_labels: Vec<FormatUnusedArg>,
}
#[derive(Diagnostic)]
#[diag(builtin_macros_format_pos_mismatch)]
pub(crate) struct FormatPositionalMismatch {
#[primary_span]
pub(crate) span: MultiSpan,
pub(crate) n: usize,
pub(crate) desc: String,
#[subdiagnostic]
pub(crate) highlight: SingleLabelManySpans,
}

View File

@ -7,7 +7,7 @@ use rustc_ast::{
FormatDebugHex, FormatOptions, FormatPlaceholder, FormatSign, FormatTrait, FormatDebugHex, FormatOptions, FormatPlaceholder, FormatSign, FormatTrait,
}; };
use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::fx::FxHashSet;
use rustc_errors::{pluralize, Applicability, MultiSpan, PResult}; use rustc_errors::{Applicability, MultiSpan, PResult, SingleLabelManySpans};
use rustc_expand::base::{self, *}; use rustc_expand::base::{self, *};
use rustc_parse_format as parse; use rustc_parse_format as parse;
use rustc_span::symbol::{Ident, Symbol}; use rustc_span::symbol::{Ident, Symbol};
@ -36,6 +36,8 @@ enum PositionUsedAs {
} }
use PositionUsedAs::*; use PositionUsedAs::*;
use crate::errors;
struct MacroInput { struct MacroInput {
fmtstr: P<Expr>, fmtstr: P<Expr>,
args: FormatArguments, args: FormatArguments,
@ -66,7 +68,7 @@ fn parse_args<'a>(ecx: &mut ExtCtxt<'a>, sp: Span, tts: TokenStream) -> PResult<
let mut p = ecx.new_parser_from_tts(tts); let mut p = ecx.new_parser_from_tts(tts);
if p.token == token::Eof { if p.token == token::Eof {
return Err(ecx.struct_span_err(sp, "requires at least a format string argument")); return Err(ecx.create_err(errors::FormatRequiresString { span: sp }));
} }
let first_token = &p.token; let first_token = &p.token;
@ -121,13 +123,12 @@ fn parse_args<'a>(ecx: &mut ExtCtxt<'a>, sp: Span, tts: TokenStream) -> PResult<
p.expect(&token::Eq)?; p.expect(&token::Eq)?;
let expr = p.parse_expr()?; let expr = p.parse_expr()?;
if let Some((_, prev)) = args.by_name(ident.name) { if let Some((_, prev)) = args.by_name(ident.name) {
ecx.struct_span_err( ecx.emit_err(errors::FormatDuplicateArg {
ident.span, span: ident.span,
&format!("duplicate argument named `{}`", ident), prev: prev.kind.ident().unwrap().span,
) duplicate: ident.span,
.span_label(prev.kind.ident().unwrap().span, "previously here") ident,
.span_label(ident.span, "duplicate argument") });
.emit();
continue; continue;
} }
args.add(FormatArgument { kind: FormatArgumentKind::Named(ident), expr }); args.add(FormatArgument { kind: FormatArgumentKind::Named(ident), expr });
@ -135,20 +136,21 @@ fn parse_args<'a>(ecx: &mut ExtCtxt<'a>, sp: Span, tts: TokenStream) -> PResult<
_ => { _ => {
let expr = p.parse_expr()?; let expr = p.parse_expr()?;
if !args.named_args().is_empty() { if !args.named_args().is_empty() {
let mut err = ecx.struct_span_err( ecx.emit_err(errors::PositionalAfterNamed {
expr.span, span: expr.span,
"positional arguments cannot follow named arguments", args: args
); .named_args()
err.span_label( .iter()
expr.span, .filter_map(|a| {
"positional arguments must be before named arguments", if let Some(ident) = a.kind.ident() {
); Some((a, ident))
for arg in args.named_args() { } else {
if let Some(name) = arg.kind.ident() { None
err.span_label(name.span.to(arg.expr.span), "named argument"); }
} })
} .map(|(arg, n)| n.span.to(arg.expr.span))
err.emit(); .collect(),
});
} }
args.add(FormatArgument { kind: FormatArgumentKind::Normal, expr }); args.add(FormatArgument { kind: FormatArgumentKind::Normal, expr });
} }
@ -234,13 +236,19 @@ fn make_format_args(
// argument span here. // argument span here.
fmt_span fmt_span
}; };
let mut e = ecx.struct_span_err(sp, &format!("invalid format string: {}", err.description)); let mut e = errors::InvalidFormatString {
e.span_label(sp, err.label + " in format string"); span: sp,
note_: None,
label_: None,
sugg_: None,
desc: err.description,
label1: err.label,
};
if let Some(note) = err.note { if let Some(note) = err.note {
e.note(&note); e.note_ = Some(errors::InvalidFormatStringNote { note });
} }
if let Some((label, span)) = err.secondary_label && is_source_literal { if let Some((label, span)) = err.secondary_label && is_source_literal {
e.span_label(fmt_span.from_inner(InnerSpan::new(span.start, span.end)), label); e.label_ = Some(errors::InvalidFormatStringLabel { span: fmt_span.from_inner(InnerSpan::new(span.start, span.end)), label } );
} }
if err.should_be_replaced_with_positional_argument { if err.should_be_replaced_with_positional_argument {
let captured_arg_span = let captured_arg_span =
@ -250,17 +258,15 @@ fn make_format_args(
Some(arg) => arg.expr.span, Some(arg) => arg.expr.span,
None => fmt_span, None => fmt_span,
}; };
e.multipart_suggestion_verbose( e.sugg_ = Some(errors::InvalidFormatStringSuggestion {
"consider using a positional formatting argument instead", captured: captured_arg_span,
vec![ len: args.unnamed_args().len().to_string(),
(captured_arg_span, args.unnamed_args().len().to_string()), span: span.shrink_to_hi(),
(span.shrink_to_hi(), format!(", {}", arg)), arg,
], });
Applicability::MachineApplicable,
);
} }
} }
e.emit(); ecx.emit_err(e);
return Err(()); return Err(());
} }
@ -318,10 +324,7 @@ fn make_format_args(
} else { } else {
// For the moment capturing variables from format strings expanded from macros is // For the moment capturing variables from format strings expanded from macros is
// disabled (see RFC #2795) // disabled (see RFC #2795)
ecx.struct_span_err(span, &format!("there is no argument named `{name}`")) ecx.emit_err(errors::FormatNoArgNamed { span, name });
.note(format!("did you intend to capture a variable `{name}` from the surrounding scope?"))
.note("to avoid ambiguity, `format_args!` cannot capture variables when the format string is expanded from a macro")
.emit();
DummyResult::raw_expr(span, true) DummyResult::raw_expr(span, true)
}; };
Ok(args.add(FormatArgument { kind: FormatArgumentKind::Captured(ident), expr })) Ok(args.add(FormatArgument { kind: FormatArgumentKind::Captured(ident), expr }))
@ -475,12 +478,8 @@ fn make_format_args(
.enumerate() .enumerate()
.filter(|&(_, used)| !used) .filter(|&(_, used)| !used)
.map(|(i, _)| { .map(|(i, _)| {
let msg = if let FormatArgumentKind::Named(_) = args.explicit_args()[i].kind { let named = matches!(args.explicit_args()[i].kind, FormatArgumentKind::Named(_));
"named argument never used" (args.explicit_args()[i].expr.span, named)
} else {
"argument never used"
};
(args.explicit_args()[i].expr.span, msg)
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();
@ -531,22 +530,8 @@ fn invalid_placeholder_type_error(
fmt_span: Span, fmt_span: Span,
) { ) {
let sp = ty_span.map(|sp| fmt_span.from_inner(InnerSpan::new(sp.start, sp.end))); let sp = ty_span.map(|sp| fmt_span.from_inner(InnerSpan::new(sp.start, sp.end)));
let mut err = let suggs = if let Some(sp) = sp {
ecx.struct_span_err(sp.unwrap_or(fmt_span), &format!("unknown format trait `{}`", ty)); [
err.note(
"the only appropriate formatting traits are:\n\
- ``, which uses the `Display` trait\n\
- `?`, which uses the `Debug` trait\n\
- `e`, which uses the `LowerExp` trait\n\
- `E`, which uses the `UpperExp` trait\n\
- `o`, which uses the `Octal` trait\n\
- `p`, which uses the `Pointer` trait\n\
- `b`, which uses the `Binary` trait\n\
- `x`, which uses the `LowerHex` trait\n\
- `X`, which uses the `UpperHex` trait",
);
if let Some(sp) = sp {
for (fmt, name) in &[
("", "Display"), ("", "Display"),
("?", "Debug"), ("?", "Debug"),
("e", "LowerExp"), ("e", "LowerExp"),
@ -556,40 +541,38 @@ fn invalid_placeholder_type_error(
("b", "Binary"), ("b", "Binary"),
("x", "LowerHex"), ("x", "LowerHex"),
("X", "UpperHex"), ("X", "UpperHex"),
] { ]
err.tool_only_span_suggestion( .into_iter()
sp, .map(|(fmt, trait_name)| errors::FormatUnknownTraitSugg { span: sp, fmt, trait_name })
&format!("use the `{}` trait", name), .collect()
*fmt, } else {
Applicability::MaybeIncorrect, vec![]
); };
} ecx.emit_err(errors::FormatUnknownTrait { span: sp.unwrap_or(fmt_span), ty, suggs });
}
err.emit();
} }
fn report_missing_placeholders( fn report_missing_placeholders(
ecx: &mut ExtCtxt<'_>, ecx: &mut ExtCtxt<'_>,
unused: Vec<(Span, &str)>, unused: Vec<(Span, bool)>,
detect_foreign_fmt: bool, detect_foreign_fmt: bool,
str_style: Option<usize>, str_style: Option<usize>,
fmt_str: &str, fmt_str: &str,
fmt_span: Span, fmt_span: Span,
) { ) {
let mut diag = if let &[(span, msg)] = &unused[..] { let mut diag = if let &[(span, named)] = &unused[..] {
let mut diag = ecx.struct_span_err(span, msg); //let mut diag = ecx.struct_span_err(span, msg);
diag.span_label(span, msg); //diag.span_label(span, msg);
diag //diag
ecx.create_err(errors::FormatUnusedArg { span, named })
} else { } else {
let mut diag = ecx.struct_span_err( let unused_labels =
unused.iter().map(|&(sp, _)| sp).collect::<Vec<Span>>(), unused.iter().map(|&(span, named)| errors::FormatUnusedArg { span, named }).collect();
"multiple unused formatting arguments", let unused_spans = unused.iter().map(|&(span, _)| span).collect();
); ecx.create_err(errors::FormatUnusedArgs {
diag.span_label(fmt_span, "multiple missing formatting specifiers"); fmt: fmt_span,
for &(span, msg) in &unused { unused: unused_spans,
diag.span_label(span, msg); unused_labels,
} })
diag
}; };
// Used to ensure we only report translations for *one* kind of foreign format. // Used to ensure we only report translations for *one* kind of foreign format.
@ -768,18 +751,16 @@ fn report_invalid_references(
} else { } else {
MultiSpan::from_spans(spans) MultiSpan::from_spans(spans)
}; };
e = ecx.struct_span_err( e = ecx.create_err(errors::FormatPositionalMismatch {
span, span,
&format!( n: num_placeholders,
"{} positional argument{} in format string, but {}", desc: num_args_desc,
num_placeholders, highlight: SingleLabelManySpans {
pluralize!(num_placeholders), spans: args.explicit_args().iter().map(|arg| arg.expr.span).collect(),
num_args_desc, label: "",
), kind: rustc_errors::LabelKind::Label,
); },
for arg in args.explicit_args() { });
e.span_label(arg.expr.span, "");
}
// Point out `{:.*}` placeholders: those take an extra argument. // Point out `{:.*}` placeholders: those take an extra argument.
let mut has_precision_star = false; let mut has_precision_star = false;
for piece in template { for piece in template {

View File

@ -9,6 +9,7 @@
#![feature(if_let_guard)] #![feature(if_let_guard)]
#![feature(is_sorted)] #![feature(is_sorted)]
#![feature(let_chains)] #![feature(let_chains)]
#![feature(lint_reasons)]
#![feature(proc_macro_internals)] #![feature(proc_macro_internals)]
#![feature(proc_macro_quote)] #![feature(proc_macro_quote)]
#![recursion_limit = "256"] #![recursion_limit = "256"]
@ -39,6 +40,7 @@ mod derive;
mod deriving; mod deriving;
mod edition_panic; mod edition_panic;
mod env; mod env;
mod errors;
mod format; mod format;
mod format_foreign; mod format_foreign;
mod global_allocator; mod global_allocator;

View File

@ -880,6 +880,7 @@ impl Diagnostic {
/// ///
/// This is intended to be used for suggestions that are *very* obvious in what the changes /// This is intended to be used for suggestions that are *very* obvious in what the changes
/// need to be from the message, but we still want other tools to be able to apply them. /// need to be from the message, but we still want other tools to be able to apply them.
#[rustc_lint_diagnostics]
pub fn tool_only_span_suggestion( pub fn tool_only_span_suggestion(
&mut self, &mut self,
sp: Span, sp: Span,

View File

@ -1,4 +1,4 @@
use crate::fluent_generated as fluent; use crate::{fluent_generated as fluent, AddToDiagnostic};
use crate::{DiagnosticArgValue, DiagnosticBuilder, Handler, IntoDiagnostic, IntoDiagnosticArg}; use crate::{DiagnosticArgValue, DiagnosticBuilder, Handler, IntoDiagnostic, IntoDiagnosticArg};
use rustc_ast as ast; use rustc_ast as ast;
use rustc_ast_pretty::pprust; use rustc_ast_pretty::pprust;
@ -6,6 +6,7 @@ use rustc_hir as hir;
use rustc_lint_defs::Level; use rustc_lint_defs::Level;
use rustc_span::edition::Edition; use rustc_span::edition::Edition;
use rustc_span::symbol::{Ident, MacroRulesNormalizedIdent, Symbol}; use rustc_span::symbol::{Ident, MacroRulesNormalizedIdent, Symbol};
use rustc_span::Span;
use rustc_target::abi::TargetDataLayoutErrors; use rustc_target::abi::TargetDataLayoutErrors;
use rustc_target::spec::{PanicStrategy, SplitDebuginfo, StackProtector, TargetTriple}; use rustc_target::spec::{PanicStrategy, SplitDebuginfo, StackProtector, TargetTriple};
use rustc_type_ir as type_ir; use rustc_type_ir as type_ir;
@ -276,3 +277,26 @@ impl IntoDiagnostic<'_, !> for TargetDataLayoutErrors<'_> {
} }
} }
} }
/// Utility struct used to apply a single label while highlighting multiple spans
pub struct SingleLabelManySpans {
pub spans: Vec<Span>,
pub label: &'static str,
pub kind: LabelKind,
}
impl AddToDiagnostic for SingleLabelManySpans {
fn add_to_diagnostic_with<F>(self, diag: &mut crate::Diagnostic, _: F) {
match self.kind {
LabelKind::Note => diag.span_note(self.spans, self.label),
LabelKind::Label => diag.span_labels(self.spans, self.label),
LabelKind::Help => diag.span_help(self.spans, self.label),
};
}
}
/// The kind of label to attach when using [`SingleLabelManySpans`]
pub enum LabelKind {
Note,
Label,
Help,
}

View File

@ -383,7 +383,9 @@ pub use diagnostic::{
DiagnosticStyledString, IntoDiagnosticArg, SubDiagnostic, DiagnosticStyledString, IntoDiagnosticArg, SubDiagnostic,
}; };
pub use diagnostic_builder::{DiagnosticBuilder, EmissionGuarantee, Noted}; pub use diagnostic_builder::{DiagnosticBuilder, EmissionGuarantee, Noted};
pub use diagnostic_impls::{DiagnosticArgFromDisplay, DiagnosticSymbolList}; pub use diagnostic_impls::{
DiagnosticArgFromDisplay, DiagnosticSymbolList, LabelKind, SingleLabelManySpans,
};
use std::backtrace::Backtrace; use std::backtrace::Backtrace;
/// A handler deals with errors and other compiler output. /// A handler deals with errors and other compiler output.

View File

@ -392,14 +392,16 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> {
} }
SubdiagnosticKind::Note | SubdiagnosticKind::Help | SubdiagnosticKind::Warn => { SubdiagnosticKind::Note | SubdiagnosticKind::Help | SubdiagnosticKind::Warn => {
let inner = info.ty.inner_type(); let inner = info.ty.inner_type();
if type_matches_path(inner, &["rustc_span", "Span"]) { if type_matches_path(inner, &["rustc_span", "Span"])
|| type_matches_path(inner, &["rustc_span", "MultiSpan"])
{
Ok(self.add_spanned_subdiagnostic(binding, &fn_ident, slug)) Ok(self.add_spanned_subdiagnostic(binding, &fn_ident, slug))
} else if type_is_unit(inner) } else if type_is_unit(inner)
|| (matches!(info.ty, FieldInnerTy::Plain(_)) && type_is_bool(inner)) || (matches!(info.ty, FieldInnerTy::Plain(_)) && type_is_bool(inner))
{ {
Ok(self.add_subdiagnostic(&fn_ident, slug)) Ok(self.add_subdiagnostic(&fn_ident, slug))
} else { } else {
report_type_error(attr, "`Span`, `bool` or `()`")? report_type_error(attr, "`Span`, `MultiSpan`, `bool` or `()`")?
} }
} }
SubdiagnosticKind::Suggestion { SubdiagnosticKind::Suggestion {

View File

@ -518,7 +518,7 @@ struct BoolField {
#[help] #[help]
foo: bool, foo: bool,
#[help(no_crate_help)] #[help(no_crate_help)]
//~^ ERROR the `#[help(...)]` attribute can only be applied to fields of type `Span`, `bool` or `()` //~^ ERROR the `#[help(...)]` attribute can only be applied to fields of type
// only allow plain 'bool' fields // only allow plain 'bool' fields
bar: Option<bool>, bar: Option<bool>,
} }

View File

@ -323,7 +323,7 @@ error: invalid applicability
LL | #[suggestion(no_crate_suggestion, code = "...", applicability = "batman")] LL | #[suggestion(no_crate_suggestion, code = "...", applicability = "batman")]
| ^^^^^^^^ | ^^^^^^^^
error: the `#[help(...)]` attribute can only be applied to fields of type `Span`, `bool` or `()` error: the `#[help(...)]` attribute can only be applied to fields of type `Span`, `MultiSpan`, `bool` or `()`
--> $DIR/diagnostic-derive.rs:520:5 --> $DIR/diagnostic-derive.rs:520:5
| |
LL | #[help(no_crate_help)] LL | #[help(no_crate_help)]

View File

@ -1 +1 @@
fn main() { env!(); } //~ ERROR: env! takes 1 or 2 arguments fn main() { env!(); } //~ ERROR: `env!()` takes 1 or 2 arguments

View File

@ -1,4 +1,4 @@
error: env! takes 1 or 2 arguments error: `env!()` takes 1 or 2 arguments
--> $DIR/extenv-no-args.rs:1:13 --> $DIR/extenv-no-args.rs:1:13
| |
LL | fn main() { env!(); } LL | fn main() { env!(); }

View File

@ -1 +1 @@
fn main() { env!("one", "two", "three"); } //~ ERROR: env! takes 1 or 2 arguments fn main() { env!("one", "two", "three"); } //~ ERROR: `env!()` takes 1 or 2 arguments

View File

@ -1,4 +1,4 @@
error: env! takes 1 or 2 arguments error: `env!()` takes 1 or 2 arguments
--> $DIR/extenv-too-many-args.rs:1:13 --> $DIR/extenv-too-many-args.rs:1:13
| |
LL | fn main() { env!("one", "two", "three"); } LL | fn main() { env!("one", "two", "three"); }

View File

@ -4,7 +4,7 @@ error: environment variable `NON_EXISTENT` not defined at compile time
LL | include!(concat!(env!("NON_EXISTENT"), "/data.rs")); LL | include!(concat!(env!("NON_EXISTENT"), "/data.rs"));
| ^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^
| |
= help: Use `std::env::var("NON_EXISTENT")` to read the variable at run time = help: use `std::env::var("NON_EXISTENT")` to read the variable at run time
= note: this error originates in the macro `env` (in Nightly builds, run with -Z macro-backtrace for more info) = note: this error originates in the macro `env` (in Nightly builds, run with -Z macro-backtrace for more info)
error: suffixes on string literals are invalid error: suffixes on string literals are invalid

View File

@ -1,5 +1,5 @@
#![feature(concat_idents)] #![feature(concat_idents)]
fn main() { fn main() {
let x = concat_idents!(); //~ ERROR concat_idents! takes 1 or more arguments let x = concat_idents!(); //~ ERROR `concat_idents!()` takes 1 or more arguments
} }

View File

@ -1,4 +1,4 @@
error: concat_idents! takes 1 or more arguments error: `concat_idents!()` takes 1 or more arguments
--> $DIR/issue-50403.rs:4:13 --> $DIR/issue-50403.rs:4:13
| |
LL | let x = concat_idents!(); LL | let x = concat_idents!();

View File

@ -4,7 +4,7 @@ error: expected a byte literal
LL | concat_bytes!(pie); LL | concat_bytes!(pie);
| ^^^ | ^^^
| |
= note: only byte literals (like `b"foo"`, `b's'`, and `[3, 4, 5]`) can be passed to `concat_bytes!()` = note: only byte literals (like `b"foo"`, `b's'` and `[3, 4, 5]`) can be passed to `concat_bytes!()`
error: expected a byte literal error: expected a byte literal
--> $DIR/concat-bytes-error.rs:5:19 --> $DIR/concat-bytes-error.rs:5:19
@ -12,7 +12,7 @@ error: expected a byte literal
LL | concat_bytes!(pie, pie); LL | concat_bytes!(pie, pie);
| ^^^ ^^^ | ^^^ ^^^
| |
= note: only byte literals (like `b"foo"`, `b's'`, and `[3, 4, 5]`) can be passed to `concat_bytes!()` = note: only byte literals (like `b"foo"`, `b's'` and `[3, 4, 5]`) can be passed to `concat_bytes!()`
error: cannot concatenate string literals error: cannot concatenate string literals
--> $DIR/concat-bytes-error.rs:6:19 --> $DIR/concat-bytes-error.rs:6:19
@ -98,7 +98,7 @@ error: expected a byte literal
LL | -33, LL | -33,
| ^^^ | ^^^
| |
= note: only byte literals (like `b"foo"`, `b's'`, and `[3, 4, 5]`) can be passed to `concat_bytes!()` = note: only byte literals (like `b"foo"`, `b's'` and `[3, 4, 5]`) can be passed to `concat_bytes!()`
error: cannot concatenate doubly nested array error: cannot concatenate doubly nested array
--> $DIR/concat-bytes-error.rs:35:9 --> $DIR/concat-bytes-error.rs:35:9
@ -151,7 +151,7 @@ error: expected a byte literal
LL | concat_bytes!([pie; 2]); LL | concat_bytes!([pie; 2]);
| ^^^ | ^^^
| |
= note: only byte literals (like `b"foo"`, `b's'`, and `[3, 4, 5]`) can be passed to `concat_bytes!()` = note: only byte literals (like `b"foo"`, `b's'` and `[3, 4, 5]`) can be passed to `concat_bytes!()`
error: cannot concatenate float literals error: cannot concatenate float literals
--> $DIR/concat-bytes-error.rs:46:20 --> $DIR/concat-bytes-error.rs:46:20

View File

@ -3,36 +3,48 @@ error: the `#[default]` attribute may only be used on unit enum variants
| |
LL | #[default] LL | #[default]
| ^^^^^^^^^^ | ^^^^^^^^^^
|
= help: consider a manual implementation of `Default`
error: the `#[default]` attribute may only be used on unit enum variants error: the `#[default]` attribute may only be used on unit enum variants
--> $DIR/macros-nonfatal-errors.rs:18:36 --> $DIR/macros-nonfatal-errors.rs:18:36
| |
LL | struct DefaultInnerAttrTupleStruct(#[default] ()); LL | struct DefaultInnerAttrTupleStruct(#[default] ());
| ^^^^^^^^^^ | ^^^^^^^^^^
|
= help: consider a manual implementation of `Default`
error: the `#[default]` attribute may only be used on unit enum variants error: the `#[default]` attribute may only be used on unit enum variants
--> $DIR/macros-nonfatal-errors.rs:22:1 --> $DIR/macros-nonfatal-errors.rs:22:1
| |
LL | #[default] LL | #[default]
| ^^^^^^^^^^ | ^^^^^^^^^^
|
= help: consider a manual implementation of `Default`
error: the `#[default]` attribute may only be used on unit enum variants error: the `#[default]` attribute may only be used on unit enum variants
--> $DIR/macros-nonfatal-errors.rs:26:1 --> $DIR/macros-nonfatal-errors.rs:26:1
| |
LL | #[default] LL | #[default]
| ^^^^^^^^^^ | ^^^^^^^^^^
|
= help: consider a manual implementation of `Default`
error: the `#[default]` attribute may only be used on unit enum variants error: the `#[default]` attribute may only be used on unit enum variants
--> $DIR/macros-nonfatal-errors.rs:36:11 --> $DIR/macros-nonfatal-errors.rs:36:11
| |
LL | Foo = #[default] 0, LL | Foo = #[default] 0,
| ^^^^^^^^^^ | ^^^^^^^^^^
|
= help: consider a manual implementation of `Default`
error: the `#[default]` attribute may only be used on unit enum variants error: the `#[default]` attribute may only be used on unit enum variants
--> $DIR/macros-nonfatal-errors.rs:37:14 --> $DIR/macros-nonfatal-errors.rs:37:14
| |
LL | Bar([u8; #[default] 1]), LL | Bar([u8; #[default] 1]),
| ^^^^^^^^^^ | ^^^^^^^^^^
|
= help: consider a manual implementation of `Default`
error: no default declared error: no default declared
--> $DIR/macros-nonfatal-errors.rs:42:10 --> $DIR/macros-nonfatal-errors.rs:42:10
@ -132,7 +144,7 @@ error: asm template must be a string literal
LL | asm!(invalid); LL | asm!(invalid);
| ^^^^^^^ | ^^^^^^^
error: concat_idents! requires ident args error: `concat_idents!()` requires ident args
--> $DIR/macros-nonfatal-errors.rs:101:5 --> $DIR/macros-nonfatal-errors.rs:101:5
| |
LL | concat_idents!("not", "idents"); LL | concat_idents!("not", "idents");
@ -150,7 +162,7 @@ error: expected string literal
LL | env!(invalid); LL | env!(invalid);
| ^^^^^^^ | ^^^^^^^
error: env! takes 1 or 2 arguments error: `env!()` takes 1 or 2 arguments
--> $DIR/macros-nonfatal-errors.rs:105:5 --> $DIR/macros-nonfatal-errors.rs:105:5
| |
LL | env!(foo, abr, baz); LL | env!(foo, abr, baz);
@ -162,7 +174,7 @@ error: environment variable `RUST_HOPEFULLY_THIS_DOESNT_EXIST` not defined at co
LL | env!("RUST_HOPEFULLY_THIS_DOESNT_EXIST"); LL | env!("RUST_HOPEFULLY_THIS_DOESNT_EXIST");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| |
= help: Use `std::env::var("RUST_HOPEFULLY_THIS_DOESNT_EXIST")` to read the variable at run time = help: use `std::env::var("RUST_HOPEFULLY_THIS_DOESNT_EXIST")` to read the variable at run time
= note: this error originates in the macro `env` (in Nightly builds, run with -Z macro-backtrace for more info) = note: this error originates in the macro `env` (in Nightly builds, run with -Z macro-backtrace for more info)
error: format argument must be a string literal error: format argument must be a string literal