Auto merge of #110092 - clubby789:builtin-macros-translatable, r=compiler-errors

Migrate most of `rustc_builtin_macros` to diagnostic impls

cc #100717

This is a couple of days work, but I decided to stop for now before the PR becomes too big. There's around 50 unresolved failures when `rustc::untranslatable_diagnostic` is denied, which I'll finish addressing once this PR goes thtough

A couple of outputs have changed, but in all instances I think the changes are an improvement/are more consistent with other diagnostics (although I'm happy to revert any which seem worse)
This commit is contained in:
bors 2023-04-11 14:40:54 +00:00
commit 45749b21b7
31 changed files with 978 additions and 349 deletions

View File

@ -3,3 +3,149 @@ builtin_macros_requires_cfg_pattern =
.label = cfg-pattern required
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 rustc_ast::ptr::P;
@ -31,7 +32,7 @@ pub fn expand(
{
(item, true, ecx.with_def_site_ctxt(fn_kind.sig.span))
} 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];
};

View File

@ -1,12 +1,13 @@
mod context;
use crate::edition_panic::use_panic_2021;
use crate::errors;
use rustc_ast::ptr::P;
use rustc_ast::token;
use rustc_ast::tokenstream::{DelimSpan, TokenStream};
use rustc_ast::{DelimArgs, Expr, ExprKind, MacCall, MacDelimiter, Path, PathSegment, UnOp};
use rustc_ast_pretty::pprust;
use rustc_errors::{Applicability, PResult};
use rustc_errors::PResult;
use rustc_expand::base::{DummyResult, ExtCtxt, MacEager, MacResult};
use rustc_parse::parser::Parser;
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);
if parser.token == token::Eof {
let mut err = cx.struct_span_err(sp, "macro requires a boolean expression as an argument");
err.span_label(sp, "boolean expression required");
return Err(err);
return Err(cx.create_err(errors::AssertRequiresBoolean { span: sp }));
}
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.
if parser.token == token::Semi {
let mut err = cx.struct_span_err(sp, "macro requires an expression as an argument");
err.span_suggestion(
parser.token.span,
"try removing semicolon",
"",
Applicability::MaybeIncorrect,
);
err.emit();
cx.emit_err(errors::AssertRequiresExpression { span: sp, token: parser.token.span });
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.
let custom_message =
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_span = parser.prev_token.span.shrink_to_hi();
err.span_suggestion_short(
comma_span,
"try adding a comma",
", ",
Applicability::MaybeIncorrect,
);
err.emit();
let comma = parser.prev_token.span.shrink_to_hi();
cx.emit_err(errors::AssertMissingComma { span: parser.token.span, comma });
parse_custom_message(&mut parser)
} 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
//! current compilation environment.
use crate::errors;
use rustc_ast as ast;
use rustc_ast::token;
use rustc_ast::tokenstream::TokenStream;
use rustc_attr as attr;
use rustc_errors::PResult;
use rustc_expand::base::{self, *};
use rustc_macros::Diagnostic;
use rustc_span::Span;
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> {
let mut p = cx.new_parser_from_tts(tts);
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()?;
@ -62,7 +47,7 @@ fn parse_cfg<'a>(cx: &mut ExtCtxt<'a>, span: Span, tts: TokenStream) -> PResult<
let _ = p.eat(&token::Comma);
if !p.eat(&token::Eof) {
return Err(cx.create_err(OneCfgPattern { span }));
return Err(cx.create_err(errors::OneCfgPattern { span }));
}
Ok(cfg)

View File

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

View File

@ -13,6 +13,11 @@ pub fn expand_compile_error<'cx>(
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());
DummyResult::any(sp)

View File

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

View File

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

View File

@ -1,8 +1,8 @@
use crate::cfg_eval::cfg_eval;
use crate::errors;
use rustc_ast as ast;
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_feature::AttributeTemplate;
use rustc_parse::validate_attr;
@ -116,49 +116,33 @@ fn report_bad_target(sess: &Session, item: &Annotatable, span: Span) -> bool {
let bad_target =
!matches!(item_kind, Some(ItemKind::Struct(..) | ItemKind::Enum(..) | ItemKind::Union(..)));
if bad_target {
struct_span_err!(
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();
sess.emit_err(errors::BadDeriveTarget { span, item: item.span() });
}
bad_target
}
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)
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",)
.span_label(lit.span, "not a trait")
.help(&help_msg)
.emit();
sess.emit_err(errors::BadDeriveLit { span: lit.span, help });
}
fn report_path_args(sess: &Session, meta: &ast::MetaItem) {
let report_error = |title, action| {
let span = meta.span.with_lo(meta.path.span.hi());
sess.struct_span_err(span, title)
.span_suggestion(span, action, "", Applicability::MachineApplicable)
.emit();
};
let span = meta.span.with_lo(meta.path.span.hi());
match meta.kind {
MetaItemKind::Word => {}
MetaItemKind::List(..) => report_error(
"traits in `#[derive(...)]` don't accept arguments",
"remove the arguments",
),
MetaItemKind::List(..) => {
sess.emit_err(errors::DerivePathArgsList { span });
}
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::*;
use crate::errors;
use rustc_ast as ast;
use rustc_ast::{attr, walk_list, EnumDef, VariantData};
use rustc_errors::Applicability;
use rustc_expand::base::{Annotatable, DummyResult, ExtCtxt};
use rustc_span::symbol::Ident;
use rustc_span::symbol::{kw, sym};
@ -118,67 +118,50 @@ fn extract_default_variant<'a>(
.filter(|variant| matches!(variant.data, VariantData::Unit(..)))
.filter(|variant| !attr::contains_name(&variant.attrs, sym::non_exhaustive));
let mut diag = cx.struct_span_err(trait_span, "no default declared");
diag.help("make a unit variant default by placing `#[default]` above it");
for variant in possible_defaults {
// Suggest making each unit variant default.
diag.tool_only_span_suggestion(
variant.span,
&format!("make `{}` default", variant.ident),
format!("#[default] {}", variant.ident),
Applicability::MaybeIncorrect,
);
}
diag.emit();
let suggs = possible_defaults
.map(|v| errors::NoDefaultVariantSugg { span: v.span, ident: v.ident })
.collect();
cx.emit_err(errors::NoDefaultVariant { span: trait_span, suggs });
return Err(());
}
[first, rest @ ..] => {
let mut diag = cx.struct_span_err(trait_span, "multiple declared defaults");
diag.span_label(first.span, "first default");
diag.span_labels(rest.iter().map(|variant| variant.span), "additional default");
diag.note("only one variant can be default");
for variant in &default_variants {
// Suggest making each variant already tagged default.
let suggestion = default_variants
.iter()
.filter_map(|v| {
if v.span == variant.span {
None
} else {
Some((attr::find_by_name(&v.attrs, kw::Default)?.span, String::new()))
}
})
.collect();
diag.tool_only_multipart_suggestion(
&format!("make `{}` default", variant.ident),
suggestion,
Applicability::MaybeIncorrect,
);
}
diag.emit();
let suggs = default_variants
.iter()
.map(|variant| {
let spans = default_variants
.iter()
.filter_map(|v| {
if v.span == variant.span {
None
} else {
Some(attr::find_by_name(&v.attrs, kw::Default)?.span)
}
})
.collect();
errors::MultipleDefaultsSugg { spans, ident: variant.ident }
})
.collect();
cx.emit_err(errors::MultipleDefaults {
span: trait_span,
first: first.span,
additional: rest.iter().map(|v| v.span).collect(),
suggs,
});
return Err(());
}
};
if !matches!(variant.data, VariantData::Unit(..)) {
cx.struct_span_err(
variant.ident.span,
"the `#[default]` attribute may only be used on unit enum variants",
)
.help("consider a manual implementation of `Default`")
.emit();
cx.emit_err(errors::NonUnitDefault { span: variant.ident.span });
return Err(());
}
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")
.span_label(non_exhaustive_attr.span, "declared `#[non_exhaustive]` here")
.help("consider a manual implementation of `Default`")
.emit();
cx.emit_err(errors::NonExhaustiveDefault {
span: variant.ident.span,
non_exhaustive: non_exhaustive_attr.span,
});
return Err(());
}
@ -199,35 +182,23 @@ fn validate_default_attribute(
"this method must only be called with a variant that has a `#[default]` attribute",
),
[first, rest @ ..] => {
let suggestion_text =
if rest.len() == 1 { "try removing this" } else { "try removing these" };
cx.struct_span_err(default_variant.ident.span, "multiple `#[default]` attributes")
.note("only one `#[default]` attribute is needed")
.span_label(first.span, "`#[default]` used here")
.span_label(rest[0].span, "`#[default]` used again here")
.span_help(rest.iter().map(|attr| attr.span).collect::<Vec<_>>(), suggestion_text)
// This would otherwise display the empty replacement, hence the otherwise
// repetitive `.span_help` call above.
.tool_only_multipart_suggestion(
suggestion_text,
rest.iter().map(|attr| (attr.span, String::new())).collect(),
Applicability::MachineApplicable,
)
.emit();
let sugg = errors::MultipleDefaultAttrsSugg {
spans: rest.iter().map(|attr| attr.span).collect(),
};
cx.emit_err(errors::MultipleDefaultAttrs {
span: default_variant.ident.span,
first: first.span,
first_rest: rest[0].span,
rest: rest.iter().map(|attr| attr.span).collect::<Vec<_>>().into(),
only_one: rest.len() == 1,
sugg,
});
return Err(());
}
};
if !attr.is_word() {
cx.struct_span_err(attr.span, "`#[default]` attribute does not accept a value")
.span_suggestion_hidden(
attr.span,
"try using `#[default]`",
"#[default]",
Applicability::MaybeIncorrect,
)
.emit();
cx.emit_err(errors::DefaultHasArg { span: attr.span });
return Err(());
}
@ -241,12 +212,7 @@ struct 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) {
if attr.has_name(kw::Default) {
self.cx
.struct_span_err(
attr.span,
"the `#[default]` attribute may only be used on unit enum variants",
)
.emit();
self.cx.emit_err(errors::NonUnitDefault { span: attr.span });
}
rustc_ast::visit::walk_attribute(self, attr);

View File

@ -162,7 +162,7 @@
pub use StaticFields::*;
pub use SubstructureFields::*;
use crate::deriving;
use crate::{deriving, errors};
use rustc_ast::ptr::P;
use rustc_ast::{
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) {
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,
)
} else {
cx.span_err(mitem.span, "this trait cannot be derived for unions");
cx.emit_err(errors::DeriveUnion { span: mitem.span });
return;
}
}

View File

@ -11,6 +11,8 @@ use rustc_span::Span;
use std::env;
use thin_vec::thin_vec;
use crate::errors;
pub fn expand_option_env<'cx>(
cx: &'cx mut ExtCtxt<'_>,
sp: Span,
@ -54,7 +56,7 @@ pub fn expand_env<'cx>(
) -> Box<dyn base::MacResult + 'cx> {
let mut exprs = match get_exprs_from_tts(cx, tts) {
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);
}
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));
let e = match value {
None => {
let (msg, help) = match custom_msg {
None => (
format!("environment variable `{var}` not defined at compile time"),
Some(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();
cx.emit_err(errors::EnvNotDefined {
span: sp,
msg: custom_msg,
var,
help: custom_msg.is_none().then(|| help_for_missing_env_var(var.as_str())),
});
return DummyResult::any(sp);
}
Some(value) => cx.expr_str(sp, value),
@ -97,15 +93,13 @@ pub fn expand_env<'cx>(
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_")
|| var.starts_with("DEP_")
|| matches!(var, "OUT_DIR" | "OPT_LEVEL" | "PROFILE" | "HOST" | "TARGET")
{
format!(
"Cargo sets build script variables at run time. Use `std::env::var(\"{var}\")` instead"
)
errors::EnvNotDefinedHelp::CargoVar
} 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,
};
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_parse_format as parse;
use rustc_span::symbol::{Ident, Symbol};
@ -36,6 +36,8 @@ enum PositionUsedAs {
}
use PositionUsedAs::*;
use crate::errors;
struct MacroInput {
fmtstr: P<Expr>,
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);
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;
@ -121,13 +123,12 @@ fn parse_args<'a>(ecx: &mut ExtCtxt<'a>, sp: Span, tts: TokenStream) -> PResult<
p.expect(&token::Eq)?;
let expr = p.parse_expr()?;
if let Some((_, prev)) = args.by_name(ident.name) {
ecx.struct_span_err(
ident.span,
&format!("duplicate argument named `{}`", ident),
)
.span_label(prev.kind.ident().unwrap().span, "previously here")
.span_label(ident.span, "duplicate argument")
.emit();
ecx.emit_err(errors::FormatDuplicateArg {
span: ident.span,
prev: prev.kind.ident().unwrap().span,
duplicate: ident.span,
ident,
});
continue;
}
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()?;
if !args.named_args().is_empty() {
let mut err = ecx.struct_span_err(
expr.span,
"positional arguments cannot follow named arguments",
);
err.span_label(
expr.span,
"positional arguments must be before named arguments",
);
for arg in args.named_args() {
if let Some(name) = arg.kind.ident() {
err.span_label(name.span.to(arg.expr.span), "named argument");
}
}
err.emit();
ecx.emit_err(errors::PositionalAfterNamed {
span: expr.span,
args: args
.named_args()
.iter()
.filter_map(|a| {
if let Some(ident) = a.kind.ident() {
Some((a, ident))
} else {
None
}
})
.map(|(arg, n)| n.span.to(arg.expr.span))
.collect(),
});
}
args.add(FormatArgument { kind: FormatArgumentKind::Normal, expr });
}
@ -234,13 +236,19 @@ fn make_format_args(
// argument span here.
fmt_span
};
let mut e = ecx.struct_span_err(sp, &format!("invalid format string: {}", err.description));
e.span_label(sp, err.label + " in format string");
let mut e = errors::InvalidFormatString {
span: sp,
note_: None,
label_: None,
sugg_: None,
desc: err.description,
label1: err.label,
};
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 {
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 {
let captured_arg_span =
@ -250,17 +258,15 @@ fn make_format_args(
Some(arg) => arg.expr.span,
None => fmt_span,
};
e.multipart_suggestion_verbose(
"consider using a positional formatting argument instead",
vec![
(captured_arg_span, args.unnamed_args().len().to_string()),
(span.shrink_to_hi(), format!(", {}", arg)),
],
Applicability::MachineApplicable,
);
e.sugg_ = Some(errors::InvalidFormatStringSuggestion {
captured: captured_arg_span,
len: args.unnamed_args().len().to_string(),
span: span.shrink_to_hi(),
arg,
});
}
}
e.emit();
ecx.emit_err(e);
return Err(());
}
@ -318,10 +324,7 @@ fn make_format_args(
} else {
// For the moment capturing variables from format strings expanded from macros is
// disabled (see RFC #2795)
ecx.struct_span_err(span, &format!("there is no argument named `{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();
ecx.emit_err(errors::FormatNoArgNamed { span, name });
DummyResult::raw_expr(span, true)
};
Ok(args.add(FormatArgument { kind: FormatArgumentKind::Captured(ident), expr }))
@ -475,12 +478,8 @@ fn make_format_args(
.enumerate()
.filter(|&(_, used)| !used)
.map(|(i, _)| {
let msg = if let FormatArgumentKind::Named(_) = args.explicit_args()[i].kind {
"named argument never used"
} else {
"argument never used"
};
(args.explicit_args()[i].expr.span, msg)
let named = matches!(args.explicit_args()[i].kind, FormatArgumentKind::Named(_));
(args.explicit_args()[i].expr.span, named)
})
.collect::<Vec<_>>();
@ -531,22 +530,8 @@ fn invalid_placeholder_type_error(
fmt_span: Span,
) {
let sp = ty_span.map(|sp| fmt_span.from_inner(InnerSpan::new(sp.start, sp.end)));
let mut err =
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 &[
let suggs = if let Some(sp) = sp {
[
("", "Display"),
("?", "Debug"),
("e", "LowerExp"),
@ -556,40 +541,38 @@ fn invalid_placeholder_type_error(
("b", "Binary"),
("x", "LowerHex"),
("X", "UpperHex"),
] {
err.tool_only_span_suggestion(
sp,
&format!("use the `{}` trait", name),
*fmt,
Applicability::MaybeIncorrect,
);
}
}
err.emit();
]
.into_iter()
.map(|(fmt, trait_name)| errors::FormatUnknownTraitSugg { span: sp, fmt, trait_name })
.collect()
} else {
vec![]
};
ecx.emit_err(errors::FormatUnknownTrait { span: sp.unwrap_or(fmt_span), ty, suggs });
}
fn report_missing_placeholders(
ecx: &mut ExtCtxt<'_>,
unused: Vec<(Span, &str)>,
unused: Vec<(Span, bool)>,
detect_foreign_fmt: bool,
str_style: Option<usize>,
fmt_str: &str,
fmt_span: Span,
) {
let mut diag = if let &[(span, msg)] = &unused[..] {
let mut diag = ecx.struct_span_err(span, msg);
diag.span_label(span, msg);
diag
let mut diag = if let &[(span, named)] = &unused[..] {
//let mut diag = ecx.struct_span_err(span, msg);
//diag.span_label(span, msg);
//diag
ecx.create_err(errors::FormatUnusedArg { span, named })
} else {
let mut diag = ecx.struct_span_err(
unused.iter().map(|&(sp, _)| sp).collect::<Vec<Span>>(),
"multiple unused formatting arguments",
);
diag.span_label(fmt_span, "multiple missing formatting specifiers");
for &(span, msg) in &unused {
diag.span_label(span, msg);
}
diag
let unused_labels =
unused.iter().map(|&(span, named)| errors::FormatUnusedArg { span, named }).collect();
let unused_spans = unused.iter().map(|&(span, _)| span).collect();
ecx.create_err(errors::FormatUnusedArgs {
fmt: fmt_span,
unused: unused_spans,
unused_labels,
})
};
// Used to ensure we only report translations for *one* kind of foreign format.
@ -768,18 +751,16 @@ fn report_invalid_references(
} else {
MultiSpan::from_spans(spans)
};
e = ecx.struct_span_err(
e = ecx.create_err(errors::FormatPositionalMismatch {
span,
&format!(
"{} positional argument{} in format string, but {}",
num_placeholders,
pluralize!(num_placeholders),
num_args_desc,
),
);
for arg in args.explicit_args() {
e.span_label(arg.expr.span, "");
}
n: num_placeholders,
desc: num_args_desc,
highlight: SingleLabelManySpans {
spans: args.explicit_args().iter().map(|arg| arg.expr.span).collect(),
label: "",
kind: rustc_errors::LabelKind::Label,
},
});
// Point out `{:.*}` placeholders: those take an extra argument.
let mut has_precision_star = false;
for piece in template {

View File

@ -9,6 +9,7 @@
#![feature(if_let_guard)]
#![feature(is_sorted)]
#![feature(let_chains)]
#![feature(lint_reasons)]
#![feature(proc_macro_internals)]
#![feature(proc_macro_quote)]
#![recursion_limit = "256"]
@ -39,6 +40,7 @@ mod derive;
mod deriving;
mod edition_panic;
mod env;
mod errors;
mod format;
mod format_foreign;
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
/// 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(
&mut self,
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 rustc_ast as ast;
use rustc_ast_pretty::pprust;
@ -6,6 +6,7 @@ use rustc_hir as hir;
use rustc_lint_defs::Level;
use rustc_span::edition::Edition;
use rustc_span::symbol::{Ident, MacroRulesNormalizedIdent, Symbol};
use rustc_span::Span;
use rustc_target::abi::TargetDataLayoutErrors;
use rustc_target::spec::{PanicStrategy, SplitDebuginfo, StackProtector, TargetTriple};
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,
};
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;
/// 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 => {
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))
} else if type_is_unit(inner)
|| (matches!(info.ty, FieldInnerTy::Plain(_)) && type_is_bool(inner))
{
Ok(self.add_subdiagnostic(&fn_ident, slug))
} else {
report_type_error(attr, "`Span`, `bool` or `()`")?
report_type_error(attr, "`Span`, `MultiSpan`, `bool` or `()`")?
}
}
SubdiagnosticKind::Suggestion {

View File

@ -518,7 +518,7 @@ struct BoolField {
#[help]
foo: bool,
#[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
bar: Option<bool>,
}

View File

@ -323,7 +323,7 @@ error: invalid applicability
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
|
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
|
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
|
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"));
| ^^^^^^^^^^^^^^^^^^^^
|
= 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)
error: suffixes on string literals are invalid

View File

@ -1,5 +1,5 @@
#![feature(concat_idents)]
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
|
LL | let x = concat_idents!();

View File

@ -4,7 +4,7 @@ error: expected a byte literal
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
--> $DIR/concat-bytes-error.rs:5:19
@ -12,7 +12,7 @@ error: expected a byte literal
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
--> $DIR/concat-bytes-error.rs:6:19
@ -98,7 +98,7 @@ error: expected a byte literal
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
--> $DIR/concat-bytes-error.rs:35:9
@ -151,7 +151,7 @@ error: expected a byte literal
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
--> $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]
| ^^^^^^^^^^
|
= help: consider a manual implementation of `Default`
error: the `#[default]` attribute may only be used on unit enum variants
--> $DIR/macros-nonfatal-errors.rs:18:36
|
LL | struct DefaultInnerAttrTupleStruct(#[default] ());
| ^^^^^^^^^^
|
= help: consider a manual implementation of `Default`
error: the `#[default]` attribute may only be used on unit enum variants
--> $DIR/macros-nonfatal-errors.rs:22:1
|
LL | #[default]
| ^^^^^^^^^^
|
= help: consider a manual implementation of `Default`
error: the `#[default]` attribute may only be used on unit enum variants
--> $DIR/macros-nonfatal-errors.rs:26:1
|
LL | #[default]
| ^^^^^^^^^^
|
= help: consider a manual implementation of `Default`
error: the `#[default]` attribute may only be used on unit enum variants
--> $DIR/macros-nonfatal-errors.rs:36:11
|
LL | Foo = #[default] 0,
| ^^^^^^^^^^
|
= help: consider a manual implementation of `Default`
error: the `#[default]` attribute may only be used on unit enum variants
--> $DIR/macros-nonfatal-errors.rs:37:14
|
LL | Bar([u8; #[default] 1]),
| ^^^^^^^^^^
|
= help: consider a manual implementation of `Default`
error: no default declared
--> $DIR/macros-nonfatal-errors.rs:42:10
@ -132,7 +144,7 @@ error: asm template must be a string literal
LL | asm!(invalid);
| ^^^^^^^
error: concat_idents! requires ident args
error: `concat_idents!()` requires ident args
--> $DIR/macros-nonfatal-errors.rs:101:5
|
LL | concat_idents!("not", "idents");
@ -150,7 +162,7 @@ error: expected string literal
LL | env!(invalid);
| ^^^^^^^
error: env! takes 1 or 2 arguments
error: `env!()` takes 1 or 2 arguments
--> $DIR/macros-nonfatal-errors.rs:105:5
|
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");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= 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)
error: format argument must be a string literal