summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_builtin_macros
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:20:39 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:20:39 +0000
commit1376c5a617be5c25655d0d7cb63e3beaa5a6e026 (patch)
tree3bb8d61aee02bc7a15eab3f36e3b921afc2075d0 /compiler/rustc_builtin_macros
parentReleasing progress-linux version 1.69.0+dfsg1-1~progress7.99u1. (diff)
downloadrustc-1376c5a617be5c25655d0d7cb63e3beaa5a6e026.tar.xz
rustc-1376c5a617be5c25655d0d7cb63e3beaa5a6e026.zip
Merging upstream version 1.70.0+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'compiler/rustc_builtin_macros')
-rw-r--r--compiler/rustc_builtin_macros/locales/en-US.ftl5
-rw-r--r--compiler/rustc_builtin_macros/messages.ftl151
-rw-r--r--compiler/rustc_builtin_macros/src/alloc_error_handler.rs3
-rw-r--r--compiler/rustc_builtin_macros/src/asm.rs35
-rw-r--r--compiler/rustc_builtin_macros/src/assert.rs28
-rw-r--r--compiler/rustc_builtin_macros/src/assert/context.rs3
-rw-r--r--compiler/rustc_builtin_macros/src/cfg.rs21
-rw-r--r--compiler/rustc_builtin_macros/src/cfg_accessible.rs18
-rw-r--r--compiler/rustc_builtin_macros/src/cmdline_attrs.rs4
-rw-r--r--compiler/rustc_builtin_macros/src/compile_error.rs5
-rw-r--r--compiler/rustc_builtin_macros/src/concat.rs23
-rw-r--r--compiler/rustc_builtin_macros/src/concat_bytes.rs77
-rw-r--r--compiler/rustc_builtin_macros/src/concat_idents.rs8
-rw-r--r--compiler/rustc_builtin_macros/src/derive.rs40
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/default.rs134
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/generic/mod.rs7
-rw-r--r--compiler/rustc_builtin_macros/src/env.rs30
-rw-r--r--compiler/rustc_builtin_macros/src/errors.rs553
-rw-r--r--compiler/rustc_builtin_macros/src/format.rs254
-rw-r--r--compiler/rustc_builtin_macros/src/global_allocator.rs4
-rw-r--r--compiler/rustc_builtin_macros/src/lib.rs5
-rw-r--r--compiler/rustc_builtin_macros/src/proc_macro_harness.rs20
-rw-r--r--compiler/rustc_builtin_macros/src/standard_library_imports.rs19
-rw-r--r--compiler/rustc_builtin_macros/src/test.rs185
-rw-r--r--compiler/rustc_builtin_macros/src/test_harness.rs30
-rw-r--r--compiler/rustc_builtin_macros/src/util.rs4
26 files changed, 1152 insertions, 514 deletions
diff --git a/compiler/rustc_builtin_macros/locales/en-US.ftl b/compiler/rustc_builtin_macros/locales/en-US.ftl
deleted file mode 100644
index 4d088e27b..000000000
--- a/compiler/rustc_builtin_macros/locales/en-US.ftl
+++ /dev/null
@@ -1,5 +0,0 @@
-builtin_macros_requires_cfg_pattern =
- macro requires a cfg-pattern as an argument
- .label = cfg-pattern required
-
-builtin_macros_expected_one_cfg_pattern = expected 1 cfg-pattern
diff --git a/compiler/rustc_builtin_macros/messages.ftl b/compiler/rustc_builtin_macros/messages.ftl
new file mode 100644
index 000000000..83dc1ac50
--- /dev/null
+++ b/compiler/rustc_builtin_macros/messages.ftl
@@ -0,0 +1,151 @@
+builtin_macros_requires_cfg_pattern =
+ macro requires a cfg-pattern as an argument
+ .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}
diff --git a/compiler/rustc_builtin_macros/src/alloc_error_handler.rs b/compiler/rustc_builtin_macros/src/alloc_error_handler.rs
index ac6697232..82bae9157 100644
--- a/compiler/rustc_builtin_macros/src/alloc_error_handler.rs
+++ b/compiler/rustc_builtin_macros/src/alloc_error_handler.rs
@@ -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];
};
diff --git a/compiler/rustc_builtin_macros/src/asm.rs b/compiler/rustc_builtin_macros/src/asm.rs
index 3fdbc9715..8c1579baa 100644
--- a/compiler/rustc_builtin_macros/src/asm.rs
+++ b/compiler/rustc_builtin_macros/src/asm.rs
@@ -203,17 +203,6 @@ pub fn parse_asm_args<'a>(
// Validate the order of named, positional & explicit register operands and
// clobber_abi/options. We do this at the end once we have the full span
// of the argument available.
- if !args.options_spans.is_empty() {
- diag.struct_span_err(span, "arguments are not allowed after options")
- .span_labels(args.options_spans.clone(), "previous options")
- .span_label(span, "argument")
- .emit();
- } else if let Some((_, abi_span)) = args.clobber_abis.last() {
- diag.struct_span_err(span, "arguments are not allowed after clobber_abi")
- .span_label(*abi_span, "clobber_abi")
- .span_label(span, "argument")
- .emit();
- }
if explicit_reg {
if name.is_some() {
diag.struct_span_err(span, "explicit register arguments cannot have names").emit();
@@ -227,17 +216,6 @@ pub fn parse_asm_args<'a>(
.emit();
continue;
}
- if !args.reg_args.is_empty() {
- let mut err = diag.struct_span_err(
- span,
- "named arguments cannot follow explicit register arguments",
- );
- err.span_label(span, "named argument");
- for pos in &args.reg_args {
- err.span_label(args.operands[*pos].1, "explicit register argument");
- }
- err.emit();
- }
args.named_args.insert(name, slot);
} else {
if !args.named_args.is_empty() || !args.reg_args.is_empty() {
@@ -478,15 +456,6 @@ fn parse_clobber_abi<'a>(p: &mut Parser<'a>, args: &mut AsmArgs) -> PResult<'a,
let full_span = span_start.to(p.prev_token.span);
- if !args.options_spans.is_empty() {
- let mut err = p
- .sess
- .span_diagnostic
- .struct_span_err(full_span, "clobber_abi is not allowed after options");
- err.span_labels(args.options_spans.clone(), "options");
- return Err(err);
- }
-
match &new_abis[..] {
// should have errored above during parsing
[] => unreachable!(),
@@ -699,6 +668,10 @@ fn expand_preparsed_asm(ecx: &mut ExtCtxt<'_>, args: AsmArgs) -> Option<ast::Inl
args.operands[idx].1,
"explicit register arguments cannot be used in the asm template",
);
+ err.span_help(
+ args.operands[idx].1,
+ "use the register name directly in the assembly code",
+ );
}
err.emit();
None
diff --git a/compiler/rustc_builtin_macros/src/assert.rs b/compiler/rustc_builtin_macros/src/assert.rs
index 75af5e2b1..0de424be2 100644
--- a/compiler/rustc_builtin_macros/src/assert.rs
+++ b/compiler/rustc_builtin_macros/src/assert.rs
@@ -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) {
diff --git a/compiler/rustc_builtin_macros/src/assert/context.rs b/compiler/rustc_builtin_macros/src/assert/context.rs
index b0b4dda16..c9e3cd486 100644
--- a/compiler/rustc_builtin_macros/src/assert/context.rs
+++ b/compiler/rustc_builtin_macros/src/assert/context.rs
@@ -287,10 +287,9 @@ impl<'cx, 'a> Context<'cx, 'a> {
// sync with the `rfc-2011-nicer-assert-messages/all-expr-kinds.rs` test.
ExprKind::Assign(_, _, _)
| ExprKind::AssignOp(_, _, _)
- | ExprKind::Async(_, _, _)
+ | ExprKind::Async(_, _)
| ExprKind::Await(_)
| ExprKind::Block(_, _)
- | ExprKind::Box(_)
| ExprKind::Break(_, _)
| ExprKind::Closure(_)
| ExprKind::ConstBlock(_)
diff --git a/compiler/rustc_builtin_macros/src/cfg.rs b/compiler/rustc_builtin_macros/src/cfg.rs
index 5638c2f61..1397cee7a 100644
--- a/compiler/rustc_builtin_macros/src/cfg.rs
+++ b/compiler/rustc_builtin_macros/src/cfg.rs
@@ -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)
diff --git a/compiler/rustc_builtin_macros/src/cfg_accessible.rs b/compiler/rustc_builtin_macros/src/cfg_accessible.rs
index 4e4cafc71..37ac09ccd 100644
--- a/compiler/rustc_builtin_macros/src/cfg_accessible.rs
+++ b/compiler/rustc_builtin_macros/src/cfg_accessible.rs
@@ -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),
diff --git a/compiler/rustc_builtin_macros/src/cmdline_attrs.rs b/compiler/rustc_builtin_macros/src/cmdline_attrs.rs
index db05c00d2..2b6fcc169 100644
--- a/compiler/rustc_builtin_macros/src/cmdline_attrs.rs
+++ b/compiler/rustc_builtin_macros/src/cmdline_attrs.rs
@@ -6,7 +6,7 @@ use rustc_ast::{self as ast, AttrItem, AttrStyle};
use rustc_session::parse::ParseSess;
use rustc_span::FileName;
-pub fn inject(mut krate: ast::Crate, parse_sess: &ParseSess, attrs: &[String]) -> ast::Crate {
+pub fn inject(krate: &mut ast::Crate, parse_sess: &ParseSess, attrs: &[String]) {
for raw_attr in attrs {
let mut parser = rustc_parse::new_parser_from_source_str(
parse_sess,
@@ -36,6 +36,4 @@ pub fn inject(mut krate: ast::Crate, parse_sess: &ParseSess, attrs: &[String]) -
start_span.to(end_span),
));
}
-
- krate
}
diff --git a/compiler/rustc_builtin_macros/src/compile_error.rs b/compiler/rustc_builtin_macros/src/compile_error.rs
index 72397aa25..aeb3bb800 100644
--- a/compiler/rustc_builtin_macros/src/compile_error.rs
+++ b/compiler/rustc_builtin_macros/src/compile_error.rs
@@ -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)
diff --git a/compiler/rustc_builtin_macros/src/concat.rs b/compiler/rustc_builtin_macros/src/concat.rs
index 7da9bdc38..b92964d03 100644
--- a/compiler/rustc_builtin_macros/src/concat.rs
+++ b/compiler/rustc_builtin_macros/src/concat.rs
@@ -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) => {
@@ -42,8 +44,20 @@ pub fn expand_concat(
has_errors = true;
}
},
+ // We also want to allow negative numeric literals.
+ ast::ExprKind::Unary(ast::UnOp::Neg, ref expr) if let ast::ExprKind::Lit(token_lit) = expr.kind => {
+ match ast::LitKind::from_token_lit(token_lit) {
+ Ok(ast::LitKind::Int(i, _)) => accumulator.push_str(&format!("-{i}")),
+ Ok(ast::LitKind::Float(f, _)) => accumulator.push_str(&format!("-{f}")),
+ Err(err) => {
+ report_lit_error(&cx.sess.parse_sess, err, token_lit, e.span);
+ has_errors = true;
+ }
+ _ => missing_literal.push(e.span),
+ }
+ }
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;
@@ -53,10 +67,9 @@ 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);
diff --git a/compiler/rustc_builtin_macros/src/concat_bytes.rs b/compiler/rustc_builtin_macros/src/concat_bytes.rs
index 4f1a7d709..ba639c0a9 100644
--- a/compiler/rustc_builtin_macros/src/concat_bytes.rs
+++ b/compiler/rustc_builtin_macros/src/concat_bytes.rs
@@ -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));
diff --git a/compiler/rustc_builtin_macros/src/concat_idents.rs b/compiler/rustc_builtin_macros/src/concat_idents.rs
index 297c604e0..8c737f043 100644
--- a/compiler/rustc_builtin_macros/src/concat_idents.rs
+++ b/compiler/rustc_builtin_macros/src/concat_idents.rs
@@ -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);
}
}
diff --git a/compiler/rustc_builtin_macros/src/derive.rs b/compiler/rustc_builtin_macros/src/derive.rs
index 2a8dc0284..fe4483104 100644
--- a/compiler/rustc_builtin_macros/src/derive.rs
+++ b/compiler/rustc_builtin_macros/src/derive.rs
@@ -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 });
}
}
}
diff --git a/compiler/rustc_builtin_macros/src/deriving/default.rs b/compiler/rustc_builtin_macros/src/deriving/default.rs
index 4d753a2ed..33fe98b40 100644
--- a/compiler/rustc_builtin_macros/src/deriving/default.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/default.rs
@@ -1,8 +1,8 @@
use crate::deriving::generic::ty::*;
use crate::deriving::generic::*;
+use crate::errors;
use rustc_ast as ast;
-use rustc_ast::{walk_list, EnumDef, VariantData};
-use rustc_errors::Applicability;
+use rustc_ast::{attr, walk_list, EnumDef, VariantData};
use rustc_expand::base::{Annotatable, DummyResult, ExtCtxt};
use rustc_span::symbol::Ident;
use rustc_span::symbol::{kw, sym};
@@ -106,7 +106,7 @@ fn extract_default_variant<'a>(
let default_variants: SmallVec<[_; 1]> = enum_def
.variants
.iter()
- .filter(|variant| cx.sess.contains_name(&variant.attrs, kw::Default))
+ .filter(|variant| attr::contains_name(&variant.attrs, kw::Default))
.collect();
let variant = match default_variants.as_slice() {
@@ -116,69 +116,52 @@ fn extract_default_variant<'a>(
.variants
.iter()
.filter(|variant| matches!(variant.data, VariantData::Unit(..)))
- .filter(|variant| !cx.sess.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");
- 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((cx.sess.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) = cx.sess.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();
+ if let Some(non_exhaustive_attr) = attr::find_by_name(&variant.attrs, sym::non_exhaustive) {
+ cx.emit_err(errors::NonExhaustiveDefault {
+ span: variant.ident.span,
+ non_exhaustive: non_exhaustive_attr.span,
+ });
return Err(());
}
@@ -191,7 +174,7 @@ fn validate_default_attribute(
default_variant: &rustc_ast::Variant,
) -> Result<(), ()> {
let attrs: SmallVec<[_; 1]> =
- cx.sess.filter_by_name(&default_variant.attrs, kw::Default).collect();
+ attr::filter_by_name(&default_variant.attrs, kw::Default).collect();
let attr = match attrs.as_slice() {
[attr] => attr,
@@ -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);
diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs
index 1f819beeb..e5a003315 100644
--- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs
@@ -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;
}
}
@@ -1052,6 +1052,7 @@ impl<'a> MethodDef<'a> {
/// ::core::hash::Hash::hash(&{ self.y }, state)
/// }
/// }
+ /// ```
fn expand_struct_method_body<'b>(
&self,
cx: &mut ExtCtxt<'_>,
diff --git a/compiler/rustc_builtin_macros/src/env.rs b/compiler/rustc_builtin_macros/src/env.rs
index f011cb754..58c972738 100644
--- a/compiler/rustc_builtin_macros/src/env.rs
+++ b/compiler/rustc_builtin_macros/src/env.rs
@@ -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
}
}
diff --git a/compiler/rustc_builtin_macros/src/errors.rs b/compiler/rustc_builtin_macros/src/errors.rs
new file mode 100644
index 000000000..630f9b87b
--- /dev/null
+++ b/compiler/rustc_builtin_macros/src/errors.rs
@@ -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,
+}
diff --git a/compiler/rustc_builtin_macros/src/format.rs b/compiler/rustc_builtin_macros/src/format.rs
index e93a23394..f0fc61d7c 100644
--- a/compiler/rustc_builtin_macros/src/format.rs
+++ b/compiler/rustc_builtin_macros/src/format.rs
@@ -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,23 @@ enum PositionUsedAs {
}
use PositionUsedAs::*;
+use crate::errors;
+
+struct MacroInput {
+ fmtstr: P<Expr>,
+ args: FormatArguments,
+ /// Whether the first argument was a string literal or a result from eager macro expansion.
+ /// If it's not a string literal, we disallow implicit argument capturing.
+ ///
+ /// This does not correspond to whether we can treat spans to the literal normally, as the whole
+ /// invocation might be the result of another macro expansion, in which case this flag may still be true.
+ ///
+ /// See [RFC 2795] for more information.
+ ///
+ /// [RFC 2795]: https://rust-lang.github.io/rfcs/2795-format-args-implicit-identifiers.html#macro-hygiene
+ is_direct_literal: bool,
+}
+
/// Parses the arguments from the given list of tokens, returning the diagnostic
/// if there's a parse error so we can continue parsing other format!
/// expressions.
@@ -45,39 +62,31 @@ use PositionUsedAs::*;
/// ```text
/// Ok((fmtstr, parsed arguments))
/// ```
-fn parse_args<'a>(
- ecx: &mut ExtCtxt<'a>,
- sp: Span,
- tts: TokenStream,
-) -> PResult<'a, (P<Expr>, FormatArguments)> {
+fn parse_args<'a>(ecx: &mut ExtCtxt<'a>, sp: Span, tts: TokenStream) -> PResult<'a, MacroInput> {
let mut args = FormatArguments::new();
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;
- let fmtstr = match first_token.kind {
- token::TokenKind::Literal(token::Lit {
- kind: token::LitKind::Str | token::LitKind::StrRaw(_),
- ..
- }) => {
- // If the first token is a string literal, then a format expression
- // is constructed from it.
- //
- // This allows us to properly handle cases when the first comma
- // after the format string is mistakenly replaced with any operator,
- // which cause the expression parser to eat too much tokens.
- p.parse_literal_maybe_minus()?
- }
- _ => {
- // Otherwise, we fall back to the expression parser.
- p.parse_expr()?
- }
+
+ let fmtstr = if let token::Literal(lit) = first_token.kind && matches!(lit.kind, token::Str | token::StrRaw(_)) {
+ // This allows us to properly handle cases when the first comma
+ // after the format string is mistakenly replaced with any operator,
+ // which cause the expression parser to eat too much tokens.
+ p.parse_literal_maybe_minus()?
+ } else {
+ // Otherwise, we fall back to the expression parser.
+ p.parse_expr()?
};
+ // Only allow implicit captures to be used when the argument is a direct literal
+ // instead of a macro expanding to one.
+ let is_direct_literal = matches!(fmtstr.kind, ExprKind::Lit(_));
+
let mut first = true;
while p.token != token::Eof {
@@ -114,13 +123,12 @@ fn parse_args<'a>(
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 });
@@ -128,36 +136,39 @@ fn parse_args<'a>(
_ => {
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 });
}
}
}
- Ok((fmtstr, args))
+ Ok(MacroInput { fmtstr, args, is_direct_literal })
}
-pub fn make_format_args(
+fn make_format_args(
ecx: &mut ExtCtxt<'_>,
- efmt: P<Expr>,
- mut args: FormatArguments,
+ input: MacroInput,
append_newline: bool,
) -> Result<FormatArgs, ()> {
let msg = "format argument must be a string literal";
- let unexpanded_fmt_span = efmt.span;
+ let unexpanded_fmt_span = input.fmtstr.span;
+
+ let MacroInput { fmtstr: efmt, mut args, is_direct_literal } = input;
+
let (fmt_str, fmt_style, fmt_span) = match expr_to_spanned_string(ecx, efmt, msg) {
Ok(mut fmt) if append_newline => {
fmt.0 = Symbol::intern(&format!("{}\n", fmt.0));
@@ -208,11 +219,11 @@ pub fn make_format_args(
}
}
- let is_literal = parser.is_literal;
+ let is_source_literal = parser.is_source_literal;
if !parser.errors.is_empty() {
let err = parser.errors.remove(0);
- let sp = if is_literal {
+ let sp = if is_source_literal {
fmt_span.from_inner(InnerSpan::new(err.span.start, err.span.end))
} else {
// The format string could be another macro invocation, e.g.:
@@ -225,13 +236,19 @@ pub 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_literal {
- e.span_label(fmt_span.from_inner(InnerSpan::new(span.start, span.end)), label);
+ if let Some((label, span)) = err.secondary_label && is_source_literal {
+ 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 =
@@ -241,22 +258,20 @@ pub 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(());
}
let to_span = |inner_span: rustc_parse_format::InnerSpan| {
- is_literal.then(|| {
+ is_source_literal.then(|| {
fmt_span.from_inner(InnerSpan { start: inner_span.start, end: inner_span.end })
})
};
@@ -304,15 +319,12 @@ pub fn make_format_args(
// Name not found in `args`, so we add it as an implicitly captured argument.
let span = span.unwrap_or(fmt_span);
let ident = Ident::new(name, span);
- let expr = if is_literal {
+ let expr = if is_direct_literal {
ecx.expr_ident(span, ident)
} 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 }))
@@ -466,12 +478,8 @@ pub 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<_>>();
@@ -522,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"),
@@ -547,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.
@@ -759,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 {
@@ -814,7 +804,7 @@ fn report_invalid_references(
// for `println!("{7:7$}", 1);`
indexes.sort();
indexes.dedup();
- let span: MultiSpan = if !parser.is_literal || parser.arg_places.is_empty() {
+ let span: MultiSpan = if !parser.is_source_literal || parser.arg_places.is_empty() {
MultiSpan::from_span(fmt_span)
} else {
MultiSpan::from_spans(invalid_refs.iter().filter_map(|&(_, span, _, _)| span).collect())
@@ -855,8 +845,8 @@ fn expand_format_args_impl<'cx>(
) -> Box<dyn base::MacResult + 'cx> {
sp = ecx.with_def_site_ctxt(sp);
match parse_args(ecx, sp, tts) {
- Ok((efmt, args)) => {
- if let Ok(format_args) = make_format_args(ecx, efmt, args, nl) {
+ Ok(input) => {
+ if let Ok(format_args) = make_format_args(ecx, input, nl) {
MacEager::expr(ecx.expr(sp, ExprKind::FormatArgs(P(format_args))))
} else {
MacEager::expr(DummyResult::raw_expr(sp, true))
diff --git a/compiler/rustc_builtin_macros/src/global_allocator.rs b/compiler/rustc_builtin_macros/src/global_allocator.rs
index 41b51bae7..866cc5adb 100644
--- a/compiler/rustc_builtin_macros/src/global_allocator.rs
+++ b/compiler/rustc_builtin_macros/src/global_allocator.rs
@@ -25,12 +25,12 @@ pub fn expand(
// FIXME - if we get deref patterns, use them to reduce duplication here
let (item, is_stmt, ty_span) =
if let Annotatable::Item(item) = &item
- && let ItemKind::Static(ty, ..) = &item.kind
+ && let ItemKind::Static(box ast::StaticItem { ty, ..}) = &item.kind
{
(item, false, ecx.with_def_site_ctxt(ty.span))
} else if let Annotatable::Stmt(stmt) = &item
&& let StmtKind::Item(item) = &stmt.kind
- && let ItemKind::Static(ty, ..) = &item.kind
+ && let ItemKind::Static(box ast::StaticItem { ty, ..}) = &item.kind
{
(item, true, ecx.with_def_site_ctxt(ty.span))
} else {
diff --git a/compiler/rustc_builtin_macros/src/lib.rs b/compiler/rustc_builtin_macros/src/lib.rs
index 8afb6e560..37fbd03a6 100644
--- a/compiler/rustc_builtin_macros/src/lib.rs
+++ b/compiler/rustc_builtin_macros/src/lib.rs
@@ -7,9 +7,9 @@
#![feature(box_patterns)]
#![feature(decl_macro)]
#![feature(if_let_guard)]
-#![feature(is_some_and)]
#![feature(is_sorted)]
#![feature(let_chains)]
+#![feature(lint_reasons)]
#![feature(proc_macro_internals)]
#![feature(proc_macro_quote)]
#![recursion_limit = "256"]
@@ -40,6 +40,7 @@ mod derive;
mod deriving;
mod edition_panic;
mod env;
+mod errors;
mod format;
mod format_foreign;
mod global_allocator;
@@ -56,7 +57,7 @@ pub mod proc_macro_harness;
pub mod standard_library_imports;
pub mod test_harness;
-fluent_messages! { "../locales/en-US.ftl" }
+fluent_messages! { "../messages.ftl" }
pub fn register_builtin_macros(resolver: &mut dyn ResolverExpand) {
let mut register = |name, kind| resolver.register_builtin_macro(name, kind);
diff --git a/compiler/rustc_builtin_macros/src/proc_macro_harness.rs b/compiler/rustc_builtin_macros/src/proc_macro_harness.rs
index bc513607d..378d5f39f 100644
--- a/compiler/rustc_builtin_macros/src/proc_macro_harness.rs
+++ b/compiler/rustc_builtin_macros/src/proc_macro_harness.rs
@@ -1,6 +1,6 @@
use rustc_ast::ptr::P;
use rustc_ast::visit::{self, Visitor};
-use rustc_ast::{self as ast, NodeId};
+use rustc_ast::{self as ast, attr, NodeId};
use rustc_ast_pretty::pprust;
use rustc_expand::base::{parse_macro_name_and_helper_attrs, ExtCtxt, ResolverExpand};
use rustc_expand::expand::{AstFragment, ExpansionConfig};
@@ -34,7 +34,6 @@ enum ProcMacro {
}
struct CollectProcMacros<'a> {
- sess: &'a Session,
macros: Vec<ProcMacro>,
in_root: bool,
handler: &'a rustc_errors::Handler,
@@ -44,19 +43,18 @@ struct CollectProcMacros<'a> {
}
pub fn inject(
+ krate: &mut ast::Crate,
sess: &Session,
resolver: &mut dyn ResolverExpand,
- mut krate: ast::Crate,
is_proc_macro_crate: bool,
has_proc_macro_decls: bool,
is_test_crate: bool,
handler: &rustc_errors::Handler,
-) -> ast::Crate {
+) {
let ecfg = ExpansionConfig::default("proc_macro".to_string());
let mut cx = ExtCtxt::new(sess, ecfg, resolver, None);
let mut collect = CollectProcMacros {
- sess,
macros: Vec::new(),
in_root: true,
handler,
@@ -66,22 +64,20 @@ pub fn inject(
};
if has_proc_macro_decls || is_proc_macro_crate {
- visit::walk_crate(&mut collect, &krate);
+ visit::walk_crate(&mut collect, krate);
}
let macros = collect.macros;
if !is_proc_macro_crate {
- return krate;
+ return;
}
if is_test_crate {
- return krate;
+ return;
}
let decls = mk_decls(&mut cx, &macros);
krate.items.push(decls);
-
- krate
}
impl<'a> CollectProcMacros<'a> {
@@ -160,7 +156,7 @@ impl<'a> CollectProcMacros<'a> {
impl<'a> Visitor<'a> for CollectProcMacros<'a> {
fn visit_item(&mut self, item: &'a ast::Item) {
if let ast::ItemKind::MacroDef(..) = item.kind {
- if self.is_proc_macro_crate && self.sess.contains_name(&item.attrs, sym::macro_export) {
+ if self.is_proc_macro_crate && attr::contains_name(&item.attrs, sym::macro_export) {
let msg =
"cannot export macro_rules! macros from a `proc-macro` crate type currently";
self.handler.span_err(self.source_map.guess_head_span(item.span), msg);
@@ -176,7 +172,7 @@ impl<'a> Visitor<'a> for CollectProcMacros<'a> {
let mut found_attr: Option<&'a ast::Attribute> = None;
for attr in &item.attrs {
- if self.sess.is_proc_macro_attr(&attr) {
+ if attr.is_proc_macro_attr() {
if let Some(prev_attr) = found_attr {
let prev_item = prev_attr.get_normal_item();
let item = attr.get_normal_item();
diff --git a/compiler/rustc_builtin_macros/src/standard_library_imports.rs b/compiler/rustc_builtin_macros/src/standard_library_imports.rs
index e67c0dba6..6493c6f13 100644
--- a/compiler/rustc_builtin_macros/src/standard_library_imports.rs
+++ b/compiler/rustc_builtin_macros/src/standard_library_imports.rs
@@ -1,4 +1,4 @@
-use rustc_ast as ast;
+use rustc_ast::{self as ast, attr};
use rustc_expand::base::{ExtCtxt, ResolverExpand};
use rustc_expand::expand::ExpansionConfig;
use rustc_session::Session;
@@ -9,17 +9,19 @@ use rustc_span::DUMMY_SP;
use thin_vec::thin_vec;
pub fn inject(
- mut krate: ast::Crate,
+ krate: &mut ast::Crate,
+ pre_configured_attrs: &[ast::Attribute],
resolver: &mut dyn ResolverExpand,
sess: &Session,
-) -> ast::Crate {
+) -> usize {
+ let orig_num_items = krate.items.len();
let edition = sess.parse_sess.edition;
// the first name in this list is the crate name of the crate with the prelude
- let names: &[Symbol] = if sess.contains_name(&krate.attrs, sym::no_core) {
- return krate;
- } else if sess.contains_name(&krate.attrs, sym::no_std) {
- if sess.contains_name(&krate.attrs, sym::compiler_builtins) {
+ let names: &[Symbol] = if attr::contains_name(pre_configured_attrs, sym::no_core) {
+ return 0;
+ } else if attr::contains_name(pre_configured_attrs, sym::no_std) {
+ if attr::contains_name(pre_configured_attrs, sym::compiler_builtins) {
&[sym::core]
} else {
&[sym::core, sym::compiler_builtins]
@@ -88,6 +90,5 @@ pub fn inject(
);
krate.items.insert(0, use_item);
-
- krate
+ krate.items.len() - orig_num_items
}
diff --git a/compiler/rustc_builtin_macros/src/test.rs b/compiler/rustc_builtin_macros/src/test.rs
index e02c7e6c0..79d8be248 100644
--- a/compiler/rustc_builtin_macros/src/test.rs
+++ b/compiler/rustc_builtin_macros/src/test.rs
@@ -1,14 +1,13 @@
/// The expansion from a test function to the appropriate test struct for libtest
/// Ideally, this code would be in libtest but for efficiency and error messages it lives here.
use crate::util::{check_builtin_macro_attribute, warn_on_duplicate_attribute};
-use rustc_ast as ast;
use rustc_ast::ptr::P;
+use rustc_ast::{self as ast, attr};
use rustc_ast_pretty::pprust;
use rustc_errors::Applicability;
use rustc_expand::base::*;
-use rustc_session::Session;
use rustc_span::symbol::{sym, Ident, Symbol};
-use rustc_span::Span;
+use rustc_span::{FileNameDisplayPreference, Span};
use std::iter;
use thin_vec::{thin_vec, ThinVec};
@@ -33,7 +32,23 @@ pub fn expand_test_case(
}
let sp = ecx.with_def_site_ctxt(attr_sp);
- let mut item = anno_item.expect_item();
+ let (mut item, is_stmt) = match anno_item {
+ Annotatable::Item(item) => (item, false),
+ Annotatable::Stmt(stmt) if let ast::StmtKind::Item(_) = stmt.kind => if let ast::StmtKind::Item(i) = stmt.into_inner().kind {
+ (i, true)
+ } else {
+ unreachable!()
+ },
+ _ => {
+ ecx.struct_span_err(
+ anno_item.span(),
+ "`#[test_case]` attribute is only allowed on items",
+ )
+ .emit();
+
+ return vec![];
+ }
+ };
item = item.map(|mut item| {
let test_path_symbol = Symbol::intern(&item_path(
// skip the name of the root module
@@ -50,7 +65,13 @@ pub fn expand_test_case(
item
});
- return vec![Annotatable::Item(item)];
+ let ret = if is_stmt {
+ Annotatable::Stmt(P(ecx.stmt_item(item.span, item)))
+ } else {
+ Annotatable::Item(item)
+ };
+
+ vec![ret]
}
pub fn expand_test(
@@ -97,34 +118,22 @@ pub fn expand_test_or_bench(
}
}
other => {
- cx.struct_span_err(
- other.span(),
- "`#[test]` attribute is only allowed on non associated functions",
- )
- .emit();
+ not_testable_error(cx, attr_sp, None);
return vec![other];
}
};
- // Note: non-associated fn items are already handled by `expand_test_or_bench`
let ast::ItemKind::Fn(fn_) = &item.kind else {
- let diag = &cx.sess.parse_sess.span_diagnostic;
- let msg = "the `#[test]` attribute may only be used on a non-associated function";
- let mut err = match item.kind {
- // These were a warning before #92959 and need to continue being that to avoid breaking
- // stable user code (#94508).
- ast::ItemKind::MacCall(_) => diag.struct_span_warn(attr_sp, msg),
- // `.forget_guarantee()` needed to get these two arms to match types. Because of how
- // locally close the `.emit()` call is I'm comfortable with it, but if it can be
- // reworked in the future to not need it, it'd be nice.
- _ => diag.struct_span_err(attr_sp, msg).forget_guarantee(),
+ not_testable_error(cx, attr_sp, Some(&item));
+ return if is_stmt {
+ vec![Annotatable::Stmt(P(ast::Stmt {
+ id: ast::DUMMY_NODE_ID,
+ span: item.span,
+ kind: ast::StmtKind::Item(item),
+ }))]
+ } else {
+ vec![Annotatable::Item(item)]
};
- err.span_label(attr_sp, "the `#[test]` macro causes a function to be run on a test and has no effect on non-functions")
- .span_label(item.span, format!("expected a non-associated function, found {} {}", item.kind.article(), item.kind.descr()))
- .span_suggestion(attr_sp, "replace with conditional compilation to make the item only exist when tests are being run", "#[cfg(test)]", Applicability::MaybeIncorrect)
- .emit();
-
- return vec![Annotatable::Item(item)];
};
// has_*_signature will report any errors in the type so compilation
@@ -231,25 +240,29 @@ pub fn expand_test_or_bench(
&item.ident,
));
- let mut test_const = cx.item(
- sp,
- Ident::new(item.ident.name, sp),
- thin_vec![
- // #[cfg(test)]
- cx.attr_nested_word(sym::cfg, sym::test, attr_sp),
- // #[rustc_test_marker = "test_case_sort_key"]
- cx.attr_name_value_str(sym::rustc_test_marker, test_path_symbol, attr_sp),
- ],
- // const $ident: test::TestDescAndFn =
- ast::ItemKind::Const(
- ast::Defaultness::Final,
- cx.ty(sp, ast::TyKind::Path(None, test_path("TestDescAndFn"))),
- // test::TestDescAndFn {
- Some(
- cx.expr_struct(
- sp,
- test_path("TestDescAndFn"),
- thin_vec![
+ let location_info = get_location_info(cx, &item);
+
+ let mut test_const =
+ cx.item(
+ sp,
+ Ident::new(item.ident.name, sp),
+ thin_vec![
+ // #[cfg(test)]
+ cx.attr_nested_word(sym::cfg, sym::test, attr_sp),
+ // #[rustc_test_marker = "test_case_sort_key"]
+ cx.attr_name_value_str(sym::rustc_test_marker, test_path_symbol, attr_sp),
+ ],
+ // const $ident: test::TestDescAndFn =
+ ast::ItemKind::Const(
+ ast::ConstItem {
+ defaultness: ast::Defaultness::Final,
+ ty: cx.ty(sp, ast::TyKind::Path(None, test_path("TestDescAndFn"))),
+ // test::TestDescAndFn {
+ expr: Some(
+ cx.expr_struct(
+ sp,
+ test_path("TestDescAndFn"),
+ thin_vec![
// desc: test::TestDesc {
field(
"desc",
@@ -267,19 +280,26 @@ pub fn expand_test_or_bench(
),
),
// ignore: true | false
- field(
- "ignore",
- cx.expr_bool(sp, should_ignore(&cx.sess, &item)),
- ),
+ field("ignore", cx.expr_bool(sp, should_ignore(&item)),),
// ignore_message: Some("...") | None
field(
"ignore_message",
- if let Some(msg) = should_ignore_message(cx, &item) {
+ if let Some(msg) = should_ignore_message(&item) {
cx.expr_some(sp, cx.expr_str(sp, msg))
} else {
cx.expr_none(sp)
},
),
+ // source_file: <relative_path_of_source_file>
+ field("source_file", cx.expr_str(sp, location_info.0)),
+ // start_line: start line of the test fn identifier.
+ field("start_line", cx.expr_usize(sp, location_info.1)),
+ // start_col: start column of the test fn identifier.
+ field("start_col", cx.expr_usize(sp, location_info.2)),
+ // end_line: end line of the test fn identifier.
+ field("end_line", cx.expr_usize(sp, location_info.3)),
+ // end_col: end column of the test fn identifier.
+ field("end_col", cx.expr_usize(sp, location_info.4)),
// compile_fail: true | false
field("compile_fail", cx.expr_bool(sp, false)),
// no_run: true | false
@@ -329,10 +349,12 @@ pub fn expand_test_or_bench(
// testfn: test::StaticTestFn(...) | test::StaticBenchFn(...)
field("testfn", test_fn), // }
],
- ), // }
+ ), // }
+ ),
+ }
+ .into(),
),
- ),
- );
+ );
test_const = test_const.map(|mut tc| {
tc.vis.kind = ast::VisibilityKind::Public;
tc
@@ -364,6 +386,49 @@ pub fn expand_test_or_bench(
}
}
+fn not_testable_error(cx: &ExtCtxt<'_>, attr_sp: Span, item: Option<&ast::Item>) {
+ let diag = &cx.sess.parse_sess.span_diagnostic;
+ let msg = "the `#[test]` attribute may only be used on a non-associated function";
+ let mut err = match item.map(|i| &i.kind) {
+ // These were a warning before #92959 and need to continue being that to avoid breaking
+ // stable user code (#94508).
+ Some(ast::ItemKind::MacCall(_)) => diag.struct_span_warn(attr_sp, msg),
+ // `.forget_guarantee()` needed to get these two arms to match types. Because of how
+ // locally close the `.emit()` call is I'm comfortable with it, but if it can be
+ // reworked in the future to not need it, it'd be nice.
+ _ => diag.struct_span_err(attr_sp, msg).forget_guarantee(),
+ };
+ if let Some(item) = item {
+ err.span_label(
+ item.span,
+ format!(
+ "expected a non-associated function, found {} {}",
+ item.kind.article(),
+ item.kind.descr()
+ ),
+ );
+ }
+ err.span_label(attr_sp, "the `#[test]` macro causes a function to be run as a test and has no effect on non-functions")
+ .span_suggestion(attr_sp,
+ "replace with conditional compilation to make the item only exist when tests are being run",
+ "#[cfg(test)]",
+ Applicability::MaybeIncorrect)
+ .emit();
+}
+
+fn get_location_info(cx: &ExtCtxt<'_>, item: &ast::Item) -> (Symbol, usize, usize, usize, usize) {
+ let span = item.ident.span;
+ let (source_file, lo_line, lo_col, hi_line, hi_col) =
+ cx.sess.source_map().span_to_location_info(span);
+
+ let file_name = match source_file {
+ Some(sf) => sf.name.display(FileNameDisplayPreference::Remapped).to_string(),
+ None => "no-location".to_string(),
+ };
+
+ (Symbol::intern(&file_name), lo_line, lo_col, hi_line, hi_col)
+}
+
fn item_path(mod_path: &[Ident], item_ident: &Ident) -> String {
mod_path
.iter()
@@ -378,12 +443,12 @@ enum ShouldPanic {
Yes(Option<Symbol>),
}
-fn should_ignore(sess: &Session, i: &ast::Item) -> bool {
- sess.contains_name(&i.attrs, sym::ignore)
+fn should_ignore(i: &ast::Item) -> bool {
+ attr::contains_name(&i.attrs, sym::ignore)
}
-fn should_ignore_message(cx: &ExtCtxt<'_>, i: &ast::Item) -> Option<Symbol> {
- match cx.sess.find_by_name(&i.attrs, sym::ignore) {
+fn should_ignore_message(i: &ast::Item) -> Option<Symbol> {
+ match attr::find_by_name(&i.attrs, sym::ignore) {
Some(attr) => {
match attr.meta_item_list() {
// Handle #[ignore(bar = "foo")]
@@ -397,7 +462,7 @@ fn should_ignore_message(cx: &ExtCtxt<'_>, i: &ast::Item) -> Option<Symbol> {
}
fn should_panic(cx: &ExtCtxt<'_>, i: &ast::Item) -> ShouldPanic {
- match cx.sess.find_by_name(&i.attrs, sym::should_panic) {
+ match attr::find_by_name(&i.attrs, sym::should_panic) {
Some(attr) => {
let sd = &cx.sess.parse_sess.span_diagnostic;
@@ -463,7 +528,7 @@ fn test_type(cx: &ExtCtxt<'_>) -> TestType {
}
fn has_test_signature(cx: &ExtCtxt<'_>, i: &ast::Item) -> bool {
- let has_should_panic_attr = cx.sess.contains_name(&i.attrs, sym::should_panic);
+ let has_should_panic_attr = attr::contains_name(&i.attrs, sym::should_panic);
let sd = &cx.sess.parse_sess.span_diagnostic;
match &i.kind {
ast::ItemKind::Fn(box ast::Fn { sig, generics, .. }) => {
diff --git a/compiler/rustc_builtin_macros/src/test_harness.rs b/compiler/rustc_builtin_macros/src/test_harness.rs
index d8e3db9e8..80f497333 100644
--- a/compiler/rustc_builtin_macros/src/test_harness.rs
+++ b/compiler/rustc_builtin_macros/src/test_harness.rs
@@ -37,7 +37,7 @@ struct TestCtxt<'a> {
/// Traverse the crate, collecting all the test functions, eliding any
/// existing main functions, and synthesizing a main test harness
-pub fn inject(sess: &Session, resolver: &mut dyn ResolverExpand, krate: &mut ast::Crate) {
+pub fn inject(krate: &mut ast::Crate, sess: &Session, resolver: &mut dyn ResolverExpand) {
let span_diagnostic = sess.diagnostic();
let panic_strategy = sess.panic_strategy();
let platform_panic_strategy = sess.target.panic_strategy;
@@ -47,13 +47,13 @@ pub fn inject(sess: &Session, resolver: &mut dyn ResolverExpand, krate: &mut ast
// unconditional, so that the attribute is still marked as used in
// non-test builds.
let reexport_test_harness_main =
- sess.first_attr_value_str_by_name(&krate.attrs, sym::reexport_test_harness_main);
+ attr::first_attr_value_str_by_name(&krate.attrs, sym::reexport_test_harness_main);
// Do this here so that the test_runner crate attribute gets marked as used
// even in non-test builds
- let test_runner = get_test_runner(sess, span_diagnostic, &krate);
+ let test_runner = get_test_runner(span_diagnostic, &krate);
- if sess.opts.test {
+ if sess.is_test_crate() {
let panic_strategy = match (panic_strategy, sess.opts.unstable_opts.panic_abort_tests) {
(PanicStrategy::Abort, true) => PanicStrategy::Abort,
(PanicStrategy::Abort, false) => {
@@ -123,7 +123,7 @@ impl<'a> MutVisitor for TestHarnessGenerator<'a> {
fn flat_map_item(&mut self, i: P<ast::Item>) -> SmallVec<[P<ast::Item>; 1]> {
let mut item = i.into_inner();
- if let Some(name) = get_test_name(&self.cx.ext_cx.sess, &item) {
+ if let Some(name) = get_test_name(&item) {
debug!("this is a test item");
let test = Test { span: item.span, ident: item.ident, name };
@@ -145,12 +145,12 @@ impl<'a> MutVisitor for TestHarnessGenerator<'a> {
// Beware, this is duplicated in librustc_passes/entry.rs (with
// `rustc_hir::Item`), so make sure to keep them in sync.
-fn entry_point_type(sess: &Session, item: &ast::Item, depth: usize) -> EntryPointType {
+fn entry_point_type(item: &ast::Item, depth: usize) -> EntryPointType {
match item.kind {
ast::ItemKind::Fn(..) => {
- if sess.contains_name(&item.attrs, sym::start) {
+ if attr::contains_name(&item.attrs, sym::start) {
EntryPointType::Start
- } else if sess.contains_name(&item.attrs, sym::rustc_main) {
+ } else if attr::contains_name(&item.attrs, sym::rustc_main) {
EntryPointType::RustcMainAttr
} else if item.ident.name == sym::main {
if depth == 0 {
@@ -184,7 +184,7 @@ impl<'a> MutVisitor for EntryPointCleaner<'a> {
// Remove any #[rustc_main] or #[start] from the AST so it doesn't
// clash with the one we're going to add, but mark it as
// #[allow(dead_code)] to avoid printing warnings.
- let item = match entry_point_type(self.sess, &item, self.depth) {
+ let item = match entry_point_type(&item, self.depth) {
EntryPointType::MainNamed | EntryPointType::RustcMainAttr | EntryPointType::Start => {
item.map(|ast::Item { id, ident, attrs, kind, vis, span, tokens }| {
let allow_dead_code = attr::mk_attr_nested_word(
@@ -373,16 +373,12 @@ fn mk_tests_slice(cx: &TestCtxt<'_>, sp: Span) -> P<ast::Expr> {
)
}
-fn get_test_name(sess: &Session, i: &ast::Item) -> Option<Symbol> {
- sess.first_attr_value_str_by_name(&i.attrs, sym::rustc_test_marker)
+fn get_test_name(i: &ast::Item) -> Option<Symbol> {
+ attr::first_attr_value_str_by_name(&i.attrs, sym::rustc_test_marker)
}
-fn get_test_runner(
- sess: &Session,
- sd: &rustc_errors::Handler,
- krate: &ast::Crate,
-) -> Option<ast::Path> {
- let test_attr = sess.find_by_name(&krate.attrs, sym::test_runner)?;
+fn get_test_runner(sd: &rustc_errors::Handler, krate: &ast::Crate) -> Option<ast::Path> {
+ let test_attr = attr::find_by_name(&krate.attrs, sym::test_runner)?;
let meta_list = test_attr.meta_item_list()?;
let span = test_attr.span;
match &*meta_list {
diff --git a/compiler/rustc_builtin_macros/src/util.rs b/compiler/rustc_builtin_macros/src/util.rs
index 83812631c..9463a1418 100644
--- a/compiler/rustc_builtin_macros/src/util.rs
+++ b/compiler/rustc_builtin_macros/src/util.rs
@@ -1,4 +1,4 @@
-use rustc_ast::{AttrStyle, Attribute, MetaItem};
+use rustc_ast::{attr, AttrStyle, Attribute, MetaItem};
use rustc_expand::base::{Annotatable, ExtCtxt};
use rustc_feature::AttributeTemplate;
use rustc_lint_defs::builtin::DUPLICATE_MACRO_ATTRIBUTES;
@@ -36,7 +36,7 @@ pub fn warn_on_duplicate_attribute(ecx: &ExtCtxt<'_>, item: &Annotatable, name:
_ => None,
};
if let Some(attrs) = attrs {
- if let Some(attr) = ecx.sess.find_by_name(attrs, name) {
+ if let Some(attr) = attr::find_by_name(attrs, name) {
ecx.parse_sess().buffer_lint(
DUPLICATE_MACRO_ATTRIBUTES,
attr.span,