diff --git a/compiler/rustc_builtin_macros/src/asm.rs b/compiler/rustc_builtin_macros/src/asm.rs index e6afc81d0396a..4500e8c6d1b94 100644 --- a/compiler/rustc_builtin_macros/src/asm.rs +++ b/compiler/rustc_builtin_macros/src/asm.rs @@ -412,7 +412,7 @@ fn expand_preparsed_asm(ecx: &mut ExtCtxt<'_>, sp: Span, args: AsmArgs) -> P template_part, Err(err) => { - if let Some(mut err) = err { + if let Some((mut err, _)) = err { err.emit(); } return DummyResult::raw_expr(sp, true); diff --git a/compiler/rustc_builtin_macros/src/format.rs b/compiler/rustc_builtin_macros/src/format.rs index 7e88b58c0e29d..215dcabf74c0b 100644 --- a/compiler/rustc_builtin_macros/src/format.rs +++ b/compiler/rustc_builtin_macros/src/format.rs @@ -946,17 +946,19 @@ pub fn expand_preparsed_format_args( } Ok(fmt) => fmt, Err(err) => { - if let Some(mut err) = err { + if let Some((mut err, suggested)) = err { let sugg_fmt = match args.len() { 0 => "{}".to_string(), _ => format!("{}{{}}", "{} ".repeat(args.len())), }; - err.span_suggestion( - fmt_sp.shrink_to_lo(), - "you might be missing a string literal to format with", - format!("\"{}\", ", sugg_fmt), - Applicability::MaybeIncorrect, - ); + if !suggested { + err.span_suggestion( + fmt_sp.shrink_to_lo(), + "you might be missing a string literal to format with", + format!("\"{}\", ", sugg_fmt), + Applicability::MaybeIncorrect, + ); + } err.emit(); } return DummyResult::raw_expr(sp, true); diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index a2035ee3c6ec9..43b94e239eb9f 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -9,7 +9,7 @@ use rustc_ast::{self as ast, AstLike, Attribute, Item, NodeId, PatKind}; use rustc_attr::{self as attr, Deprecation, Stability}; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::sync::{self, Lrc}; -use rustc_errors::{DiagnosticBuilder, ErrorReported}; +use rustc_errors::{Applicability, DiagnosticBuilder, ErrorReported}; use rustc_lint_defs::builtin::PROC_MACRO_BACK_COMPAT; use rustc_lint_defs::BuiltinLintDiagnostics; use rustc_parse::{self, nt_to_tokenstream, parser, MACRO_ARGUMENTS}; @@ -1133,13 +1133,15 @@ impl<'a> ExtCtxt<'a> { } /// Extracts a string literal from the macro expanded version of `expr`, -/// emitting `err_msg` if `expr` is not a string literal. This does not stop -/// compilation on error, merely emits a non-fatal error and returns `None`. +/// returning a diagnostic error of `err_msg` if `expr` is not a string literal. +/// The returned bool indicates whether an applicable suggestion has already been +/// added to the diagnostic to avoid emitting multiple suggestions. `Err(None)` +/// indicates that an ast error was encountered. pub fn expr_to_spanned_string<'a>( cx: &'a mut ExtCtxt<'_>, expr: P, err_msg: &str, -) -> Result<(Symbol, ast::StrStyle, Span), Option>> { +) -> Result<(Symbol, ast::StrStyle, Span), Option<(DiagnosticBuilder<'a>, bool)>> { // Perform eager expansion on the expression. // We want to be able to handle e.g., `concat!("foo", "bar")`. let expr = cx.expander().fully_expand_fragment(AstFragment::Expr(expr)).make_expr(); @@ -1147,14 +1149,27 @@ pub fn expr_to_spanned_string<'a>( Err(match expr.kind { ast::ExprKind::Lit(ref l) => match l.kind { ast::LitKind::Str(s, style) => return Ok((s, style, expr.span)), + ast::LitKind::ByteStr(_) => { + let mut err = cx.struct_span_err(l.span, err_msg); + err.span_suggestion( + expr.span.shrink_to_lo(), + "consider removing the leading `b`", + String::new(), + Applicability::MaybeIncorrect, + ); + Some((err, true)) + } ast::LitKind::Err(_) => None, - _ => Some(cx.struct_span_err(l.span, err_msg)), + _ => Some((cx.struct_span_err(l.span, err_msg), false)), }, ast::ExprKind::Err => None, - _ => Some(cx.struct_span_err(expr.span, err_msg)), + _ => Some((cx.struct_span_err(expr.span, err_msg), false)), }) } +/// Extracts a string literal from the macro expanded version of `expr`, +/// emitting `err_msg` if `expr` is not a string literal. This does not stop +/// compilation on error, merely emits a non-fatal error and returns `None`. pub fn expr_to_string( cx: &mut ExtCtxt<'_>, expr: P, @@ -1162,7 +1177,7 @@ pub fn expr_to_string( ) -> Option<(Symbol, ast::StrStyle)> { expr_to_spanned_string(cx, expr, err_msg) .map_err(|err| { - err.map(|mut err| { + err.map(|(mut err, _)| { err.emit(); }) }) diff --git a/src/test/ui/issues/issue-86865.rs b/src/test/ui/issues/issue-86865.rs new file mode 100644 index 0000000000000..01e0a20a5115c --- /dev/null +++ b/src/test/ui/issues/issue-86865.rs @@ -0,0 +1,11 @@ +use std::fmt::Write; + +fn main() { + println!(b"foo"); + //~^ ERROR format argument must be a string literal + //~| HELP consider removing the leading `b` + let mut s = String::new(); + write!(s, b"foo{}", "bar"); + //~^ ERROR format argument must be a string literal + //~| HELP consider removing the leading `b` +} diff --git a/src/test/ui/issues/issue-86865.stderr b/src/test/ui/issues/issue-86865.stderr new file mode 100644 index 0000000000000..eed755366311b --- /dev/null +++ b/src/test/ui/issues/issue-86865.stderr @@ -0,0 +1,18 @@ +error: format argument must be a string literal + --> $DIR/issue-86865.rs:4:14 + | +LL | println!(b"foo"); + | -^^^^^ + | | + | help: consider removing the leading `b` + +error: format argument must be a string literal + --> $DIR/issue-86865.rs:8:15 + | +LL | write!(s, b"foo{}", "bar"); + | -^^^^^^^ + | | + | help: consider removing the leading `b` + +error: aborting due to 2 previous errors +