From 1376c5a617be5c25655d0d7cb63e3beaa5a6e026 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 17 Apr 2024 14:20:39 +0200 Subject: Merging upstream version 1.70.0+dfsg1. Signed-off-by: Daniel Baumann --- compiler/rustc_builtin_macros/src/format.rs | 254 +++++++++++++--------------- 1 file changed, 122 insertions(+), 132 deletions(-) (limited to 'compiler/rustc_builtin_macros/src/format.rs') 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, + 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, 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, - mut args: FormatArguments, + input: MacroInput, append_newline: bool, ) -> Result { 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(¬e); + 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::>(); @@ -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, 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::>(), - "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 { 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)) -- cgit v1.2.3