From d5e47558d4913a572c10075f8bd84fb7ba45ff65 Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Mon, 8 Aug 2022 16:17:08 +0200 Subject: [PATCH 01/25] Rewrite and refactor format_args!() builtin macro. --- compiler/rustc_builtin_macros/src/format.rs | 2079 +++++++---------- .../rustc_builtin_macros/src/format/expand.rs | 352 +++ compiler/rustc_builtin_macros/src/lib.rs | 1 + compiler/rustc_expand/src/build.rs | 4 + compiler/rustc_span/src/symbol.rs | 11 + src/test/ui/fmt/ifmt-bad-arg.stderr | 14 +- src/test/ui/macros/format-parse-errors.stderr | 2 +- 7 files changed, 1168 insertions(+), 1295 deletions(-) create mode 100644 compiler/rustc_builtin_macros/src/format/expand.rs diff --git a/compiler/rustc_builtin_macros/src/format.rs b/compiler/rustc_builtin_macros/src/format.rs index 2100487107517..06a831e36692e 100644 --- a/compiler/rustc_builtin_macros/src/format.rs +++ b/compiler/rustc_builtin_macros/src/format.rs @@ -1,271 +1,192 @@ -use ArgumentType::*; -use Position::*; - use rustc_ast as ast; use rustc_ast::ptr::P; +use rustc_ast::token; use rustc_ast::tokenstream::TokenStream; -use rustc_ast::visit::{self, Visitor}; -use rustc_ast::{token, BlockCheckMode, UnsafeSource}; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::fx::FxHashSet; use rustc_errors::{pluralize, Applicability, MultiSpan, PResult}; use rustc_expand::base::{self, *}; use rustc_parse_format as parse; -use rustc_span::symbol::{sym, Ident, Symbol}; +use rustc_span::symbol::{Ident, Symbol}; use rustc_span::{BytePos, InnerSpan, Span}; -use smallvec::SmallVec; use rustc_lint_defs::builtin::NAMED_ARGUMENTS_USED_POSITIONALLY; use rustc_lint_defs::{BufferedEarlyLint, BuiltinLintDiagnostics, LintId}; -use rustc_parse_format::Count; -use std::borrow::Cow; -use std::collections::hash_map::Entry; - -#[derive(PartialEq)] -enum ArgumentType { - Placeholder(&'static str), - Count, -} -enum Position { - Exact(usize), - Capture(usize), - Named(Symbol, InnerSpan), +mod expand; +use expand::expand_parsed_format_args; + +// The format_args!() macro is expanded in three steps: +// 1. First, `parse_args` will parse the `(literal, arg, arg, name=arg, name=arg)` syntax, +// but doesn't parse the template (the literal) itself. +// 2. Second, `make_format_args` will parse the template, the format options, resolve argument references, +// produce diagnostics, and turn the whole thing into a `FormatArgs` structure. +// 3. Finally, `expand_parsed_format_args` will turn that `FormatArgs` structure +// into the expression that the macro expands to. + +// Definitions: +// +// format_args!("hello {abc:.xyz$}!!", abc="world"); +// └──────────────────────────────────────────────┘ +// FormatArgs +// +// format_args!("hello {abc:.xyz$}!!", abc="world"); +// └─────────┘ +// argument +// +// format_args!("hello {abc:.xyz$}!!", abc="world"); +// └───────────────────┘ +// template +// +// format_args!("hello {abc:.xyz$}!!", abc="world"); +// └────┘└─────────┘└┘ +// pieces +// +// format_args!("hello {abc:.xyz$}!!", abc="world"); +// └────┘ └┘ +// literal pieces +// +// format_args!("hello {abc:.xyz$}!!", abc="world"); +// └─────────┘ +// placeholder +// +// format_args!("hello {abc:.xyz$}!!", abc="world"); +// └─┘ └─┘ +// positions (could be names, numbers, empty, or `*`) + +/// (Parsed) format args. +/// +/// Basically the "AST" for a complete `format_args!()`. +/// +/// E.g., `format_args!("hello {name}");`. +#[derive(Clone, Debug)] +pub struct FormatArgs { + pub span: Span, + pub template: Vec, + pub arguments: Vec<(P, FormatArgKind)>, } -/// Indicates how positional named argument (i.e. an named argument which is used by position -/// instead of by name) is used in format string -/// * `Arg` is the actual argument to print -/// * `Width` is width format argument -/// * `Precision` is precion format argument -/// Example: `{Arg:Width$.Precision$} -#[derive(Debug, Eq, PartialEq)] -enum PositionalNamedArgType { - Arg, - Width, - Precision, +#[derive(Clone, Debug)] +pub enum FormatArgsPiece { + Literal(Symbol), + Placeholder(FormatPlaceholder), } -/// Contains information necessary to create a lint for a positional named argument -#[derive(Debug)] -struct PositionalNamedArg { - ty: PositionalNamedArgType, - /// The piece of the using this argument (multiple pieces can use the same argument) - cur_piece: usize, - /// The InnerSpan for in the string to be replaced with the named argument - /// This will be None when the position is implicit - inner_span_to_replace: Option, - /// The name to use instead of the position - replacement: Symbol, - /// The span for the positional named argument (so the lint can point a message to it) - positional_named_arg_span: Span, - has_formatting: bool, +#[derive(Clone, Debug)] +pub enum FormatArgKind { + /// `format_args(…, arg)` + Normal, + /// `format_args(…, arg = 1)` + Named(Ident), + /// `format_args("… {arg} …")` + Captured(Ident), } -impl PositionalNamedArg { - /// Determines: - /// 1) span to be replaced with the name of the named argument and - /// 2) span to be underlined for error messages - fn get_positional_arg_spans(&self, cx: &Context<'_, '_>) -> (Option, Option) { - if let Some(inner_span) = &self.inner_span_to_replace { - let span = - cx.fmtsp.from_inner(InnerSpan { start: inner_span.start, end: inner_span.end }); - (Some(span), Some(span)) - } else if self.ty == PositionalNamedArgType::Arg { - // In the case of a named argument whose position is implicit, if the argument *has* - // formatting, there will not be a span to replace. Instead, we insert the name after - // the `{`, which will be the first character of arg_span. If the argument does *not* - // have formatting, there may or may not be a span to replace. This is because - // whitespace is allowed in arguments without formatting (such as `format!("{ }", 1);`) - // but is not allowed in arguments with formatting (an error will be generated in cases - // like `format!("{ :1.1}", 1.0f32);`. - // For the message span, if there is formatting, we want to use the opening `{` and the - // next character, which will the `:` indicating the start of formatting. If there is - // not any formatting, we want to underline the entire span. - cx.arg_spans.get(self.cur_piece).map_or((None, None), |arg_span| { - if self.has_formatting { - ( - Some(arg_span.with_lo(arg_span.lo() + BytePos(1)).shrink_to_lo()), - Some(arg_span.with_hi(arg_span.lo() + BytePos(2))), - ) - } else { - let replace_start = arg_span.lo() + BytePos(1); - let replace_end = arg_span.hi() - BytePos(1); - let to_replace = arg_span.with_lo(replace_start).with_hi(replace_end); - (Some(to_replace), Some(*arg_span)) - } - }) - } else { - (None, None) +impl FormatArgKind { + pub fn ident(&self) -> Option { + match self { + &Self::Normal => None, + &Self::Named(id) => Some(id), + &Self::Captured(id) => Some(id), } } } -/// Encapsulates all the named arguments that have been used positionally -#[derive(Debug)] -struct PositionalNamedArgsLint { - positional_named_args: Vec, +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct FormatPlaceholder { + /// Index into [`FormatArgs::arguments`]. + pub argument: FormatArgPosition, + /// The span inside the format string for the full `{…}` placeholder. + pub span: Option, + /// `{}`, `{:?}`, or `{:x}`, etc. + pub format_trait: FormatTrait, + /// `{}` or `{:.5}` or `{:-^20}`, etc. + pub format_options: FormatOptions, } -impl PositionalNamedArgsLint { - /// For a given positional argument, check if the index is for a named argument. - /// - /// Since positional arguments are required to come before named arguments, if the positional - /// index is greater than or equal to the start of named arguments, we know it's a named - /// argument used positionally. - /// - /// Example: - /// println!("{} {} {2}", 0, a=1, b=2); - /// - /// In this case, the first piece (`{}`) would be ArgumentImplicitlyIs with an index of 0. The - /// total number of arguments is 3 and the number of named arguments is 2, so the start of named - /// arguments is index 1. Therefore, the index of 0 is okay. - /// - /// The second piece (`{}`) would be ArgumentImplicitlyIs with an index of 1, which is the start - /// of named arguments, and so we should add a lint to use the named argument `a`. - /// - /// The third piece (`{2}`) would be ArgumentIs with an index of 2, which is greater than the - /// start of named arguments, and so we should add a lint to use the named argument `b`. - /// - /// This same check also works for width and precision formatting when either or both are - /// CountIsParam, which contains an index into the arguments. - fn maybe_add_positional_named_arg( - &mut self, - arg: Option<&FormatArg>, - ty: PositionalNamedArgType, - cur_piece: usize, - inner_span_to_replace: Option, - has_formatting: bool, - ) { - if let Some(arg) = arg { - if let Some(name) = arg.name { - self.push(name, ty, cur_piece, inner_span_to_replace, has_formatting) - } - } - } +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct FormatArgPosition { + /// Which argument this position refers to (Ok), + /// or would've referred to if it existed (Err). + pub index: Result, + /// What kind of position this is. See [`FormatArgsPositionKind`]. + pub kind: FormatArgPositionKind, + /// The span of the name or number. + pub span: Option, +} - /// Construct a PositionalNamedArg struct and push it into the vec of positional - /// named arguments. - fn push( - &mut self, - arg_name: Ident, - ty: PositionalNamedArgType, - cur_piece: usize, - inner_span_to_replace: Option, - has_formatting: bool, - ) { - // In FormatSpec, `precision_span` starts at the leading `.`, which we want to keep in - // the lint suggestion, so increment `start` by 1 when `PositionalArgumentType` is - // `Precision`. - let inner_span_to_replace = if ty == PositionalNamedArgType::Precision { - inner_span_to_replace - .map(|is| rustc_parse_format::InnerSpan { start: is.start + 1, end: is.end }) - } else { - inner_span_to_replace - }; - self.positional_named_args.push(PositionalNamedArg { - ty, - cur_piece, - inner_span_to_replace, - replacement: arg_name.name, - positional_named_arg_span: arg_name.span, - has_formatting, - }); - } +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum FormatArgPositionKind { + /// `{}` or `{.*}` + Implicit, + /// `{1}` or `{:1$}` or `{:.1$}` + Number, + /// `{a}` or `{:a$}` or `{:.a$}` + Named, +} + +#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] +pub enum FormatTrait { + /// `{}` + Display, + /// `{:?}` + Debug, + /// `{:e}` + LowerExp, + /// `{:E}` + UpperExp, + /// `{:o}` + Octal, + /// `{:p}` + Pointer, + /// `{:b}` + Binary, + /// `{:x}` + LowerHex, + /// `{:X}` + UpperHex, +} + +#[derive(Clone, Debug, Default, PartialEq, Eq)] +pub struct FormatOptions { + /// The width. E.g. `{:5}` or `{:width$}`. + pub width: Option, + /// The precision. E.g. `{:.5}` or `{:.precision$}`. + pub precision: Option, + /// The alignment. E.g. `{:>}` or `{:<}` or `{:^}`. + pub alignment: Option, + /// The fill character. E.g. the `.` in `{:.>10}`. + pub fill: Option, + /// The `+`, `-`, `0`, `#`, `x?` and `X?` flags. + pub flags: u32, } -struct Context<'a, 'b> { - ecx: &'a mut ExtCtxt<'b>, - /// The macro's call site. References to unstable formatting internals must - /// use this span to pass the stability checker. - macsp: Span, - /// The span of the format string literal. - fmtsp: Span, - - /// List of parsed argument expressions. - /// Named expressions are resolved early, and are appended to the end of - /// argument expressions. - /// - /// Example showing the various data structures in motion: - /// - /// * Original: `"{foo:o} {:o} {foo:x} {0:x} {1:o} {:x} {1:x} {0:o}"` - /// * Implicit argument resolution: `"{foo:o} {0:o} {foo:x} {0:x} {1:o} {1:x} {1:x} {0:o}"` - /// * Name resolution: `"{2:o} {0:o} {2:x} {0:x} {1:o} {1:x} {1:x} {0:o}"` - /// * `arg_types` (in JSON): `[[0, 1, 0], [0, 1, 1], [0, 1]]` - /// * `arg_unique_types` (in simplified JSON): `[["o", "x"], ["o", "x"], ["o", "x"]]` - /// * `names` (in JSON): `{"foo": 2}` - args: Vec, - /// The number of arguments that were added by implicit capturing. - num_captured_args: usize, - /// Placeholder slot numbers indexed by argument. - arg_types: Vec>, - /// Unique format specs seen for each argument. - arg_unique_types: Vec>, - /// Map from named arguments to their resolved indices. - names: FxHashMap, - - /// The latest consecutive literal strings, or empty if there weren't any. - literal: String, - - /// Collection of the compiled `rt::Argument` structures - pieces: Vec>, - /// Collection of string literals - str_pieces: Vec>, - /// Stays `true` if all formatting parameters are default (as in "{}{}"). - all_pieces_simple: bool, - - /// Mapping between positional argument references and indices into the - /// final generated static argument array. We record the starting indices - /// corresponding to each positional argument, and number of references - /// consumed so far for each argument, to facilitate correct `Position` - /// mapping in `build_piece`. In effect this can be seen as a "flattened" - /// version of `arg_unique_types`. - /// - /// Again with the example described above in docstring for `args`: - /// - /// * `arg_index_map` (in JSON): `[[0, 1, 0], [2, 3, 3], [4, 5]]` - arg_index_map: Vec>, - - /// Starting offset of count argument slots. - count_args_index_offset: usize, - - /// Count argument slots and tracking data structures. - /// Count arguments are separately tracked for de-duplication in case - /// multiple references are made to one argument. For example, in this - /// format string: - /// - /// * Original: `"{:.*} {:.foo$} {1:.*} {:.0$}"` - /// * Implicit argument resolution: `"{1:.0$} {2:.foo$} {1:.3$} {4:.0$}"` - /// * Name resolution: `"{1:.0$} {2:.5$} {1:.3$} {4:.0$}"` - /// * `count_positions` (in JSON): `{0: 0, 5: 1, 3: 2}` - /// * `count_args`: `vec![0, 5, 3]` - count_args: Vec, - /// Relative slot numbers for count arguments. - count_positions: FxHashMap, - /// Number of count slots assigned. - count_positions_count: usize, - - /// Current position of the implicit positional arg pointer, as if it - /// still existed in this phase of processing. - /// Used only for `all_pieces_simple` tracking in `build_piece`. - curarg: usize, - /// Current piece being evaluated, used for error reporting. - curpiece: usize, - /// Keep track of invalid references to positional arguments. - invalid_refs: Vec<(usize, usize)>, - /// Spans of all the formatting arguments, in order. - arg_spans: Vec, - /// All the formatting arguments that have formatting flags set, in order for diagnostics. - arg_with_formatting: Vec>, - - /// Whether this format string came from a string literal, as opposed to a macro. - is_literal: bool, - unused_names_lint: PositionalNamedArgsLint, +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum FormatAlignment { + /// `{:<}` + Left, + /// `{:>}` + Right, + /// `{:^}` + Center, } -pub struct FormatArg { - expr: P, - name: Option, +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum FormatCount { + /// `{:0}` or `{:.0}` + Literal(usize), + /// `{:.*}`, `{:.0$}`, or `{:a$}`, etc. + Argument(FormatArgPosition), +} + +// Only used in parse_args and report_invalid_references, +// to indicate how a referred argument was used. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +enum PositionUsedAs { + Placeholder(Option), + Precision, + Width, } +use PositionUsedAs::*; /// 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! @@ -274,15 +195,14 @@ pub struct FormatArg { /// If parsing succeeds, the return value is: /// /// ```text -/// Some((fmtstr, parsed arguments, index map for named arguments)) +/// Some((fmtstr, parsed arguments)) /// ``` fn parse_args<'a>( ecx: &mut ExtCtxt<'a>, sp: Span, tts: TokenStream, -) -> PResult<'a, (P, Vec, FxHashMap)> { - let mut args = Vec::::new(); - let mut names = FxHashMap::::default(); +) -> PResult<'a, (P, Vec<(P, FormatArgKind)>)> { + let mut args = Vec::<(P, FormatArgKind)>::new(); let mut p = ecx.new_parser_from_tts(tts); @@ -347,21 +267,19 @@ fn parse_args<'a>( p.bump(); p.expect(&token::Eq)?; let e = p.parse_expr()?; - if let Some(&prev) = names.get(&ident.name) { - ecx.struct_span_err(e.span, &format!("duplicate argument named `{}`", ident)) - .span_label(args[prev].expr.span, "previously here") - .span_label(e.span, "duplicate argument") - .emit(); + if let Some(prev) = + args.iter().rev().map_while(|a| a.1.ident()).find(|n| n.name == ident.name) + { + ecx.struct_span_err( + ident.span, + &format!("duplicate argument named `{}`", ident), + ) + .span_label(prev.span, "previously here") + .span_label(ident.span, "duplicate argument") + .emit(); continue; } - - // Resolve names into slots early. - // Since all the positional args are already seen at this point - // if the input is valid, we can simply append to the positional - // args. And remember the names. - let slot = args.len(); - names.insert(ident.name, slot); - args.push(FormatArg { expr: e, name: Some(ident) }); + args.push((e, FormatArgKind::Named(ident))); } _ => { let e = p.parse_expr()?; @@ -371,851 +289,31 @@ fn parse_args<'a>( "positional arguments cannot follow named arguments", ); err.span_label(e.span, "positional arguments must be before named arguments"); - for &pos in names.values() { - err.span_label(args[pos].expr.span, "named argument"); + for arg in &args { + if let Some(name) = arg.1.ident() { + err.span_label(name.span.to(arg.0.span), "named argument"); + } } err.emit(); } - args.push(FormatArg { expr: e, name: None }); + args.push((e, FormatArgKind::Normal)); } } } - Ok((fmtstr, args, names)) + Ok((fmtstr, args)) } -impl<'a, 'b> Context<'a, 'b> { - /// The number of arguments that were explicitly given. - fn num_args(&self) -> usize { - self.args.len() - self.num_captured_args - } - - fn resolve_name_inplace(&mut self, p: &mut parse::Piece<'_>) { - // NOTE: the `unwrap_or` branch is needed in case of invalid format - // arguments, e.g., `format_args!("{foo}")`. - let lookup = |s: &str| self.names.get(&Symbol::intern(s)).copied().unwrap_or(0); - - match *p { - parse::String(_) => {} - parse::NextArgument(ref mut arg) => { - if let parse::ArgumentNamed(s) = arg.position { - arg.position = parse::ArgumentIs(lookup(s)); - } - if let parse::CountIsName(s, _) = arg.format.width { - arg.format.width = parse::CountIsParam(lookup(s)); - } - if let parse::CountIsName(s, _) = arg.format.precision { - arg.format.precision = parse::CountIsParam(lookup(s)); - } - } - } - } - - /// Verifies one piece of a parse string, and remembers it if valid. - /// All errors are not emitted as fatal so we can continue giving errors - /// about this and possibly other format strings. - fn verify_piece(&mut self, p: &parse::Piece<'a>) { - match *p { - parse::String(..) => {} - parse::NextArgument(ref arg) => { - // width/precision first, if they have implicit positional - // parameters it makes more sense to consume them first. - self.verify_count( - arg.format.width, - &arg.format.width_span, - PositionalNamedArgType::Width, - ); - self.verify_count( - arg.format.precision, - &arg.format.precision_span, - PositionalNamedArgType::Precision, - ); - - let has_precision = arg.format.precision != Count::CountImplied; - let has_width = arg.format.width != Count::CountImplied; - - if has_precision || has_width { - // push before named params are resolved to aid diagnostics - self.arg_with_formatting.push(arg.format); - } - - // argument second, if it's an implicit positional parameter - // it's written second, so it should come after width/precision. - let pos = match arg.position { - parse::ArgumentIs(i) => { - self.unused_names_lint.maybe_add_positional_named_arg( - self.args.get(i), - PositionalNamedArgType::Arg, - self.curpiece, - Some(arg.position_span), - has_precision || has_width, - ); - - Exact(i) - } - parse::ArgumentImplicitlyIs(i) => { - self.unused_names_lint.maybe_add_positional_named_arg( - self.args.get(i), - PositionalNamedArgType::Arg, - self.curpiece, - None, - has_precision || has_width, - ); - Exact(i) - } - parse::ArgumentNamed(s) => { - let symbol = Symbol::intern(s); - let span = arg.position_span; - Named(symbol, InnerSpan::new(span.start, span.end)) - } - }; - - let ty = Placeholder(match arg.format.ty { - "" => "Display", - "?" => "Debug", - "e" => "LowerExp", - "E" => "UpperExp", - "o" => "Octal", - "p" => "Pointer", - "b" => "Binary", - "x" => "LowerHex", - "X" => "UpperHex", - _ => { - let fmtsp = self.fmtsp; - let sp = arg - .format - .ty_span - .map(|sp| fmtsp.from_inner(InnerSpan::new(sp.start, sp.end))); - let mut err = self.ecx.struct_span_err( - sp.unwrap_or(fmtsp), - &format!("unknown format trait `{}`", arg.format.ty), - ); - err.note( - "the only appropriate formatting traits are:\n\ - - ``, which uses the `Display` trait\n\ - - `?`, which uses the `Debug` trait\n\ - - `e`, which uses the `LowerExp` trait\n\ - - `E`, which uses the `UpperExp` trait\n\ - - `o`, which uses the `Octal` trait\n\ - - `p`, which uses the `Pointer` trait\n\ - - `b`, which uses the `Binary` trait\n\ - - `x`, which uses the `LowerHex` trait\n\ - - `X`, which uses the `UpperHex` trait", - ); - if let Some(sp) = sp { - for (fmt, name) in &[ - ("", "Display"), - ("?", "Debug"), - ("e", "LowerExp"), - ("E", "UpperExp"), - ("o", "Octal"), - ("p", "Pointer"), - ("b", "Binary"), - ("x", "LowerHex"), - ("X", "UpperHex"), - ] { - // FIXME: rustfix (`run-rustfix`) fails to apply suggestions. - // > "Cannot replace slice of data that was already replaced" - err.tool_only_span_suggestion( - sp, - &format!("use the `{}` trait", name), - *fmt, - Applicability::MaybeIncorrect, - ); - } - } - err.emit(); - "" - } - }); - self.verify_arg_type(pos, ty); - self.curpiece += 1; - } - } - } - - fn verify_count( - &mut self, - c: parse::Count<'_>, - inner_span: &Option, - named_arg_type: PositionalNamedArgType, - ) { - match c { - parse::CountImplied | parse::CountIs(..) => {} - parse::CountIsParam(i) | parse::CountIsStar(i) => { - self.unused_names_lint.maybe_add_positional_named_arg( - self.args.get(i), - named_arg_type, - self.curpiece, - *inner_span, - true, - ); - self.verify_arg_type(Exact(i), Count); - } - parse::CountIsName(s, span) => { - self.verify_arg_type( - Named(Symbol::intern(s), InnerSpan::new(span.start, span.end)), - Count, - ); - } - } - } - - fn describe_num_args(&self) -> Cow<'_, str> { - match self.num_args() { - 0 => "no arguments were given".into(), - 1 => "there is 1 argument".into(), - x => format!("there are {} arguments", x).into(), - } - } - - /// Handle invalid references to positional arguments. Output different - /// errors for the case where all arguments are positional and for when - /// there are named arguments or numbered positional arguments in the - /// format string. - fn report_invalid_references(&self, numbered_position_args: bool) { - let mut e; - let sp = if !self.arg_spans.is_empty() { - // Point at the formatting arguments. - MultiSpan::from_spans(self.arg_spans.clone()) - } else { - MultiSpan::from_span(self.fmtsp) - }; - let refs = - self.invalid_refs.iter().map(|(r, pos)| (r.to_string(), self.arg_spans.get(*pos))); - - let mut zero_based_note = false; - - let count = self.pieces.len() - + self - .arg_with_formatting - .iter() - .filter(|fmt| matches!(fmt.precision, parse::CountIsStar(_))) - .count(); - if self.names.is_empty() && !numbered_position_args && count != self.num_args() { - e = self.ecx.struct_span_err( - sp, - &format!( - "{} positional argument{} in format string, but {}", - count, - pluralize!(count), - self.describe_num_args(), - ), - ); - for arg in &self.args { - // Point at the arguments that will be formatted. - e.span_label(arg.expr.span, ""); - } - } else { - let (mut refs, spans): (Vec<_>, Vec<_>) = refs.unzip(); - // Avoid `invalid reference to positional arguments 7 and 7 (there is 1 argument)` - // for `println!("{7:7$}", 1);` - refs.sort(); - refs.dedup(); - let spans: Vec<_> = spans.into_iter().filter_map(|sp| sp.copied()).collect(); - let sp = if self.arg_spans.is_empty() || spans.is_empty() { - MultiSpan::from_span(self.fmtsp) - } else { - MultiSpan::from_spans(spans) - }; - let arg_list = if refs.len() == 1 { - format!("argument {}", refs[0]) - } else { - let reg = refs.pop().unwrap(); - format!("arguments {head} and {tail}", head = refs.join(", "), tail = reg) - }; - - e = self.ecx.struct_span_err( - sp, - &format!( - "invalid reference to positional {} ({})", - arg_list, - self.describe_num_args() - ), - ); - zero_based_note = true; - }; - - for fmt in &self.arg_with_formatting { - if let Some(span) = fmt.precision_span { - let span = self.fmtsp.from_inner(InnerSpan::new(span.start, span.end)); - match fmt.precision { - parse::CountIsParam(pos) if pos >= self.num_args() => { - e.span_label( - span, - &format!( - "this precision flag expects an `usize` argument at position {}, \ - but {}", - pos, - self.describe_num_args(), - ), - ); - zero_based_note = true; - } - parse::CountIsStar(pos) => { - let count = self.pieces.len() - + self - .arg_with_formatting - .iter() - .filter(|fmt| matches!(fmt.precision, parse::CountIsStar(_))) - .count(); - e.span_label( - span, - &format!( - "this precision flag adds an extra required argument at position {}, \ - which is why there {} expected", - pos, - if count == 1 { - "is 1 argument".to_string() - } else { - format!("are {} arguments", count) - }, - ), - ); - if let Some(arg) = self.args.get(pos) { - e.span_label( - arg.expr.span, - "this parameter corresponds to the precision flag", - ); - } - zero_based_note = true; - } - _ => {} - } - } - if let Some(span) = fmt.width_span { - let span = self.fmtsp.from_inner(InnerSpan::new(span.start, span.end)); - match fmt.width { - parse::CountIsParam(pos) if pos >= self.num_args() => { - e.span_label( - span, - &format!( - "this width flag expects an `usize` argument at position {}, \ - but {}", - pos, - self.describe_num_args(), - ), - ); - zero_based_note = true; - } - _ => {} - } - } - } - if zero_based_note { - e.note("positional arguments are zero-based"); - } - if !self.arg_with_formatting.is_empty() { - e.note( - "for information about formatting flags, visit \ - https://doc.rust-lang.org/std/fmt/index.html", - ); - } - - e.emit(); - } - - /// Actually verifies and tracks a given format placeholder - /// (a.k.a. argument). - fn verify_arg_type(&mut self, arg: Position, ty: ArgumentType) { - if let Exact(arg) = arg { - if arg >= self.num_args() { - self.invalid_refs.push((arg, self.curpiece)); - return; - } - } - - match arg { - Exact(arg) | Capture(arg) => { - match ty { - Placeholder(_) => { - // record every (position, type) combination only once - let seen_ty = &mut self.arg_unique_types[arg]; - let i = seen_ty.iter().position(|x| *x == ty).unwrap_or_else(|| { - let i = seen_ty.len(); - seen_ty.push(ty); - i - }); - self.arg_types[arg].push(i); - } - Count => { - if let Entry::Vacant(e) = self.count_positions.entry(arg) { - let i = self.count_positions_count; - e.insert(i); - self.count_args.push(arg); - self.count_positions_count += 1; - } - } - } - } - - Named(name, span) => { - match self.names.get(&name) { - Some(&idx) => { - // Treat as positional arg. - self.verify_arg_type(Capture(idx), ty) - } - None => { - // For the moment capturing variables from format strings expanded from macros is - // disabled (see RFC #2795) - if self.is_literal { - // Treat this name as a variable to capture from the surrounding scope - let idx = self.args.len(); - self.arg_types.push(Vec::new()); - self.arg_unique_types.push(Vec::new()); - let span = if self.is_literal { - self.fmtsp.from_inner(span) - } else { - self.fmtsp - }; - self.num_captured_args += 1; - self.args.push(FormatArg { - expr: self.ecx.expr_ident(span, Ident::new(name, span)), - name: Some(Ident::new(name, span)), - }); - self.names.insert(name, idx); - self.verify_arg_type(Capture(idx), ty) - } else { - let msg = format!("there is no argument named `{}`", name); - let sp = if self.is_literal { - self.fmtsp.from_inner(span) - } else { - self.fmtsp - }; - let mut err = self.ecx.struct_span_err(sp, &msg); - - err.note(&format!( - "did you intend to capture a variable `{}` from \ - the surrounding scope?", - name - )); - err.note( - "to avoid ambiguity, `format_args!` cannot capture variables \ - when the format string is expanded from a macro", - ); - - err.emit(); - } - } - } - } - } - } - - /// Builds the mapping between format placeholders and argument objects. - fn build_index_map(&mut self) { - // NOTE: Keep the ordering the same as `into_expr`'s expansion would do! - let args_len = self.args.len(); - self.arg_index_map.reserve(args_len); - - let mut sofar = 0usize; - - // Map the arguments - for i in 0..args_len { - let arg_types = &self.arg_types[i]; - let arg_offsets = arg_types.iter().map(|offset| sofar + *offset).collect::>(); - self.arg_index_map.push(arg_offsets); - sofar += self.arg_unique_types[i].len(); - } - - // Record starting index for counts, which appear just after arguments - self.count_args_index_offset = sofar; - } - - fn rtpath(ecx: &ExtCtxt<'_>, s: Symbol) -> Vec { - ecx.std_path(&[sym::fmt, sym::rt, sym::v1, s]) - } - - fn build_count(&self, c: parse::Count<'_>) -> P { - let sp = self.macsp; - let count = |c, arg| { - let mut path = Context::rtpath(self.ecx, sym::Count); - path.push(Ident::new(c, sp)); - match arg { - Some(arg) => self.ecx.expr_call_global(sp, path, vec![arg]), - None => self.ecx.expr_path(self.ecx.path_global(sp, path)), - } - }; - match c { - parse::CountIs(i) => count(sym::Is, Some(self.ecx.expr_usize(sp, i))), - parse::CountIsParam(i) | parse::CountIsStar(i) => { - // This needs mapping too, as `i` is referring to a macro - // argument. If `i` is not found in `count_positions` then - // the error had already been emitted elsewhere. - let i = self.count_positions.get(&i).cloned().unwrap_or(0) - + self.count_args_index_offset; - count(sym::Param, Some(self.ecx.expr_usize(sp, i))) - } - parse::CountImplied => count(sym::Implied, None), - // should never be the case, names are already resolved - parse::CountIsName(..) => panic!("should never happen"), - } - } - - /// Build a literal expression from the accumulated string literals - fn build_literal_string(&mut self) -> P { - let sp = self.fmtsp; - let s = Symbol::intern(&self.literal); - self.literal.clear(); - self.ecx.expr_str(sp, s) - } - - /// Builds a static `rt::Argument` from a `parse::Piece` or append - /// to the `literal` string. - fn build_piece( - &mut self, - piece: &parse::Piece<'a>, - arg_index_consumed: &mut Vec, - ) -> Option> { - let sp = self.macsp; - match *piece { - parse::String(s) => { - self.literal.push_str(s); - None - } - parse::NextArgument(ref arg) => { - // Build the position - let pos = { - match arg.position { - parse::ArgumentIs(i, ..) | parse::ArgumentImplicitlyIs(i) => { - // Map to index in final generated argument array - // in case of multiple types specified - let arg_idx = match arg_index_consumed.get_mut(i) { - None => 0, // error already emitted elsewhere - Some(offset) => { - let idx_map = &self.arg_index_map[i]; - // unwrap_or branch: error already emitted elsewhere - let arg_idx = *idx_map.get(*offset).unwrap_or(&0); - *offset += 1; - arg_idx - } - }; - self.ecx.expr_usize(sp, arg_idx) - } - - // should never be the case, because names are already - // resolved. - parse::ArgumentNamed(..) => panic!("should never happen"), - } - }; - - let simple_arg = parse::Argument { - position: { - // We don't have ArgumentNext any more, so we have to - // track the current argument ourselves. - let i = self.curarg; - self.curarg += 1; - parse::ArgumentIs(i) - }, - position_span: arg.position_span, - format: parse::FormatSpec { - fill: None, - align: parse::AlignUnknown, - flags: 0, - precision: parse::CountImplied, - precision_span: arg.format.precision_span, - width: parse::CountImplied, - width_span: arg.format.width_span, - ty: arg.format.ty, - ty_span: arg.format.ty_span, - }, - }; - - let fill = arg.format.fill.unwrap_or(' '); - let pos_simple = arg.position.index() == simple_arg.position.index(); - - if !pos_simple || arg.format != simple_arg.format { - self.all_pieces_simple = false; - } - - // Build the format - let fill = self.ecx.expr_char(sp, fill); - let align = |name| { - let mut p = Context::rtpath(self.ecx, sym::Alignment); - p.push(Ident::new(name, sp)); - self.ecx.path_global(sp, p) - }; - let align = match arg.format.align { - parse::AlignLeft => align(sym::Left), - parse::AlignRight => align(sym::Right), - parse::AlignCenter => align(sym::Center), - parse::AlignUnknown => align(sym::Unknown), - }; - let align = self.ecx.expr_path(align); - let flags = self.ecx.expr_u32(sp, arg.format.flags); - let prec = self.build_count(arg.format.precision); - let width = self.build_count(arg.format.width); - let path = self.ecx.path_global(sp, Context::rtpath(self.ecx, sym::FormatSpec)); - let fmt = self.ecx.expr_struct( - sp, - path, - vec![ - self.ecx.field_imm(sp, Ident::new(sym::fill, sp), fill), - self.ecx.field_imm(sp, Ident::new(sym::align, sp), align), - self.ecx.field_imm(sp, Ident::new(sym::flags, sp), flags), - self.ecx.field_imm(sp, Ident::new(sym::precision, sp), prec), - self.ecx.field_imm(sp, Ident::new(sym::width, sp), width), - ], - ); - - let path = self.ecx.path_global(sp, Context::rtpath(self.ecx, sym::Argument)); - Some(self.ecx.expr_struct( - sp, - path, - vec![ - self.ecx.field_imm(sp, Ident::new(sym::position, sp), pos), - self.ecx.field_imm(sp, Ident::new(sym::format, sp), fmt), - ], - )) - } - } - } - - /// Actually builds the expression which the format_args! block will be - /// expanded to. - fn into_expr(self) -> P { - let mut original_args = self.args; - let mut fmt_args = Vec::with_capacity( - self.arg_unique_types.iter().map(|v| v.len()).sum::() + self.count_args.len(), - ); - - // First, build up the static array which will become our precompiled - // format "string" - let pieces = self.ecx.expr_array_ref(self.fmtsp, self.str_pieces); - - // We need to construct a &[ArgumentV1] to pass into the fmt::Arguments - // constructor. In general the expressions in this slice might be - // permuted from their order in original_args (such as in the case of - // "{1} {0}"), or may have multiple entries referring to the same - // element of original_args ("{0} {0}"). - // - // The following vector has one item per element of our output slice, - // identifying the index of which element of original_args it's passing, - // and that argument's type. - let mut fmt_arg_index_and_ty = SmallVec::<[(usize, &ArgumentType); 8]>::new(); - for (i, unique_types) in self.arg_unique_types.iter().enumerate() { - fmt_arg_index_and_ty.extend(unique_types.iter().map(|ty| (i, ty))); - } - fmt_arg_index_and_ty.extend(self.count_args.iter().map(|&i| (i, &Count))); - - // Figure out whether there are permuted or repeated elements. If not, - // we can generate simpler code. - // - // The sequence has no indices out of order or repeated if: for every - // adjacent pair of elements, the first one's index is less than the - // second one's index. - let nicely_ordered = - fmt_arg_index_and_ty.array_windows().all(|[(i, _i_ty), (j, _j_ty)]| i < j); - - // We want to emit: - // - // [ArgumentV1::new(&$arg0, …), ArgumentV1::new(&$arg1, …), …] - // - // However, it's only legal to do so if $arg0, $arg1, … were written in - // exactly that order by the programmer. When arguments are permuted, we - // want them evaluated in the order written by the programmer, not in - // the order provided to fmt::Arguments. When arguments are repeated, we - // want the expression evaluated only once. - // - // Further, if any arg _after the first one_ contains a yield point such - // as `await` or `yield`, the above short form is inconvenient for the - // caller because it would keep a temporary of type ArgumentV1 alive - // across the yield point. ArgumentV1 can't implement Send since it - // holds a type-erased arbitrary type. - // - // Thus in the not nicely ordered case, and in the yielding case, we - // emit the following instead: - // - // match (&$arg0, &$arg1, …) { - // args => [ArgumentV1::new(args.$i, …), ArgumentV1::new(args.$j, …), …] - // } - // - // for the sequence of indices $i, $j, … governed by fmt_arg_index_and_ty. - // This more verbose representation ensures that all arguments are - // evaluated a single time each, in the order written by the programmer, - // and that the surrounding future/generator (if any) is Send whenever - // possible. - let no_need_for_match = nicely_ordered - && !original_args.iter().skip(1).any(|arg| may_contain_yield_point(&arg.expr)); - - for (arg_index, arg_ty) in fmt_arg_index_and_ty { - let e = &mut original_args[arg_index].expr; - let span = e.span; - let arg = if no_need_for_match { - let expansion_span = e.span.with_ctxt(self.macsp.ctxt()); - // The indices are strictly ordered so e has not been taken yet. - self.ecx.expr_addr_of(expansion_span, P(e.take())) - } else { - let def_site = self.ecx.with_def_site_ctxt(span); - let args_tuple = self.ecx.expr_ident(def_site, Ident::new(sym::args, def_site)); - let member = Ident::new(sym::integer(arg_index), def_site); - self.ecx.expr(def_site, ast::ExprKind::Field(args_tuple, member)) - }; - fmt_args.push(Context::format_arg(self.ecx, self.macsp, span, arg_ty, arg)); - } - - let args_array = self.ecx.expr_array(self.macsp, fmt_args); - let args_slice = self.ecx.expr_addr_of( - self.macsp, - if no_need_for_match { - args_array - } else { - // In the !no_need_for_match case, none of the exprs were moved - // away in the previous loop. - // - // This uses the arg span for `&arg` so that borrowck errors - // point to the specific expression passed to the macro (the - // span is otherwise unavailable in the MIR used by borrowck). - let heads = original_args - .into_iter() - .map(|arg| { - self.ecx.expr_addr_of(arg.expr.span.with_ctxt(self.macsp.ctxt()), arg.expr) - }) - .collect(); - - let pat = self.ecx.pat_ident(self.macsp, Ident::new(sym::args, self.macsp)); - let arm = self.ecx.arm(self.macsp, pat, args_array); - let head = self.ecx.expr(self.macsp, ast::ExprKind::Tup(heads)); - self.ecx.expr_match(self.macsp, head, vec![arm]) - }, - ); - - // Now create the fmt::Arguments struct with all our locals we created. - let (fn_name, fn_args) = if self.all_pieces_simple { - ("new_v1", vec![pieces, args_slice]) - } else { - // Build up the static array which will store our precompiled - // nonstandard placeholders, if there are any. - let fmt = self.ecx.expr_array_ref(self.macsp, self.pieces); - - let path = self.ecx.std_path(&[sym::fmt, sym::UnsafeArg, sym::new]); - let unsafe_arg = self.ecx.expr_call_global(self.macsp, path, Vec::new()); - let unsafe_expr = self.ecx.expr_block(P(ast::Block { - stmts: vec![self.ecx.stmt_expr(unsafe_arg)], - id: ast::DUMMY_NODE_ID, - rules: BlockCheckMode::Unsafe(UnsafeSource::CompilerGenerated), - span: self.macsp, - tokens: None, - could_be_bare_literal: false, - })); - - ("new_v1_formatted", vec![pieces, args_slice, fmt, unsafe_expr]) - }; - - let path = self.ecx.std_path(&[sym::fmt, sym::Arguments, Symbol::intern(fn_name)]); - self.ecx.expr_call_global(self.macsp, path, fn_args) - } - - fn format_arg( - ecx: &ExtCtxt<'_>, - macsp: Span, - mut sp: Span, - ty: &ArgumentType, - arg: P, - ) -> P { - sp = ecx.with_def_site_ctxt(sp); - let trait_ = match *ty { - Placeholder(trait_) if trait_ == "" => return DummyResult::raw_expr(sp, true), - Placeholder(trait_) => trait_, - Count => { - let path = ecx.std_path(&[sym::fmt, sym::ArgumentV1, sym::from_usize]); - return ecx.expr_call_global(macsp, path, vec![arg]); - } - }; - let new_fn_name = match trait_ { - "Display" => "new_display", - "Debug" => "new_debug", - "LowerExp" => "new_lower_exp", - "UpperExp" => "new_upper_exp", - "Octal" => "new_octal", - "Pointer" => "new_pointer", - "Binary" => "new_binary", - "LowerHex" => "new_lower_hex", - "UpperHex" => "new_upper_hex", - _ => unreachable!(), - }; - - let path = ecx.std_path(&[sym::fmt, sym::ArgumentV1, Symbol::intern(new_fn_name)]); - ecx.expr_call_global(sp, path, vec![arg]) - } -} - -fn expand_format_args_impl<'cx>( - ecx: &'cx mut ExtCtxt<'_>, - mut sp: Span, - tts: TokenStream, - nl: bool, -) -> Box { - sp = ecx.with_def_site_ctxt(sp); - match parse_args(ecx, sp, tts) { - Ok((efmt, args, names)) => { - MacEager::expr(expand_preparsed_format_args(ecx, sp, efmt, args, names, nl)) - } - Err(mut err) => { - err.emit(); - DummyResult::any(sp) - } - } -} - -pub fn expand_format_args<'cx>( - ecx: &'cx mut ExtCtxt<'_>, - sp: Span, - tts: TokenStream, -) -> Box { - expand_format_args_impl(ecx, sp, tts, false) -} - -pub fn expand_format_args_nl<'cx>( - ecx: &'cx mut ExtCtxt<'_>, - sp: Span, - tts: TokenStream, -) -> Box { - expand_format_args_impl(ecx, sp, tts, true) -} - -fn create_lints_for_named_arguments_used_positionally(cx: &mut Context<'_, '_>) { - for named_arg in &cx.unused_names_lint.positional_named_args { - let (position_sp_to_replace, position_sp_for_msg) = named_arg.get_positional_arg_spans(cx); - - let msg = format!("named argument `{}` is not used by name", named_arg.replacement); - - cx.ecx.buffered_early_lint.push(BufferedEarlyLint { - span: MultiSpan::from_span(named_arg.positional_named_arg_span), - msg: msg.into(), - node_id: ast::CRATE_NODE_ID, - lint_id: LintId::of(&NAMED_ARGUMENTS_USED_POSITIONALLY), - diagnostic: BuiltinLintDiagnostics::NamedArgumentUsedPositionally { - position_sp_to_replace, - position_sp_for_msg, - named_arg_sp: named_arg.positional_named_arg_span, - named_arg_name: named_arg.replacement.to_string(), - is_formatting_arg: named_arg.ty != PositionalNamedArgType::Arg, - }, - }); - } -} - -/// Take the various parts of `format_args!(efmt, args..., name=names...)` -/// and construct the appropriate formatting expression. -pub fn expand_preparsed_format_args( +pub fn make_format_args( ecx: &mut ExtCtxt<'_>, - sp: Span, efmt: P, - args: Vec, - names: FxHashMap, + mut args: Vec<(P, FormatArgKind)>, append_newline: bool, -) -> P { - // NOTE: this verbose way of initializing `Vec>` is because - // `ArgumentType` does not derive `Clone`. - let arg_types: Vec<_> = (0..args.len()).map(|_| Vec::new()).collect(); - let arg_unique_types: Vec<_> = (0..args.len()).map(|_| Vec::new()).collect(); - - let mut macsp = ecx.call_site(); - macsp = ecx.with_def_site_ctxt(macsp); +) -> Result { + let start_of_named_args = + args.iter().position(|arg| arg.1.ident().is_some()).unwrap_or(args.len()); let msg = "format argument must be a string literal"; - let fmt_sp = efmt.span; - let efmt_kind_is_lit: bool = matches!(efmt.kind, ast::ExprKind::Lit(_)); + let fmt_span = efmt.span; 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)); @@ -1230,7 +328,7 @@ pub fn expand_preparsed_format_args( }; if !suggested { err.span_suggestion( - fmt_sp.shrink_to_lo(), + fmt_span.shrink_to_lo(), "you might be missing a string literal to format with", format!("\"{}\", ", sugg_fmt), Applicability::MaybeIncorrect, @@ -1238,7 +336,7 @@ pub fn expand_preparsed_format_args( } err.emit(); } - return DummyResult::raw_expr(sp, true); + return Err(()); } }; @@ -1248,7 +346,7 @@ pub fn expand_preparsed_format_args( }; let fmt_str = fmt_str.as_str(); // for the suggestions below - let fmt_snippet = ecx.source_map().span_to_snippet(fmt_sp).ok(); + let fmt_snippet = ecx.source_map().span_to_snippet(fmt_span).ok(); let mut parser = parse::Parser::new( fmt_str, str_style, @@ -1257,18 +355,20 @@ pub fn expand_preparsed_format_args( parse::ParseMode::Format, ); - let mut unverified_pieces = Vec::new(); + let mut pieces = Vec::new(); while let Some(piece) = parser.next() { if !parser.errors.is_empty() { break; } else { - unverified_pieces.push(piece); + pieces.push(piece); } } + let is_literal = parser.is_literal; + if !parser.errors.is_empty() { let err = parser.errors.remove(0); - let sp = if efmt_kind_is_lit { + let sp = if is_literal { fmt_span.from_inner(InnerSpan::new(err.span.start, err.span.end)) } else { // The format string could be another macro invocation, e.g.: @@ -1287,24 +387,22 @@ pub fn expand_preparsed_format_args( e.note(¬e); } if let Some((label, span)) = err.secondary_label { - if efmt_kind_is_lit { + if is_literal { e.span_label(fmt_span.from_inner(InnerSpan::new(span.start, span.end)), label); } } if err.should_be_replaced_with_positional_argument { let captured_arg_span = fmt_span.from_inner(InnerSpan::new(err.span.start, err.span.end)); - let n_positional_args = - args.iter().rposition(|arg| arg.name.is_none()).map_or(0, |i| i + 1); if let Ok(arg) = ecx.source_map().span_to_snippet(captured_arg_span) { - let span = match args[..n_positional_args].last() { - Some(arg) => arg.expr.span, - None => fmt_sp, + let span = match args[..start_of_named_args].last() { + Some(arg) => arg.0.span, + None => fmt_span, }; e.multipart_suggestion_verbose( "consider using a positional formatting argument instead", vec![ - (captured_arg_span, n_positional_args.to_string()), + (captured_arg_span, start_of_named_args.to_string()), (span.shrink_to_hi(), format!(", {}", arg)), ], Applicability::MachineApplicable, @@ -1312,241 +410,648 @@ pub fn expand_preparsed_format_args( } } e.emit(); - return DummyResult::raw_expr(sp, true); + return Err(()); } - let arg_spans = parser - .arg_places - .iter() - .map(|span| fmt_span.from_inner(InnerSpan::new(span.start, span.end))) - .collect(); - - let mut cx = Context { - ecx, - args, - num_captured_args: 0, - arg_types, - arg_unique_types, - names, - curarg: 0, - curpiece: 0, - arg_index_map: Vec::new(), - count_args: Vec::new(), - count_positions: FxHashMap::default(), - count_positions_count: 0, - count_args_index_offset: 0, - literal: String::new(), - pieces: Vec::with_capacity(unverified_pieces.len()), - str_pieces: Vec::with_capacity(unverified_pieces.len()), - all_pieces_simple: true, - macsp, - fmtsp: fmt_span, - invalid_refs: Vec::new(), - arg_spans, - arg_with_formatting: Vec::new(), - is_literal: parser.is_literal, - unused_names_lint: PositionalNamedArgsLint { positional_named_args: vec![] }, + let to_span = |inner_span: rustc_parse_format::InnerSpan| { + is_literal.then(|| { + fmt_span.from_inner(InnerSpan { start: inner_span.start, end: inner_span.end }) + }) }; - // This needs to happen *after* the Parser has consumed all pieces to create all the spans - let pieces = unverified_pieces - .into_iter() - .map(|mut piece| { - cx.verify_piece(&piece); - cx.resolve_name_inplace(&mut piece); - piece - }) - .collect::>(); + enum ArgRef<'a> { + Index(usize), + Name(&'a str, Option), + } + use ArgRef::*; + + let num_explicit_args = args.len(); + let mut used = vec![false; num_explicit_args]; + let mut invalid_refs = Vec::new(); + let mut numeric_refences_to_named_arg = Vec::new(); + + let mut lookup_arg = |arg: ArgRef<'_>, + span: Option, + used_as: PositionUsedAs, + kind: FormatArgPositionKind| + -> FormatArgPosition { + let index = match arg { + Index(index) => { + if let Some((_, arg_kind)) = args.get(index) { + match arg_kind { + FormatArgKind::Normal => { + used[index] = true; + Ok(index) + } + FormatArgKind::Named(_) => { + used[index] = true; + numeric_refences_to_named_arg.push((index, span, used_as)); + Ok(index) + } + FormatArgKind::Captured(_) => { + // Doesn't exist as an explicit argument. + invalid_refs.push((index, span, used_as, kind)); + Err(index) + } + } + } else { + invalid_refs.push((index, span, used_as, kind)); + Err(index) + } + } + Name(name, span) => { + let name = Symbol::intern(name); + if let Some(i) = args[start_of_named_args..] + .iter() + .position(|arg| arg.1.ident().is_some_and(|id| id.name == name)) + { + let index = start_of_named_args + i; + if !matches!(args[index].1, FormatArgKind::Captured(_)) { + used[index] = true; + } + Ok(index) + } else { + let span = span.unwrap_or(fmt_span); + let ident = Ident::new(name, span); + let arg = if is_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(); + DummyResult::raw_expr(span, true) + }; + args.push((arg, FormatArgKind::Captured(ident))); + Ok(args.len() - 1) + } + } + }; + FormatArgPosition { index, kind, span } + }; + + let mut template = Vec::new(); + let mut unfinished_literal = String::new(); + let mut placeholder_index = 0; - let numbered_position_args = pieces.iter().any(|arg: &parse::Piece<'_>| match *arg { - parse::String(_) => false, - parse::NextArgument(arg) => matches!(arg.position, parse::Position::ArgumentIs(..)), - }); + for piece in pieces { + match piece { + parse::Piece::String(s) => { + unfinished_literal.push_str(s); + } + parse::Piece::NextArgument(parse::Argument { position, position_span, format }) => { + if !unfinished_literal.is_empty() { + template.push(FormatArgsPiece::Literal(Symbol::intern(&unfinished_literal))); + unfinished_literal.clear(); + } - cx.build_index_map(); + let span = parser.arg_places.get(placeholder_index).and_then(|&s| to_span(s)); + placeholder_index += 1; + + let position_span = to_span(position_span); + let argument = match position { + parse::ArgumentImplicitlyIs(i) => lookup_arg( + Index(i), + position_span, + Placeholder(span), + FormatArgPositionKind::Implicit, + ), + parse::ArgumentIs(i) => lookup_arg( + Index(i), + position_span, + Placeholder(span), + FormatArgPositionKind::Number, + ), + parse::ArgumentNamed(name) => lookup_arg( + Name(name, position_span), + position_span, + Placeholder(span), + FormatArgPositionKind::Named, + ), + }; - let mut arg_index_consumed = vec![0usize; cx.arg_index_map.len()]; + let alignment = match format.align { + parse::AlignUnknown => None, + parse::AlignLeft => Some(FormatAlignment::Left), + parse::AlignRight => Some(FormatAlignment::Right), + parse::AlignCenter => Some(FormatAlignment::Center), + }; - for piece in pieces { - if let Some(piece) = cx.build_piece(&piece, &mut arg_index_consumed) { - let s = cx.build_literal_string(); - cx.str_pieces.push(s); - cx.pieces.push(piece); + let format_trait = match format.ty { + "" => FormatTrait::Display, + "?" => FormatTrait::Debug, + "e" => FormatTrait::LowerExp, + "E" => FormatTrait::UpperExp, + "o" => FormatTrait::Octal, + "p" => FormatTrait::Pointer, + "b" => FormatTrait::Binary, + "x" => FormatTrait::LowerHex, + "X" => FormatTrait::UpperHex, + _ => { + invalid_placeholder_type_error(ecx, format.ty, format.ty_span, fmt_span); + FormatTrait::Display + } + }; + + let precision_span = format.precision_span.and_then(to_span); + let precision = match format.precision { + parse::CountIs(n) => Some(FormatCount::Literal(n)), + parse::CountIsName(name, name_span) => Some(FormatCount::Argument(lookup_arg( + Name(name, to_span(name_span)), + precision_span, + Precision, + FormatArgPositionKind::Named, + ))), + parse::CountIsParam(i) => Some(FormatCount::Argument(lookup_arg( + Index(i), + precision_span, + Precision, + FormatArgPositionKind::Number, + ))), + parse::CountIsStar(i) => Some(FormatCount::Argument(lookup_arg( + Index(i), + precision_span, + Precision, + FormatArgPositionKind::Implicit, + ))), + parse::CountImplied => None, + }; + + let width_span = format.width_span.and_then(to_span); + let width = match format.width { + parse::CountIs(n) => Some(FormatCount::Literal(n)), + parse::CountIsName(name, name_span) => Some(FormatCount::Argument(lookup_arg( + Name(name, to_span(name_span)), + width_span, + Width, + FormatArgPositionKind::Named, + ))), + parse::CountIsParam(i) => Some(FormatCount::Argument(lookup_arg( + Index(i), + width_span, + Width, + FormatArgPositionKind::Number, + ))), + parse::CountIsStar(_) => unreachable!(), + parse::CountImplied => None, + }; + + template.push(FormatArgsPiece::Placeholder(FormatPlaceholder { + argument, + span, + format_trait, + format_options: FormatOptions { + fill: format.fill, + alignment, + flags: format.flags, + precision, + width, + }, + })); + } } } - if !cx.literal.is_empty() { - let s = cx.build_literal_string(); - cx.str_pieces.push(s); + if !unfinished_literal.is_empty() { + template.push(FormatArgsPiece::Literal(Symbol::intern(&unfinished_literal))); + drop(unfinished_literal); } - if !cx.invalid_refs.is_empty() { - cx.report_invalid_references(numbered_position_args); + if !invalid_refs.is_empty() { + report_invalid_references( + ecx, + &invalid_refs, + &template, + fmt_span, + num_explicit_args, + &args, + parser, + ); } - // Make sure that all arguments were used and all arguments have types. - let errs = cx - .arg_types + let unused = used .iter() .enumerate() - .filter(|(i, ty)| ty.is_empty() && !cx.count_positions.contains_key(&i)) + .filter(|&(_, used)| !used) .map(|(i, _)| { - let msg = if cx.args[i].name.is_some() { + let msg = if let FormatArgKind::Named(_) = args[i].1 { "named argument never used" } else { "argument never used" }; - (cx.args[i].expr.span, msg) + (args[i].0.span, msg) }) .collect::>(); - let errs_len = errs.len(); - if !errs.is_empty() { - let args_used = cx.arg_types.len() - errs_len; - let args_unused = errs_len; + if !unused.is_empty() { + // If there's a lot of unused arguments, + // let's check if this format arguments looks like another syntax (printf / shell). + let detect_foreign_fmt = unused.len() > num_explicit_args / 2; + report_missing_placeholders(ecx, unused, detect_foreign_fmt, str_style, fmt_str, fmt_span); + } - let mut diag = { - if let [(sp, msg)] = &errs[..] { - let mut diag = cx.ecx.struct_span_err(*sp, *msg); - diag.span_label(*sp, *msg); - diag - } else { - let mut diag = cx.ecx.struct_span_err( - errs.iter().map(|&(sp, _)| sp).collect::>(), - "multiple unused formatting arguments", - ); - diag.span_label(cx.fmtsp, "multiple missing formatting specifiers"); - for (sp, msg) in errs { - diag.span_label(sp, msg); + // Only check for unused named argument names if there are no other errors to avoid causing + // too much noise in output errors, such as when a named argument is entirely unused. + if invalid_refs.is_empty() && ecx.sess.err_count() == 0 { + for &(index, span, used_as) in &numeric_refences_to_named_arg { + let (position_sp_to_replace, position_sp_for_msg) = match used_as { + Placeholder(pspan) => (span, pspan), + Precision => { + // Strip the leading `.` for precision. + let span = span.map(|span| span.with_lo(span.lo() + BytePos(1))); + (span, span) } - diag - } - }; + Width => (span, span), + }; + let arg_name = args[index].1.ident().unwrap(); + ecx.buffered_early_lint.push(BufferedEarlyLint { + span: arg_name.span.into(), + msg: format!("named argument `{}` is not used by name", arg_name.name).into(), + node_id: ast::CRATE_NODE_ID, + lint_id: LintId::of(&NAMED_ARGUMENTS_USED_POSITIONALLY), + diagnostic: BuiltinLintDiagnostics::NamedArgumentUsedPositionally { + position_sp_to_replace, + position_sp_for_msg, + named_arg_sp: arg_name.span, + named_arg_name: arg_name.name.to_string(), + is_formatting_arg: matches!(used_as, Width | Precision), + }, + }); + } + } - // Used to ensure we only report translations for *one* kind of foreign format. - let mut found_foreign = false; - // Decide if we want to look for foreign formatting directives. - if args_used < args_unused { - use super::format_foreign as foreign; - - // The set of foreign substitutions we've explained. This prevents spamming the user - // with `%d should be written as {}` over and over again. - let mut explained = FxHashSet::default(); - - macro_rules! check_foreign { - ($kind:ident) => {{ - let mut show_doc_note = false; - - let mut suggestions = vec![]; - // account for `"` and account for raw strings `r#` - let padding = str_style.map(|i| i + 2).unwrap_or(1); - for sub in foreign::$kind::iter_subs(fmt_str, padding) { - let (trn, success) = match sub.translate() { - Ok(trn) => (trn, true), - Err(Some(msg)) => (msg, false), - - // If it has no translation, don't call it out specifically. - _ => continue, - }; - - let pos = sub.position(); - let sub = String::from(sub.as_str()); - if explained.contains(&sub) { - continue; - } - explained.insert(sub.clone()); + Ok(FormatArgs { span: fmt_span, template, arguments: args }) +} - if !found_foreign { - found_foreign = true; - show_doc_note = true; - } +fn invalid_placeholder_type_error( + ecx: &ExtCtxt<'_>, + ty: &str, + ty_span: Option, + 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 &[ + ("", "Display"), + ("?", "Debug"), + ("e", "LowerExp"), + ("E", "UpperExp"), + ("o", "Octal"), + ("p", "Pointer"), + ("b", "Binary"), + ("x", "LowerHex"), + ("X", "UpperHex"), + ] { + err.tool_only_span_suggestion( + sp, + &format!("use the `{}` trait", name), + *fmt, + Applicability::MaybeIncorrect, + ); + } + } + err.emit(); +} - if let Some(inner_sp) = pos { - let sp = fmt_sp.from_inner(inner_sp); - - if success { - suggestions.push((sp, trn)); - } else { - diag.span_note( - sp, - &format!("format specifiers use curly braces, and {}", trn), - ); - } - } else { - if success { - diag.help(&format!("`{}` should be written as `{}`", sub, trn)); - } else { - diag.note(&format!( - "`{}` should use curly braces, and {}", - sub, trn - )); - } - } +fn report_missing_placeholders( + ecx: &mut ExtCtxt<'_>, + unused: Vec<(Span, &str)>, + 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 + } 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 + }; + + // Used to ensure we only report translations for *one* kind of foreign format. + let mut found_foreign = false; + + // Decide if we want to look for foreign formatting directives. + if detect_foreign_fmt { + use super::format_foreign as foreign; + + // The set of foreign substitutions we've explained. This prevents spamming the user + // with `%d should be written as {}` over and over again. + let mut explained = FxHashSet::default(); + + macro_rules! check_foreign { + ($kind:ident) => {{ + let mut show_doc_note = false; + + let mut suggestions = vec![]; + // account for `"` and account for raw strings `r#` + let padding = str_style.map(|i| i + 2).unwrap_or(1); + for sub in foreign::$kind::iter_subs(fmt_str, padding) { + let (trn, success) = match sub.translate() { + Ok(trn) => (trn, true), + Err(Some(msg)) => (msg, false), + + // If it has no translation, don't call it out specifically. + _ => continue, + }; + + let pos = sub.position(); + let sub = String::from(sub.as_str()); + if explained.contains(&sub) { + continue; } + explained.insert(sub.clone()); - if show_doc_note { - diag.note(concat!( - stringify!($kind), - " formatting not supported; see the documentation for `std::fmt`", - )); + if !found_foreign { + found_foreign = true; + show_doc_note = true; } - if suggestions.len() > 0 { - diag.multipart_suggestion( - "format specifiers use curly braces", - suggestions, - Applicability::MachineApplicable, - ); + + if let Some(inner_sp) = pos { + let sp = fmt_span.from_inner(inner_sp); + + if success { + suggestions.push((sp, trn)); + } else { + diag.span_note( + sp, + &format!("format specifiers use curly braces, and {}", trn), + ); + } + } else { + if success { + diag.help(&format!("`{}` should be written as `{}`", sub, trn)); + } else { + diag.note(&format!("`{}` should use curly braces, and {}", sub, trn)); + } } - }}; - } + } - check_foreign!(printf); - if !found_foreign { - check_foreign!(shell); - } - } - if !found_foreign && errs_len == 1 { - diag.span_label(cx.fmtsp, "formatting specifier missing"); + if show_doc_note { + diag.note(concat!( + stringify!($kind), + " formatting not supported; see the documentation for `std::fmt`", + )); + } + if suggestions.len() > 0 { + diag.multipart_suggestion( + "format specifiers use curly braces", + suggestions, + Applicability::MachineApplicable, + ); + } + }}; } - diag.emit(); - } else if cx.invalid_refs.is_empty() && cx.ecx.sess.err_count() == 0 { - // Only check for unused named argument names if there are no other errors to avoid causing - // too much noise in output errors, such as when a named argument is entirely unused. - create_lints_for_named_arguments_used_positionally(&mut cx); + check_foreign!(printf); + if !found_foreign { + check_foreign!(shell); + } + } + if !found_foreign && unused.len() == 1 { + diag.span_label(fmt_span, "formatting specifier missing"); } - cx.into_expr() + diag.emit(); } -fn may_contain_yield_point(e: &ast::Expr) -> bool { - struct MayContainYieldPoint(bool); +/// Handle invalid references to positional arguments. Output different +/// errors for the case where all arguments are positional and for when +/// there are named arguments or numbered positional arguments in the +/// format string. +fn report_invalid_references( + ecx: &mut ExtCtxt<'_>, + invalid_refs: &[(usize, Option, PositionUsedAs, FormatArgPositionKind)], + template: &[FormatArgsPiece], + fmt_span: Span, + num_explicit_args: usize, + args: &[(P, FormatArgKind)], + parser: parse::Parser<'_>, +) { + let num_args_desc = match num_explicit_args { + 0 => "no arguments were given".to_string(), + 1 => "there is 1 argument".to_string(), + n => format!("there are {} arguments", n), + }; - impl Visitor<'_> for MayContainYieldPoint { - fn visit_expr(&mut self, e: &ast::Expr) { - if let ast::ExprKind::Await(_) | ast::ExprKind::Yield(_) = e.kind { - self.0 = true; - } else { - visit::walk_expr(self, e); + let mut e; + + if template.iter().all(|piece| match piece { + FormatArgsPiece::Placeholder(FormatPlaceholder { + argument: FormatArgPosition { kind: FormatArgPositionKind::Number, .. }, + .. + }) => false, + FormatArgsPiece::Placeholder(FormatPlaceholder { + format_options: + FormatOptions { + precision: + Some(FormatCount::Argument(FormatArgPosition { + kind: FormatArgPositionKind::Number, + .. + })), + .. + } + | FormatOptions { + width: + Some(FormatCount::Argument(FormatArgPosition { + kind: FormatArgPositionKind::Number, + .. + })), + .. + }, + .. + }) => false, + _ => true, + }) { + // There are no numeric positions. + // Collect all the implicit positions: + let mut spans = Vec::new(); + let mut num_placeholders = 0; + for piece in template { + let mut placeholder = None; + // `{arg:.*}` + if let FormatArgsPiece::Placeholder(FormatPlaceholder { + format_options: + FormatOptions { + precision: + Some(FormatCount::Argument(FormatArgPosition { + span, + kind: FormatArgPositionKind::Implicit, + .. + })), + .. + }, + .. + }) = piece + { + placeholder = *span; + num_placeholders += 1; + } + // `{}` + if let FormatArgsPiece::Placeholder(FormatPlaceholder { + argument: FormatArgPosition { kind: FormatArgPositionKind::Implicit, .. }, + span, + .. + }) = piece + { + placeholder = *span; + num_placeholders += 1; } + // For `{:.*}`, we only push one span. + spans.extend(placeholder); } - - fn visit_mac_call(&mut self, _: &ast::MacCall) { - self.0 = true; + let span = if spans.is_empty() { + MultiSpan::from_span(fmt_span) + } else { + MultiSpan::from_spans(spans) + }; + e = ecx.struct_span_err( + span, + &format!( + "{} positional argument{} in format string, but {}", + num_placeholders, + pluralize!(num_placeholders), + num_args_desc, + ), + ); + for (arg, _) in &args[..num_explicit_args] { + e.span_label(arg.span, ""); + } + // Point out `{:.*}` placeholders: those take an extra argument. + let mut has_precision_star = false; + for piece in template { + if let FormatArgsPiece::Placeholder(FormatPlaceholder { + format_options: + FormatOptions { + precision: + Some(FormatCount::Argument(FormatArgPosition { + index, + span: Some(span), + kind: FormatArgPositionKind::Implicit, + .. + })), + .. + }, + .. + }) = piece + { + let (Ok(index) | Err(index)) = index; + has_precision_star = true; + e.span_label( + *span, + &format!( + "this precision flag adds an extra required argument at position {}, which is why there {} expected", + index, + if num_placeholders == 1 { + "is 1 argument".to_string() + } else { + format!("are {} arguments", num_placeholders) + }, + ), + ); + } + } + if has_precision_star { + e.note("positional arguments are zero-based"); } + } else { + let mut indexes: Vec<_> = invalid_refs.iter().map(|&(index, _, _, _)| index).collect(); + // Avoid `invalid reference to positional arguments 7 and 7 (there is 1 argument)` + // for `println!("{7:7$}", 1);` + indexes.sort(); + indexes.dedup(); + let span: MultiSpan = if !parser.is_literal || parser.arg_places.is_empty() { + MultiSpan::from_span(fmt_span) + } else { + MultiSpan::from_spans(invalid_refs.iter().filter_map(|&(_, span, _, _)| span).collect()) + }; + let arg_list = if let &[index] = &indexes[..] { + format!("argument {index}") + } else { + let tail = indexes.pop().unwrap(); + format!( + "arguments {head} and {tail}", + head = indexes.into_iter().map(|i| i.to_string()).collect::>().join(", ") + ) + }; + e = ecx.struct_span_err( + span, + &format!("invalid reference to positional {} ({})", arg_list, num_args_desc), + ); + e.note("positional arguments are zero-based"); + } - fn visit_attribute(&mut self, _: &ast::Attribute) { - // Conservatively assume this may be a proc macro attribute in - // expression position. - self.0 = true; + if template.iter().any(|piece| match piece { + FormatArgsPiece::Placeholder(FormatPlaceholder { format_options: f, .. }) => { + *f != FormatOptions::default() } + _ => false, + }) { + e.note("for information about formatting flags, visit https://doc.rust-lang.org/std/fmt/index.html"); + } + + e.emit(); +} - fn visit_item(&mut self, _: &ast::Item) { - // Do not recurse into nested items. +fn expand_format_args_impl<'cx>( + ecx: &'cx mut ExtCtxt<'_>, + mut sp: Span, + tts: TokenStream, + nl: bool, +) -> 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) { + MacEager::expr(expand_parsed_format_args(ecx, format_args)) + } else { + MacEager::expr(DummyResult::raw_expr(sp, true)) + } + } + Err(mut err) => { + err.emit(); + DummyResult::any(sp) } } +} - let mut visitor = MayContainYieldPoint(false); - visitor.visit_expr(e); - visitor.0 +pub fn expand_format_args<'cx>( + ecx: &'cx mut ExtCtxt<'_>, + sp: Span, + tts: TokenStream, +) -> Box { + expand_format_args_impl(ecx, sp, tts, false) +} + +pub fn expand_format_args_nl<'cx>( + ecx: &'cx mut ExtCtxt<'_>, + sp: Span, + tts: TokenStream, +) -> Box { + expand_format_args_impl(ecx, sp, tts, true) } diff --git a/compiler/rustc_builtin_macros/src/format/expand.rs b/compiler/rustc_builtin_macros/src/format/expand.rs new file mode 100644 index 0000000000000..d1c114a67ac1e --- /dev/null +++ b/compiler/rustc_builtin_macros/src/format/expand.rs @@ -0,0 +1,352 @@ +use super::*; +use rustc_ast as ast; +use rustc_ast::visit::{self, Visitor}; +use rustc_ast::{BlockCheckMode, UnsafeSource}; +use rustc_data_structures::fx::FxIndexSet; +use rustc_span::{sym, symbol::kw}; + +#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] +enum ArgumentType { + Format(FormatTrait), + Usize, +} + +fn make_argument(ecx: &ExtCtxt<'_>, sp: Span, arg: P, ty: ArgumentType) -> P { + // Generate: + // ::core::fmt::ArgumentV1::new_…(arg) + use ArgumentType::*; + use FormatTrait::*; + ecx.expr_call_global( + sp, + ecx.std_path(&[ + sym::fmt, + sym::ArgumentV1, + match ty { + Format(Display) => sym::new_display, + Format(Debug) => sym::new_debug, + Format(LowerExp) => sym::new_lower_exp, + Format(UpperExp) => sym::new_upper_exp, + Format(Octal) => sym::new_octal, + Format(Pointer) => sym::new_pointer, + Format(Binary) => sym::new_binary, + Format(LowerHex) => sym::new_lower_hex, + Format(UpperHex) => sym::new_upper_hex, + Usize => sym::from_usize, + }, + ]), + vec![arg], + ) +} + +fn make_count( + ecx: &ExtCtxt<'_>, + sp: Span, + count: &Option, + argmap: &mut FxIndexSet<(usize, ArgumentType)>, +) -> P { + // Generate: + // ::core::fmt::rt::v1::Count::…(…) + match count { + Some(FormatCount::Literal(n)) => ecx.expr_call_global( + sp, + ecx.std_path(&[sym::fmt, sym::rt, sym::v1, sym::Count, sym::Is]), + vec![ecx.expr_usize(sp, *n)], + ), + Some(FormatCount::Argument(arg)) => { + if let Ok(arg_index) = arg.index { + let (i, _) = argmap.insert_full((arg_index, ArgumentType::Usize)); + ecx.expr_call_global( + sp, + ecx.std_path(&[sym::fmt, sym::rt, sym::v1, sym::Count, sym::Param]), + vec![ecx.expr_usize(sp, i)], + ) + } else { + DummyResult::raw_expr(sp, true) + } + } + None => ecx.expr_path(ecx.path_global( + sp, + ecx.std_path(&[sym::fmt, sym::rt, sym::v1, sym::Count, sym::Implied]), + )), + } +} + +fn make_format_spec( + ecx: &ExtCtxt<'_>, + sp: Span, + placeholder: &FormatPlaceholder, + argmap: &mut FxIndexSet<(usize, ArgumentType)>, +) -> P { + // Generate: + // ::core::fmt::rt::v1::Argument { + // position: 0usize, + // format: ::core::fmt::rt::v1::FormatSpec { + // fill: ' ', + // align: ::core::fmt::rt::v1::Alignment::Unknown, + // flags: 0u32, + // precision: ::core::fmt::rt::v1::Count::Implied, + // width: ::core::fmt::rt::v1::Count::Implied, + // }, + // } + let position = match placeholder.argument.index { + Ok(arg_index) => { + let (i, _) = + argmap.insert_full((arg_index, ArgumentType::Format(placeholder.format_trait))); + ecx.expr_usize(sp, i) + } + Err(_) => DummyResult::raw_expr(sp, true), + }; + let fill = ecx.expr_char(sp, placeholder.format_options.fill.unwrap_or(' ')); + let align = ecx.expr_path(ecx.path_global( + sp, + ecx.std_path(&[ + sym::fmt, + sym::rt, + sym::v1, + sym::Alignment, + match placeholder.format_options.alignment { + Some(FormatAlignment::Left) => sym::Left, + Some(FormatAlignment::Right) => sym::Right, + Some(FormatAlignment::Center) => sym::Center, + None => sym::Unknown, + }, + ]), + )); + let flags = ecx.expr_u32(sp, placeholder.format_options.flags); + let prec = make_count(ecx, sp, &placeholder.format_options.precision, argmap); + let width = make_count(ecx, sp, &placeholder.format_options.width, argmap); + ecx.expr_struct( + sp, + ecx.path_global(sp, ecx.std_path(&[sym::fmt, sym::rt, sym::v1, sym::Argument])), + vec![ + ecx.field_imm(sp, Ident::new(sym::position, sp), position), + ecx.field_imm( + sp, + Ident::new(sym::format, sp), + ecx.expr_struct( + sp, + ecx.path_global( + sp, + ecx.std_path(&[sym::fmt, sym::rt, sym::v1, sym::FormatSpec]), + ), + vec![ + ecx.field_imm(sp, Ident::new(sym::fill, sp), fill), + ecx.field_imm(sp, Ident::new(sym::align, sp), align), + ecx.field_imm(sp, Ident::new(sym::flags, sp), flags), + ecx.field_imm(sp, Ident::new(sym::precision, sp), prec), + ecx.field_imm(sp, Ident::new(sym::width, sp), width), + ], + ), + ), + ], + ) +} + +pub fn expand_parsed_format_args(ecx: &mut ExtCtxt<'_>, fmt: FormatArgs) -> P { + let macsp = ecx.with_def_site_ctxt(ecx.call_site()); + + let lit_pieces = ecx.expr_array_ref( + fmt.span, + fmt.template + .iter() + .enumerate() + .filter_map(|(i, piece)| match piece { + &FormatArgsPiece::Literal(s) => Some(ecx.expr_str(fmt.span, s)), + &FormatArgsPiece::Placeholder(_) => { + // Inject empty string before placeholders when not already preceded by a literal piece. + if i == 0 || matches!(fmt.template[i - 1], FormatArgsPiece::Placeholder(_)) { + Some(ecx.expr_str(fmt.span, kw::Empty)) + } else { + None + } + } + }) + .collect(), + ); + + let has_any_format_options = fmt.template.iter().any(|piece| { + let FormatArgsPiece::Placeholder(placeholder) = piece else { return false }; + placeholder.format_options != Default::default() + }); + + let (args, format_options) = if has_any_format_options { + // Create a list of all _unique_ (argument, format trait) combinations. + // E.g. "{0} {0:x} {0} {1}" -> [(0, Display), (0, LowerHex), (1, Display)] + let mut argmap = FxIndexSet::default(); + // Generate: + // &[format_spec_0, format_spec_1, format_spec_2] + let format_options = ecx.expr_array_ref( + macsp, + fmt.template + .iter() + .filter_map(|piece| { + let FormatArgsPiece::Placeholder(placeholder) = piece else { return None }; + Some(make_format_spec(ecx, macsp, placeholder, &mut argmap)) + }) + .collect(), + ); + (Vec::from_iter(argmap), Some(format_options)) + } else { + // Create a list of all (argument, format trait) pairs, one for each placeholder. + // E.g. "{0} {0:x} {0} {1}" -> [(0, Display), (0, LowerHex), (0, Display), (1, Display)] + let args = fmt + .template + .iter() + .filter_map(|piece| { + let FormatArgsPiece::Placeholder(placeholder) = piece else { return None }; + Some(( + placeholder.argument.index.ok()?, + ArgumentType::Format(placeholder.format_trait), + )) + }) + .collect(); + (args, None) + }; + + // If the args array contains exactly all the original arguments once, + // in order, we can use a simple array instead of a `match` construction. + // However, if there's a yield point in any argument except the first one, + // we don't do this, because an ArgumentV1 cannot be kept across yield points. + let use_simple_array = args.len() == fmt.arguments.len() + && args.iter().enumerate().all(|(i, &(j, _))| i == j) + && fmt.arguments.iter().skip(1).all(|(arg, _)| !may_contain_yield_point(arg)); + + let args_expr = if use_simple_array { + // Generate: + // &[ + // ::core::fmt::ArgumentV1::new_display(&arg0), + // ::core::fmt::ArgumentV1::new_lower_hex(&arg1), + // ::core::fmt::ArgumentV1::new_debug(&arg2), + // ] + ecx.expr_array_ref( + macsp, + fmt.arguments + .into_iter() + .zip(args) + .map(|((arg, _), (_, ty))| { + let sp = arg.span.with_ctxt(macsp.ctxt()); + make_argument(ecx, sp, ecx.expr_addr_of(sp, arg), ty) + }) + .collect(), + ) + } else { + // Generate: + // match (&arg0, &arg1, &arg2) { + // args => &[ + // ::core::fmt::ArgumentV1::new_display(args.0), + // ::core::fmt::ArgumentV1::new_lower_hex(args.1), + // ::core::fmt::ArgumentV1::new_debug(args.0), + // ] + // } + let args_ident = Ident::new(sym::args, macsp); + let args = args + .iter() + .map(|&(arg_index, ty)| { + if let Some((arg, _)) = fmt.arguments.get(arg_index) { + let sp = arg.span.with_ctxt(macsp.ctxt()); + make_argument( + ecx, + sp, + ecx.expr_field( + sp, + ecx.expr_ident(macsp, args_ident), + Ident::new(sym::integer(arg_index), macsp), + ), + ty, + ) + } else { + DummyResult::raw_expr(macsp, true) + } + }) + .collect(); + ecx.expr_addr_of( + macsp, + ecx.expr_match( + macsp, + ecx.expr_tuple( + macsp, + fmt.arguments + .into_iter() + .map(|(arg, _)| ecx.expr_addr_of(arg.span.with_ctxt(macsp.ctxt()), arg)) + .collect(), + ), + [ecx.arm(macsp, ecx.pat_ident(macsp, args_ident), ecx.expr_array(macsp, args))] + .into(), + ), + ) + }; + + if let Some(format_options) = format_options { + // Generate: + // ::core::fmt::Arguments::new_v1_formatted( + // lit_pieces, + // args, + // format_options, + // unsafe { ::core::fmt::UnsafeArg::new() } + // ) + ecx.expr_call_global( + macsp, + ecx.std_path(&[sym::fmt, sym::Arguments, sym::new_v1_formatted]), + vec![ + lit_pieces, + args_expr, + format_options, + ecx.expr_block(P(ast::Block { + stmts: vec![ecx.stmt_expr(ecx.expr_call_global( + macsp, + ecx.std_path(&[sym::fmt, sym::UnsafeArg, sym::new]), + Vec::new(), + ))], + id: ast::DUMMY_NODE_ID, + rules: BlockCheckMode::Unsafe(UnsafeSource::CompilerGenerated), + span: macsp, + tokens: None, + could_be_bare_literal: false, + })), + ], + ) + } else { + // Generate: + // ::core::fmt::Arguments::new_v1( + // lit_pieces, + // args, + // ) + ecx.expr_call_global( + macsp, + ecx.std_path(&[sym::fmt, sym::Arguments, sym::new_v1]), + vec![lit_pieces, args_expr], + ) + } +} + +fn may_contain_yield_point(e: &ast::Expr) -> bool { + struct MayContainYieldPoint(bool); + + impl Visitor<'_> for MayContainYieldPoint { + fn visit_expr(&mut self, e: &ast::Expr) { + if let ast::ExprKind::Await(_) | ast::ExprKind::Yield(_) = e.kind { + self.0 = true; + } else { + visit::walk_expr(self, e); + } + } + + fn visit_mac_call(&mut self, _: &ast::MacCall) { + self.0 = true; + } + + fn visit_attribute(&mut self, _: &ast::Attribute) { + // Conservatively assume this may be a proc macro attribute in + // expression position. + self.0 = true; + } + + fn visit_item(&mut self, _: &ast::Item) { + // Do not recurse into nested items. + } + } + + let mut visitor = MayContainYieldPoint(false); + visitor.visit_expr(e); + visitor.0 +} diff --git a/compiler/rustc_builtin_macros/src/lib.rs b/compiler/rustc_builtin_macros/src/lib.rs index 280fa70451141..d76369a34a865 100644 --- a/compiler/rustc_builtin_macros/src/lib.rs +++ b/compiler/rustc_builtin_macros/src/lib.rs @@ -7,6 +7,7 @@ #![feature(box_patterns)] #![feature(decl_macro)] #![feature(if_let_guard)] +#![feature(is_some_with)] #![feature(is_sorted)] #![feature(let_chains)] #![feature(let_else)] diff --git a/compiler/rustc_expand/src/build.rs b/compiler/rustc_expand/src/build.rs index cf2c023c2f89f..36910fc36ec66 100644 --- a/compiler/rustc_expand/src/build.rs +++ b/compiler/rustc_expand/src/build.rs @@ -252,6 +252,10 @@ impl<'a> ExtCtxt<'a> { self.expr_ident(span, Ident::with_dummy_span(kw::SelfLower)) } + pub fn expr_field(&self, span: Span, expr: P, field: Ident) -> P { + self.expr(span, ast::ExprKind::Field(expr, field)) + } + pub fn expr_binary( &self, sp: Span, diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 81b0ebfb42c54..326befa108473 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -989,7 +989,18 @@ symbols! { never_type, never_type_fallback, new, + new_binary, + new_debug, + new_display, + new_lower_exp, + new_lower_hex, + new_octal, + new_pointer, new_unchecked, + new_upper_exp, + new_upper_hex, + new_v1, + new_v1_formatted, next, nll, no, diff --git a/src/test/ui/fmt/ifmt-bad-arg.stderr b/src/test/ui/fmt/ifmt-bad-arg.stderr index dbb4bc6d9370e..af645c2ba3ad7 100644 --- a/src/test/ui/fmt/ifmt-bad-arg.stderr +++ b/src/test/ui/fmt/ifmt-bad-arg.stderr @@ -117,20 +117,20 @@ LL | format!("{} {}", 1, 2, foo=1, bar=2); | multiple missing formatting specifiers error: duplicate argument named `foo` - --> $DIR/ifmt-bad-arg.rs:40:33 + --> $DIR/ifmt-bad-arg.rs:40:29 | LL | format!("{foo}", foo=1, foo=2); - | - ^ duplicate argument - | | - | previously here + | --- ^^^ duplicate argument + | | + | previously here error: positional arguments cannot follow named arguments --> $DIR/ifmt-bad-arg.rs:41:35 | LL | format!("{foo} {} {}", foo=1, 2); - | - ^ positional arguments must be before named arguments - | | - | named argument + | ----- ^ positional arguments must be before named arguments + | | + | named argument error: named argument never used --> $DIR/ifmt-bad-arg.rs:45:51 diff --git a/src/test/ui/macros/format-parse-errors.stderr b/src/test/ui/macros/format-parse-errors.stderr index 1a7578e6076eb..f9ea4c63377b0 100644 --- a/src/test/ui/macros/format-parse-errors.stderr +++ b/src/test/ui/macros/format-parse-errors.stderr @@ -22,7 +22,7 @@ error: positional arguments cannot follow named arguments --> $DIR/format-parse-errors.rs:10:9 | LL | foo = foo, - | --- named argument + | --------- named argument LL | bar, | ^^^ positional arguments must be before named arguments From 51efc531fb184a2409bf4bc48c16a028ab68a23c Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Fri, 26 Aug 2022 18:52:08 +0200 Subject: [PATCH 02/25] Update tests. --- .../diagnostic-derive.stderr | 10 +- .../ui/fmt/format-args-capture-issue-93378.rs | 4 +- .../format-args-capture-issue-93378.stderr | 17 +- src/test/ui/fmt/ifmt-bad-arg.rs | 6 +- src/test/ui/fmt/ifmt-bad-arg.stderr | 76 ++--- src/test/ui/issues/issue-75307.rs | 2 +- src/test/ui/issues/issue-75307.stderr | 8 +- src/test/ui/macros/issue-99265.stderr | 278 +++++++++--------- src/test/ui/macros/issue-99907.stderr | 4 +- 9 files changed, 190 insertions(+), 215 deletions(-) diff --git a/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr b/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr index ab5c28fe47332..f49bdff63f869 100644 --- a/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr +++ b/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr @@ -212,21 +212,19 @@ LL | #[suggestion(typeck::suggestion, code = "{name}")] | ^^^^^^^^ error: invalid format string: expected `'}'` but string was terminated - --> $DIR/diagnostic-derive.rs:171:16 + --> $DIR/diagnostic-derive.rs:171:10 | LL | #[derive(SessionDiagnostic)] - | - ^ expected `'}'` in format string - | | - | because of this opening brace + | ^^^^^^^^^^^^^^^^^ expected `'}'` in format string | = note: if you intended to print `{`, you can escape it using `{{` = note: this error originates in the derive macro `SessionDiagnostic` (in Nightly builds, run with -Z macro-backtrace for more info) error: invalid format string: unmatched `}` found - --> $DIR/diagnostic-derive.rs:181:15 + --> $DIR/diagnostic-derive.rs:181:10 | LL | #[derive(SessionDiagnostic)] - | ^ unmatched `}` in format string + | ^^^^^^^^^^^^^^^^^ unmatched `}` in format string | = note: if you intended to print `}`, you can escape it using `}}` = note: this error originates in the derive macro `SessionDiagnostic` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/src/test/ui/fmt/format-args-capture-issue-93378.rs b/src/test/ui/fmt/format-args-capture-issue-93378.rs index 6744444426472..9d722a0287a5c 100644 --- a/src/test/ui/fmt/format-args-capture-issue-93378.rs +++ b/src/test/ui/fmt/format-args-capture-issue-93378.rs @@ -3,9 +3,9 @@ fn main() { let b = "b"; println!("{a} {b} {} {} {c} {}", c = "c"); - //~^ ERROR: invalid reference to positional arguments 1 and 2 (there is 1 argument) + //~^ ERROR: 3 positional arguments in format string, but there is 1 argument let n = 1; println!("{a:.n$} {b:.*}"); - //~^ ERROR: invalid reference to positional argument 0 (no arguments were given) + //~^ ERROR: 1 positional argument in format string, but no arguments were given } diff --git a/src/test/ui/fmt/format-args-capture-issue-93378.stderr b/src/test/ui/fmt/format-args-capture-issue-93378.stderr index b8e2b2afb3867..6429b0d46f6af 100644 --- a/src/test/ui/fmt/format-args-capture-issue-93378.stderr +++ b/src/test/ui/fmt/format-args-capture-issue-93378.stderr @@ -1,19 +1,14 @@ -error: invalid reference to positional arguments 1 and 2 (there is 1 argument) - --> $DIR/format-args-capture-issue-93378.rs:5:26 +error: 3 positional arguments in format string, but there is 1 argument + --> $DIR/format-args-capture-issue-93378.rs:5:23 | LL | println!("{a} {b} {} {} {c} {}", c = "c"); - | ^^ ^^ - | - = note: positional arguments are zero-based + | ^^ ^^ ^^ --- -error: invalid reference to positional argument 0 (no arguments were given) - --> $DIR/format-args-capture-issue-93378.rs:9:23 +error: 1 positional argument in format string, but no arguments were given + --> $DIR/format-args-capture-issue-93378.rs:9:26 | LL | println!("{a:.n$} {b:.*}"); - | - ^^^--^ - | | | - | | this precision flag adds an extra required argument at position 0, which is why there are 3 arguments expected - | this parameter corresponds to the precision flag + | ^^ this precision flag adds an extra required argument at position 0, which is why there is 1 argument expected | = note: positional arguments are zero-based = note: for information about formatting flags, visit https://doc.rust-lang.org/std/fmt/index.html diff --git a/src/test/ui/fmt/ifmt-bad-arg.rs b/src/test/ui/fmt/ifmt-bad-arg.rs index f00cb05c9ebc3..1bd9668de314e 100644 --- a/src/test/ui/fmt/ifmt-bad-arg.rs +++ b/src/test/ui/fmt/ifmt-bad-arg.rs @@ -20,9 +20,9 @@ fn main() { //~^ ERROR: invalid reference to positional argument 2 (there are 2 arguments) format!("{} {value} {} {}", 1, value=2); - //~^ ERROR: invalid reference to positional argument 2 (there are 2 arguments) + //~^ ERROR: 3 positional arguments in format string, but there are 2 arguments format!("{name} {value} {} {} {} {} {} {}", 0, name=1, value=2); - //~^ ERROR: invalid reference to positional arguments 3, 4 and 5 (there are 3 arguments) + //~^ ERROR: 6 positional arguments in format string, but there are 3 arguments format!("{} {foo} {} {bar} {}", 1, 2, 3); //~^ ERROR: cannot find value `foo` in this scope @@ -79,7 +79,7 @@ tenth number: {}", //~^ ERROR 4 positional arguments in format string, but there are 3 arguments //~| ERROR mismatched types println!("{} {:07$.*} {}", 1, 3.2, 4); - //~^ ERROR 4 positional arguments in format string, but there are 3 arguments + //~^ ERROR invalid reference to positional arguments 3 and 7 (there are 3 arguments) //~| ERROR mismatched types println!("{} {:07$} {}", 1, 3.2, 4); //~^ ERROR invalid reference to positional argument 7 (there are 3 arguments) diff --git a/src/test/ui/fmt/ifmt-bad-arg.stderr b/src/test/ui/fmt/ifmt-bad-arg.stderr index af645c2ba3ad7..a71792d1ec912 100644 --- a/src/test/ui/fmt/ifmt-bad-arg.stderr +++ b/src/test/ui/fmt/ifmt-bad-arg.stderr @@ -5,10 +5,10 @@ LL | format!("{}"); | ^^ error: invalid reference to positional argument 1 (there is 1 argument) - --> $DIR/ifmt-bad-arg.rs:9:14 + --> $DIR/ifmt-bad-arg.rs:9:15 | LL | format!("{1}", 1); - | ^^^ + | ^ | = note: positional arguments are zero-based @@ -27,36 +27,32 @@ LL | format!("{} {}"); | ^^ ^^ error: invalid reference to positional argument 1 (there is 1 argument) - --> $DIR/ifmt-bad-arg.rs:16:18 + --> $DIR/ifmt-bad-arg.rs:16:19 | LL | format!("{0} {1}", 1); - | ^^^ + | ^ | = note: positional arguments are zero-based error: invalid reference to positional argument 2 (there are 2 arguments) - --> $DIR/ifmt-bad-arg.rs:19:22 + --> $DIR/ifmt-bad-arg.rs:19:23 | LL | format!("{0} {1} {2}", 1, 2); - | ^^^ + | ^ | = note: positional arguments are zero-based -error: invalid reference to positional argument 2 (there are 2 arguments) - --> $DIR/ifmt-bad-arg.rs:22:28 +error: 3 positional arguments in format string, but there are 2 arguments + --> $DIR/ifmt-bad-arg.rs:22:14 | LL | format!("{} {value} {} {}", 1, value=2); - | ^^ - | - = note: positional arguments are zero-based + | ^^ ^^ ^^ - - -error: invalid reference to positional arguments 3, 4 and 5 (there are 3 arguments) - --> $DIR/ifmt-bad-arg.rs:24:38 +error: 6 positional arguments in format string, but there are 3 arguments + --> $DIR/ifmt-bad-arg.rs:24:29 | LL | format!("{name} {value} {} {} {} {} {} {}", 0, name=1, value=2); - | ^^ ^^ ^^ - | - = note: positional arguments are zero-based + | ^^ ^^ ^^ ^^ ^^ ^^ - - - error: multiple unused formatting arguments --> $DIR/ifmt-bad-arg.rs:32:17 @@ -191,33 +187,26 @@ error: 4 positional arguments in format string, but there are 3 arguments | LL | println!("{} {:.*} {}", 1, 3.2, 4); | ^^ ^^--^ ^^ - --- - - | | | - | | this parameter corresponds to the precision flag + | | | this precision flag adds an extra required argument at position 1, which is why there are 4 arguments expected | = note: positional arguments are zero-based = note: for information about formatting flags, visit https://doc.rust-lang.org/std/fmt/index.html -error: 4 positional arguments in format string, but there are 3 arguments - --> $DIR/ifmt-bad-arg.rs:81:15 +error: invalid reference to positional arguments 3 and 7 (there are 3 arguments) + --> $DIR/ifmt-bad-arg.rs:81:21 | LL | println!("{} {:07$.*} {}", 1, 3.2, 4); - | ^^ ^^^----^ ^^ - --- - - | | | | - | | | this parameter corresponds to the precision flag - | | this precision flag adds an extra required argument at position 1, which is why there are 4 arguments expected - | this width flag expects an `usize` argument at position 7, but there are 3 arguments + | ^^ ^ | = note: positional arguments are zero-based = note: for information about formatting flags, visit https://doc.rust-lang.org/std/fmt/index.html error: invalid reference to positional argument 7 (there are 3 arguments) - --> $DIR/ifmt-bad-arg.rs:84:18 + --> $DIR/ifmt-bad-arg.rs:84:21 | LL | println!("{} {:07$} {}", 1, 3.2, 4); - | ^^^--^ - | | - | this width flag expects an `usize` argument at position 7, but there are 3 arguments + | ^^ | = note: positional arguments are zero-based = note: for information about formatting flags, visit https://doc.rust-lang.org/std/fmt/index.html @@ -240,24 +229,19 @@ LL | println!("{:foo}", 1); - `X`, which uses the `UpperHex` trait error: invalid reference to positional arguments 4, 5, 6 and 7 (there is 1 argument) - --> $DIR/ifmt-bad-arg.rs:87:15 + --> $DIR/ifmt-bad-arg.rs:87:16 | LL | println!("{5} {:4$} {6:7$}", 1); - | ^^^ ^^--^ ^^^--^ - | | | - | | this width flag expects an `usize` argument at position 7, but there is 1 argument - | this width flag expects an `usize` argument at position 4, but there is 1 argument + | ^ ^^ ^ ^^ | = note: positional arguments are zero-based = note: for information about formatting flags, visit https://doc.rust-lang.org/std/fmt/index.html error: invalid reference to positional argument 0 (no arguments were given) - --> $DIR/ifmt-bad-arg.rs:90:15 + --> $DIR/ifmt-bad-arg.rs:90:20 | LL | println!("{foo:0$}"); - | ^^^^^--^ - | | - | this width flag expects an `usize` argument at position 0, but no arguments were given + | ^^ | = note: positional arguments are zero-based = note: for information about formatting flags, visit https://doc.rust-lang.org/std/fmt/index.html @@ -318,10 +302,10 @@ error[E0308]: mismatched types --> $DIR/ifmt-bad-arg.rs:78:32 | LL | println!("{} {:.*} {}", 1, 3.2, 4); - | ---------------------------^^^---- - | | | - | | expected `usize`, found floating-point number - | arguments to this function are incorrect + | ^^^ + | | + | expected `usize`, found floating-point number + | arguments to this function are incorrect | = note: expected reference `&usize` found reference `&{float}` @@ -336,10 +320,10 @@ error[E0308]: mismatched types --> $DIR/ifmt-bad-arg.rs:81:35 | LL | println!("{} {:07$.*} {}", 1, 3.2, 4); - | ------------------------------^^^---- - | | | - | | expected `usize`, found floating-point number - | arguments to this function are incorrect + | ^^^ + | | + | expected `usize`, found floating-point number + | arguments to this function are incorrect | = note: expected reference `&usize` found reference `&{float}` diff --git a/src/test/ui/issues/issue-75307.rs b/src/test/ui/issues/issue-75307.rs index 2fe112a3b95d4..cffa6bea8ed38 100644 --- a/src/test/ui/issues/issue-75307.rs +++ b/src/test/ui/issues/issue-75307.rs @@ -1,3 +1,3 @@ fn main() { - format!(r"{}{}{}", named_arg=1); //~ ERROR invalid reference to positional arguments 1 and 2 + format!(r"{}{}{}", named_arg=1); //~ ERROR 3 positional arguments in format string, but there is 1 argument } diff --git a/src/test/ui/issues/issue-75307.stderr b/src/test/ui/issues/issue-75307.stderr index 10c952006c208..c5b0b11e7d099 100644 --- a/src/test/ui/issues/issue-75307.stderr +++ b/src/test/ui/issues/issue-75307.stderr @@ -1,10 +1,8 @@ -error: invalid reference to positional arguments 1 and 2 (there is 1 argument) - --> $DIR/issue-75307.rs:2:17 +error: 3 positional arguments in format string, but there is 1 argument + --> $DIR/issue-75307.rs:2:15 | LL | format!(r"{}{}{}", named_arg=1); - | ^^^^ - | - = note: positional arguments are zero-based + | ^^^^^^ - error: aborting due to previous error diff --git a/src/test/ui/macros/issue-99265.stderr b/src/test/ui/macros/issue-99265.stderr index 2bfeedd7d0737..9185dbff61ee0 100644 --- a/src/test/ui/macros/issue-99265.stderr +++ b/src/test/ui/macros/issue-99265.stderr @@ -77,18 +77,18 @@ help: use the named argument by name to avoid ambiguity LL | println!("Hello {:width$}!", "x", width = 5); | ~~~~~~ -warning: named argument `width` is not used by name - --> $DIR/issue-99265.rs:23:46 +warning: named argument `f` is not used by name + --> $DIR/issue-99265.rs:23:33 | LL | println!("Hello {:1$.2$}!", f = 0.02f32, width = 5, precision = 2); - | -- ^^^^^ this named argument is referred to by position in formatting string - | | - | this formatting argument uses named argument `width` by position + | -------- ^ this named argument is referred to by position in formatting string + | | + | this formatting argument uses named argument `f` by position | help: use the named argument by name to avoid ambiguity | -LL | println!("Hello {:width$.2$}!", f = 0.02f32, width = 5, precision = 2); - | ~~~~~~ +LL | println!("Hello {f:1$.2$}!", f = 0.02f32, width = 5, precision = 2); + | + warning: named argument `precision` is not used by name --> $DIR/issue-99265.rs:23:57 @@ -103,31 +103,31 @@ help: use the named argument by name to avoid ambiguity LL | println!("Hello {:1$.precision$}!", f = 0.02f32, width = 5, precision = 2); | ~~~~~~~~~~ -warning: named argument `f` is not used by name - --> $DIR/issue-99265.rs:23:33 +warning: named argument `width` is not used by name + --> $DIR/issue-99265.rs:23:46 | LL | println!("Hello {:1$.2$}!", f = 0.02f32, width = 5, precision = 2); - | -- ^ this named argument is referred to by position in formatting string - | | - | this formatting argument uses named argument `f` by position + | -- ^^^^^ this named argument is referred to by position in formatting string + | | + | this formatting argument uses named argument `width` by position | help: use the named argument by name to avoid ambiguity | -LL | println!("Hello {f:1$.2$}!", f = 0.02f32, width = 5, precision = 2); - | + +LL | println!("Hello {:width$.2$}!", f = 0.02f32, width = 5, precision = 2); + | ~~~~~~ -warning: named argument `width` is not used by name - --> $DIR/issue-99265.rs:31:47 +warning: named argument `f` is not used by name + --> $DIR/issue-99265.rs:31:34 | LL | println!("Hello {0:1$.2$}!", f = 0.02f32, width = 5, precision = 2); - | -- ^^^^^ this named argument is referred to by position in formatting string - | | - | this formatting argument uses named argument `width` by position + | --------- ^ this named argument is referred to by position in formatting string + | | + | this formatting argument uses named argument `f` by position | help: use the named argument by name to avoid ambiguity | -LL | println!("Hello {0:width$.2$}!", f = 0.02f32, width = 5, precision = 2); - | ~~~~~~ +LL | println!("Hello {f:1$.2$}!", f = 0.02f32, width = 5, precision = 2); + | ~ warning: named argument `precision` is not used by name --> $DIR/issue-99265.rs:31:58 @@ -142,32 +142,32 @@ help: use the named argument by name to avoid ambiguity LL | println!("Hello {0:1$.precision$}!", f = 0.02f32, width = 5, precision = 2); | ~~~~~~~~~~ -warning: named argument `f` is not used by name - --> $DIR/issue-99265.rs:31:34 +warning: named argument `width` is not used by name + --> $DIR/issue-99265.rs:31:47 | LL | println!("Hello {0:1$.2$}!", f = 0.02f32, width = 5, precision = 2); - | - ^ this named argument is referred to by position in formatting string - | | - | this formatting argument uses named argument `f` by position + | -- ^^^^^ this named argument is referred to by position in formatting string + | | + | this formatting argument uses named argument `width` by position | help: use the named argument by name to avoid ambiguity | -LL | println!("Hello {f:1$.2$}!", f = 0.02f32, width = 5, precision = 2); - | ~ +LL | println!("Hello {0:width$.2$}!", f = 0.02f32, width = 5, precision = 2); + | ~~~~~~ -warning: named argument `width` is not used by name - --> $DIR/issue-99265.rs:52:9 +warning: named argument `f` is not used by name + --> $DIR/issue-99265.rs:49:9 | LL | "{}, Hello {1:2$.3$} {4:5$.6$}! {1}", - | -- this formatting argument uses named argument `width` by position + | --------- this formatting argument uses named argument `f` by position ... -LL | width = 5, - | ^^^^^ this named argument is referred to by position in formatting string +LL | f = 0.02f32, + | ^ this named argument is referred to by position in formatting string | help: use the named argument by name to avoid ambiguity | -LL | "{}, Hello {1:width$.3$} {4:5$.6$}! {1}", - | ~~~~~~ +LL | "{}, Hello {f:2$.3$} {4:5$.6$}! {1}", + | ~ warning: named argument `precision` is not used by name --> $DIR/issue-99265.rs:54:9 @@ -183,33 +183,33 @@ help: use the named argument by name to avoid ambiguity LL | "{}, Hello {1:2$.precision$} {4:5$.6$}! {1}", | ~~~~~~~~~~ -warning: named argument `f` is not used by name - --> $DIR/issue-99265.rs:49:9 +warning: named argument `width` is not used by name + --> $DIR/issue-99265.rs:52:9 | LL | "{}, Hello {1:2$.3$} {4:5$.6$}! {1}", - | - this formatting argument uses named argument `f` by position + | -- this formatting argument uses named argument `width` by position ... -LL | f = 0.02f32, - | ^ this named argument is referred to by position in formatting string +LL | width = 5, + | ^^^^^ this named argument is referred to by position in formatting string | help: use the named argument by name to avoid ambiguity | -LL | "{}, Hello {f:2$.3$} {4:5$.6$}! {1}", - | ~ +LL | "{}, Hello {1:width$.3$} {4:5$.6$}! {1}", + | ~~~~~~ -warning: named argument `width2` is not used by name - --> $DIR/issue-99265.rs:58:9 +warning: named argument `g` is not used by name + --> $DIR/issue-99265.rs:56:9 | LL | "{}, Hello {1:2$.3$} {4:5$.6$}! {1}", - | -- this formatting argument uses named argument `width2` by position + | --------- this formatting argument uses named argument `g` by position ... -LL | width2 = 5, - | ^^^^^^ this named argument is referred to by position in formatting string +LL | g = 0.02f32, + | ^ this named argument is referred to by position in formatting string | help: use the named argument by name to avoid ambiguity | -LL | "{}, Hello {1:2$.3$} {4:width2$.6$}! {1}", - | ~~~~~~~ +LL | "{}, Hello {1:2$.3$} {g:5$.6$}! {1}", + | ~ warning: named argument `precision2` is not used by name --> $DIR/issue-99265.rs:60:9 @@ -225,25 +225,25 @@ help: use the named argument by name to avoid ambiguity LL | "{}, Hello {1:2$.3$} {4:5$.precision2$}! {1}", | ~~~~~~~~~~~ -warning: named argument `g` is not used by name - --> $DIR/issue-99265.rs:56:9 +warning: named argument `width2` is not used by name + --> $DIR/issue-99265.rs:58:9 | LL | "{}, Hello {1:2$.3$} {4:5$.6$}! {1}", - | - this formatting argument uses named argument `g` by position + | -- this formatting argument uses named argument `width2` by position ... -LL | g = 0.02f32, - | ^ this named argument is referred to by position in formatting string +LL | width2 = 5, + | ^^^^^^ this named argument is referred to by position in formatting string | help: use the named argument by name to avoid ambiguity | -LL | "{}, Hello {1:2$.3$} {g:5$.6$}! {1}", - | ~ +LL | "{}, Hello {1:2$.3$} {4:width2$.6$}! {1}", + | ~~~~~~~ warning: named argument `f` is not used by name --> $DIR/issue-99265.rs:49:9 | LL | "{}, Hello {1:2$.3$} {4:5$.6$}! {1}", - | - this formatting argument uses named argument `f` by position + | --- this formatting argument uses named argument `f` by position ... LL | f = 0.02f32, | ^ this named argument is referred to by position in formatting string @@ -257,7 +257,7 @@ warning: named argument `f` is not used by name --> $DIR/issue-99265.rs:64:31 | LL | println!("Hello {:0.1}!", f = 0.02f32); - | -- ^ this named argument is referred to by position in formatting string + | ------ ^ this named argument is referred to by position in formatting string | | | this formatting argument uses named argument `f` by position | @@ -270,15 +270,28 @@ warning: named argument `f` is not used by name --> $DIR/issue-99265.rs:68:32 | LL | println!("Hello {0:0.1}!", f = 0.02f32); - | - ^ this named argument is referred to by position in formatting string - | | - | this formatting argument uses named argument `f` by position + | ------- ^ this named argument is referred to by position in formatting string + | | + | this formatting argument uses named argument `f` by position | help: use the named argument by name to avoid ambiguity | LL | println!("Hello {f:0.1}!", f = 0.02f32); | ~ +warning: named argument `v` is not used by name + --> $DIR/issue-99265.rs:79:23 + | +LL | println!("{:0$}", v = val); + | ----- ^ this named argument is referred to by position in formatting string + | | + | this formatting argument uses named argument `v` by position + | +help: use the named argument by name to avoid ambiguity + | +LL | println!("{v:0$}", v = val); + | + + warning: named argument `v` is not used by name --> $DIR/issue-99265.rs:79:23 | @@ -293,17 +306,17 @@ LL | println!("{:v$}", v = val); | ~~ warning: named argument `v` is not used by name - --> $DIR/issue-99265.rs:79:23 + --> $DIR/issue-99265.rs:84:24 | -LL | println!("{:0$}", v = val); - | -- ^ this named argument is referred to by position in formatting string +LL | println!("{0:0$}", v = val); + | ------ ^ this named argument is referred to by position in formatting string | | | this formatting argument uses named argument `v` by position | help: use the named argument by name to avoid ambiguity | LL | println!("{v:0$}", v = val); - | + + | ~ warning: named argument `v` is not used by name --> $DIR/issue-99265.rs:84:24 @@ -318,31 +331,18 @@ help: use the named argument by name to avoid ambiguity LL | println!("{0:v$}", v = val); | ~~ -warning: named argument `v` is not used by name - --> $DIR/issue-99265.rs:84:24 - | -LL | println!("{0:0$}", v = val); - | - ^ this named argument is referred to by position in formatting string - | | - | this formatting argument uses named argument `v` by position - | -help: use the named argument by name to avoid ambiguity - | -LL | println!("{v:0$}", v = val); - | ~ - warning: named argument `v` is not used by name --> $DIR/issue-99265.rs:89:26 | LL | println!("{:0$.0$}", v = val); - | -- ^ this named argument is referred to by position in formatting string - | | - | this formatting argument uses named argument `v` by position + | -------- ^ this named argument is referred to by position in formatting string + | | + | this formatting argument uses named argument `v` by position | help: use the named argument by name to avoid ambiguity | -LL | println!("{:v$.0$}", v = val); - | ~~ +LL | println!("{v:0$.0$}", v = val); + | + warning: named argument `v` is not used by name --> $DIR/issue-99265.rs:89:26 @@ -361,27 +361,27 @@ warning: named argument `v` is not used by name --> $DIR/issue-99265.rs:89:26 | LL | println!("{:0$.0$}", v = val); - | -- ^ this named argument is referred to by position in formatting string - | | - | this formatting argument uses named argument `v` by position + | -- ^ this named argument is referred to by position in formatting string + | | + | this formatting argument uses named argument `v` by position | help: use the named argument by name to avoid ambiguity | -LL | println!("{v:0$.0$}", v = val); - | + +LL | println!("{:v$.0$}", v = val); + | ~~ warning: named argument `v` is not used by name --> $DIR/issue-99265.rs:96:27 | LL | println!("{0:0$.0$}", v = val); - | -- ^ this named argument is referred to by position in formatting string - | | - | this formatting argument uses named argument `v` by position + | --------- ^ this named argument is referred to by position in formatting string + | | + | this formatting argument uses named argument `v` by position | help: use the named argument by name to avoid ambiguity | -LL | println!("{0:v$.0$}", v = val); - | ~~ +LL | println!("{v:0$.0$}", v = val); + | ~ warning: named argument `v` is not used by name --> $DIR/issue-99265.rs:96:27 @@ -400,14 +400,14 @@ warning: named argument `v` is not used by name --> $DIR/issue-99265.rs:96:27 | LL | println!("{0:0$.0$}", v = val); - | - ^ this named argument is referred to by position in formatting string - | | - | this formatting argument uses named argument `v` by position + | -- ^ this named argument is referred to by position in formatting string + | | + | this formatting argument uses named argument `v` by position | help: use the named argument by name to avoid ambiguity | -LL | println!("{v:0$.0$}", v = val); - | ~ +LL | println!("{0:v$.0$}", v = val); + | ~~ warning: named argument `a` is not used by name --> $DIR/issue-99265.rs:104:28 @@ -426,28 +426,28 @@ warning: named argument `a` is not used by name --> $DIR/issue-99265.rs:104:28 | LL | println!("{} {a} {0}", a = 1); - | - ^ this named argument is referred to by position in formatting string - | | - | this formatting argument uses named argument `a` by position + | --- ^ this named argument is referred to by position in formatting string + | | + | this formatting argument uses named argument `a` by position | help: use the named argument by name to avoid ambiguity | LL | println!("{} {a} {a}", a = 1); | ~ -warning: named argument `b` is not used by name - --> $DIR/issue-99265.rs:115:23 +warning: named argument `a` is not used by name + --> $DIR/issue-99265.rs:115:14 | LL | {:1$.2$}", - | -- this formatting argument uses named argument `b` by position + | -------- this formatting argument uses named argument `a` by position ... LL | a = 1.0, b = 1, c = 2, - | ^ this named argument is referred to by position in formatting string + | ^ this named argument is referred to by position in formatting string | help: use the named argument by name to avoid ambiguity | -LL | {:b$.2$}", - | ~~ +LL | {a:1$.2$}", + | + warning: named argument `c` is not used by name --> $DIR/issue-99265.rs:115:30 @@ -463,33 +463,33 @@ help: use the named argument by name to avoid ambiguity LL | {:1$.c$}", | ~~ -warning: named argument `a` is not used by name - --> $DIR/issue-99265.rs:115:14 +warning: named argument `b` is not used by name + --> $DIR/issue-99265.rs:115:23 | LL | {:1$.2$}", - | -- this formatting argument uses named argument `a` by position + | -- this formatting argument uses named argument `b` by position ... LL | a = 1.0, b = 1, c = 2, - | ^ this named argument is referred to by position in formatting string + | ^ this named argument is referred to by position in formatting string | help: use the named argument by name to avoid ambiguity | -LL | {a:1$.2$}", - | + +LL | {:b$.2$}", + | ~~ -warning: named argument `b` is not used by name - --> $DIR/issue-99265.rs:126:23 +warning: named argument `a` is not used by name + --> $DIR/issue-99265.rs:126:14 | LL | {0:1$.2$}", - | -- this formatting argument uses named argument `b` by position + | --------- this formatting argument uses named argument `a` by position ... LL | a = 1.0, b = 1, c = 2, - | ^ this named argument is referred to by position in formatting string + | ^ this named argument is referred to by position in formatting string | help: use the named argument by name to avoid ambiguity | -LL | {0:b$.2$}", - | ~~ +LL | {a:1$.2$}", + | ~ warning: named argument `c` is not used by name --> $DIR/issue-99265.rs:126:30 @@ -505,32 +505,32 @@ help: use the named argument by name to avoid ambiguity LL | {0:1$.c$}", | ~~ -warning: named argument `a` is not used by name - --> $DIR/issue-99265.rs:126:14 +warning: named argument `b` is not used by name + --> $DIR/issue-99265.rs:126:23 | LL | {0:1$.2$}", - | - this formatting argument uses named argument `a` by position + | -- this formatting argument uses named argument `b` by position ... LL | a = 1.0, b = 1, c = 2, - | ^ this named argument is referred to by position in formatting string + | ^ this named argument is referred to by position in formatting string | help: use the named argument by name to avoid ambiguity | -LL | {a:1$.2$}", - | ~ +LL | {0:b$.2$}", + | ~~ -warning: named argument `width` is not used by name - --> $DIR/issue-99265.rs:132:39 +warning: named argument `x` is not used by name + --> $DIR/issue-99265.rs:132:30 | LL | println!("{{{:1$.2$}}}", x = 1.0, width = 3, precision = 2); - | -- ^^^^^ this named argument is referred to by position in formatting string - | | - | this formatting argument uses named argument `width` by position + | -------- ^ this named argument is referred to by position in formatting string + | | + | this formatting argument uses named argument `x` by position | help: use the named argument by name to avoid ambiguity | -LL | println!("{{{:width$.2$}}}", x = 1.0, width = 3, precision = 2); - | ~~~~~~ +LL | println!("{{{x:1$.2$}}}", x = 1.0, width = 3, precision = 2); + | + warning: named argument `precision` is not used by name --> $DIR/issue-99265.rs:132:50 @@ -545,18 +545,18 @@ help: use the named argument by name to avoid ambiguity LL | println!("{{{:1$.precision$}}}", x = 1.0, width = 3, precision = 2); | ~~~~~~~~~~ -warning: named argument `x` is not used by name - --> $DIR/issue-99265.rs:132:30 +warning: named argument `width` is not used by name + --> $DIR/issue-99265.rs:132:39 | LL | println!("{{{:1$.2$}}}", x = 1.0, width = 3, precision = 2); - | -- ^ this named argument is referred to by position in formatting string - | | - | this formatting argument uses named argument `x` by position + | -- ^^^^^ this named argument is referred to by position in formatting string + | | + | this formatting argument uses named argument `width` by position | help: use the named argument by name to avoid ambiguity | -LL | println!("{{{x:1$.2$}}}", x = 1.0, width = 3, precision = 2); - | + +LL | println!("{{{:width$.2$}}}", x = 1.0, width = 3, precision = 2); + | ~~~~~~ warning: 42 warnings emitted diff --git a/src/test/ui/macros/issue-99907.stderr b/src/test/ui/macros/issue-99907.stderr index 4786ce003b4c2..eefb28dee35b8 100644 --- a/src/test/ui/macros/issue-99907.stderr +++ b/src/test/ui/macros/issue-99907.stderr @@ -2,7 +2,7 @@ warning: named argument `f` is not used by name --> $DIR/issue-99907.rs:5:30 | LL | println!("Hello {:.1}!", f = 0.02f32); - | -- ^ this named argument is referred to by position in formatting string + | ----- ^ this named argument is referred to by position in formatting string | | | this formatting argument uses named argument `f` by position | @@ -16,7 +16,7 @@ warning: named argument `f` is not used by name --> $DIR/issue-99907.rs:9:31 | LL | println!("Hello {:1.1}!", f = 0.02f32); - | -- ^ this named argument is referred to by position in formatting string + | ------ ^ this named argument is referred to by position in formatting string | | | this formatting argument uses named argument `f` by position | From 406412692296a6a521c081258b86ce665e49cb32 Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Fri, 26 Aug 2022 19:13:17 +0200 Subject: [PATCH 03/25] Move FormatArgs structure to its own module. --- compiler/rustc_builtin_macros/src/format.rs | 176 ++---------------- .../rustc_builtin_macros/src/format/ast.rs | 159 ++++++++++++++++ 2 files changed, 172 insertions(+), 163 deletions(-) create mode 100644 compiler/rustc_builtin_macros/src/format/ast.rs diff --git a/compiler/rustc_builtin_macros/src/format.rs b/compiler/rustc_builtin_macros/src/format.rs index 06a831e36692e..42a413210c764 100644 --- a/compiler/rustc_builtin_macros/src/format.rs +++ b/compiler/rustc_builtin_macros/src/format.rs @@ -1,7 +1,7 @@ -use rustc_ast as ast; use rustc_ast::ptr::P; use rustc_ast::token; use rustc_ast::tokenstream::TokenStream; +use rustc_ast::Expr; use rustc_data_structures::fx::FxHashSet; use rustc_errors::{pluralize, Applicability, MultiSpan, PResult}; use rustc_expand::base::{self, *}; @@ -12,6 +12,9 @@ use rustc_span::{BytePos, InnerSpan, Span}; use rustc_lint_defs::builtin::NAMED_ARGUMENTS_USED_POSITIONALLY; use rustc_lint_defs::{BufferedEarlyLint, BuiltinLintDiagnostics, LintId}; +mod ast; +use ast::*; + mod expand; use expand::expand_parsed_format_args; @@ -23,160 +26,7 @@ use expand::expand_parsed_format_args; // 3. Finally, `expand_parsed_format_args` will turn that `FormatArgs` structure // into the expression that the macro expands to. -// Definitions: -// -// format_args!("hello {abc:.xyz$}!!", abc="world"); -// └──────────────────────────────────────────────┘ -// FormatArgs -// -// format_args!("hello {abc:.xyz$}!!", abc="world"); -// └─────────┘ -// argument -// -// format_args!("hello {abc:.xyz$}!!", abc="world"); -// └───────────────────┘ -// template -// -// format_args!("hello {abc:.xyz$}!!", abc="world"); -// └────┘└─────────┘└┘ -// pieces -// -// format_args!("hello {abc:.xyz$}!!", abc="world"); -// └────┘ └┘ -// literal pieces -// -// format_args!("hello {abc:.xyz$}!!", abc="world"); -// └─────────┘ -// placeholder -// -// format_args!("hello {abc:.xyz$}!!", abc="world"); -// └─┘ └─┘ -// positions (could be names, numbers, empty, or `*`) - -/// (Parsed) format args. -/// -/// Basically the "AST" for a complete `format_args!()`. -/// -/// E.g., `format_args!("hello {name}");`. -#[derive(Clone, Debug)] -pub struct FormatArgs { - pub span: Span, - pub template: Vec, - pub arguments: Vec<(P, FormatArgKind)>, -} - -#[derive(Clone, Debug)] -pub enum FormatArgsPiece { - Literal(Symbol), - Placeholder(FormatPlaceholder), -} - -#[derive(Clone, Debug)] -pub enum FormatArgKind { - /// `format_args(…, arg)` - Normal, - /// `format_args(…, arg = 1)` - Named(Ident), - /// `format_args("… {arg} …")` - Captured(Ident), -} - -impl FormatArgKind { - pub fn ident(&self) -> Option { - match self { - &Self::Normal => None, - &Self::Named(id) => Some(id), - &Self::Captured(id) => Some(id), - } - } -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct FormatPlaceholder { - /// Index into [`FormatArgs::arguments`]. - pub argument: FormatArgPosition, - /// The span inside the format string for the full `{…}` placeholder. - pub span: Option, - /// `{}`, `{:?}`, or `{:x}`, etc. - pub format_trait: FormatTrait, - /// `{}` or `{:.5}` or `{:-^20}`, etc. - pub format_options: FormatOptions, -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct FormatArgPosition { - /// Which argument this position refers to (Ok), - /// or would've referred to if it existed (Err). - pub index: Result, - /// What kind of position this is. See [`FormatArgsPositionKind`]. - pub kind: FormatArgPositionKind, - /// The span of the name or number. - pub span: Option, -} - -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub enum FormatArgPositionKind { - /// `{}` or `{.*}` - Implicit, - /// `{1}` or `{:1$}` or `{:.1$}` - Number, - /// `{a}` or `{:a$}` or `{:.a$}` - Named, -} - -#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] -pub enum FormatTrait { - /// `{}` - Display, - /// `{:?}` - Debug, - /// `{:e}` - LowerExp, - /// `{:E}` - UpperExp, - /// `{:o}` - Octal, - /// `{:p}` - Pointer, - /// `{:b}` - Binary, - /// `{:x}` - LowerHex, - /// `{:X}` - UpperHex, -} - -#[derive(Clone, Debug, Default, PartialEq, Eq)] -pub struct FormatOptions { - /// The width. E.g. `{:5}` or `{:width$}`. - pub width: Option, - /// The precision. E.g. `{:.5}` or `{:.precision$}`. - pub precision: Option, - /// The alignment. E.g. `{:>}` or `{:<}` or `{:^}`. - pub alignment: Option, - /// The fill character. E.g. the `.` in `{:.>10}`. - pub fill: Option, - /// The `+`, `-`, `0`, `#`, `x?` and `X?` flags. - pub flags: u32, -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum FormatAlignment { - /// `{:<}` - Left, - /// `{:>}` - Right, - /// `{:^}` - Center, -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum FormatCount { - /// `{:0}` or `{:.0}` - Literal(usize), - /// `{:.*}`, `{:.0$}`, or `{:a$}`, etc. - Argument(FormatArgPosition), -} +// See format/ast.rs forthe FormatArgs structure. // Only used in parse_args and report_invalid_references, // to indicate how a referred argument was used. @@ -201,8 +51,8 @@ fn parse_args<'a>( ecx: &mut ExtCtxt<'a>, sp: Span, tts: TokenStream, -) -> PResult<'a, (P, Vec<(P, FormatArgKind)>)> { - let mut args = Vec::<(P, FormatArgKind)>::new(); +) -> PResult<'a, (P, Vec<(P, FormatArgKind)>)> { + let mut args = Vec::<(P, FormatArgKind)>::new(); let mut p = ecx.new_parser_from_tts(tts); @@ -305,8 +155,8 @@ fn parse_args<'a>( pub fn make_format_args( ecx: &mut ExtCtxt<'_>, - efmt: P, - mut args: Vec<(P, FormatArgKind)>, + efmt: P, + mut args: Vec<(P, FormatArgKind)>, append_newline: bool, ) -> Result { let start_of_named_args = @@ -341,8 +191,8 @@ pub fn make_format_args( }; let str_style = match fmt_style { - ast::StrStyle::Cooked => None, - ast::StrStyle::Raw(raw) => Some(raw as usize), + rustc_ast::StrStyle::Cooked => None, + rustc_ast::StrStyle::Raw(raw) => Some(raw as usize), }; let fmt_str = fmt_str.as_str(); // for the suggestions below @@ -669,7 +519,7 @@ pub fn make_format_args( ecx.buffered_early_lint.push(BufferedEarlyLint { span: arg_name.span.into(), msg: format!("named argument `{}` is not used by name", arg_name.name).into(), - node_id: ast::CRATE_NODE_ID, + node_id: rustc_ast::CRATE_NODE_ID, lint_id: LintId::of(&NAMED_ARGUMENTS_USED_POSITIONALLY), diagnostic: BuiltinLintDiagnostics::NamedArgumentUsedPositionally { position_sp_to_replace, @@ -850,7 +700,7 @@ fn report_invalid_references( template: &[FormatArgsPiece], fmt_span: Span, num_explicit_args: usize, - args: &[(P, FormatArgKind)], + args: &[(P, FormatArgKind)], parser: parse::Parser<'_>, ) { let num_args_desc = match num_explicit_args { diff --git a/compiler/rustc_builtin_macros/src/format/ast.rs b/compiler/rustc_builtin_macros/src/format/ast.rs new file mode 100644 index 0000000000000..d6f72497acff8 --- /dev/null +++ b/compiler/rustc_builtin_macros/src/format/ast.rs @@ -0,0 +1,159 @@ +use rustc_ast::ptr::P; +use rustc_ast::Expr; +use rustc_span::symbol::{Ident, Symbol}; +use rustc_span::Span; + +// Definitions: +// +// format_args!("hello {abc:.xyz$}!!", abc="world"); +// └──────────────────────────────────────────────┘ +// FormatArgs +// +// format_args!("hello {abc:.xyz$}!!", abc="world"); +// └─────────┘ +// argument +// +// format_args!("hello {abc:.xyz$}!!", abc="world"); +// └───────────────────┘ +// template +// +// format_args!("hello {abc:.xyz$}!!", abc="world"); +// └────┘└─────────┘└┘ +// pieces +// +// format_args!("hello {abc:.xyz$}!!", abc="world"); +// └────┘ └┘ +// literal pieces +// +// format_args!("hello {abc:.xyz$}!!", abc="world"); +// └─────────┘ +// placeholder +// +// format_args!("hello {abc:.xyz$}!!", abc="world"); +// └─┘ └─┘ +// positions (could be names, numbers, empty, or `*`) + +/// (Parsed) format args. +/// +/// Basically the "AST" for a complete `format_args!()`. +/// +/// E.g., `format_args!("hello {name}");`. +#[derive(Clone, Debug)] +pub struct FormatArgs { + pub span: Span, + pub template: Vec, + pub arguments: Vec<(P, FormatArgKind)>, +} + +#[derive(Clone, Debug)] +pub enum FormatArgsPiece { + Literal(Symbol), + Placeholder(FormatPlaceholder), +} + +#[derive(Clone, Debug)] +pub enum FormatArgKind { + /// `format_args(…, arg)` + Normal, + /// `format_args(…, arg = 1)` + Named(Ident), + /// `format_args("… {arg} …")` + Captured(Ident), +} + +impl FormatArgKind { + pub fn ident(&self) -> Option { + match self { + &Self::Normal => None, + &Self::Named(id) => Some(id), + &Self::Captured(id) => Some(id), + } + } +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct FormatPlaceholder { + /// Index into [`FormatArgs::arguments`]. + pub argument: FormatArgPosition, + /// The span inside the format string for the full `{…}` placeholder. + pub span: Option, + /// `{}`, `{:?}`, or `{:x}`, etc. + pub format_trait: FormatTrait, + /// `{}` or `{:.5}` or `{:-^20}`, etc. + pub format_options: FormatOptions, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct FormatArgPosition { + /// Which argument this position refers to (Ok), + /// or would've referred to if it existed (Err). + pub index: Result, + /// What kind of position this is. See [`FormatArgsPositionKind`]. + pub kind: FormatArgPositionKind, + /// The span of the name or number. + pub span: Option, +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum FormatArgPositionKind { + /// `{}` or `{.*}` + Implicit, + /// `{1}` or `{:1$}` or `{:.1$}` + Number, + /// `{a}` or `{:a$}` or `{:.a$}` + Named, +} + +#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] +pub enum FormatTrait { + /// `{}` + Display, + /// `{:?}` + Debug, + /// `{:e}` + LowerExp, + /// `{:E}` + UpperExp, + /// `{:o}` + Octal, + /// `{:p}` + Pointer, + /// `{:b}` + Binary, + /// `{:x}` + LowerHex, + /// `{:X}` + UpperHex, +} + +#[derive(Clone, Debug, Default, PartialEq, Eq)] +pub struct FormatOptions { + /// The width. E.g. `{:5}` or `{:width$}`. + pub width: Option, + /// The precision. E.g. `{:.5}` or `{:.precision$}`. + pub precision: Option, + /// The alignment. E.g. `{:>}` or `{:<}` or `{:^}`. + pub alignment: Option, + /// The fill character. E.g. the `.` in `{:.>10}`. + pub fill: Option, + /// The `+`, `-`, `0`, `#`, `x?` and `X?` flags. + pub flags: u32, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum FormatAlignment { + /// `{:<}` + Left, + /// `{:>}` + Right, + /// `{:^}` + Center, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum FormatCount { + /// `{:0}` or `{:.0}` + Literal(usize), + /// `{:.*}`, `{:.0$}`, or `{:a$}`, etc. + Argument(FormatArgPosition), +} From 2928e2976a98b344bd2c1c38ca9529f0a1968161 Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Fri, 26 Aug 2022 22:56:21 +0200 Subject: [PATCH 04/25] Fix typo. --- compiler/rustc_builtin_macros/src/format/ast.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_builtin_macros/src/format/ast.rs b/compiler/rustc_builtin_macros/src/format/ast.rs index d6f72497acff8..cc0a3b0b24447 100644 --- a/compiler/rustc_builtin_macros/src/format/ast.rs +++ b/compiler/rustc_builtin_macros/src/format/ast.rs @@ -88,7 +88,7 @@ pub struct FormatArgPosition { /// Which argument this position refers to (Ok), /// or would've referred to if it existed (Err). pub index: Result, - /// What kind of position this is. See [`FormatArgsPositionKind`]. + /// What kind of position this is. See [`FormatArgPositionKind`]. pub kind: FormatArgPositionKind, /// The span of the name or number. pub span: Option, From 7edd9197384a0690cdffbc0766b086a42beab100 Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Sat, 27 Aug 2022 17:51:28 +0200 Subject: [PATCH 05/25] Prefer new_v1_formatted instead of new_v1 with duplicates. --- .../rustc_builtin_macros/src/format/expand.rs | 69 +++++++++---------- 1 file changed, 33 insertions(+), 36 deletions(-) diff --git a/compiler/rustc_builtin_macros/src/format/expand.rs b/compiler/rustc_builtin_macros/src/format/expand.rs index d1c114a67ac1e..a2a8213dafc27 100644 --- a/compiler/rustc_builtin_macros/src/format/expand.rs +++ b/compiler/rustc_builtin_macros/src/format/expand.rs @@ -164,18 +164,32 @@ pub fn expand_parsed_format_args(ecx: &mut ExtCtxt<'_>, fmt: FormatArgs) -> P [(0, Display), (0, LowerHex), (1, Display)] - let mut argmap = FxIndexSet::default(); + // Create a list of all _unique_ (argument, format trait) combinations. + // E.g. "{0} {0:x} {0} {1}" -> [(0, Display), (0, LowerHex), (1, Display)] + let mut argmap = FxIndexSet::default(); + for piece in &fmt.template { + let FormatArgsPiece::Placeholder(placeholder) = piece else { continue }; + if placeholder.format_options != Default::default() { + // Can't use basic form if there's any formatting options. + use_format_options = true; + } + if let Ok(index) = placeholder.argument.index { + if !argmap.insert((index, ArgumentType::Format(placeholder.format_trait))) { + // Duplicate (argument, format trait) combination, + // which we'll only put once in the args array. + use_format_options = true; + } + } + } + + let format_options = use_format_options.then(|| { // Generate: // &[format_spec_0, format_spec_1, format_spec_2] - let format_options = ecx.expr_array_ref( + ecx.expr_array_ref( macsp, fmt.template .iter() @@ -184,34 +198,18 @@ pub fn expand_parsed_format_args(ecx: &mut ExtCtxt<'_>, fmt: FormatArgs) -> P [(0, Display), (0, LowerHex), (0, Display), (1, Display)] - let args = fmt - .template - .iter() - .filter_map(|piece| { - let FormatArgsPiece::Placeholder(placeholder) = piece else { return None }; - Some(( - placeholder.argument.index.ok()?, - ArgumentType::Format(placeholder.format_trait), - )) - }) - .collect(); - (args, None) - }; + ) + }); // If the args array contains exactly all the original arguments once, // in order, we can use a simple array instead of a `match` construction. // However, if there's a yield point in any argument except the first one, // we don't do this, because an ArgumentV1 cannot be kept across yield points. - let use_simple_array = args.len() == fmt.arguments.len() - && args.iter().enumerate().all(|(i, &(j, _))| i == j) + let use_simple_array = argmap.len() == fmt.arguments.len() + && argmap.iter().enumerate().all(|(i, &(j, _))| i == j) && fmt.arguments.iter().skip(1).all(|(arg, _)| !may_contain_yield_point(arg)); - let args_expr = if use_simple_array { + let args = if use_simple_array { // Generate: // &[ // ::core::fmt::ArgumentV1::new_display(&arg0), @@ -222,7 +220,7 @@ pub fn expand_parsed_format_args(ecx: &mut ExtCtxt<'_>, fmt: FormatArgs) -> P, fmt: FormatArgs) -> P, fmt: FormatArgs) -> P, fmt: FormatArgs) -> P, fmt: FormatArgs) -> P Date: Sat, 27 Aug 2022 18:32:20 +0200 Subject: [PATCH 06/25] Tweak comments. --- compiler/rustc_builtin_macros/src/format.rs | 2 +- compiler/rustc_builtin_macros/src/format/ast.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_builtin_macros/src/format.rs b/compiler/rustc_builtin_macros/src/format.rs index 42a413210c764..7228b270698b2 100644 --- a/compiler/rustc_builtin_macros/src/format.rs +++ b/compiler/rustc_builtin_macros/src/format.rs @@ -26,7 +26,7 @@ use expand::expand_parsed_format_args; // 3. Finally, `expand_parsed_format_args` will turn that `FormatArgs` structure // into the expression that the macro expands to. -// See format/ast.rs forthe FormatArgs structure. +// See format/ast.rs for the FormatArgs structure and glossary. // Only used in parse_args and report_invalid_references, // to indicate how a referred argument was used. diff --git a/compiler/rustc_builtin_macros/src/format/ast.rs b/compiler/rustc_builtin_macros/src/format/ast.rs index cc0a3b0b24447..cb76482fe5314 100644 --- a/compiler/rustc_builtin_macros/src/format/ast.rs +++ b/compiler/rustc_builtin_macros/src/format/ast.rs @@ -96,7 +96,7 @@ pub struct FormatArgPosition { #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum FormatArgPositionKind { - /// `{}` or `{.*}` + /// `{}` or `{:.*}` Implicit, /// `{1}` or `{:1$}` or `{:.1$}` Number, From e9383e77f6554fe3709795d82fd4f39ff8b2a072 Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Tue, 30 Aug 2022 17:18:01 +0200 Subject: [PATCH 07/25] Use if let chain. --- compiler/rustc_builtin_macros/src/format.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_builtin_macros/src/format.rs b/compiler/rustc_builtin_macros/src/format.rs index 7228b270698b2..fe963a0ea3aa6 100644 --- a/compiler/rustc_builtin_macros/src/format.rs +++ b/compiler/rustc_builtin_macros/src/format.rs @@ -236,10 +236,8 @@ pub fn make_format_args( if let Some(note) = err.note { e.note(¬e); } - if let Some((label, span)) = err.secondary_label { - if 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_literal { + e.span_label(fmt_span.from_inner(InnerSpan::new(span.start, span.end)), label); } if err.should_be_replaced_with_positional_argument { let captured_arg_span = From b76a2f9c061cfcc4ad56faffc33226e17021a798 Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Tue, 30 Aug 2022 17:18:09 +0200 Subject: [PATCH 08/25] Move enum definition closer to its usage. --- compiler/rustc_builtin_macros/src/format.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_builtin_macros/src/format.rs b/compiler/rustc_builtin_macros/src/format.rs index fe963a0ea3aa6..f7c7e00ab22bc 100644 --- a/compiler/rustc_builtin_macros/src/format.rs +++ b/compiler/rustc_builtin_macros/src/format.rs @@ -267,17 +267,17 @@ pub fn make_format_args( }) }; + let num_explicit_args = args.len(); + let mut used = vec![false; num_explicit_args]; + let mut invalid_refs = Vec::new(); + let mut numeric_refences_to_named_arg = Vec::new(); + enum ArgRef<'a> { Index(usize), Name(&'a str, Option), } use ArgRef::*; - let num_explicit_args = args.len(); - let mut used = vec![false; num_explicit_args]; - let mut invalid_refs = Vec::new(); - let mut numeric_refences_to_named_arg = Vec::new(); - let mut lookup_arg = |arg: ArgRef<'_>, span: Option, used_as: PositionUsedAs, From dc6c3a0242c476aad2e847fc6011b6d14e25e50f Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Tue, 30 Aug 2022 17:21:41 +0200 Subject: [PATCH 09/25] Flatten if-let and match into one. --- compiler/rustc_builtin_macros/src/format.rs | 33 +++++++++------------ 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/compiler/rustc_builtin_macros/src/format.rs b/compiler/rustc_builtin_macros/src/format.rs index f7c7e00ab22bc..1dd913ac6d2f0 100644 --- a/compiler/rustc_builtin_macros/src/format.rs +++ b/compiler/rustc_builtin_macros/src/format.rs @@ -285,26 +285,21 @@ pub fn make_format_args( -> FormatArgPosition { let index = match arg { Index(index) => { - if let Some((_, arg_kind)) = args.get(index) { - match arg_kind { - FormatArgKind::Normal => { - used[index] = true; - Ok(index) - } - FormatArgKind::Named(_) => { - used[index] = true; - numeric_refences_to_named_arg.push((index, span, used_as)); - Ok(index) - } - FormatArgKind::Captured(_) => { - // Doesn't exist as an explicit argument. - invalid_refs.push((index, span, used_as, kind)); - Err(index) - } + match args.get(index) { + Some((_, FormatArgKind::Normal)) => { + used[index] = true; + Ok(index) + } + Some((_, FormatArgKind::Named(_))) => { + used[index] = true; + numeric_refences_to_named_arg.push((index, span, used_as)); + Ok(index) + } + Some((_, FormatArgKind::Captured(_))) | None => { + // Doesn't exist as an explicit argument. + invalid_refs.push((index, span, used_as, kind)); + Err(index) } - } else { - invalid_refs.push((index, span, used_as, kind)); - Err(index) } } Name(name, span) => { From 82133450bc1fb3ca38197428e0fffe4757ade384 Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Tue, 30 Aug 2022 17:26:50 +0200 Subject: [PATCH 10/25] Add clarifying comments. --- compiler/rustc_builtin_macros/src/format.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/compiler/rustc_builtin_macros/src/format.rs b/compiler/rustc_builtin_macros/src/format.rs index 1dd913ac6d2f0..26a9169a30b13 100644 --- a/compiler/rustc_builtin_macros/src/format.rs +++ b/compiler/rustc_builtin_macros/src/format.rs @@ -308,12 +308,15 @@ pub fn make_format_args( .iter() .position(|arg| arg.1.ident().is_some_and(|id| id.name == name)) { + // Name found in `args`, so we resolve it to its index in that Vec. let index = start_of_named_args + i; if !matches!(args[index].1, FormatArgKind::Captured(_)) { + // Mark it as used, if it was an explicit argument. used[index] = true; } Ok(index) } else { + // 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 arg = if is_literal { From 25c0a2ac553460922e2812bbbaa23eedf2f7dc9c Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Tue, 6 Sep 2022 23:08:27 +0200 Subject: [PATCH 11/25] Update test. --- src/test/ui/fmt/ifmt-bad-arg.rs | 2 +- src/test/ui/fmt/ifmt-bad-arg.stderr | 8 +++----- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/test/ui/fmt/ifmt-bad-arg.rs b/src/test/ui/fmt/ifmt-bad-arg.rs index 1bd9668de314e..68861d7bf3faf 100644 --- a/src/test/ui/fmt/ifmt-bad-arg.rs +++ b/src/test/ui/fmt/ifmt-bad-arg.rs @@ -95,5 +95,5 @@ tenth number: {}", println!("{:.*}"); //~^ ERROR 2 positional arguments in format string, but no arguments were given println!("{:.0$}"); - //~^ ERROR 1 positional argument in format string, but no arguments were given + //~^ ERROR invalid reference to positional argument 0 (no arguments were given) } diff --git a/src/test/ui/fmt/ifmt-bad-arg.stderr b/src/test/ui/fmt/ifmt-bad-arg.stderr index a71792d1ec912..1b595a50e9984 100644 --- a/src/test/ui/fmt/ifmt-bad-arg.stderr +++ b/src/test/ui/fmt/ifmt-bad-arg.stderr @@ -257,13 +257,11 @@ LL | println!("{:.*}"); = note: positional arguments are zero-based = note: for information about formatting flags, visit https://doc.rust-lang.org/std/fmt/index.html -error: 1 positional argument in format string, but no arguments were given - --> $DIR/ifmt-bad-arg.rs:97:15 +error: invalid reference to positional argument 0 (no arguments were given) + --> $DIR/ifmt-bad-arg.rs:97:16 | LL | println!("{:.0$}"); - | ^^---^ - | | - | this precision flag expects an `usize` argument at position 0, but no arguments were given + | ^^^^ | = note: positional arguments are zero-based = note: for information about formatting flags, visit https://doc.rust-lang.org/std/fmt/index.html From 4146f8ed31db71d92adffab395b529c084be2420 Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Tue, 6 Sep 2022 23:15:13 +0200 Subject: [PATCH 12/25] Turn format arguments Vec into its own struct. With efficient lookup through a hash map. --- compiler/rustc_builtin_macros/src/format.rs | 119 +++++++----------- .../rustc_builtin_macros/src/format/ast.rs | 87 ++++++++++++- .../rustc_builtin_macros/src/format/expand.rs | 24 ++-- 3 files changed, 146 insertions(+), 84 deletions(-) diff --git a/compiler/rustc_builtin_macros/src/format.rs b/compiler/rustc_builtin_macros/src/format.rs index 26a9169a30b13..d3e8d854c7d34 100644 --- a/compiler/rustc_builtin_macros/src/format.rs +++ b/compiler/rustc_builtin_macros/src/format.rs @@ -45,14 +45,14 @@ use PositionUsedAs::*; /// If parsing succeeds, the return value is: /// /// ```text -/// Some((fmtstr, parsed arguments)) +/// Ok((fmtstr, parsed arguments)) /// ``` fn parse_args<'a>( ecx: &mut ExtCtxt<'a>, sp: Span, tts: TokenStream, -) -> PResult<'a, (P, Vec<(P, FormatArgKind)>)> { - let mut args = Vec::<(P, FormatArgKind)>::new(); +) -> PResult<'a, (P, FormatArguments)> { + let mut args = FormatArguments::new(); let mut p = ecx.new_parser_from_tts(tts); @@ -81,7 +81,6 @@ fn parse_args<'a>( }; let mut first = true; - let mut named = false; while p.token != token::Eof { if !p.eat(&token::Comma) { @@ -113,40 +112,40 @@ fn parse_args<'a>( } // accept trailing commas match p.token.ident() { Some((ident, _)) if p.look_ahead(1, |t| *t == token::Eq) => { - named = true; p.bump(); p.expect(&token::Eq)?; - let e = p.parse_expr()?; - if let Some(prev) = - args.iter().rev().map_while(|a| a.1.ident()).find(|n| n.name == ident.name) - { + 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.span, "previously here") + .span_label(prev.kind.ident().unwrap().span, "previously here") .span_label(ident.span, "duplicate argument") .emit(); continue; } - args.push((e, FormatArgKind::Named(ident))); + args.add(FormatArgument { kind: FormatArgumentKind::Named(ident), expr }); } _ => { - let e = p.parse_expr()?; - if named { + let expr = p.parse_expr()?; + if !args.named_args().is_empty() { let mut err = ecx.struct_span_err( - e.span, + expr.span, "positional arguments cannot follow named arguments", ); - err.span_label(e.span, "positional arguments must be before named arguments"); - for arg in &args { - if let Some(name) = arg.1.ident() { - err.span_label(name.span.to(arg.0.span), "named argument"); + 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(); } - args.push((e, FormatArgKind::Normal)); + args.add(FormatArgument { kind: FormatArgumentKind::Normal, expr }); } } } @@ -156,12 +155,9 @@ fn parse_args<'a>( pub fn make_format_args( ecx: &mut ExtCtxt<'_>, efmt: P, - mut args: Vec<(P, FormatArgKind)>, + mut args: FormatArguments, append_newline: bool, ) -> Result { - let start_of_named_args = - args.iter().position(|arg| arg.1.ident().is_some()).unwrap_or(args.len()); - let msg = "format argument must be a string literal"; let fmt_span = efmt.span; let (fmt_str, fmt_style, fmt_span) = match expr_to_spanned_string(ecx, efmt, msg) { @@ -172,9 +168,9 @@ pub fn make_format_args( Ok(fmt) => fmt, Err(err) => { if let Some((mut err, suggested)) = err { - let sugg_fmt = match args.len() { + let sugg_fmt = match args.explicit_args().len() { 0 => "{}".to_string(), - _ => format!("{}{{}}", "{} ".repeat(args.len())), + _ => format!("{}{{}}", "{} ".repeat(args.explicit_args().len())), }; if !suggested { err.span_suggestion( @@ -243,14 +239,14 @@ pub fn make_format_args( let captured_arg_span = fmt_span.from_inner(InnerSpan::new(err.span.start, err.span.end)); if let Ok(arg) = ecx.source_map().span_to_snippet(captured_arg_span) { - let span = match args[..start_of_named_args].last() { - Some(arg) => arg.0.span, + let span = match args.unnamed_args().last() { + Some(arg) => arg.expr.span, None => fmt_span, }; e.multipart_suggestion_verbose( "consider using a positional formatting argument instead", vec![ - (captured_arg_span, start_of_named_args.to_string()), + (captured_arg_span, args.unnamed_args().len().to_string()), (span.shrink_to_hi(), format!(", {}", arg)), ], Applicability::MachineApplicable, @@ -267,8 +263,7 @@ pub fn make_format_args( }) }; - let num_explicit_args = args.len(); - let mut used = vec![false; num_explicit_args]; + let mut used = vec![false; args.explicit_args().len()]; let mut invalid_refs = Vec::new(); let mut numeric_refences_to_named_arg = Vec::new(); @@ -285,32 +280,24 @@ pub fn make_format_args( -> FormatArgPosition { let index = match arg { Index(index) => { - match args.get(index) { - Some((_, FormatArgKind::Normal)) => { - used[index] = true; - Ok(index) - } - Some((_, FormatArgKind::Named(_))) => { - used[index] = true; + if let Some(arg) = args.by_index(index) { + used[index] = true; + if arg.kind.ident().is_some() { + // This was a named argument, but it was used as a positional argument. numeric_refences_to_named_arg.push((index, span, used_as)); - Ok(index) - } - Some((_, FormatArgKind::Captured(_))) | None => { - // Doesn't exist as an explicit argument. - invalid_refs.push((index, span, used_as, kind)); - Err(index) } + Ok(index) + } else { + // Doesn't exist as an explicit argument. + invalid_refs.push((index, span, used_as, kind)); + Err(index) } } Name(name, span) => { let name = Symbol::intern(name); - if let Some(i) = args[start_of_named_args..] - .iter() - .position(|arg| arg.1.ident().is_some_and(|id| id.name == name)) - { - // Name found in `args`, so we resolve it to its index in that Vec. - let index = start_of_named_args + i; - if !matches!(args[index].1, FormatArgKind::Captured(_)) { + if let Some((index, _)) = args.by_name(name) { + // Name found in `args`, so we resolve it to its index. + if index < args.explicit_args().len() { // Mark it as used, if it was an explicit argument. used[index] = true; } @@ -319,7 +306,7 @@ 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 arg = if is_literal { + let expr = if is_literal { ecx.expr_ident(span, ident) } else { // For the moment capturing variables from format strings expanded from macros is @@ -330,8 +317,7 @@ pub fn make_format_args( .emit(); DummyResult::raw_expr(span, true) }; - args.push((arg, FormatArgKind::Captured(ident))); - Ok(args.len() - 1) + Ok(args.add(FormatArgument { kind: FormatArgumentKind::Captured(ident), expr })) } } }; @@ -466,15 +452,7 @@ pub fn make_format_args( } if !invalid_refs.is_empty() { - report_invalid_references( - ecx, - &invalid_refs, - &template, - fmt_span, - num_explicit_args, - &args, - parser, - ); + report_invalid_references(ecx, &invalid_refs, &template, fmt_span, &args, parser); } let unused = used @@ -482,19 +460,19 @@ pub fn make_format_args( .enumerate() .filter(|&(_, used)| !used) .map(|(i, _)| { - let msg = if let FormatArgKind::Named(_) = args[i].1 { + let msg = if let FormatArgumentKind::Named(_) = args.explicit_args()[i].kind { "named argument never used" } else { "argument never used" }; - (args[i].0.span, msg) + (args.explicit_args()[i].expr.span, msg) }) .collect::>(); if !unused.is_empty() { // If there's a lot of unused arguments, // let's check if this format arguments looks like another syntax (printf / shell). - let detect_foreign_fmt = unused.len() > num_explicit_args / 2; + let detect_foreign_fmt = unused.len() > args.explicit_args().len() / 2; report_missing_placeholders(ecx, unused, detect_foreign_fmt, str_style, fmt_str, fmt_span); } @@ -511,7 +489,7 @@ pub fn make_format_args( } Width => (span, span), }; - let arg_name = args[index].1.ident().unwrap(); + let arg_name = args.explicit_args()[index].kind.ident().unwrap(); ecx.buffered_early_lint.push(BufferedEarlyLint { span: arg_name.span.into(), msg: format!("named argument `{}` is not used by name", arg_name.name).into(), @@ -695,11 +673,10 @@ fn report_invalid_references( invalid_refs: &[(usize, Option, PositionUsedAs, FormatArgPositionKind)], template: &[FormatArgsPiece], fmt_span: Span, - num_explicit_args: usize, - args: &[(P, FormatArgKind)], + args: &FormatArguments, parser: parse::Parser<'_>, ) { - let num_args_desc = match num_explicit_args { + let num_args_desc = match args.explicit_args().len() { 0 => "no arguments were given".to_string(), 1 => "there is 1 argument".to_string(), n => format!("there are {} arguments", n), @@ -785,8 +762,8 @@ fn report_invalid_references( num_args_desc, ), ); - for (arg, _) in &args[..num_explicit_args] { - e.span_label(arg.span, ""); + for arg in args.explicit_args() { + e.span_label(arg.expr.span, ""); } // Point out `{:.*}` placeholders: those take an extra argument. let mut has_precision_star = false; diff --git a/compiler/rustc_builtin_macros/src/format/ast.rs b/compiler/rustc_builtin_macros/src/format/ast.rs index cb76482fe5314..139645248a1df 100644 --- a/compiler/rustc_builtin_macros/src/format/ast.rs +++ b/compiler/rustc_builtin_macros/src/format/ast.rs @@ -1,5 +1,6 @@ use rustc_ast::ptr::P; use rustc_ast::Expr; +use rustc_data_structures::fx::FxHashMap; use rustc_span::symbol::{Ident, Symbol}; use rustc_span::Span; @@ -42,17 +43,97 @@ use rustc_span::Span; pub struct FormatArgs { pub span: Span, pub template: Vec, - pub arguments: Vec<(P, FormatArgKind)>, + pub arguments: FormatArguments, } +/// A piece of a format template string. +/// +/// E.g. "hello" or "{name}". #[derive(Clone, Debug)] pub enum FormatArgsPiece { Literal(Symbol), Placeholder(FormatPlaceholder), } +/// The arguments to format_args!(). +/// +/// E.g. `1, 2, name="ferris", n=3`, +/// but also implicit captured arguments like `x` in `format_args!("{x}")`. +#[derive(Clone, Debug)] +pub struct FormatArguments { + arguments: Vec, + num_unnamed_args: usize, + num_explicit_args: usize, + names: FxHashMap, +} + +impl FormatArguments { + pub fn new() -> Self { + Self { + arguments: Vec::new(), + names: FxHashMap::default(), + num_unnamed_args: 0, + num_explicit_args: 0, + } + } + + pub fn add(&mut self, arg: FormatArgument) -> usize { + let index = self.arguments.len(); + if let Some(name) = arg.kind.ident() { + self.names.insert(name.name, index); + } else if self.names.is_empty() { + // Only count the unnamed args before the first named arg. + // (Any later ones are errors.) + self.num_unnamed_args += 1; + } + if !matches!(arg.kind, FormatArgumentKind::Captured(..)) { + // This is an explicit argument. + // Make sure that all arguments so far are explcit. + assert_eq!( + self.num_explicit_args, + self.arguments.len(), + "captured arguments must be added last" + ); + self.num_explicit_args += 1; + } + self.arguments.push(arg); + index + } + + pub fn by_name(&self, name: Symbol) -> Option<(usize, &FormatArgument)> { + let i = *self.names.get(&name)?; + Some((i, &self.arguments[i])) + } + + pub fn by_index(&self, i: usize) -> Option<&FormatArgument> { + (i < self.num_explicit_args).then(|| &self.arguments[i]) + } + + pub fn unnamed_args(&self) -> &[FormatArgument] { + &self.arguments[..self.num_unnamed_args] + } + + pub fn named_args(&self) -> &[FormatArgument] { + &self.arguments[self.num_unnamed_args..self.num_explicit_args] + } + + pub fn explicit_args(&self) -> &[FormatArgument] { + &self.arguments[..self.num_explicit_args] + } + + pub fn into_vec(self) -> Vec { + self.arguments + } +} + +#[derive(Clone, Debug)] +pub struct FormatArgument { + pub kind: FormatArgumentKind, + pub expr: P, +} + #[derive(Clone, Debug)] -pub enum FormatArgKind { +pub enum FormatArgumentKind { /// `format_args(…, arg)` Normal, /// `format_args(…, arg = 1)` @@ -61,7 +142,7 @@ pub enum FormatArgKind { Captured(Ident), } -impl FormatArgKind { +impl FormatArgumentKind { pub fn ident(&self) -> Option { match self { &Self::Normal => None, diff --git a/compiler/rustc_builtin_macros/src/format/expand.rs b/compiler/rustc_builtin_macros/src/format/expand.rs index a2a8213dafc27..9dde5efcb28b7 100644 --- a/compiler/rustc_builtin_macros/src/format/expand.rs +++ b/compiler/rustc_builtin_macros/src/format/expand.rs @@ -201,13 +201,15 @@ pub fn expand_parsed_format_args(ecx: &mut ExtCtxt<'_>, fmt: FormatArgs) -> P, fmt: FormatArgs) -> P, fmt: FormatArgs) -> P, fmt: FormatArgs) -> P Date: Tue, 6 Sep 2022 23:15:44 +0200 Subject: [PATCH 13/25] Update doc comments. --- compiler/rustc_builtin_macros/src/format/ast.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_builtin_macros/src/format/ast.rs b/compiler/rustc_builtin_macros/src/format/ast.rs index 139645248a1df..01dbffa21b8aa 100644 --- a/compiler/rustc_builtin_macros/src/format/ast.rs +++ b/compiler/rustc_builtin_macros/src/format/ast.rs @@ -233,8 +233,8 @@ pub enum FormatAlignment { #[derive(Clone, Debug, PartialEq, Eq)] pub enum FormatCount { - /// `{:0}` or `{:.0}` + /// `{:5}` or `{:.5}` Literal(usize), - /// `{:.*}`, `{:.0$}`, or `{:a$}`, etc. + /// `{:.*}`, `{:.5$}`, or `{:a$}`, etc. Argument(FormatArgPosition), } From ceb278242f878f12b2366230a709831ae371e700 Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Tue, 6 Sep 2022 23:19:20 +0200 Subject: [PATCH 14/25] Remove confusing drop. --- compiler/rustc_builtin_macros/src/format.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/compiler/rustc_builtin_macros/src/format.rs b/compiler/rustc_builtin_macros/src/format.rs index d3e8d854c7d34..b15e2d084ef7f 100644 --- a/compiler/rustc_builtin_macros/src/format.rs +++ b/compiler/rustc_builtin_macros/src/format.rs @@ -448,7 +448,6 @@ pub fn make_format_args( if !unfinished_literal.is_empty() { template.push(FormatArgsPiece::Literal(Symbol::intern(&unfinished_literal))); - drop(unfinished_literal); } if !invalid_refs.is_empty() { From 83037ae3a716bebde8951532c71c0b17bd403079 Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Thu, 1 Sep 2022 11:40:33 +0200 Subject: [PATCH 15/25] Make space for a new fmt::Arguments implementation. --- .../rustc_builtin_macros/src/format/expand.rs | 356 +----------------- library/core/src/fmt/mod.rs | 71 ++++ library/core/src/panicking.rs | 3 + 3 files changed, 85 insertions(+), 345 deletions(-) diff --git a/compiler/rustc_builtin_macros/src/format/expand.rs b/compiler/rustc_builtin_macros/src/format/expand.rs index 9dde5efcb28b7..3c4a1728220a1 100644 --- a/compiler/rustc_builtin_macros/src/format/expand.rs +++ b/compiler/rustc_builtin_macros/src/format/expand.rs @@ -1,353 +1,19 @@ use super::*; use rustc_ast as ast; -use rustc_ast::visit::{self, Visitor}; -use rustc_ast::{BlockCheckMode, UnsafeSource}; -use rustc_data_structures::fx::FxIndexSet; -use rustc_span::{sym, symbol::kw}; - -#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] -enum ArgumentType { - Format(FormatTrait), - Usize, -} - -fn make_argument(ecx: &ExtCtxt<'_>, sp: Span, arg: P, ty: ArgumentType) -> P { - // Generate: - // ::core::fmt::ArgumentV1::new_…(arg) - use ArgumentType::*; - use FormatTrait::*; - ecx.expr_call_global( - sp, - ecx.std_path(&[ - sym::fmt, - sym::ArgumentV1, - match ty { - Format(Display) => sym::new_display, - Format(Debug) => sym::new_debug, - Format(LowerExp) => sym::new_lower_exp, - Format(UpperExp) => sym::new_upper_exp, - Format(Octal) => sym::new_octal, - Format(Pointer) => sym::new_pointer, - Format(Binary) => sym::new_binary, - Format(LowerHex) => sym::new_lower_hex, - Format(UpperHex) => sym::new_upper_hex, - Usize => sym::from_usize, - }, - ]), - vec![arg], - ) -} - -fn make_count( - ecx: &ExtCtxt<'_>, - sp: Span, - count: &Option, - argmap: &mut FxIndexSet<(usize, ArgumentType)>, -) -> P { - // Generate: - // ::core::fmt::rt::v1::Count::…(…) - match count { - Some(FormatCount::Literal(n)) => ecx.expr_call_global( - sp, - ecx.std_path(&[sym::fmt, sym::rt, sym::v1, sym::Count, sym::Is]), - vec![ecx.expr_usize(sp, *n)], - ), - Some(FormatCount::Argument(arg)) => { - if let Ok(arg_index) = arg.index { - let (i, _) = argmap.insert_full((arg_index, ArgumentType::Usize)); - ecx.expr_call_global( - sp, - ecx.std_path(&[sym::fmt, sym::rt, sym::v1, sym::Count, sym::Param]), - vec![ecx.expr_usize(sp, i)], - ) - } else { - DummyResult::raw_expr(sp, true) - } - } - None => ecx.expr_path(ecx.path_global( - sp, - ecx.std_path(&[sym::fmt, sym::rt, sym::v1, sym::Count, sym::Implied]), - )), - } -} - -fn make_format_spec( - ecx: &ExtCtxt<'_>, - sp: Span, - placeholder: &FormatPlaceholder, - argmap: &mut FxIndexSet<(usize, ArgumentType)>, -) -> P { - // Generate: - // ::core::fmt::rt::v1::Argument { - // position: 0usize, - // format: ::core::fmt::rt::v1::FormatSpec { - // fill: ' ', - // align: ::core::fmt::rt::v1::Alignment::Unknown, - // flags: 0u32, - // precision: ::core::fmt::rt::v1::Count::Implied, - // width: ::core::fmt::rt::v1::Count::Implied, - // }, - // } - let position = match placeholder.argument.index { - Ok(arg_index) => { - let (i, _) = - argmap.insert_full((arg_index, ArgumentType::Format(placeholder.format_trait))); - ecx.expr_usize(sp, i) - } - Err(_) => DummyResult::raw_expr(sp, true), - }; - let fill = ecx.expr_char(sp, placeholder.format_options.fill.unwrap_or(' ')); - let align = ecx.expr_path(ecx.path_global( - sp, - ecx.std_path(&[ - sym::fmt, - sym::rt, - sym::v1, - sym::Alignment, - match placeholder.format_options.alignment { - Some(FormatAlignment::Left) => sym::Left, - Some(FormatAlignment::Right) => sym::Right, - Some(FormatAlignment::Center) => sym::Center, - None => sym::Unknown, - }, - ]), - )); - let flags = ecx.expr_u32(sp, placeholder.format_options.flags); - let prec = make_count(ecx, sp, &placeholder.format_options.precision, argmap); - let width = make_count(ecx, sp, &placeholder.format_options.width, argmap); - ecx.expr_struct( - sp, - ecx.path_global(sp, ecx.std_path(&[sym::fmt, sym::rt, sym::v1, sym::Argument])), - vec![ - ecx.field_imm(sp, Ident::new(sym::position, sp), position), - ecx.field_imm( - sp, - Ident::new(sym::format, sp), - ecx.expr_struct( - sp, - ecx.path_global( - sp, - ecx.std_path(&[sym::fmt, sym::rt, sym::v1, sym::FormatSpec]), - ), - vec![ - ecx.field_imm(sp, Ident::new(sym::fill, sp), fill), - ecx.field_imm(sp, Ident::new(sym::align, sp), align), - ecx.field_imm(sp, Ident::new(sym::flags, sp), flags), - ecx.field_imm(sp, Ident::new(sym::precision, sp), prec), - ecx.field_imm(sp, Ident::new(sym::width, sp), width), - ], - ), - ), - ], - ) -} +use rustc_span::sym; pub fn expand_parsed_format_args(ecx: &mut ExtCtxt<'_>, fmt: FormatArgs) -> P { let macsp = ecx.with_def_site_ctxt(ecx.call_site()); - let lit_pieces = ecx.expr_array_ref( - fmt.span, - fmt.template - .iter() - .enumerate() - .filter_map(|(i, piece)| match piece { - &FormatArgsPiece::Literal(s) => Some(ecx.expr_str(fmt.span, s)), - &FormatArgsPiece::Placeholder(_) => { - // Inject empty string before placeholders when not already preceded by a literal piece. - if i == 0 || matches!(fmt.template[i - 1], FormatArgsPiece::Placeholder(_)) { - Some(ecx.expr_str(fmt.span, kw::Empty)) - } else { - None - } - } - }) - .collect(), - ); - - // Whether we'll use the `Arguments::new_v1_formatted` form (true), - // or the `Arguments::new_v1` form (false). - let mut use_format_options = false; + … // TODO - // Create a list of all _unique_ (argument, format trait) combinations. - // E.g. "{0} {0:x} {0} {1}" -> [(0, Display), (0, LowerHex), (1, Display)] - let mut argmap = FxIndexSet::default(); - for piece in &fmt.template { - let FormatArgsPiece::Placeholder(placeholder) = piece else { continue }; - if placeholder.format_options != Default::default() { - // Can't use basic form if there's any formatting options. - use_format_options = true; - } - if let Ok(index) = placeholder.argument.index { - if !argmap.insert((index, ArgumentType::Format(placeholder.format_trait))) { - // Duplicate (argument, format trait) combination, - // which we'll only put once in the args array. - use_format_options = true; - } - } - } - - let format_options = use_format_options.then(|| { - // Generate: - // &[format_spec_0, format_spec_1, format_spec_2] - ecx.expr_array_ref( - macsp, - fmt.template - .iter() - .filter_map(|piece| { - let FormatArgsPiece::Placeholder(placeholder) = piece else { return None }; - Some(make_format_spec(ecx, macsp, placeholder, &mut argmap)) - }) - .collect(), - ) - }); - - let arguments = fmt.arguments.into_vec(); - - // If the args array contains exactly all the original arguments once, - // in order, we can use a simple array instead of a `match` construction. - // However, if there's a yield point in any argument except the first one, - // we don't do this, because an ArgumentV1 cannot be kept across yield points. - let use_simple_array = argmap.len() == arguments.len() - && argmap.iter().enumerate().all(|(i, &(j, _))| i == j) - && arguments.iter().skip(1).all(|arg| !may_contain_yield_point(&arg.expr)); - - let args = if use_simple_array { - // Generate: - // &[ - // ::core::fmt::ArgumentV1::new_display(&arg0), - // ::core::fmt::ArgumentV1::new_lower_hex(&arg1), - // ::core::fmt::ArgumentV1::new_debug(&arg2), - // ] - ecx.expr_array_ref( - macsp, - arguments - .into_iter() - .zip(argmap) - .map(|(arg, (_, ty))| { - let sp = arg.expr.span.with_ctxt(macsp.ctxt()); - make_argument(ecx, sp, ecx.expr_addr_of(sp, arg.expr), ty) - }) - .collect(), - ) - } else { - // Generate: - // match (&arg0, &arg1, &arg2) { - // args => &[ - // ::core::fmt::ArgumentV1::new_display(args.0), - // ::core::fmt::ArgumentV1::new_lower_hex(args.1), - // ::core::fmt::ArgumentV1::new_debug(args.0), - // ] - // } - let args_ident = Ident::new(sym::args, macsp); - let args = argmap - .iter() - .map(|&(arg_index, ty)| { - if let Some(arg) = arguments.get(arg_index) { - let sp = arg.expr.span.with_ctxt(macsp.ctxt()); - make_argument( - ecx, - sp, - ecx.expr_field( - sp, - ecx.expr_ident(macsp, args_ident), - Ident::new(sym::integer(arg_index), macsp), - ), - ty, - ) - } else { - DummyResult::raw_expr(macsp, true) - } - }) - .collect(); - ecx.expr_addr_of( - macsp, - ecx.expr_match( - macsp, - ecx.expr_tuple( - macsp, - arguments - .into_iter() - .map(|arg| { - ecx.expr_addr_of(arg.expr.span.with_ctxt(macsp.ctxt()), arg.expr) - }) - .collect(), - ), - vec![ecx.arm(macsp, ecx.pat_ident(macsp, args_ident), ecx.expr_array(macsp, args))], - ), - ) - }; - - if let Some(format_options) = format_options { - // Generate: - // ::core::fmt::Arguments::new_v1_formatted( - // lit_pieces, - // args, - // format_options, - // unsafe { ::core::fmt::UnsafeArg::new() } - // ) - ecx.expr_call_global( - macsp, - ecx.std_path(&[sym::fmt, sym::Arguments, sym::new_v1_formatted]), - vec![ - lit_pieces, - args, - format_options, - ecx.expr_block(P(ast::Block { - stmts: vec![ecx.stmt_expr(ecx.expr_call_global( - macsp, - ecx.std_path(&[sym::fmt, sym::UnsafeArg, sym::new]), - Vec::new(), - ))], - id: ast::DUMMY_NODE_ID, - rules: BlockCheckMode::Unsafe(UnsafeSource::CompilerGenerated), - span: macsp, - tokens: None, - could_be_bare_literal: false, - })), - ], - ) - } else { - // Generate: - // ::core::fmt::Arguments::new_v1( - // lit_pieces, - // args, - // ) - ecx.expr_call_global( - macsp, - ecx.std_path(&[sym::fmt, sym::Arguments, sym::new_v1]), - vec![lit_pieces, args], - ) - } -} - -fn may_contain_yield_point(e: &ast::Expr) -> bool { - struct MayContainYieldPoint(bool); - - impl Visitor<'_> for MayContainYieldPoint { - fn visit_expr(&mut self, e: &ast::Expr) { - if let ast::ExprKind::Await(_) | ast::ExprKind::Yield(_) = e.kind { - self.0 = true; - } else { - visit::walk_expr(self, e); - } - } - - fn visit_mac_call(&mut self, _: &ast::MacCall) { - self.0 = true; - } - - fn visit_attribute(&mut self, _: &ast::Attribute) { - // Conservatively assume this may be a proc macro attribute in - // expression position. - self.0 = true; - } - - fn visit_item(&mut self, _: &ast::Item) { - // Do not recurse into nested items. - } - } - - let mut visitor = MayContainYieldPoint(false); - visitor.visit_expr(e); - visitor.0 + // Generate: + // ::core::fmt::Arguments::new( + // … + // ) + ecx.expr_call_global( + macsp, + ecx.std_path(&[sym::fmt, sym::Arguments, sym::new]), + vec![…], + ) } diff --git a/library/core/src/fmt/mod.rs b/library/core/src/fmt/mod.rs index 905212eb372b1..3d91ad9502e3e 100644 --- a/library/core/src/fmt/mod.rs +++ b/library/core/src/fmt/mod.rs @@ -6,6 +6,7 @@ use crate::cell::{Cell, Ref, RefCell, RefMut, SyncUnsafeCell, UnsafeCell}; use crate::char::EscapeDebugExtArgs; use crate::iter; use crate::marker::PhantomData; +#[cfg(bootstrap)] use crate::mem; use crate::num::fmt as numfmt; use crate::ops::Deref; @@ -254,6 +255,7 @@ impl<'a> Formatter<'a> { // NB. Argument is essentially an optimized partially applied formatting function, // equivalent to `exists T.(&T, fn(&T, &mut Formatter<'_>) -> Result`. +#[cfg(bootstrap)] extern "C" { type Opaque; } @@ -266,6 +268,7 @@ extern "C" { #[allow(missing_debug_implementations)] #[unstable(feature = "fmt_internals", reason = "internal to format_args!", issue = "none")] #[doc(hidden)] +#[cfg(bootstrap)] pub struct ArgumentV1<'a> { value: &'a Opaque, formatter: fn(&Opaque, &mut Formatter<'_>) -> Result, @@ -277,10 +280,12 @@ pub struct ArgumentV1<'a> { #[allow(missing_debug_implementations)] #[doc(hidden)] #[unstable(feature = "fmt_internals", reason = "internal to format_args!", issue = "none")] +#[cfg(bootstrap)] pub struct UnsafeArg { _private: (), } +#[cfg(bootstrap)] impl UnsafeArg { /// See documentation where `UnsafeArg` is required to know when it is safe to /// create and use `UnsafeArg`. @@ -307,6 +312,7 @@ impl UnsafeArg { // first argument. The read_volatile here ensures that we can safely ready out a // usize from the passed reference and that this address does not point at a // non-usize taking function. +#[cfg(bootstrap)] #[unstable(feature = "fmt_internals", reason = "internal to format_args!", issue = "none")] static USIZE_MARKER: fn(&usize, &mut Formatter<'_>) -> Result = |ptr, _| { // SAFETY: ptr is a reference @@ -314,6 +320,7 @@ static USIZE_MARKER: fn(&usize, &mut Formatter<'_>) -> Result = |ptr, _| { loop {} }; +#[cfg(bootstrap)] macro_rules! arg_new { ($f: ident, $t: ident) => { #[doc(hidden)] @@ -325,6 +332,7 @@ macro_rules! arg_new { }; } +#[cfg(bootstrap)] #[rustc_diagnostic_item = "ArgumentV1Methods"] impl<'a> ArgumentV1<'a> { #[doc(hidden)] @@ -391,6 +399,7 @@ impl<'a> Arguments<'a> { #[inline] #[unstable(feature = "fmt_internals", reason = "internal to format_args!", issue = "none")] #[rustc_const_unstable(feature = "const_fmt_arguments_new", issue = "none")] + #[cfg(bootstrap)] pub const fn new_v1(pieces: &'a [&'static str], args: &'a [ArgumentV1<'a>]) -> Arguments<'a> { if pieces.len() < args.len() || pieces.len() > args.len() + 1 { panic!("invalid args"); @@ -411,6 +420,7 @@ impl<'a> Arguments<'a> { #[inline] #[unstable(feature = "fmt_internals", reason = "internal to format_args!", issue = "none")] #[rustc_const_unstable(feature = "const_fmt_arguments_new", issue = "none")] + #[cfg(bootstrap)] pub const fn new_v1_formatted( pieces: &'a [&'static str], args: &'a [ArgumentV1<'a>], @@ -420,6 +430,26 @@ impl<'a> Arguments<'a> { Arguments { pieces, fmt: Some(fmt), args } } + #[doc(hidden)] + #[inline] + #[unstable(feature = "fmt_internals", reason = "internal to format_args!", issue = "none")] + #[rustc_const_unstable(feature = "const_fmt_arguments_new", issue = "none")] + #[cfg(not(bootstrap))] + pub const fn new( + // TODO + ) -> Arguments<'a> { + unimplemented!() // TODO + } + + #[doc(hidden)] + #[inline] + #[unstable(feature = "fmt_internals", reason = "internal to format_args!", issue = "none")] + #[rustc_const_unstable(feature = "const_fmt_arguments_new", issue = "none")] + #[cfg(not(bootstrap))] + pub const fn from_static_str(s: &'static str) -> Arguments<'a> { + unimplemented!() // TODO + } + /// Estimates the length of the formatted text. /// /// This is intended to be used for setting initial `String` capacity @@ -427,6 +457,16 @@ impl<'a> Arguments<'a> { #[doc(hidden)] #[inline] #[unstable(feature = "fmt_internals", reason = "internal to format_args!", issue = "none")] + #[cfg(not(bootstrap))] + pub fn estimated_capacity(&self) -> usize { + unimplemented!() // TODO + } + + /// Old estimated_capacity(). + #[doc(hidden)] + #[inline] + #[unstable(feature = "fmt_internals", reason = "internal to format_args!", issue = "none")] + #[cfg(bootstrap)] pub fn estimated_capacity(&self) -> usize { let pieces_length: usize = self.pieces.iter().map(|x| x.len()).sum(); @@ -471,6 +511,16 @@ impl<'a> Arguments<'a> { #[stable(feature = "rust1", since = "1.0.0")] #[cfg_attr(not(test), rustc_diagnostic_item = "Arguments")] #[derive(Copy, Clone)] +#[cfg(not(bootstrap))] +pub struct Arguments<'a> { + // TODO +} + +/// Old fmt::Arguments. +#[stable(feature = "rust1", since = "1.0.0")] +#[cfg_attr(not(test), rustc_diagnostic_item = "Arguments")] +#[derive(Copy, Clone)] +#[cfg(bootstrap)] pub struct Arguments<'a> { // Format string pieces to print. pieces: &'a [&'static str], @@ -513,6 +563,17 @@ impl<'a> Arguments<'a> { #[rustc_const_unstable(feature = "const_arguments_as_str", issue = "none")] #[must_use] #[inline] + #[cfg(not(bootstrap))] + pub const fn as_str(&self) -> Option<&'static str> { + unimplemented!() // TODO + } + + /// Old as_str(). + #[stable(feature = "fmt_as_str", since = "1.52.0")] + #[rustc_const_unstable(feature = "const_arguments_as_str", issue = "none")] + #[must_use] + #[inline] + #[cfg(bootstrap)] pub const fn as_str(&self) -> Option<&'static str> { match (self.pieces, self.args) { ([], []) => Some(""), @@ -1185,6 +1246,14 @@ pub trait UpperExp { /// /// [`write!`]: crate::write! #[stable(feature = "rust1", since = "1.0.0")] +#[cfg(not(bootstrap))] +pub fn write(output: &mut dyn Write, args: Arguments<'_>) -> Result { + unimplemented!() // TODO +} + +/// Old write(). +#[stable(feature = "rust1", since = "1.0.0")] +#[cfg(bootstrap)] pub fn write(output: &mut dyn Write, args: Arguments<'_>) -> Result { let mut formatter = Formatter::new(output); let mut idx = 0; @@ -1229,6 +1298,7 @@ pub fn write(output: &mut dyn Write, args: Arguments<'_>) -> Result { Ok(()) } +#[cfg(bootstrap)] unsafe fn run(fmt: &mut Formatter<'_>, arg: &rt::v1::Argument, args: &[ArgumentV1<'_>]) -> Result { fmt.fill = arg.format.fill; fmt.align = arg.format.align; @@ -1250,6 +1320,7 @@ unsafe fn run(fmt: &mut Formatter<'_>, arg: &rt::v1::Argument, args: &[ArgumentV (value.formatter)(value.value, fmt) } +#[cfg(bootstrap)] unsafe fn getcount(args: &[ArgumentV1<'_>], cnt: &rt::v1::Count) -> Option { match *cnt { rt::v1::Count::Is(n) => Some(n), diff --git a/library/core/src/panicking.rs b/library/core/src/panicking.rs index d4afe0f5326a3..87125e5ce04ca 100644 --- a/library/core/src/panicking.rs +++ b/library/core/src/panicking.rs @@ -45,7 +45,10 @@ pub const fn panic(expr: &'static str) -> ! { // truncation and padding (even though none is used here). Using // Arguments::new_v1 may allow the compiler to omit Formatter::pad from the // output binary, saving up to a few kilobytes. + #[cfg(bootstrap)] panic_fmt(fmt::Arguments::new_v1(&[expr], &[])); + #[cfg(not(bootstrap))] + panic_fmt(fmt::Arguments::from_static_str(expr)); } #[inline] From 5276b1aebfafc74636a572649b1de2f78ee4027b Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Thu, 8 Sep 2022 12:26:27 +0200 Subject: [PATCH 16/25] Don't re-export private/unstable ArgumentV1 from `alloc`. --- library/alloc/src/fmt.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/alloc/src/fmt.rs b/library/alloc/src/fmt.rs index ed398b56612ce..6a0178274d36e 100644 --- a/library/alloc/src/fmt.rs +++ b/library/alloc/src/fmt.rs @@ -558,7 +558,7 @@ pub use core::fmt::Alignment; #[stable(feature = "rust1", since = "1.0.0")] pub use core::fmt::Error; #[stable(feature = "rust1", since = "1.0.0")] -pub use core::fmt::{write, ArgumentV1, Arguments}; +pub use core::fmt::{write, Arguments}; #[stable(feature = "rust1", since = "1.0.0")] pub use core::fmt::{Binary, Octal}; #[stable(feature = "rust1", since = "1.0.0")] From 9f6299e6d043a311eb06bcd45dd85e4240daf194 Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Thu, 8 Sep 2022 12:29:53 +0200 Subject: [PATCH 17/25] Implement fmt::Arguments as a closure. --- .../rustc_builtin_macros/src/format/expand.rs | 220 +++++++++++++++++- compiler/rustc_span/src/symbol.rs | 9 + library/core/src/fmt/mod.rs | 51 +++- 3 files changed, 267 insertions(+), 13 deletions(-) diff --git a/compiler/rustc_builtin_macros/src/format/expand.rs b/compiler/rustc_builtin_macros/src/format/expand.rs index 3c4a1728220a1..7077754584de9 100644 --- a/compiler/rustc_builtin_macros/src/format/expand.rs +++ b/compiler/rustc_builtin_macros/src/format/expand.rs @@ -1,19 +1,231 @@ use super::*; use rustc_ast as ast; -use rustc_span::sym; +use rustc_span::symbol::{kw, sym, Ident}; pub fn expand_parsed_format_args(ecx: &mut ExtCtxt<'_>, fmt: FormatArgs) -> P { let macsp = ecx.with_def_site_ctxt(ecx.call_site()); - … // TODO + if fmt.template.is_empty() { + return ecx.expr_call_global( + macsp, + ecx.std_path(&[sym::fmt, sym::Arguments, sym::from_static_str]), + vec![ecx.expr_str(macsp, kw::Empty)], + ); + } + + if let &[FormatArgsPiece::Literal(s)] = &fmt.template[..] { + return ecx.expr_call_global( + macsp, + ecx.std_path(&[sym::fmt, sym::Arguments, sym::from_static_str]), + vec![ecx.expr_str(macsp, s)], + ); + } + + let args = Ident::new(sym::_args, macsp); + let f = Ident::new(sym::f, macsp); + + let arguments = fmt.arguments.into_vec(); + + let mut statements = Vec::new(); + + let mut default_options = true; + + for piece in fmt.template { + match piece { + FormatArgsPiece::Literal(s) => { + // Generate: + // f.write_str("…")?; + statements.push(ecx.stmt_expr(ecx.expr( + macsp, + ast::ExprKind::Try(ecx.expr( + macsp, + ast::ExprKind::MethodCall( + ast::PathSegment::from_ident(Ident::new(sym::write_str, macsp)), + ecx.expr_ident(macsp, f), + vec![ecx.expr_str(macsp, s)], + macsp, + ), + )), + ))); + } + FormatArgsPiece::Placeholder(p) => { + // Don't set options if they're still set to defaults + // and this placeholder also uses default options. + let d = p.format_options == FormatOptions::default(); + if !default_options || !d { + default_options = d; + // Generate: + // f.set_options(…); + statements.push(ecx.stmt_expr(ecx.expr( + macsp, + ast::ExprKind::MethodCall( + ast::PathSegment::from_ident(Ident::new(sym::set_options, macsp)), + ecx.expr_ident(macsp, f), + vec![ + ecx.expr_u32(macsp, p.format_options.flags), + ecx.expr_char(macsp, p.format_options.fill.unwrap_or(' ')), + ecx.expr_path(ecx.path_global( + macsp, + ecx.std_path(&[ + sym::fmt, + sym::rt, + sym::v1, + sym::Alignment, + match p.format_options.alignment { + Some(FormatAlignment::Left) => sym::Left, + Some(FormatAlignment::Right) => sym::Right, + Some(FormatAlignment::Center) => sym::Center, + None => sym::Unknown, + }, + ]), + )), + make_count(ecx, macsp, &arguments, args, p.format_options.width), + make_count( + ecx, + macsp, + &arguments, + args, + p.format_options.precision, + ), + ], + macsp, + ), + ))); + } + // Generate: + // ::core::fmt::Display::fmt(arg.0, f)?; + let arg = if let Ok(i) = p.argument.index { + ecx.expr_field( + arguments[i].expr.span.with_ctxt(macsp.ctxt()), + ecx.expr_ident(macsp, args), + Ident::new(sym::integer(i), macsp), + ) + } else { + DummyResult::raw_expr(macsp, true) + }; + let fmt_trait = match p.format_trait { + FormatTrait::Display => sym::Display, + FormatTrait::Debug => sym::Debug, + FormatTrait::LowerExp => sym::LowerExp, + FormatTrait::UpperExp => sym::UpperExp, + FormatTrait::Octal => sym::Octal, + FormatTrait::Pointer => sym::Pointer, + FormatTrait::Binary => sym::Binary, + FormatTrait::LowerHex => sym::LowerHex, + FormatTrait::UpperHex => sym::UpperHex, + }; + statements.push(ecx.stmt_expr(ecx.expr( + macsp, + ast::ExprKind::Try(ecx.expr_call_global( + arg.span, + ecx.std_path(&[sym::fmt, fmt_trait, sym::fmt]), + vec![arg, ecx.expr_ident(macsp, f)], + )), + ))); + } + } + } + + // Generate: + // Ok(()) + statements.push(ecx.stmt_expr(ecx.expr_ok(macsp, ecx.expr_tuple(macsp, Vec::new())))); + + // Generate: + // |f: &mut ::core::fmt::Formatter| -> ::core::fmt::Result { + // … // statements + // } + let closure = ecx.expr( + macsp, + ast::ExprKind::Closure( + ast::ClosureBinder::NotPresent, + ast::CaptureBy::Ref, + ast::Async::No, + ast::Movability::Movable, + ecx.fn_decl( + vec![ecx.param( + macsp, + f, + ecx.ty_rptr( + macsp, + ecx.ty_path(ecx.path_all( + macsp, + true, + ecx.std_path(&[sym::fmt, sym::Formatter]), + vec![ast::GenericArg::Lifetime(ast::Lifetime { + id: ast::DUMMY_NODE_ID, + ident: Ident::new(kw::UnderscoreLifetime, macsp), + })], + )), + None, + ast::Mutability::Mut, + ), + )], + ast::FnRetTy::Ty( + ecx.ty_path(ecx.path_global(macsp, ecx.std_path(&[sym::fmt, sym::Result]))), + ), + ), + ecx.expr_block(ecx.block(macsp, statements)), + macsp, + ), + ); // Generate: // ::core::fmt::Arguments::new( - // … + // &match (&arg0, &arg1, …) { + // args => closure, + // } // ) ecx.expr_call_global( macsp, ecx.std_path(&[sym::fmt, sym::Arguments, sym::new]), - vec![…], + vec![ + ecx.expr_addr_of( + macsp, + ecx.expr_match( + macsp, + ecx.expr_tuple( + macsp, + arguments + .into_iter() + .map(|arg| { + ecx.expr_addr_of(arg.expr.span.with_ctxt(macsp.ctxt()), arg.expr) + }) + .collect(), + ), + vec![ecx.arm(macsp, ecx.pat_ident(macsp, args), closure)], + ), + ), + ], ) } + +pub fn make_count( + ecx: &ExtCtxt<'_>, + macsp: Span, + arguments: &[FormatArgument], + args: Ident, + count: Option, +) -> P { + match count { + Some(FormatCount::Literal(n)) => ecx.expr_some(macsp, ecx.expr_usize(macsp, n)), + Some(FormatCount::Argument(arg)) => { + if let Ok(i) = arg.index { + let sp = arguments[i].expr.span.with_ctxt(macsp.ctxt()); + ecx.expr_some( + sp, + ecx.expr_deref( + sp, + ecx.expr_field( + sp, + ecx.expr_ident(macsp, args), + Ident::new(sym::integer(i), macsp), + ), + ), + ) + } else { + DummyResult::raw_expr(macsp, true) + } + } + None => ecx.expr_none(macsp), + } +} diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 326befa108473..d9d73e720579c 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -155,6 +155,7 @@ symbols! { BTreeEntry, BTreeMap, BTreeSet, + Binary, BinaryHeap, Borrow, BorrowMut, @@ -223,6 +224,8 @@ symbols! { Left, LinkedList, LintPass, + LowerExp, + LowerHex, Mutex, MutexGuard, N, @@ -237,6 +240,7 @@ symbols! { NonZeroU64, NonZeroU8, None, + Octal, Ok, Option, Ord, @@ -299,6 +303,8 @@ symbols! { TyKind, Unknown, UnsafeArg, + UpperExp, + UpperHex, Vec, VecDeque, Wrapper, @@ -310,6 +316,7 @@ symbols! { __S, __awaitee, __try_var, + _args, _d, _e, _task_context, @@ -754,6 +761,7 @@ symbols! { from_output, from_residual, from_size_align_unchecked, + from_static_str, from_usize, from_yeet, fsub_fast, @@ -1317,6 +1325,7 @@ symbols! { self_in_typedefs, self_struct_ctor, semitransparent, + set_options, shadow_call_stack, shl, shl_assign, diff --git a/library/core/src/fmt/mod.rs b/library/core/src/fmt/mod.rs index 3d91ad9502e3e..113590f5e6856 100644 --- a/library/core/src/fmt/mod.rs +++ b/library/core/src/fmt/mod.rs @@ -250,6 +250,25 @@ impl<'a> Formatter<'a> { buf, } } + + /// Set all the formatting options. + #[unstable(feature = "fmt_internals", issue = "none")] + #[doc(hidden)] + #[inline] + pub fn set_options( + &mut self, + flags: u32, + fill: char, + align: rt::v1::Alignment, + width: Option, + precision: Option, + ) { + self.flags = flags; + self.fill = fill; + self.align = align; + self.width = width; + self.precision = precision; + } } // NB. Argument is essentially an optimized partially applied formatting function, @@ -435,10 +454,8 @@ impl<'a> Arguments<'a> { #[unstable(feature = "fmt_internals", reason = "internal to format_args!", issue = "none")] #[rustc_const_unstable(feature = "const_fmt_arguments_new", issue = "none")] #[cfg(not(bootstrap))] - pub const fn new( - // TODO - ) -> Arguments<'a> { - unimplemented!() // TODO + pub const fn new(f: &'a dyn Fn(&mut Formatter<'_>) -> Result) -> Arguments<'a> { + Arguments { inner: Inner::Fn(f) } } #[doc(hidden)] @@ -447,7 +464,7 @@ impl<'a> Arguments<'a> { #[rustc_const_unstable(feature = "const_fmt_arguments_new", issue = "none")] #[cfg(not(bootstrap))] pub const fn from_static_str(s: &'static str) -> Arguments<'a> { - unimplemented!() // TODO + Arguments { inner: Inner::StaticStr(s) } } /// Estimates the length of the formatted text. @@ -459,7 +476,10 @@ impl<'a> Arguments<'a> { #[unstable(feature = "fmt_internals", reason = "internal to format_args!", issue = "none")] #[cfg(not(bootstrap))] pub fn estimated_capacity(&self) -> usize { - unimplemented!() // TODO + match self.inner { + Inner::Fn(_) => 0, // FIXME + Inner::StaticStr(s) => s.len(), + } } /// Old estimated_capacity(). @@ -513,7 +533,14 @@ impl<'a> Arguments<'a> { #[derive(Copy, Clone)] #[cfg(not(bootstrap))] pub struct Arguments<'a> { - // TODO + inner: Inner<'a>, +} + +#[cfg(not(bootstrap))] +#[derive(Copy, Clone)] +enum Inner<'a> { + Fn(&'a dyn Fn(&mut Formatter<'_>) -> Result), + StaticStr(&'static str), } /// Old fmt::Arguments. @@ -565,7 +592,10 @@ impl<'a> Arguments<'a> { #[inline] #[cfg(not(bootstrap))] pub const fn as_str(&self) -> Option<&'static str> { - unimplemented!() // TODO + match self.inner { + Inner::Fn(_) => None, + Inner::StaticStr(s) => Some(s), + } } /// Old as_str(). @@ -1248,7 +1278,10 @@ pub trait UpperExp { #[stable(feature = "rust1", since = "1.0.0")] #[cfg(not(bootstrap))] pub fn write(output: &mut dyn Write, args: Arguments<'_>) -> Result { - unimplemented!() // TODO + match args.inner { + Inner::Fn(f) => f(&mut Formatter::new(output)), + Inner::StaticStr(s) => output.write_str(s), + } } /// Old write(). From abb8906f15913b4ebabbb47b228417a9cdd23347 Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Thu, 8 Sep 2022 12:30:07 +0200 Subject: [PATCH 18/25] Update tests. --- .../ui/attributes/key-value-expansion.stderr | 10 +- src/test/ui/borrowck/issue-64453.stderr | 2 +- ...re-print-generic-trim-off-verbose-2.stderr | 2 +- .../closure-print-generic-verbose-2.stderr | 2 +- .../const-ptr/forbidden_slices.64bit.stderr | 4 +- src/test/ui/consts/const-eval/format.rs | 20 ++- src/test/ui/consts/const-eval/format.stderr | 121 ++++++------------ src/test/ui/fmt/ifmt-bad-arg.stderr | 24 ++-- src/test/ui/fmt/ifmt-unimpl.stderr | 7 +- src/test/ui/fmt/send-sync.stderr | 24 ++-- src/test/ui/issues/issue-69455.stderr | 18 +-- .../ui/lint/function-item-references.stderr | 96 +++++++------- ...ming-methods-have-optimized-codegen.stdout | 117 ++++++++++++++--- 13 files changed, 240 insertions(+), 207 deletions(-) diff --git a/src/test/ui/attributes/key-value-expansion.stderr b/src/test/ui/attributes/key-value-expansion.stderr index 1b776322aaa64..601743921699f 100644 --- a/src/test/ui/attributes/key-value-expansion.stderr +++ b/src/test/ui/attributes/key-value-expansion.stderr @@ -17,8 +17,14 @@ LL | bug!(); error: unexpected expression: `{ let res = - ::alloc::fmt::format(::core::fmt::Arguments::new_v1(&[""], - &[::core::fmt::ArgumentV1::new_display(&"u8")])); + ::alloc::fmt::format(::core::fmt::Arguments::new(&match (&"u8",) { + _args => + |f: &mut ::core::fmt::Formatter<'_>| -> ::core::fmt::Result + { + ::core::fmt::Display::fmt(_args.0, f)?; + ::core::result::Result::Ok(()) + }, + })); res }.as_str()` --> $DIR/key-value-expansion.rs:48:23 diff --git a/src/test/ui/borrowck/issue-64453.stderr b/src/test/ui/borrowck/issue-64453.stderr index 245c3a40e0506..89853ee3344a4 100644 --- a/src/test/ui/borrowck/issue-64453.stderr +++ b/src/test/ui/borrowck/issue-64453.stderr @@ -1,4 +1,4 @@ -error: `Arguments::<'a>::new_v1` is not yet stable as a const fn +error: `Arguments::<'a>::from_static_str` is not yet stable as a const fn --> $DIR/issue-64453.rs:4:31 | LL | static settings_dir: String = format!(""); diff --git a/src/test/ui/closures/print/closure-print-generic-trim-off-verbose-2.stderr b/src/test/ui/closures/print/closure-print-generic-trim-off-verbose-2.stderr index ff89dd340349b..034bec32fdcb4 100644 --- a/src/test/ui/closures/print/closure-print-generic-trim-off-verbose-2.stderr +++ b/src/test/ui/closures/print/closure-print-generic-trim-off-verbose-2.stderr @@ -9,7 +9,7 @@ LL | let c1 : () = c; | expected due to this | = note: expected unit type `()` - found closure `[mod1::f::{closure#0} closure_substs=(unavailable) substs=[T, _#16t, extern "rust-call" fn(()), _#15t]]` + found closure `[mod1::f::{closure#0} closure_substs=(unavailable) substs=[T, _#88t, extern "rust-call" fn(()), _#89t]]` help: use parentheses to call this closure | LL | let c1 : () = c(); diff --git a/src/test/ui/closures/print/closure-print-generic-verbose-2.stderr b/src/test/ui/closures/print/closure-print-generic-verbose-2.stderr index 5bbf84f963d7e..46a67daa1b113 100644 --- a/src/test/ui/closures/print/closure-print-generic-verbose-2.stderr +++ b/src/test/ui/closures/print/closure-print-generic-verbose-2.stderr @@ -9,7 +9,7 @@ LL | let c1 : () = c; | expected due to this | = note: expected unit type `()` - found closure `[f::{closure#0} closure_substs=(unavailable) substs=[T, _#16t, extern "rust-call" fn(()), _#15t]]` + found closure `[f::{closure#0} closure_substs=(unavailable) substs=[T, _#88t, extern "rust-call" fn(()), _#89t]]` help: use parentheses to call this closure | LL | let c1 : () = c(); diff --git a/src/test/ui/const-ptr/forbidden_slices.64bit.stderr b/src/test/ui/const-ptr/forbidden_slices.64bit.stderr index f88746af9769d..1244fab10e9e0 100644 --- a/src/test/ui/const-ptr/forbidden_slices.64bit.stderr +++ b/src/test/ui/const-ptr/forbidden_slices.64bit.stderr @@ -164,7 +164,7 @@ LL | pub static R4: &[u8] = unsafe { | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 16, align: 8) { - ╾──────ALLOC_ID───────╼ 01 00 00 00 00 00 00 00 │ ╾──────╼........ + ╾───────ALLOC_ID───────╼ 01 00 00 00 00 00 00 00 │ ╾──────╼........ } error[E0080]: it is undefined behavior to use this value @@ -176,7 +176,7 @@ LL | pub static R5: &[u8] = unsafe { = help: this code performed an operation that depends on the underlying bytes representing a pointer = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported = note: the raw bytes of the constant (size: 16, align: 8) { - ╾──────ALLOC_ID───────╼ 08 00 00 00 00 00 00 00 │ ╾──────╼........ + ╾───────ALLOC_ID───────╼ 08 00 00 00 00 00 00 00 │ ╾──────╼........ } error[E0080]: it is undefined behavior to use this value diff --git a/src/test/ui/consts/const-eval/format.rs b/src/test/ui/consts/const-eval/format.rs index e43633da3ccb9..2eb2e2b024b1f 100644 --- a/src/test/ui/consts/const-eval/format.rs +++ b/src/test/ui/consts/const-eval/format.rs @@ -1,21 +1,17 @@ const fn failure() { panic!("{:?}", 0); - //~^ ERROR cannot call non-const formatting macro in constant functions - //~| ERROR erroneous constant used - //~| ERROR erroneous constant used - //~| WARN this was previously accepted by the compiler - //~| WARN this was previously accepted by the compiler + //~^ ERROR + //~| ERROR + //~| ERROR + //~| ERROR } const fn print() { println!("{:?}", 0); - //~^ ERROR cannot call non-const formatting macro in constant functions - //~| ERROR `Arguments::<'a>::new_v1` is not yet stable as a const fn - //~| ERROR cannot call non-const fn `_print` in constant functions - //~| ERROR erroneous constant used - //~| ERROR erroneous constant used - //~| WARN this was previously accepted by the compiler - //~| WARN this was previously accepted by the compiler + //~^ ERROR + //~| ERROR + //~| ERROR + //~| WARN } fn main() {} diff --git a/src/test/ui/consts/const-eval/format.stderr b/src/test/ui/consts/const-eval/format.stderr index a476b0f587fe1..1632f38752ce3 100644 --- a/src/test/ui/consts/const-eval/format.stderr +++ b/src/test/ui/consts/const-eval/format.stderr @@ -1,23 +1,45 @@ -error[E0015]: cannot call non-const formatting macro in constant functions - --> $DIR/format.rs:2:20 +error[E0658]: mutable references are not allowed in constant functions + --> $DIR/format.rs:2:5 | LL | panic!("{:?}", 0); - | ^ + | ^^^^^^^^^^^^^^^^^ | - = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants + = note: see issue #57349 for more information + = help: add `#![feature(const_mut_refs)]` to the crate attributes to enable = note: this error originates in the macro `$crate::const_format_args` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) -error[E0015]: cannot call non-const formatting macro in constant functions - --> $DIR/format.rs:11:22 +error[E0658]: mutable references are not allowed in constant functions + --> $DIR/format.rs:2:5 | -LL | println!("{:?}", 0); - | ^ +LL | panic!("{:?}", 0); + | ^^^^^^^^^^^^^^^^^ | - = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants - = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: see issue #57349 for more information + = help: add `#![feature(const_mut_refs)]` to the crate attributes to enable + = note: this error originates in the macro `$crate::const_format_args` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) -error: `Arguments::<'a>::new_v1` is not yet stable as a const fn - --> $DIR/format.rs:11:5 +error[E0658]: mutable references are not allowed in constant functions + --> $DIR/format.rs:2:5 + | +LL | panic!("{:?}", 0); + | ^^^^^^^^^^^^^^^^^ + | + = note: see issue #57349 for more information + = help: add `#![feature(const_mut_refs)]` to the crate attributes to enable + = note: this error originates in the macro `$crate::const_format_args` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0658]: mutable references are not allowed in constant functions + --> $DIR/format.rs:2:5 + | +LL | panic!("{:?}", 0); + | ^^^^^^^^^^^^^^^^^ + | + = note: see issue #57349 for more information + = help: add `#![feature(const_mut_refs)]` to the crate attributes to enable + = note: this error originates in the macro `$crate::const_format_args` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: `Arguments::<'a>::new` is not yet stable as a const fn + --> $DIR/format.rs:10:5 | LL | println!("{:?}", 0); | ^^^^^^^^^^^^^^^^^^^ @@ -26,7 +48,7 @@ LL | println!("{:?}", 0); = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0015]: cannot call non-const fn `_print` in constant functions - --> $DIR/format.rs:11:5 + --> $DIR/format.rs:10:5 | LL | println!("{:?}", 0); | ^^^^^^^^^^^^^^^^^^^ @@ -35,84 +57,23 @@ LL | println!("{:?}", 0); = note: this error originates in the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) error: erroneous constant used - --> $DIR/format.rs:2:12 - | -LL | panic!("{:?}", 0); - | ^^^^^^ referenced constant has errors - | - = note: `#[deny(const_err)]` on by default - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #71800 - -error: erroneous constant used - --> $DIR/format.rs:2:20 - | -LL | panic!("{:?}", 0); - | ^ referenced constant has errors - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #71800 - = note: this error originates in the macro `$crate::const_format_args` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) - -error: erroneous constant used - --> $DIR/format.rs:11:14 - | -LL | println!("{:?}", 0); - | ^^^^^^ referenced constant has errors - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #71800 - -error: erroneous constant used - --> $DIR/format.rs:11:22 + --> $DIR/format.rs:10:22 | LL | println!("{:?}", 0); | ^ referenced constant has errors | + = note: `#[deny(const_err)]` on by default = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #71800 = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 8 previous errors +error: aborting due to 7 previous errors -For more information about this error, try `rustc --explain E0015`. +Some errors have detailed explanations: E0015, E0658. +For more information about an error, try `rustc --explain E0015`. Future incompatibility report: Future breakage diagnostic: error: erroneous constant used - --> $DIR/format.rs:2:12 - | -LL | panic!("{:?}", 0); - | ^^^^^^ referenced constant has errors - | - = note: `#[deny(const_err)]` on by default - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #71800 - -Future breakage diagnostic: -error: erroneous constant used - --> $DIR/format.rs:2:20 - | -LL | panic!("{:?}", 0); - | ^ referenced constant has errors - | - = note: `#[deny(const_err)]` on by default - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #71800 - = note: this error originates in the macro `$crate::const_format_args` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) - -Future breakage diagnostic: -error: erroneous constant used - --> $DIR/format.rs:11:14 - | -LL | println!("{:?}", 0); - | ^^^^^^ referenced constant has errors - | - = note: `#[deny(const_err)]` on by default - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #71800 - -Future breakage diagnostic: -error: erroneous constant used - --> $DIR/format.rs:11:22 + --> $DIR/format.rs:10:22 | LL | println!("{:?}", 0); | ^ referenced constant has errors diff --git a/src/test/ui/fmt/ifmt-bad-arg.stderr b/src/test/ui/fmt/ifmt-bad-arg.stderr index 1b595a50e9984..9df5d58145a4b 100644 --- a/src/test/ui/fmt/ifmt-bad-arg.stderr +++ b/src/test/ui/fmt/ifmt-bad-arg.stderr @@ -303,15 +303,13 @@ LL | println!("{} {:.*} {}", 1, 3.2, 4); | ^^^ | | | expected `usize`, found floating-point number - | arguments to this function are incorrect + | arguments to this enum variant are incorrect | - = note: expected reference `&usize` - found reference `&{float}` -note: associated function defined here - --> $SRC_DIR/core/src/fmt/mod.rs:LL:COL +note: tuple variant defined here + --> $SRC_DIR/core/src/option.rs:LL:COL | -LL | pub fn from_usize(x: &usize) -> ArgumentV1<'_> { - | ^^^^^^^^^^ +LL | Some(#[stable(feature = "rust1", since = "1.0.0")] T), + | ^^^^ = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0308]: mismatched types @@ -321,15 +319,13 @@ LL | println!("{} {:07$.*} {}", 1, 3.2, 4); | ^^^ | | | expected `usize`, found floating-point number - | arguments to this function are incorrect + | arguments to this enum variant are incorrect | - = note: expected reference `&usize` - found reference `&{float}` -note: associated function defined here - --> $SRC_DIR/core/src/fmt/mod.rs:LL:COL +note: tuple variant defined here + --> $SRC_DIR/core/src/option.rs:LL:COL | -LL | pub fn from_usize(x: &usize) -> ArgumentV1<'_> { - | ^^^^^^^^^^ +LL | Some(#[stable(feature = "rust1", since = "1.0.0")] T), + | ^^^^ = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 38 previous errors diff --git a/src/test/ui/fmt/ifmt-unimpl.stderr b/src/test/ui/fmt/ifmt-unimpl.stderr index 0e34f913511a3..44b9461f407fa 100644 --- a/src/test/ui/fmt/ifmt-unimpl.stderr +++ b/src/test/ui/fmt/ifmt-unimpl.stderr @@ -15,12 +15,7 @@ LL | format!("{:X}", "3"); NonZeroIsize and 21 others = note: required for `&str` to implement `UpperHex` -note: required by a bound in `ArgumentV1::<'a>::new_upper_hex` - --> $SRC_DIR/core/src/fmt/mod.rs:LL:COL - | -LL | arg_new!(new_upper_hex, UpperHex); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `ArgumentV1::<'a>::new_upper_hex` - = note: this error originates in the macro `$crate::__export::format_args` which comes from the expansion of the macro `arg_new` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `$crate::__export::format_args` which comes from the expansion of the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to previous error diff --git a/src/test/ui/fmt/send-sync.stderr b/src/test/ui/fmt/send-sync.stderr index 3ed040c3ab359..28afcb6eccbd5 100644 --- a/src/test/ui/fmt/send-sync.stderr +++ b/src/test/ui/fmt/send-sync.stderr @@ -1,16 +1,14 @@ -error[E0277]: `core::fmt::Opaque` cannot be shared between threads safely +error[E0277]: `dyn for<'r, 's> Fn(&'r mut Formatter<'s>) -> Result<(), std::fmt::Error>` cannot be shared between threads safely --> $DIR/send-sync.rs:8:10 | LL | send(format_args!("{:?}", c)); - | ---- ^^^^^^^^^^^^^^^^^^^^^^^ `core::fmt::Opaque` cannot be shared between threads safely + | ---- ^^^^^^^^^^^^^^^^^^^^^^^ `dyn for<'r, 's> Fn(&'r mut Formatter<'s>) -> Result<(), std::fmt::Error>` cannot be shared between threads safely | | | required by a bound introduced by this call | - = help: within `[ArgumentV1<'_>]`, the trait `Sync` is not implemented for `core::fmt::Opaque` - = note: required because it appears within the type `&core::fmt::Opaque` - = note: required because it appears within the type `ArgumentV1<'_>` - = note: required because it appears within the type `[ArgumentV1<'_>]` - = note: required for `&[ArgumentV1<'_>]` to implement `Send` + = help: the trait `Sync` is not implemented for `dyn for<'r, 's> Fn(&'r mut Formatter<'s>) -> Result<(), std::fmt::Error>` + = note: required for `&dyn for<'r, 's> Fn(&'r mut Formatter<'s>) -> Result<(), std::fmt::Error>` to implement `Send` + = note: required because it appears within the type `core::fmt::Inner<'_>` = note: required because it appears within the type `Arguments<'_>` note: required by a bound in `send` --> $DIR/send-sync.rs:1:12 @@ -18,19 +16,17 @@ note: required by a bound in `send` LL | fn send(_: T) {} | ^^^^ required by this bound in `send` -error[E0277]: `core::fmt::Opaque` cannot be shared between threads safely +error[E0277]: `dyn for<'r, 's> Fn(&'r mut Formatter<'s>) -> Result<(), std::fmt::Error>` cannot be shared between threads safely --> $DIR/send-sync.rs:9:10 | LL | sync(format_args!("{:?}", c)); - | ---- ^^^^^^^^^^^^^^^^^^^^^^^ `core::fmt::Opaque` cannot be shared between threads safely + | ---- ^^^^^^^^^^^^^^^^^^^^^^^ `dyn for<'r, 's> Fn(&'r mut Formatter<'s>) -> Result<(), std::fmt::Error>` cannot be shared between threads safely | | | required by a bound introduced by this call | - = help: within `Arguments<'_>`, the trait `Sync` is not implemented for `core::fmt::Opaque` - = note: required because it appears within the type `&core::fmt::Opaque` - = note: required because it appears within the type `ArgumentV1<'_>` - = note: required because it appears within the type `[ArgumentV1<'_>]` - = note: required because it appears within the type `&[ArgumentV1<'_>]` + = help: within `Arguments<'_>`, the trait `Sync` is not implemented for `dyn for<'r, 's> Fn(&'r mut Formatter<'s>) -> Result<(), std::fmt::Error>` + = note: required because it appears within the type `&dyn for<'r, 's> Fn(&'r mut Formatter<'s>) -> Result<(), std::fmt::Error>` + = note: required because it appears within the type `core::fmt::Inner<'_>` = note: required because it appears within the type `Arguments<'_>` note: required by a bound in `sync` --> $DIR/send-sync.rs:2:12 diff --git a/src/test/ui/issues/issue-69455.stderr b/src/test/ui/issues/issue-69455.stderr index 9d11cf19ea77c..fc343bb54aace 100644 --- a/src/test/ui/issues/issue-69455.stderr +++ b/src/test/ui/issues/issue-69455.stderr @@ -1,14 +1,16 @@ -error[E0282]: type annotations needed - --> $DIR/issue-69455.rs:29:20 +error[E0284]: type annotations needed + --> $DIR/issue-69455.rs:29:41 | LL | println!("{}", 23u64.test(xs.iter().sum())); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type of the type parameter `T` declared on the associated function `new_display` + | ---- ^^^ cannot infer type of the type parameter `S` declared on the associated function `sum` + | | + | type must be known at this point | - = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: cannot satisfy `>::Output == _` help: consider specifying the generic argument | -LL | println!("{}", 23u64.test(xs.iter().sum())::); - | +++++ +LL | println!("{}", 23u64.test(xs.iter().sum::())); + | +++++ error[E0283]: type annotations needed --> $DIR/issue-69455.rs:29:41 @@ -33,5 +35,5 @@ LL | println!("{}", 23u64.test(xs.iter().sum::())); error: aborting due to 2 previous errors -Some errors have detailed explanations: E0282, E0283. -For more information about an error, try `rustc --explain E0282`. +Some errors have detailed explanations: E0283, E0284. +For more information about an error, try `rustc --explain E0283`. diff --git a/src/test/ui/lint/function-item-references.stderr b/src/test/ui/lint/function-item-references.stderr index a9d18bb6a4743..485ec4ed06438 100644 --- a/src/test/ui/lint/function-item-references.stderr +++ b/src/test/ui/lint/function-item-references.stderr @@ -10,6 +10,54 @@ note: the lint level is defined here LL | #![warn(function_item_references)] | ^^^^^^^^^^^^^^^^^^^^^^^^ +warning: taking a reference to a function item does not give a function pointer + --> $DIR/function-item-references.rs:142:41 + | +LL | std::mem::transmute::<_, usize>(&foo); + | ^^^^ help: cast `foo` to obtain a function pointer: `foo as fn() -> _` + +warning: taking a reference to a function item does not give a function pointer + --> $DIR/function-item-references.rs:144:50 + | +LL | std::mem::transmute::<_, (usize, usize)>((&foo, &bar)); + | ^^^^^^^^^^^^ help: cast `foo` to obtain a function pointer: `foo as fn() -> _` + +warning: taking a reference to a function item does not give a function pointer + --> $DIR/function-item-references.rs:144:50 + | +LL | std::mem::transmute::<_, (usize, usize)>((&foo, &bar)); + | ^^^^^^^^^^^^ help: cast `bar` to obtain a function pointer: `bar as fn(_) -> _` + +warning: taking a reference to a function item does not give a function pointer + --> $DIR/function-item-references.rs:147:41 + | +LL | std::mem::transmute::<_, usize>(&take_generic_ref::); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: cast `take_generic_ref` to obtain a function pointer: `take_generic_ref:: as fn(_)` + +warning: taking a reference to a function item does not give a function pointer + --> $DIR/function-item-references.rs:156:15 + | +LL | print_ptr(&bar); + | ^^^^ help: cast `bar` to obtain a function pointer: `bar as fn(_) -> _` + +warning: taking a reference to a function item does not give a function pointer + --> $DIR/function-item-references.rs:158:24 + | +LL | bound_by_ptr_trait(&bar); + | ^^^^ help: cast `bar` to obtain a function pointer: `bar as fn(_) -> _` + +warning: taking a reference to a function item does not give a function pointer + --> $DIR/function-item-references.rs:160:30 + | +LL | bound_by_ptr_trait_tuple((&foo, &bar)); + | ^^^^^^^^^^^^ help: cast `bar` to obtain a function pointer: `bar as fn(_) -> _` + +warning: taking a reference to a function item does not give a function pointer + --> $DIR/function-item-references.rs:160:30 + | +LL | bound_by_ptr_trait_tuple((&foo, &bar)); + | ^^^^^^^^^^^^ help: cast `foo` to obtain a function pointer: `foo as fn() -> _` + warning: taking a reference to a function item does not give a function pointer --> $DIR/function-item-references.rs:81:22 | @@ -154,53 +202,5 @@ warning: taking a reference to a function item does not give a function pointer LL | println!("{:p} {:p} {:p}", &nop, &foo, &bar); | ^^^^ help: cast `bar` to obtain a function pointer: `bar as fn(_) -> _` -warning: taking a reference to a function item does not give a function pointer - --> $DIR/function-item-references.rs:142:41 - | -LL | std::mem::transmute::<_, usize>(&foo); - | ^^^^ help: cast `foo` to obtain a function pointer: `foo as fn() -> _` - -warning: taking a reference to a function item does not give a function pointer - --> $DIR/function-item-references.rs:144:50 - | -LL | std::mem::transmute::<_, (usize, usize)>((&foo, &bar)); - | ^^^^^^^^^^^^ help: cast `foo` to obtain a function pointer: `foo as fn() -> _` - -warning: taking a reference to a function item does not give a function pointer - --> $DIR/function-item-references.rs:144:50 - | -LL | std::mem::transmute::<_, (usize, usize)>((&foo, &bar)); - | ^^^^^^^^^^^^ help: cast `bar` to obtain a function pointer: `bar as fn(_) -> _` - -warning: taking a reference to a function item does not give a function pointer - --> $DIR/function-item-references.rs:147:41 - | -LL | std::mem::transmute::<_, usize>(&take_generic_ref::); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: cast `take_generic_ref` to obtain a function pointer: `take_generic_ref:: as fn(_)` - -warning: taking a reference to a function item does not give a function pointer - --> $DIR/function-item-references.rs:156:15 - | -LL | print_ptr(&bar); - | ^^^^ help: cast `bar` to obtain a function pointer: `bar as fn(_) -> _` - -warning: taking a reference to a function item does not give a function pointer - --> $DIR/function-item-references.rs:158:24 - | -LL | bound_by_ptr_trait(&bar); - | ^^^^ help: cast `bar` to obtain a function pointer: `bar as fn(_) -> _` - -warning: taking a reference to a function item does not give a function pointer - --> $DIR/function-item-references.rs:160:30 - | -LL | bound_by_ptr_trait_tuple((&foo, &bar)); - | ^^^^^^^^^^^^ help: cast `bar` to obtain a function pointer: `bar as fn(_) -> _` - -warning: taking a reference to a function item does not give a function pointer - --> $DIR/function-item-references.rs:160:30 - | -LL | bound_by_ptr_trait_tuple((&foo, &bar)); - | ^^^^^^^^^^^^ help: cast `foo` to obtain a function pointer: `foo as fn() -> _` - warning: 33 warnings emitted diff --git a/src/test/ui/macros/rfc-2011-nicer-assert-messages/non-consuming-methods-have-optimized-codegen.stdout b/src/test/ui/macros/rfc-2011-nicer-assert-messages/non-consuming-methods-have-optimized-codegen.stdout index 90f858f80e6b5..176675e345852 100644 --- a/src/test/ui/macros/rfc-2011-nicer-assert-messages/non-consuming-methods-have-optimized-codegen.stdout +++ b/src/test/ui/macros/rfc-2011-nicer-assert-messages/non-consuming-methods-have-optimized-codegen.stdout @@ -25,8 +25,17 @@ fn arbitrary_consuming_method_for_demonstration_purposes() { { - ::std::rt::panic_fmt(::core::fmt::Arguments::new_v1(&["Assertion failed: elem as usize\nWith captures:\n elem = ", - "\n"], &[::core::fmt::ArgumentV1::new_debug(&__capture0)])) + ::std::rt::panic_fmt(::core::fmt::Arguments::new(&match (&__capture0,) + { + _args => + |f: &mut ::core::fmt::Formatter<'_>| -> ::core::fmt::Result + { + f.write_str("Assertion failed: elem as usize\nWith captures:\n elem = ")?; + ::core::fmt::Debug::fmt(_args.0, f)?; + f.write_str("\n")?; + ::core::result::Result::Ok(()) + }, + })) } } }; @@ -41,8 +50,17 @@ fn addr_of() { if ::core::intrinsics::unlikely(!&*__local_bind0) { (&::core::asserting::Wrapper(__local_bind0)).try_capture(&mut __capture0); { - ::std::rt::panic_fmt(::core::fmt::Arguments::new_v1(&["Assertion failed: &elem\nWith captures:\n elem = ", - "\n"], &[::core::fmt::ArgumentV1::new_debug(&__capture0)])) + ::std::rt::panic_fmt(::core::fmt::Arguments::new(&match (&__capture0,) + { + _args => + |f: &mut ::core::fmt::Formatter<'_>| -> ::core::fmt::Result + { + f.write_str("Assertion failed: &elem\nWith captures:\n elem = ")?; + ::core::fmt::Debug::fmt(_args.0, f)?; + f.write_str("\n")?; + ::core::result::Result::Ok(()) + }, + })) } } }; @@ -57,8 +75,17 @@ fn binary() { if ::core::intrinsics::unlikely(!(*__local_bind0 == 1)) { (&::core::asserting::Wrapper(__local_bind0)).try_capture(&mut __capture0); { - ::std::rt::panic_fmt(::core::fmt::Arguments::new_v1(&["Assertion failed: elem == 1\nWith captures:\n elem = ", - "\n"], &[::core::fmt::ArgumentV1::new_debug(&__capture0)])) + ::std::rt::panic_fmt(::core::fmt::Arguments::new(&match (&__capture0,) + { + _args => + |f: &mut ::core::fmt::Formatter<'_>| -> ::core::fmt::Result + { + f.write_str("Assertion failed: elem == 1\nWith captures:\n elem = ")?; + ::core::fmt::Debug::fmt(_args.0, f)?; + f.write_str("\n")?; + ::core::result::Result::Ok(()) + }, + })) } } }; @@ -70,8 +97,17 @@ fn binary() { if ::core::intrinsics::unlikely(!(*__local_bind0 >= 1)) { (&::core::asserting::Wrapper(__local_bind0)).try_capture(&mut __capture0); { - ::std::rt::panic_fmt(::core::fmt::Arguments::new_v1(&["Assertion failed: elem >= 1\nWith captures:\n elem = ", - "\n"], &[::core::fmt::ArgumentV1::new_debug(&__capture0)])) + ::std::rt::panic_fmt(::core::fmt::Arguments::new(&match (&__capture0,) + { + _args => + |f: &mut ::core::fmt::Formatter<'_>| -> ::core::fmt::Result + { + f.write_str("Assertion failed: elem >= 1\nWith captures:\n elem = ")?; + ::core::fmt::Debug::fmt(_args.0, f)?; + f.write_str("\n")?; + ::core::result::Result::Ok(()) + }, + })) } } }; @@ -83,8 +119,17 @@ fn binary() { if ::core::intrinsics::unlikely(!(*__local_bind0 > 0)) { (&::core::asserting::Wrapper(__local_bind0)).try_capture(&mut __capture0); { - ::std::rt::panic_fmt(::core::fmt::Arguments::new_v1(&["Assertion failed: elem > 0\nWith captures:\n elem = ", - "\n"], &[::core::fmt::ArgumentV1::new_debug(&__capture0)])) + ::std::rt::panic_fmt(::core::fmt::Arguments::new(&match (&__capture0,) + { + _args => + |f: &mut ::core::fmt::Formatter<'_>| -> ::core::fmt::Result + { + f.write_str("Assertion failed: elem > 0\nWith captures:\n elem = ")?; + ::core::fmt::Debug::fmt(_args.0, f)?; + f.write_str("\n")?; + ::core::result::Result::Ok(()) + }, + })) } } }; @@ -96,8 +141,17 @@ fn binary() { if ::core::intrinsics::unlikely(!(*__local_bind0 < 3)) { (&::core::asserting::Wrapper(__local_bind0)).try_capture(&mut __capture0); { - ::std::rt::panic_fmt(::core::fmt::Arguments::new_v1(&["Assertion failed: elem < 3\nWith captures:\n elem = ", - "\n"], &[::core::fmt::ArgumentV1::new_debug(&__capture0)])) + ::std::rt::panic_fmt(::core::fmt::Arguments::new(&match (&__capture0,) + { + _args => + |f: &mut ::core::fmt::Formatter<'_>| -> ::core::fmt::Result + { + f.write_str("Assertion failed: elem < 3\nWith captures:\n elem = ")?; + ::core::fmt::Debug::fmt(_args.0, f)?; + f.write_str("\n")?; + ::core::result::Result::Ok(()) + }, + })) } } }; @@ -109,8 +163,17 @@ fn binary() { if ::core::intrinsics::unlikely(!(*__local_bind0 <= 3)) { (&::core::asserting::Wrapper(__local_bind0)).try_capture(&mut __capture0); { - ::std::rt::panic_fmt(::core::fmt::Arguments::new_v1(&["Assertion failed: elem <= 3\nWith captures:\n elem = ", - "\n"], &[::core::fmt::ArgumentV1::new_debug(&__capture0)])) + ::std::rt::panic_fmt(::core::fmt::Arguments::new(&match (&__capture0,) + { + _args => + |f: &mut ::core::fmt::Formatter<'_>| -> ::core::fmt::Result + { + f.write_str("Assertion failed: elem <= 3\nWith captures:\n elem = ")?; + ::core::fmt::Debug::fmt(_args.0, f)?; + f.write_str("\n")?; + ::core::result::Result::Ok(()) + }, + })) } } }; @@ -122,8 +185,17 @@ fn binary() { if ::core::intrinsics::unlikely(!(*__local_bind0 != 3)) { (&::core::asserting::Wrapper(__local_bind0)).try_capture(&mut __capture0); { - ::std::rt::panic_fmt(::core::fmt::Arguments::new_v1(&["Assertion failed: elem != 3\nWith captures:\n elem = ", - "\n"], &[::core::fmt::ArgumentV1::new_debug(&__capture0)])) + ::std::rt::panic_fmt(::core::fmt::Arguments::new(&match (&__capture0,) + { + _args => + |f: &mut ::core::fmt::Formatter<'_>| -> ::core::fmt::Result + { + f.write_str("Assertion failed: elem != 3\nWith captures:\n elem = ")?; + ::core::fmt::Debug::fmt(_args.0, f)?; + f.write_str("\n")?; + ::core::result::Result::Ok(()) + }, + })) } } }; @@ -138,8 +210,17 @@ fn unary() { if ::core::intrinsics::unlikely(!**__local_bind0) { (&::core::asserting::Wrapper(__local_bind0)).try_capture(&mut __capture0); { - ::std::rt::panic_fmt(::core::fmt::Arguments::new_v1(&["Assertion failed: *elem\nWith captures:\n elem = ", - "\n"], &[::core::fmt::ArgumentV1::new_debug(&__capture0)])) + ::std::rt::panic_fmt(::core::fmt::Arguments::new(&match (&__capture0,) + { + _args => + |f: &mut ::core::fmt::Formatter<'_>| -> ::core::fmt::Result + { + f.write_str("Assertion failed: *elem\nWith captures:\n elem = ")?; + ::core::fmt::Debug::fmt(_args.0, f)?; + f.write_str("\n")?; + ::core::result::Result::Ok(()) + }, + })) } } }; From afeb43a9247643362b1958e2c5edeaef97e44280 Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Tue, 13 Sep 2022 14:08:08 +0200 Subject: [PATCH 19/25] Inline fast paths in formatting. --- library/core/src/fmt/mod.rs | 30 +++++++++++++++++++++++++++++- library/std/src/io/mod.rs | 1 + 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/library/core/src/fmt/mod.rs b/library/core/src/fmt/mod.rs index 113590f5e6856..0853c298896a3 100644 --- a/library/core/src/fmt/mod.rs +++ b/library/core/src/fmt/mod.rs @@ -166,6 +166,7 @@ pub trait Write { /// assert_eq!(&buf, "ab"); /// ``` #[stable(feature = "fmt_write_char", since = "1.1.0")] + #[inline] fn write_char(&mut self, c: char) -> Result { self.write_str(c.encode_utf8(&mut [0; 4])) } @@ -189,6 +190,7 @@ pub trait Write { /// assert_eq!(&buf, "world"); /// ``` #[stable(feature = "rust1", since = "1.0.0")] + #[inline] fn write_fmt(mut self: &mut Self, args: Arguments<'_>) -> Result { write(&mut self, args) } @@ -196,14 +198,17 @@ pub trait Write { #[stable(feature = "fmt_write_blanket_impl", since = "1.4.0")] impl Write for &mut W { + #[inline] fn write_str(&mut self, s: &str) -> Result { (**self).write_str(s) } + #[inline] fn write_char(&mut self, c: char) -> Result { (**self).write_char(c) } + #[inline] fn write_fmt(&mut self, args: Arguments<'_>) -> Result { (**self).write_fmt(args) } @@ -240,6 +245,7 @@ impl<'a> Formatter<'a> { /// Currently not intended for use outside of the standard library. #[unstable(feature = "fmt_internals", reason = "internal to standard library", issue = "none")] #[doc(hidden)] + #[inline] pub fn new(buf: &'a mut (dyn Write + 'a)) -> Formatter<'a> { Formatter { flags: 0, @@ -1277,6 +1283,7 @@ pub trait UpperExp { /// [`write!`]: crate::write! #[stable(feature = "rust1", since = "1.0.0")] #[cfg(not(bootstrap))] +#[inline] pub fn write(output: &mut dyn Write, args: Arguments<'_>) -> Result { match args.inner { Inner::Fn(f) => f(&mut Formatter::new(output)), @@ -1549,11 +1556,17 @@ impl<'a> Formatter<'a> { /// assert_eq!(&format!("{Foo:0>4}"), "0Foo"); /// ``` #[stable(feature = "rust1", since = "1.0.0")] + #[inline] pub fn pad(&mut self, s: &str) -> Result { // Make sure there's a fast path up front if self.width.is_none() && self.precision.is_none() { - return self.buf.write_str(s); + self.buf.write_str(s) + } else { + self.pad_slow(s) } + } + + fn pad_slow(&mut self, s: &str) -> Result { // The `precision` field can be interpreted as a `max-width` for the // string being formatted. let s = if let Some(max) = self.precision { @@ -1733,6 +1746,7 @@ impl<'a> Formatter<'a> { /// assert_eq!(&format!("{Foo:0>8}"), "Foo"); /// ``` #[stable(feature = "rust1", since = "1.0.0")] + #[inline] pub fn write_str(&mut self, data: &str) -> Result { self.buf.write_str(data) } @@ -1756,6 +1770,7 @@ impl<'a> Formatter<'a> { /// assert_eq!(&format!("{:0>8}", Foo(2)), "Foo 2"); /// ``` #[stable(feature = "rust1", since = "1.0.0")] + #[inline] pub fn write_fmt(&mut self, fmt: Arguments<'_>) -> Result { write(self.buf, fmt) } @@ -1768,6 +1783,7 @@ impl<'a> Formatter<'a> { note = "use the `sign_plus`, `sign_minus`, `alternate`, \ or `sign_aware_zero_pad` methods instead" )] + #[inline] pub fn flags(&self) -> u32 { self.flags } @@ -1801,6 +1817,7 @@ impl<'a> Formatter<'a> { /// ``` #[must_use] #[stable(feature = "fmt_flags", since = "1.5.0")] + #[inline] pub fn fill(&self) -> char { self.fill } @@ -1838,6 +1855,7 @@ impl<'a> Formatter<'a> { /// ``` #[must_use] #[stable(feature = "fmt_flags_align", since = "1.28.0")] + #[inline] pub fn align(&self) -> Option { match self.align { rt::v1::Alignment::Left => Some(Alignment::Left), @@ -1873,6 +1891,7 @@ impl<'a> Formatter<'a> { /// ``` #[must_use] #[stable(feature = "fmt_flags", since = "1.5.0")] + #[inline] pub fn width(&self) -> Option { self.width } @@ -1904,6 +1923,7 @@ impl<'a> Formatter<'a> { /// ``` #[must_use] #[stable(feature = "fmt_flags", since = "1.5.0")] + #[inline] pub fn precision(&self) -> Option { self.precision } @@ -1936,6 +1956,7 @@ impl<'a> Formatter<'a> { /// ``` #[must_use] #[stable(feature = "fmt_flags", since = "1.5.0")] + #[inline] pub fn sign_plus(&self) -> bool { self.flags & (1 << FlagV1::SignPlus as u32) != 0 } @@ -1965,6 +1986,7 @@ impl<'a> Formatter<'a> { /// ``` #[must_use] #[stable(feature = "fmt_flags", since = "1.5.0")] + #[inline] pub fn sign_minus(&self) -> bool { self.flags & (1 << FlagV1::SignMinus as u32) != 0 } @@ -1993,6 +2015,7 @@ impl<'a> Formatter<'a> { /// ``` #[must_use] #[stable(feature = "fmt_flags", since = "1.5.0")] + #[inline] pub fn alternate(&self) -> bool { self.flags & (1 << FlagV1::Alternate as u32) != 0 } @@ -2019,6 +2042,7 @@ impl<'a> Formatter<'a> { /// ``` #[must_use] #[stable(feature = "fmt_flags", since = "1.5.0")] + #[inline] pub fn sign_aware_zero_pad(&self) -> bool { self.flags & (1 << FlagV1::SignAwareZeroPad as u32) != 0 } @@ -2440,14 +2464,17 @@ impl<'a> Formatter<'a> { #[stable(since = "1.2.0", feature = "formatter_write")] impl Write for Formatter<'_> { + #[inline] fn write_str(&mut self, s: &str) -> Result { self.buf.write_str(s) } + #[inline] fn write_char(&mut self, c: char) -> Result { self.buf.write_char(c) } + #[inline] fn write_fmt(&mut self, args: Arguments<'_>) -> Result { write(self.buf, args) } @@ -2535,6 +2562,7 @@ impl Debug for str { #[stable(feature = "rust1", since = "1.0.0")] impl Display for str { + #[inline] fn fmt(&self, f: &mut Formatter<'_>) -> Result { f.pad(self) } diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index 01a3873c75cec..9b1288a90b3ad 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -1655,6 +1655,7 @@ pub trait Write { /// } /// ``` #[stable(feature = "rust1", since = "1.0.0")] + #[inline] fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> Result<()> { // Create a shim which translates a Write to a fmt::Write and saves // off I/O errors. instead of discarding them From ba9001af6253cfbb9290e0e7d6ade3503a14491d Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Tue, 13 Sep 2022 14:08:34 +0200 Subject: [PATCH 20/25] Fix incremental tests. The new println!() expansion doesn't result in any promotions, so `promoted_mir` is removed from these tests. --- src/test/incremental/hashes/inherent_impls.rs | 16 ++++++++-------- .../hygiene/auxiliary/cached_hygiene.rs | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/test/incremental/hashes/inherent_impls.rs b/src/test/incremental/hashes/inherent_impls.rs index 1abbff32c6f34..de4fa0e3cca8c 100644 --- a/src/test/incremental/hashes/inherent_impls.rs +++ b/src/test/incremental/hashes/inherent_impls.rs @@ -44,9 +44,9 @@ impl Foo { // This should affect the method itself, but not the impl. #[cfg(any(cfail1,cfail4))] impl Foo { - //-------------------------------------------------------------------------------------- + //------------------------------------------------------------------------- //-------------------------- - //-------------------------------------------------------------------------------------- + //------------------------------------------------------------------------- //-------------------------- pub fn method_body() { // ----------------------- @@ -59,9 +59,9 @@ impl Foo { #[rustc_clean(cfg="cfail5")] #[rustc_clean(cfg="cfail6")] impl Foo { - #[rustc_clean(cfg="cfail2", except="hir_owner_nodes,optimized_mir,promoted_mir,typeck")] + #[rustc_clean(cfg="cfail2", except="hir_owner_nodes,optimized_mir,typeck")] #[rustc_clean(cfg="cfail3")] - #[rustc_clean(cfg="cfail5", except="hir_owner_nodes,optimized_mir,promoted_mir,typeck")] + #[rustc_clean(cfg="cfail5", except="hir_owner_nodes,optimized_mir,typeck")] #[rustc_clean(cfg="cfail6")] pub fn method_body() { println!("Hello, world!"); @@ -76,12 +76,12 @@ impl Foo { impl Foo { //------------ //--------------- - //------------------------------------------------------------ + //----------------------------------------------- // //-------------------------- //------------ //--------------- - //------------------------------------------------------------ + //----------------------------------------------- // //-------------------------- #[inline] @@ -98,12 +98,12 @@ impl Foo { impl Foo { #[rustc_clean( cfg="cfail2", - except="hir_owner_nodes,optimized_mir,promoted_mir,typeck" + except="hir_owner_nodes,optimized_mir,typeck" )] #[rustc_clean(cfg="cfail3")] #[rustc_clean( cfg="cfail5", - except="hir_owner_nodes,optimized_mir,promoted_mir,typeck" + except="hir_owner_nodes,optimized_mir,typeck" )] #[rustc_clean(cfg="cfail6")] #[inline] diff --git a/src/test/incremental/hygiene/auxiliary/cached_hygiene.rs b/src/test/incremental/hygiene/auxiliary/cached_hygiene.rs index b31f60e972bf0..676324943ede3 100644 --- a/src/test/incremental/hygiene/auxiliary/cached_hygiene.rs +++ b/src/test/incremental/hygiene/auxiliary/cached_hygiene.rs @@ -13,7 +13,7 @@ macro_rules! first_macro { } } -#[rustc_clean(except="hir_owner_nodes,typeck,optimized_mir,promoted_mir", cfg="rpass2")] +#[rustc_clean(except="hir_owner_nodes,typeck,optimized_mir", cfg="rpass2")] #[inline(always)] pub fn changed_fn() { // This will cause additional hygiene to be generate, From 5f1950c8678fb3c92b64ac4418bcfa8fd28fd48e Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Tue, 13 Sep 2022 22:27:17 +0200 Subject: [PATCH 21/25] Update fmt::Arguments closure to take dyn Write. Using a new Formatter for every fmt invocation allows the compiler to make assumptions about the state of the formatting flags. --- .../rustc_builtin_macros/src/format/expand.rs | 77 ++++++++++++------- compiler/rustc_span/src/symbol.rs | 4 +- library/core/src/fmt/mod.rs | 13 ++-- library/std/src/io/stdio.rs | 2 + 4 files changed, 63 insertions(+), 33 deletions(-) diff --git a/compiler/rustc_builtin_macros/src/format/expand.rs b/compiler/rustc_builtin_macros/src/format/expand.rs index 7077754584de9..37ff9c7549b3d 100644 --- a/compiler/rustc_builtin_macros/src/format/expand.rs +++ b/compiler/rustc_builtin_macros/src/format/expand.rs @@ -22,26 +22,24 @@ pub fn expand_parsed_format_args(ecx: &mut ExtCtxt<'_>, fmt: FormatArgs) -> P { // Generate: - // f.write_str("…")?; + // w.write_str("…")?; statements.push(ecx.stmt_expr(ecx.expr( macsp, ast::ExprKind::Try(ecx.expr( macsp, ast::ExprKind::MethodCall( ast::PathSegment::from_ident(Ident::new(sym::write_str, macsp)), - ecx.expr_ident(macsp, f), + ecx.expr_ident(macsp, w), vec![ecx.expr_str(macsp, s)], macsp, ), @@ -51,16 +49,23 @@ pub fn expand_parsed_format_args(ecx: &mut ExtCtxt<'_>, fmt: FormatArgs) -> P { // Don't set options if they're still set to defaults // and this placeholder also uses default options. - let d = p.format_options == FormatOptions::default(); - if !default_options || !d { - default_options = d; - // Generate: - // f.set_options(…); - statements.push(ecx.stmt_expr(ecx.expr( + + // Generate: + // ::core::fmt::Formatter::new(w) + let mut formatter = ecx.expr_call_global( + macsp, + ecx.std_path(&[sym::fmt, sym::Formatter, sym::new]), + vec![ecx.expr_ident(macsp, w)], + ); + + if p.format_options != FormatOptions::default() { + // Add: + // .with_options(…) + formatter = ecx.expr( macsp, ast::ExprKind::MethodCall( - ast::PathSegment::from_ident(Ident::new(sym::set_options, macsp)), - ecx.expr_ident(macsp, f), + ast::PathSegment::from_ident(Ident::new(sym::with_options, macsp)), + formatter, vec![ ecx.expr_u32(macsp, p.format_options.flags), ecx.expr_char(macsp, p.format_options.fill.unwrap_or(' ')), @@ -90,10 +95,12 @@ pub fn expand_parsed_format_args(ecx: &mut ExtCtxt<'_>, fmt: FormatArgs) -> P, fmt: FormatArgs) -> P, fmt: FormatArgs) -> P ::core::fmt::Result { + // |w: &mut dyn ::core::fmt::Write| -> ::core::fmt::Result { // … // statements // } let closure = ecx.expr( @@ -144,18 +161,26 @@ pub fn expand_parsed_format_args(ecx: &mut ExtCtxt<'_>, fmt: FormatArgs) -> P Formatter<'a> { #[unstable(feature = "fmt_internals", issue = "none")] #[doc(hidden)] #[inline] - pub fn set_options( - &mut self, + pub fn with_options( + mut self, flags: u32, fill: char, align: rt::v1::Alignment, width: Option, precision: Option, - ) { + ) -> Self { self.flags = flags; self.fill = fill; self.align = align; self.width = width; self.precision = precision; + self } } @@ -460,7 +461,7 @@ impl<'a> Arguments<'a> { #[unstable(feature = "fmt_internals", reason = "internal to format_args!", issue = "none")] #[rustc_const_unstable(feature = "const_fmt_arguments_new", issue = "none")] #[cfg(not(bootstrap))] - pub const fn new(f: &'a dyn Fn(&mut Formatter<'_>) -> Result) -> Arguments<'a> { + pub const fn new(f: &'a dyn Fn(&mut dyn Write) -> Result) -> Arguments<'a> { Arguments { inner: Inner::Fn(f) } } @@ -545,7 +546,7 @@ pub struct Arguments<'a> { #[cfg(not(bootstrap))] #[derive(Copy, Clone)] enum Inner<'a> { - Fn(&'a dyn Fn(&mut Formatter<'_>) -> Result), + Fn(&'a dyn Fn(&mut dyn Write) -> Result), StaticStr(&'static str), } @@ -1286,7 +1287,7 @@ pub trait UpperExp { #[inline] pub fn write(output: &mut dyn Write, args: Arguments<'_>) -> Result { match args.inner { - Inner::Fn(f) => f(&mut Formatter::new(output)), + Inner::Fn(f) => f(output), Inner::StaticStr(s) => output.write_str(s), } } diff --git a/library/std/src/io/stdio.rs b/library/std/src/io/stdio.rs index 2dc12a18a8a66..abd3e3134eeb3 100644 --- a/library/std/src/io/stdio.rs +++ b/library/std/src/io/stdio.rs @@ -1026,6 +1026,7 @@ where )] #[doc(hidden)] #[cfg(not(test))] +#[inline] pub fn _print(args: fmt::Arguments<'_>) { print_to(args, stdout, "stdout"); } @@ -1037,6 +1038,7 @@ pub fn _print(args: fmt::Arguments<'_>) { )] #[doc(hidden)] #[cfg(not(test))] +#[inline] pub fn _eprint(args: fmt::Arguments<'_>) { print_to(args, stderr, "stderr"); } From 95f1fe45c5af3ab11682964b1f92c74efb0175a9 Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Tue, 13 Sep 2022 22:27:59 +0200 Subject: [PATCH 22/25] Update tests. --- ...float_to_exponential_common.ConstProp.diff | 67 ++++++++------- .../ui/attributes/key-value-expansion.stderr | 5 +- ...re-print-generic-trim-off-verbose-2.stderr | 2 +- .../closure-print-generic-verbose-2.stderr | 2 +- src/test/ui/fmt/send-sync.stderr | 16 ++-- ...ming-methods-have-optimized-codegen.stdout | 81 ++++++++++--------- 6 files changed, 95 insertions(+), 78 deletions(-) diff --git a/src/test/mir-opt/funky_arms.float_to_exponential_common.ConstProp.diff b/src/test/mir-opt/funky_arms.float_to_exponential_common.ConstProp.diff index dca36b1a76d0f..960b7794842fa 100644 --- a/src/test/mir-opt/funky_arms.float_to_exponential_common.ConstProp.diff +++ b/src/test/mir-opt/funky_arms.float_to_exponential_common.ConstProp.diff @@ -30,55 +30,62 @@ scope 3 { debug precision => _10; // in scope 3 at $DIR/funky_arms.rs:+13:17: +13:26 let _10: usize; // in scope 3 at $DIR/funky_arms.rs:+13:17: +13:26 + scope 5 (inlined Formatter::precision) { // at $DIR/funky_arms.rs:24:30: 24:45 + debug self => _8; // in scope 5 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + } } } } + scope 4 (inlined Formatter::sign_plus) { // at $DIR/funky_arms.rs:15:22: 15:37 + debug self => _5; // in scope 4 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + let mut _22: u32; // in scope 4 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + let mut _23: u32; // in scope 4 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + let mut _24: u32; // in scope 4 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + } bb0: { StorageLive(_4); // scope 0 at $DIR/funky_arms.rs:+4:9: +4:19 StorageLive(_5); // scope 0 at $DIR/funky_arms.rs:+4:22: +4:37 _5 = &(*_1); // scope 0 at $DIR/funky_arms.rs:+4:22: +4:37 - _4 = Formatter::sign_plus(move _5) -> bb1; // scope 0 at $DIR/funky_arms.rs:+4:22: +4:37 - // mir::Constant - // + span: $DIR/funky_arms.rs:15:26: 15:35 - // + literal: Const { ty: for<'r> fn(&'r Formatter) -> bool {Formatter::sign_plus}, val: Value() } - } - - bb1: { + StorageLive(_22); // scope 4 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + StorageLive(_23); // scope 4 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + _23 = ((*_5).0: u32); // scope 4 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + StorageLive(_24); // scope 4 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + _24 = const 1_u32; // scope 4 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL +- _22 = BitAnd(move _23, move _24); // scope 4 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL ++ _22 = BitAnd(move _23, const 1_u32); // scope 4 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + StorageDead(_24); // scope 4 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + StorageDead(_23); // scope 4 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + _4 = Ne(move _22, const 0_u32); // scope 4 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL + StorageDead(_22); // scope 4 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL StorageDead(_5); // scope 0 at $DIR/funky_arms.rs:+4:36: +4:37 StorageLive(_6); // scope 1 at $DIR/funky_arms.rs:+8:9: +8:13 - switchInt(_4) -> [false: bb3, otherwise: bb2]; // scope 1 at $DIR/funky_arms.rs:+8:16: +8:32 + switchInt(_4) -> [false: bb2, otherwise: bb1]; // scope 1 at $DIR/funky_arms.rs:+8:16: +8:32 } - bb2: { + bb1: { Deinit(_6); // scope 1 at $DIR/funky_arms.rs:+10:17: +10:41 discriminant(_6) = 1; // scope 1 at $DIR/funky_arms.rs:+10:17: +10:41 - goto -> bb4; // scope 1 at $DIR/funky_arms.rs:+10:17: +10:41 + goto -> bb3; // scope 1 at $DIR/funky_arms.rs:+10:17: +10:41 } - bb3: { + bb2: { Deinit(_6); // scope 1 at $DIR/funky_arms.rs:+9:18: +9:38 discriminant(_6) = 0; // scope 1 at $DIR/funky_arms.rs:+9:18: +9:38 - goto -> bb4; // scope 1 at $DIR/funky_arms.rs:+9:18: +9:38 + goto -> bb3; // scope 1 at $DIR/funky_arms.rs:+9:18: +9:38 } - bb4: { + bb3: { StorageLive(_7); // scope 3 at $DIR/funky_arms.rs:+13:30: +13:45 StorageLive(_8); // scope 3 at $DIR/funky_arms.rs:+13:30: +13:45 _8 = &(*_1); // scope 3 at $DIR/funky_arms.rs:+13:30: +13:45 - _7 = Formatter::precision(move _8) -> bb5; // scope 3 at $DIR/funky_arms.rs:+13:30: +13:45 - // mir::Constant - // + span: $DIR/funky_arms.rs:24:34: 24:43 - // + literal: Const { ty: for<'r> fn(&'r Formatter) -> Option {Formatter::precision}, val: Value() } - } - - bb5: { + _7 = ((*_8).4: std::option::Option); // scope 5 at $SRC_DIR/core/src/fmt/mod.rs:LL:COL StorageDead(_8); // scope 3 at $DIR/funky_arms.rs:+13:44: +13:45 _9 = discriminant(_7); // scope 3 at $DIR/funky_arms.rs:+13:12: +13:27 - switchInt(move _9) -> [1_isize: bb6, otherwise: bb8]; // scope 3 at $DIR/funky_arms.rs:+13:12: +13:27 + switchInt(move _9) -> [1_isize: bb4, otherwise: bb6]; // scope 3 at $DIR/funky_arms.rs:+13:12: +13:27 } - bb6: { + bb4: { StorageLive(_10); // scope 3 at $DIR/funky_arms.rs:+13:17: +13:26 _10 = ((_7 as Some).0: usize); // scope 3 at $DIR/funky_arms.rs:+13:17: +13:26 StorageLive(_11); // scope 3 at $DIR/funky_arms.rs:+15:43: +15:46 @@ -97,23 +104,23 @@ StorageDead(_15); // scope 3 at $DIR/funky_arms.rs:+15:78: +15:79 StorageLive(_17); // scope 3 at $DIR/funky_arms.rs:+15:81: +15:86 _17 = _3; // scope 3 at $DIR/funky_arms.rs:+15:81: +15:86 - _0 = float_to_exponential_common_exact::(move _11, move _12, move _13, move _14, move _17) -> bb7; // scope 3 at $DIR/funky_arms.rs:+15:9: +15:87 + _0 = float_to_exponential_common_exact::(move _11, move _12, move _13, move _14, move _17) -> bb5; // scope 3 at $DIR/funky_arms.rs:+15:9: +15:87 // mir::Constant // + span: $DIR/funky_arms.rs:26:9: 26:42 // + literal: Const { ty: for<'r, 's, 't0> fn(&'r mut Formatter<'s>, &'t0 T, Sign, u32, bool) -> Result<(), std::fmt::Error> {float_to_exponential_common_exact::}, val: Value() } } - bb7: { + bb5: { StorageDead(_17); // scope 3 at $DIR/funky_arms.rs:+15:86: +15:87 StorageDead(_14); // scope 3 at $DIR/funky_arms.rs:+15:86: +15:87 StorageDead(_13); // scope 3 at $DIR/funky_arms.rs:+15:86: +15:87 StorageDead(_12); // scope 3 at $DIR/funky_arms.rs:+15:86: +15:87 StorageDead(_11); // scope 3 at $DIR/funky_arms.rs:+15:86: +15:87 StorageDead(_10); // scope 2 at $DIR/funky_arms.rs:+16:5: +16:6 - goto -> bb10; // scope 2 at $DIR/funky_arms.rs:+13:5: +18:6 + goto -> bb8; // scope 2 at $DIR/funky_arms.rs:+13:5: +18:6 } - bb8: { + bb6: { StorageLive(_18); // scope 2 at $DIR/funky_arms.rs:+17:46: +17:49 _18 = &mut (*_1); // scope 2 at $DIR/funky_arms.rs:+17:46: +17:49 StorageLive(_19); // scope 2 at $DIR/funky_arms.rs:+17:51: +17:54 @@ -122,21 +129,21 @@ _20 = _6; // scope 2 at $DIR/funky_arms.rs:+17:56: +17:60 StorageLive(_21); // scope 2 at $DIR/funky_arms.rs:+17:62: +17:67 _21 = _3; // scope 2 at $DIR/funky_arms.rs:+17:62: +17:67 - _0 = float_to_exponential_common_shortest::(move _18, move _19, move _20, move _21) -> bb9; // scope 2 at $DIR/funky_arms.rs:+17:9: +17:68 + _0 = float_to_exponential_common_shortest::(move _18, move _19, move _20, move _21) -> bb7; // scope 2 at $DIR/funky_arms.rs:+17:9: +17:68 // mir::Constant // + span: $DIR/funky_arms.rs:28:9: 28:45 // + literal: Const { ty: for<'r, 's, 't0> fn(&'r mut Formatter<'s>, &'t0 T, Sign, bool) -> Result<(), std::fmt::Error> {float_to_exponential_common_shortest::}, val: Value() } } - bb9: { + bb7: { StorageDead(_21); // scope 2 at $DIR/funky_arms.rs:+17:67: +17:68 StorageDead(_20); // scope 2 at $DIR/funky_arms.rs:+17:67: +17:68 StorageDead(_19); // scope 2 at $DIR/funky_arms.rs:+17:67: +17:68 StorageDead(_18); // scope 2 at $DIR/funky_arms.rs:+17:67: +17:68 - goto -> bb10; // scope 2 at $DIR/funky_arms.rs:+13:5: +18:6 + goto -> bb8; // scope 2 at $DIR/funky_arms.rs:+13:5: +18:6 } - bb10: { + bb8: { StorageDead(_6); // scope 1 at $DIR/funky_arms.rs:+19:1: +19:2 StorageDead(_4); // scope 0 at $DIR/funky_arms.rs:+19:1: +19:2 StorageDead(_7); // scope 0 at $DIR/funky_arms.rs:+19:1: +19:2 diff --git a/src/test/ui/attributes/key-value-expansion.stderr b/src/test/ui/attributes/key-value-expansion.stderr index 601743921699f..6fb076eb5b6cc 100644 --- a/src/test/ui/attributes/key-value-expansion.stderr +++ b/src/test/ui/attributes/key-value-expansion.stderr @@ -19,9 +19,10 @@ error: unexpected expression: `{ let res = ::alloc::fmt::format(::core::fmt::Arguments::new(&match (&"u8",) { _args => - |f: &mut ::core::fmt::Formatter<'_>| -> ::core::fmt::Result + |w: &mut dyn ::core::fmt::Write| -> ::core::fmt::Result { - ::core::fmt::Display::fmt(_args.0, f)?; + ::core::fmt::Display::fmt(_args.0, + &mut ::core::fmt::Formatter::new(w))?; ::core::result::Result::Ok(()) }, })); diff --git a/src/test/ui/closures/print/closure-print-generic-trim-off-verbose-2.stderr b/src/test/ui/closures/print/closure-print-generic-trim-off-verbose-2.stderr index 034bec32fdcb4..3449dee2b012e 100644 --- a/src/test/ui/closures/print/closure-print-generic-trim-off-verbose-2.stderr +++ b/src/test/ui/closures/print/closure-print-generic-trim-off-verbose-2.stderr @@ -9,7 +9,7 @@ LL | let c1 : () = c; | expected due to this | = note: expected unit type `()` - found closure `[mod1::f::{closure#0} closure_substs=(unavailable) substs=[T, _#88t, extern "rust-call" fn(()), _#89t]]` + found closure `[mod1::f::{closure#0} closure_substs=(unavailable) substs=[T, _#94t, extern "rust-call" fn(()), _#95t]]` help: use parentheses to call this closure | LL | let c1 : () = c(); diff --git a/src/test/ui/closures/print/closure-print-generic-verbose-2.stderr b/src/test/ui/closures/print/closure-print-generic-verbose-2.stderr index 46a67daa1b113..adc4f2a6e8f9a 100644 --- a/src/test/ui/closures/print/closure-print-generic-verbose-2.stderr +++ b/src/test/ui/closures/print/closure-print-generic-verbose-2.stderr @@ -9,7 +9,7 @@ LL | let c1 : () = c; | expected due to this | = note: expected unit type `()` - found closure `[f::{closure#0} closure_substs=(unavailable) substs=[T, _#88t, extern "rust-call" fn(()), _#89t]]` + found closure `[f::{closure#0} closure_substs=(unavailable) substs=[T, _#94t, extern "rust-call" fn(()), _#95t]]` help: use parentheses to call this closure | LL | let c1 : () = c(); diff --git a/src/test/ui/fmt/send-sync.stderr b/src/test/ui/fmt/send-sync.stderr index 28afcb6eccbd5..9f776473fcb97 100644 --- a/src/test/ui/fmt/send-sync.stderr +++ b/src/test/ui/fmt/send-sync.stderr @@ -1,13 +1,13 @@ -error[E0277]: `dyn for<'r, 's> Fn(&'r mut Formatter<'s>) -> Result<(), std::fmt::Error>` cannot be shared between threads safely +error[E0277]: `dyn for<'r> Fn(&'r mut (dyn std::fmt::Write + 'r)) -> Result<(), std::fmt::Error>` cannot be shared between threads safely --> $DIR/send-sync.rs:8:10 | LL | send(format_args!("{:?}", c)); - | ---- ^^^^^^^^^^^^^^^^^^^^^^^ `dyn for<'r, 's> Fn(&'r mut Formatter<'s>) -> Result<(), std::fmt::Error>` cannot be shared between threads safely + | ---- ^^^^^^^^^^^^^^^^^^^^^^^ `dyn for<'r> Fn(&'r mut (dyn std::fmt::Write + 'r)) -> Result<(), std::fmt::Error>` cannot be shared between threads safely | | | required by a bound introduced by this call | - = help: the trait `Sync` is not implemented for `dyn for<'r, 's> Fn(&'r mut Formatter<'s>) -> Result<(), std::fmt::Error>` - = note: required for `&dyn for<'r, 's> Fn(&'r mut Formatter<'s>) -> Result<(), std::fmt::Error>` to implement `Send` + = help: the trait `Sync` is not implemented for `dyn for<'r> Fn(&'r mut (dyn std::fmt::Write + 'r)) -> Result<(), std::fmt::Error>` + = note: required for `&dyn for<'r> Fn(&'r mut (dyn std::fmt::Write + 'r)) -> Result<(), std::fmt::Error>` to implement `Send` = note: required because it appears within the type `core::fmt::Inner<'_>` = note: required because it appears within the type `Arguments<'_>` note: required by a bound in `send` @@ -16,16 +16,16 @@ note: required by a bound in `send` LL | fn send(_: T) {} | ^^^^ required by this bound in `send` -error[E0277]: `dyn for<'r, 's> Fn(&'r mut Formatter<'s>) -> Result<(), std::fmt::Error>` cannot be shared between threads safely +error[E0277]: `dyn for<'r> Fn(&'r mut (dyn std::fmt::Write + 'r)) -> Result<(), std::fmt::Error>` cannot be shared between threads safely --> $DIR/send-sync.rs:9:10 | LL | sync(format_args!("{:?}", c)); - | ---- ^^^^^^^^^^^^^^^^^^^^^^^ `dyn for<'r, 's> Fn(&'r mut Formatter<'s>) -> Result<(), std::fmt::Error>` cannot be shared between threads safely + | ---- ^^^^^^^^^^^^^^^^^^^^^^^ `dyn for<'r> Fn(&'r mut (dyn std::fmt::Write + 'r)) -> Result<(), std::fmt::Error>` cannot be shared between threads safely | | | required by a bound introduced by this call | - = help: within `Arguments<'_>`, the trait `Sync` is not implemented for `dyn for<'r, 's> Fn(&'r mut Formatter<'s>) -> Result<(), std::fmt::Error>` - = note: required because it appears within the type `&dyn for<'r, 's> Fn(&'r mut Formatter<'s>) -> Result<(), std::fmt::Error>` + = help: within `Arguments<'_>`, the trait `Sync` is not implemented for `dyn for<'r> Fn(&'r mut (dyn std::fmt::Write + 'r)) -> Result<(), std::fmt::Error>` + = note: required because it appears within the type `&dyn for<'r> Fn(&'r mut (dyn std::fmt::Write + 'r)) -> Result<(), std::fmt::Error>` = note: required because it appears within the type `core::fmt::Inner<'_>` = note: required because it appears within the type `Arguments<'_>` note: required by a bound in `sync` diff --git a/src/test/ui/macros/rfc-2011-nicer-assert-messages/non-consuming-methods-have-optimized-codegen.stdout b/src/test/ui/macros/rfc-2011-nicer-assert-messages/non-consuming-methods-have-optimized-codegen.stdout index 176675e345852..6fca4f937d4b5 100644 --- a/src/test/ui/macros/rfc-2011-nicer-assert-messages/non-consuming-methods-have-optimized-codegen.stdout +++ b/src/test/ui/macros/rfc-2011-nicer-assert-messages/non-consuming-methods-have-optimized-codegen.stdout @@ -28,11 +28,12 @@ fn arbitrary_consuming_method_for_demonstration_purposes() { ::std::rt::panic_fmt(::core::fmt::Arguments::new(&match (&__capture0,) { _args => - |f: &mut ::core::fmt::Formatter<'_>| -> ::core::fmt::Result + |w: &mut dyn ::core::fmt::Write| -> ::core::fmt::Result { - f.write_str("Assertion failed: elem as usize\nWith captures:\n elem = ")?; - ::core::fmt::Debug::fmt(_args.0, f)?; - f.write_str("\n")?; + w.write_str("Assertion failed: elem as usize\nWith captures:\n elem = ")?; + ::core::fmt::Debug::fmt(_args.0, + &mut ::core::fmt::Formatter::new(w))?; + w.write_str("\n")?; ::core::result::Result::Ok(()) }, })) @@ -53,11 +54,12 @@ fn addr_of() { ::std::rt::panic_fmt(::core::fmt::Arguments::new(&match (&__capture0,) { _args => - |f: &mut ::core::fmt::Formatter<'_>| -> ::core::fmt::Result + |w: &mut dyn ::core::fmt::Write| -> ::core::fmt::Result { - f.write_str("Assertion failed: &elem\nWith captures:\n elem = ")?; - ::core::fmt::Debug::fmt(_args.0, f)?; - f.write_str("\n")?; + w.write_str("Assertion failed: &elem\nWith captures:\n elem = ")?; + ::core::fmt::Debug::fmt(_args.0, + &mut ::core::fmt::Formatter::new(w))?; + w.write_str("\n")?; ::core::result::Result::Ok(()) }, })) @@ -78,11 +80,12 @@ fn binary() { ::std::rt::panic_fmt(::core::fmt::Arguments::new(&match (&__capture0,) { _args => - |f: &mut ::core::fmt::Formatter<'_>| -> ::core::fmt::Result + |w: &mut dyn ::core::fmt::Write| -> ::core::fmt::Result { - f.write_str("Assertion failed: elem == 1\nWith captures:\n elem = ")?; - ::core::fmt::Debug::fmt(_args.0, f)?; - f.write_str("\n")?; + w.write_str("Assertion failed: elem == 1\nWith captures:\n elem = ")?; + ::core::fmt::Debug::fmt(_args.0, + &mut ::core::fmt::Formatter::new(w))?; + w.write_str("\n")?; ::core::result::Result::Ok(()) }, })) @@ -100,11 +103,12 @@ fn binary() { ::std::rt::panic_fmt(::core::fmt::Arguments::new(&match (&__capture0,) { _args => - |f: &mut ::core::fmt::Formatter<'_>| -> ::core::fmt::Result + |w: &mut dyn ::core::fmt::Write| -> ::core::fmt::Result { - f.write_str("Assertion failed: elem >= 1\nWith captures:\n elem = ")?; - ::core::fmt::Debug::fmt(_args.0, f)?; - f.write_str("\n")?; + w.write_str("Assertion failed: elem >= 1\nWith captures:\n elem = ")?; + ::core::fmt::Debug::fmt(_args.0, + &mut ::core::fmt::Formatter::new(w))?; + w.write_str("\n")?; ::core::result::Result::Ok(()) }, })) @@ -122,11 +126,12 @@ fn binary() { ::std::rt::panic_fmt(::core::fmt::Arguments::new(&match (&__capture0,) { _args => - |f: &mut ::core::fmt::Formatter<'_>| -> ::core::fmt::Result + |w: &mut dyn ::core::fmt::Write| -> ::core::fmt::Result { - f.write_str("Assertion failed: elem > 0\nWith captures:\n elem = ")?; - ::core::fmt::Debug::fmt(_args.0, f)?; - f.write_str("\n")?; + w.write_str("Assertion failed: elem > 0\nWith captures:\n elem = ")?; + ::core::fmt::Debug::fmt(_args.0, + &mut ::core::fmt::Formatter::new(w))?; + w.write_str("\n")?; ::core::result::Result::Ok(()) }, })) @@ -144,11 +149,12 @@ fn binary() { ::std::rt::panic_fmt(::core::fmt::Arguments::new(&match (&__capture0,) { _args => - |f: &mut ::core::fmt::Formatter<'_>| -> ::core::fmt::Result + |w: &mut dyn ::core::fmt::Write| -> ::core::fmt::Result { - f.write_str("Assertion failed: elem < 3\nWith captures:\n elem = ")?; - ::core::fmt::Debug::fmt(_args.0, f)?; - f.write_str("\n")?; + w.write_str("Assertion failed: elem < 3\nWith captures:\n elem = ")?; + ::core::fmt::Debug::fmt(_args.0, + &mut ::core::fmt::Formatter::new(w))?; + w.write_str("\n")?; ::core::result::Result::Ok(()) }, })) @@ -166,11 +172,12 @@ fn binary() { ::std::rt::panic_fmt(::core::fmt::Arguments::new(&match (&__capture0,) { _args => - |f: &mut ::core::fmt::Formatter<'_>| -> ::core::fmt::Result + |w: &mut dyn ::core::fmt::Write| -> ::core::fmt::Result { - f.write_str("Assertion failed: elem <= 3\nWith captures:\n elem = ")?; - ::core::fmt::Debug::fmt(_args.0, f)?; - f.write_str("\n")?; + w.write_str("Assertion failed: elem <= 3\nWith captures:\n elem = ")?; + ::core::fmt::Debug::fmt(_args.0, + &mut ::core::fmt::Formatter::new(w))?; + w.write_str("\n")?; ::core::result::Result::Ok(()) }, })) @@ -188,11 +195,12 @@ fn binary() { ::std::rt::panic_fmt(::core::fmt::Arguments::new(&match (&__capture0,) { _args => - |f: &mut ::core::fmt::Formatter<'_>| -> ::core::fmt::Result + |w: &mut dyn ::core::fmt::Write| -> ::core::fmt::Result { - f.write_str("Assertion failed: elem != 3\nWith captures:\n elem = ")?; - ::core::fmt::Debug::fmt(_args.0, f)?; - f.write_str("\n")?; + w.write_str("Assertion failed: elem != 3\nWith captures:\n elem = ")?; + ::core::fmt::Debug::fmt(_args.0, + &mut ::core::fmt::Formatter::new(w))?; + w.write_str("\n")?; ::core::result::Result::Ok(()) }, })) @@ -213,11 +221,12 @@ fn unary() { ::std::rt::panic_fmt(::core::fmt::Arguments::new(&match (&__capture0,) { _args => - |f: &mut ::core::fmt::Formatter<'_>| -> ::core::fmt::Result + |w: &mut dyn ::core::fmt::Write| -> ::core::fmt::Result { - f.write_str("Assertion failed: *elem\nWith captures:\n elem = ")?; - ::core::fmt::Debug::fmt(_args.0, f)?; - f.write_str("\n")?; + w.write_str("Assertion failed: *elem\nWith captures:\n elem = ")?; + ::core::fmt::Debug::fmt(_args.0, + &mut ::core::fmt::Formatter::new(w))?; + w.write_str("\n")?; ::core::result::Result::Ok(()) }, })) From ee7d03897a88caba3af6e11e029a6f26540e1407 Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Wed, 14 Sep 2022 00:35:00 +0200 Subject: [PATCH 23/25] Fix src/test/ui/const-ptr/forbidden_slices.rs test. The number of line drawing characters depends on the number of digits in the allocation number. This removes the characters to avoid spurious failures. --- .../const-ptr/forbidden_slices.32bit.stderr | 72 +++++++++---------- .../const-ptr/forbidden_slices.64bit.stderr | 72 +++++++++---------- src/test/ui/const-ptr/forbidden_slices.rs | 1 + 3 files changed, 73 insertions(+), 72 deletions(-) diff --git a/src/test/ui/const-ptr/forbidden_slices.32bit.stderr b/src/test/ui/const-ptr/forbidden_slices.32bit.stderr index 82a3c92e66fdf..7e4709e52b0a1 100644 --- a/src/test/ui/const-ptr/forbidden_slices.32bit.stderr +++ b/src/test/ui/const-ptr/forbidden_slices.32bit.stderr @@ -7,10 +7,10 @@ LL | &*ptr::slice_from_raw_parts(data, len) | dereferencing pointer failed: null pointer is a dangling pointer (it has no provenance) | inside `std::slice::from_raw_parts::` at $SRC_DIR/core/src/slice/raw.rs:LL:COL | - ::: $DIR/forbidden_slices.rs:18:34 + ::: $DIR/forbidden_slices.rs:19:34 | LL | pub static S0: &[u32] = unsafe { from_raw_parts(ptr::null(), 0) }; - | ------------------------------ inside `S0` at $DIR/forbidden_slices.rs:18:34 + | ------------------------------ inside `S0` at $DIR/forbidden_slices.rs:19:34 error[E0080]: could not evaluate static initializer --> $SRC_DIR/core/src/slice/raw.rs:LL:COL @@ -21,10 +21,10 @@ LL | &*ptr::slice_from_raw_parts(data, len) | dereferencing pointer failed: null pointer is a dangling pointer (it has no provenance) | inside `std::slice::from_raw_parts::<()>` at $SRC_DIR/core/src/slice/raw.rs:LL:COL | - ::: $DIR/forbidden_slices.rs:19:33 + ::: $DIR/forbidden_slices.rs:20:33 | LL | pub static S1: &[()] = unsafe { from_raw_parts(ptr::null(), 0) }; - | ------------------------------ inside `S1` at $DIR/forbidden_slices.rs:19:33 + | ------------------------------ inside `S1` at $DIR/forbidden_slices.rs:20:33 error[E0080]: could not evaluate static initializer --> $SRC_DIR/core/src/slice/raw.rs:LL:COL @@ -35,24 +35,24 @@ LL | &*ptr::slice_from_raw_parts(data, len) | dereferencing pointer failed: ALLOC_ID has size 4, so pointer to 8 bytes starting at offset 0 is out-of-bounds | inside `std::slice::from_raw_parts::` at $SRC_DIR/core/src/slice/raw.rs:LL:COL | - ::: $DIR/forbidden_slices.rs:22:34 + ::: $DIR/forbidden_slices.rs:23:34 | LL | pub static S2: &[u32] = unsafe { from_raw_parts(&D0, 2) }; - | ---------------------- inside `S2` at $DIR/forbidden_slices.rs:22:34 + | ---------------------- inside `S2` at $DIR/forbidden_slices.rs:23:34 error[E0080]: it is undefined behavior to use this value - --> $DIR/forbidden_slices.rs:25:1 + --> $DIR/forbidden_slices.rs:26:1 | LL | pub static S4: &[u8] = unsafe { from_raw_parts((&D1) as *const _ as _, 1) }; | ^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .[0]: encountered uninitialized bytes | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 4) { - ╾─ALLOC_ID─╼ 01 00 00 00 │ ╾──╼.... + ╾ALLOC_ID╼ 01 00 00 00 │ ╾╼.... } error[E0080]: it is undefined behavior to use this value - --> $DIR/forbidden_slices.rs:27:1 + --> $DIR/forbidden_slices.rs:28:1 | LL | pub static S5: &[u8] = unsafe { from_raw_parts((&D3) as *const _ as _, size_of::<&u32>()) }; | ^^^^^^^^^^^^^^^^^^^^ unable to turn pointer into raw bytes @@ -60,29 +60,29 @@ LL | pub static S5: &[u8] = unsafe { from_raw_parts((&D3) as *const _ as _, size = help: this code performed an operation that depends on the underlying bytes representing a pointer = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported = note: the raw bytes of the constant (size: 8, align: 4) { - ╾─ALLOC_ID─╼ 04 00 00 00 │ ╾──╼.... + ╾ALLOC_ID╼ 04 00 00 00 │ ╾╼.... } error[E0080]: it is undefined behavior to use this value - --> $DIR/forbidden_slices.rs:29:1 + --> $DIR/forbidden_slices.rs:30:1 | LL | pub static S6: &[bool] = unsafe { from_raw_parts((&D0) as *const _ as _, 4) }; | ^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .[0]: encountered 0x11, but expected a boolean | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 4) { - ╾─ALLOC_ID─╼ 04 00 00 00 │ ╾──╼.... + ╾ALLOC_ID╼ 04 00 00 00 │ ╾╼.... } error[E0080]: it is undefined behavior to use this value - --> $DIR/forbidden_slices.rs:32:1 + --> $DIR/forbidden_slices.rs:33:1 | LL | pub static S7: &[u16] = unsafe { | ^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered an unaligned reference (required 2 byte alignment but found 1) | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 4) { - ╾─A_ID+0x1─╼ 04 00 00 00 │ ╾──╼.... + ╾A_ID+0x1╼ 04 00 00 00 │ ╾╼.... } error[E0080]: could not evaluate static initializer @@ -94,10 +94,10 @@ LL | &*ptr::slice_from_raw_parts(data, len) | dereferencing pointer failed: ALLOC_ID has size 8, so pointer to 8 bytes starting at offset 1 is out-of-bounds | inside `std::slice::from_raw_parts::` at $SRC_DIR/core/src/slice/raw.rs:LL:COL | - ::: $DIR/forbidden_slices.rs:43:5 + ::: $DIR/forbidden_slices.rs:44:5 | LL | from_raw_parts(ptr, 1) - | ---------------------- inside `S8` at $DIR/forbidden_slices.rs:43:5 + | ---------------------- inside `S8` at $DIR/forbidden_slices.rs:44:5 error[E0080]: could not evaluate static initializer --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL @@ -113,10 +113,10 @@ LL | unsafe { intrinsics::ptr_offset_from_unsigned(self, origin) } LL | unsafe { from_raw_parts(range.start, range.end.sub_ptr(range.start)) } | ------------------------------ inside `from_ptr_range::` at $SRC_DIR/core/src/slice/raw.rs:LL:COL | - ::: $DIR/forbidden_slices.rs:46:34 + ::: $DIR/forbidden_slices.rs:47:34 | LL | pub static R0: &[u32] = unsafe { from_ptr_range(ptr::null()..ptr::null()) }; - | ---------------------------------------- inside `R0` at $DIR/forbidden_slices.rs:46:34 + | ---------------------------------------- inside `R0` at $DIR/forbidden_slices.rs:47:34 error[E0080]: could not evaluate static initializer --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL @@ -132,10 +132,10 @@ LL | assert!(0 < pointee_size && pointee_size <= isize::MAX as usize); LL | unsafe { from_raw_parts(range.start, range.end.sub_ptr(range.start)) } | ------------------------------ inside `from_ptr_range::<()>` at $SRC_DIR/core/src/slice/raw.rs:LL:COL | - ::: $DIR/forbidden_slices.rs:47:33 + ::: $DIR/forbidden_slices.rs:48:33 | LL | pub static R1: &[()] = unsafe { from_ptr_range(ptr::null()..ptr::null()) }; - | ---------------------------------------- inside `R1` at $DIR/forbidden_slices.rs:47:33 + | ---------------------------------------- inside `R1` at $DIR/forbidden_slices.rs:48:33 | = note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info) @@ -151,24 +151,24 @@ LL | unsafe { intrinsics::offset(self, count) } LL | unsafe { self.offset(count as isize) } | --------------------------- inside `ptr::const_ptr::::add` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL | - ::: $DIR/forbidden_slices.rs:50:25 + ::: $DIR/forbidden_slices.rs:51:25 | LL | from_ptr_range(ptr..ptr.add(2)) - | ---------- inside `R2` at $DIR/forbidden_slices.rs:50:25 + | ---------- inside `R2` at $DIR/forbidden_slices.rs:51:25 error[E0080]: it is undefined behavior to use this value - --> $DIR/forbidden_slices.rs:52:1 + --> $DIR/forbidden_slices.rs:53:1 | LL | pub static R4: &[u8] = unsafe { | ^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .[0]: encountered uninitialized bytes | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 4) { - ╾ALLOC_ID─╼ 01 00 00 00 │ ╾──╼.... + ╾ALLOC_ID╼ 01 00 00 00 │ ╾╼.... } error[E0080]: it is undefined behavior to use this value - --> $DIR/forbidden_slices.rs:57:1 + --> $DIR/forbidden_slices.rs:58:1 | LL | pub static R5: &[u8] = unsafe { | ^^^^^^^^^^^^^^^^^^^^ unable to turn pointer into raw bytes @@ -176,29 +176,29 @@ LL | pub static R5: &[u8] = unsafe { = help: this code performed an operation that depends on the underlying bytes representing a pointer = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported = note: the raw bytes of the constant (size: 8, align: 4) { - ╾ALLOC_ID─╼ 04 00 00 00 │ ╾──╼.... + ╾ALLOC_ID╼ 04 00 00 00 │ ╾╼.... } error[E0080]: it is undefined behavior to use this value - --> $DIR/forbidden_slices.rs:62:1 + --> $DIR/forbidden_slices.rs:63:1 | LL | pub static R6: &[bool] = unsafe { | ^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .[0]: encountered 0x11, but expected a boolean | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 4) { - ╾ALLOC_ID─╼ 04 00 00 00 │ ╾──╼.... + ╾ALLOC_ID╼ 04 00 00 00 │ ╾╼.... } error[E0080]: it is undefined behavior to use this value - --> $DIR/forbidden_slices.rs:67:1 + --> $DIR/forbidden_slices.rs:68:1 | LL | pub static R7: &[u16] = unsafe { | ^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered an unaligned reference (required 2 byte alignment but found 1) | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 8, align: 4) { - ╾A_ID+0x1─╼ 04 00 00 00 │ ╾──╼.... + ╾A_ID+0x1╼ 04 00 00 00 │ ╾╼.... } error[E0080]: could not evaluate static initializer @@ -213,10 +213,10 @@ LL | unsafe { intrinsics::offset(self, count) } LL | unsafe { self.offset(count as isize) } | --------------------------- inside `ptr::const_ptr::::add` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL | - ::: $DIR/forbidden_slices.rs:74:25 + ::: $DIR/forbidden_slices.rs:75:25 | LL | from_ptr_range(ptr..ptr.add(1)) - | ---------- inside `R8` at $DIR/forbidden_slices.rs:74:25 + | ---------- inside `R8` at $DIR/forbidden_slices.rs:75:25 error[E0080]: could not evaluate static initializer --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL @@ -232,10 +232,10 @@ LL | unsafe { intrinsics::ptr_offset_from_unsigned(self, origin) } LL | unsafe { from_raw_parts(range.start, range.end.sub_ptr(range.start)) } | ------------------------------ inside `from_ptr_range::` at $SRC_DIR/core/src/slice/raw.rs:LL:COL | - ::: $DIR/forbidden_slices.rs:79:34 + ::: $DIR/forbidden_slices.rs:80:34 | LL | pub static R9: &[u32] = unsafe { from_ptr_range(&D0..(&D0 as *const u32).add(1)) }; - | ----------------------------------------------- inside `R9` at $DIR/forbidden_slices.rs:79:34 + | ----------------------------------------------- inside `R9` at $DIR/forbidden_slices.rs:80:34 error[E0080]: could not evaluate static initializer --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL @@ -251,10 +251,10 @@ LL | unsafe { intrinsics::ptr_offset_from_unsigned(self, origin) } LL | unsafe { from_raw_parts(range.start, range.end.sub_ptr(range.start)) } | ------------------------------ inside `from_ptr_range::` at $SRC_DIR/core/src/slice/raw.rs:LL:COL | - ::: $DIR/forbidden_slices.rs:80:35 + ::: $DIR/forbidden_slices.rs:81:35 | LL | pub static R10: &[u32] = unsafe { from_ptr_range(&D0..&D0) }; - | ------------------------ inside `R10` at $DIR/forbidden_slices.rs:80:35 + | ------------------------ inside `R10` at $DIR/forbidden_slices.rs:81:35 error: aborting due to 18 previous errors diff --git a/src/test/ui/const-ptr/forbidden_slices.64bit.stderr b/src/test/ui/const-ptr/forbidden_slices.64bit.stderr index 1244fab10e9e0..14edd7694e44c 100644 --- a/src/test/ui/const-ptr/forbidden_slices.64bit.stderr +++ b/src/test/ui/const-ptr/forbidden_slices.64bit.stderr @@ -7,10 +7,10 @@ LL | &*ptr::slice_from_raw_parts(data, len) | dereferencing pointer failed: null pointer is a dangling pointer (it has no provenance) | inside `std::slice::from_raw_parts::` at $SRC_DIR/core/src/slice/raw.rs:LL:COL | - ::: $DIR/forbidden_slices.rs:18:34 + ::: $DIR/forbidden_slices.rs:19:34 | LL | pub static S0: &[u32] = unsafe { from_raw_parts(ptr::null(), 0) }; - | ------------------------------ inside `S0` at $DIR/forbidden_slices.rs:18:34 + | ------------------------------ inside `S0` at $DIR/forbidden_slices.rs:19:34 error[E0080]: could not evaluate static initializer --> $SRC_DIR/core/src/slice/raw.rs:LL:COL @@ -21,10 +21,10 @@ LL | &*ptr::slice_from_raw_parts(data, len) | dereferencing pointer failed: null pointer is a dangling pointer (it has no provenance) | inside `std::slice::from_raw_parts::<()>` at $SRC_DIR/core/src/slice/raw.rs:LL:COL | - ::: $DIR/forbidden_slices.rs:19:33 + ::: $DIR/forbidden_slices.rs:20:33 | LL | pub static S1: &[()] = unsafe { from_raw_parts(ptr::null(), 0) }; - | ------------------------------ inside `S1` at $DIR/forbidden_slices.rs:19:33 + | ------------------------------ inside `S1` at $DIR/forbidden_slices.rs:20:33 error[E0080]: could not evaluate static initializer --> $SRC_DIR/core/src/slice/raw.rs:LL:COL @@ -35,24 +35,24 @@ LL | &*ptr::slice_from_raw_parts(data, len) | dereferencing pointer failed: ALLOC_ID has size 4, so pointer to 8 bytes starting at offset 0 is out-of-bounds | inside `std::slice::from_raw_parts::` at $SRC_DIR/core/src/slice/raw.rs:LL:COL | - ::: $DIR/forbidden_slices.rs:22:34 + ::: $DIR/forbidden_slices.rs:23:34 | LL | pub static S2: &[u32] = unsafe { from_raw_parts(&D0, 2) }; - | ---------------------- inside `S2` at $DIR/forbidden_slices.rs:22:34 + | ---------------------- inside `S2` at $DIR/forbidden_slices.rs:23:34 error[E0080]: it is undefined behavior to use this value - --> $DIR/forbidden_slices.rs:25:1 + --> $DIR/forbidden_slices.rs:26:1 | LL | pub static S4: &[u8] = unsafe { from_raw_parts((&D1) as *const _ as _, 1) }; | ^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .[0]: encountered uninitialized bytes | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 16, align: 8) { - ╾───────ALLOC_ID───────╼ 01 00 00 00 00 00 00 00 │ ╾──────╼........ + ╾ALLOC_ID╼ 01 00 00 00 00 00 00 00 │ ╾╼........ } error[E0080]: it is undefined behavior to use this value - --> $DIR/forbidden_slices.rs:27:1 + --> $DIR/forbidden_slices.rs:28:1 | LL | pub static S5: &[u8] = unsafe { from_raw_parts((&D3) as *const _ as _, size_of::<&u32>()) }; | ^^^^^^^^^^^^^^^^^^^^ unable to turn pointer into raw bytes @@ -60,29 +60,29 @@ LL | pub static S5: &[u8] = unsafe { from_raw_parts((&D3) as *const _ as _, size = help: this code performed an operation that depends on the underlying bytes representing a pointer = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported = note: the raw bytes of the constant (size: 16, align: 8) { - ╾───────ALLOC_ID───────╼ 08 00 00 00 00 00 00 00 │ ╾──────╼........ + ╾ALLOC_ID╼ 08 00 00 00 00 00 00 00 │ ╾╼........ } error[E0080]: it is undefined behavior to use this value - --> $DIR/forbidden_slices.rs:29:1 + --> $DIR/forbidden_slices.rs:30:1 | LL | pub static S6: &[bool] = unsafe { from_raw_parts((&D0) as *const _ as _, 4) }; | ^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .[0]: encountered 0x11, but expected a boolean | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 16, align: 8) { - ╾───────ALLOC_ID───────╼ 04 00 00 00 00 00 00 00 │ ╾──────╼........ + ╾ALLOC_ID╼ 04 00 00 00 00 00 00 00 │ ╾╼........ } error[E0080]: it is undefined behavior to use this value - --> $DIR/forbidden_slices.rs:32:1 + --> $DIR/forbidden_slices.rs:33:1 | LL | pub static S7: &[u16] = unsafe { | ^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered an unaligned reference (required 2 byte alignment but found 1) | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 16, align: 8) { - ╾─────ALLOC_ID+0x1─────╼ 04 00 00 00 00 00 00 00 │ ╾──────╼........ + ╾ALLOC_ID+0x1╼ 04 00 00 00 00 00 00 00 │ ╾╼........ } error[E0080]: could not evaluate static initializer @@ -94,10 +94,10 @@ LL | &*ptr::slice_from_raw_parts(data, len) | dereferencing pointer failed: ALLOC_ID has size 8, so pointer to 8 bytes starting at offset 1 is out-of-bounds | inside `std::slice::from_raw_parts::` at $SRC_DIR/core/src/slice/raw.rs:LL:COL | - ::: $DIR/forbidden_slices.rs:43:5 + ::: $DIR/forbidden_slices.rs:44:5 | LL | from_raw_parts(ptr, 1) - | ---------------------- inside `S8` at $DIR/forbidden_slices.rs:43:5 + | ---------------------- inside `S8` at $DIR/forbidden_slices.rs:44:5 error[E0080]: could not evaluate static initializer --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL @@ -113,10 +113,10 @@ LL | unsafe { intrinsics::ptr_offset_from_unsigned(self, origin) } LL | unsafe { from_raw_parts(range.start, range.end.sub_ptr(range.start)) } | ------------------------------ inside `from_ptr_range::` at $SRC_DIR/core/src/slice/raw.rs:LL:COL | - ::: $DIR/forbidden_slices.rs:46:34 + ::: $DIR/forbidden_slices.rs:47:34 | LL | pub static R0: &[u32] = unsafe { from_ptr_range(ptr::null()..ptr::null()) }; - | ---------------------------------------- inside `R0` at $DIR/forbidden_slices.rs:46:34 + | ---------------------------------------- inside `R0` at $DIR/forbidden_slices.rs:47:34 error[E0080]: could not evaluate static initializer --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL @@ -132,10 +132,10 @@ LL | assert!(0 < pointee_size && pointee_size <= isize::MAX as usize); LL | unsafe { from_raw_parts(range.start, range.end.sub_ptr(range.start)) } | ------------------------------ inside `from_ptr_range::<()>` at $SRC_DIR/core/src/slice/raw.rs:LL:COL | - ::: $DIR/forbidden_slices.rs:47:33 + ::: $DIR/forbidden_slices.rs:48:33 | LL | pub static R1: &[()] = unsafe { from_ptr_range(ptr::null()..ptr::null()) }; - | ---------------------------------------- inside `R1` at $DIR/forbidden_slices.rs:47:33 + | ---------------------------------------- inside `R1` at $DIR/forbidden_slices.rs:48:33 | = note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info) @@ -151,24 +151,24 @@ LL | unsafe { intrinsics::offset(self, count) } LL | unsafe { self.offset(count as isize) } | --------------------------- inside `ptr::const_ptr::::add` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL | - ::: $DIR/forbidden_slices.rs:50:25 + ::: $DIR/forbidden_slices.rs:51:25 | LL | from_ptr_range(ptr..ptr.add(2)) - | ---------- inside `R2` at $DIR/forbidden_slices.rs:50:25 + | ---------- inside `R2` at $DIR/forbidden_slices.rs:51:25 error[E0080]: it is undefined behavior to use this value - --> $DIR/forbidden_slices.rs:52:1 + --> $DIR/forbidden_slices.rs:53:1 | LL | pub static R4: &[u8] = unsafe { | ^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .[0]: encountered uninitialized bytes | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 16, align: 8) { - ╾───────ALLOC_ID───────╼ 01 00 00 00 00 00 00 00 │ ╾──────╼........ + ╾ALLOC_ID╼ 01 00 00 00 00 00 00 00 │ ╾╼........ } error[E0080]: it is undefined behavior to use this value - --> $DIR/forbidden_slices.rs:57:1 + --> $DIR/forbidden_slices.rs:58:1 | LL | pub static R5: &[u8] = unsafe { | ^^^^^^^^^^^^^^^^^^^^ unable to turn pointer into raw bytes @@ -176,29 +176,29 @@ LL | pub static R5: &[u8] = unsafe { = help: this code performed an operation that depends on the underlying bytes representing a pointer = help: the absolute address of a pointer is not known at compile-time, so such operations are not supported = note: the raw bytes of the constant (size: 16, align: 8) { - ╾───────ALLOC_ID───────╼ 08 00 00 00 00 00 00 00 │ ╾──────╼........ + ╾ALLOC_ID╼ 08 00 00 00 00 00 00 00 │ ╾╼........ } error[E0080]: it is undefined behavior to use this value - --> $DIR/forbidden_slices.rs:62:1 + --> $DIR/forbidden_slices.rs:63:1 | LL | pub static R6: &[bool] = unsafe { | ^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value at .[0]: encountered 0x11, but expected a boolean | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 16, align: 8) { - ╾──────ALLOC_ID───────╼ 04 00 00 00 00 00 00 00 │ ╾──────╼........ + ╾ALLOC_ID╼ 04 00 00 00 00 00 00 00 │ ╾╼........ } error[E0080]: it is undefined behavior to use this value - --> $DIR/forbidden_slices.rs:67:1 + --> $DIR/forbidden_slices.rs:68:1 | LL | pub static R7: &[u16] = unsafe { | ^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered an unaligned reference (required 2 byte alignment but found 1) | = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. = note: the raw bytes of the constant (size: 16, align: 8) { - ╾────ALLOC_ID+0x1─────╼ 04 00 00 00 00 00 00 00 │ ╾──────╼........ + ╾ALLOC_ID+0x1╼ 04 00 00 00 00 00 00 00 │ ╾╼........ } error[E0080]: could not evaluate static initializer @@ -213,10 +213,10 @@ LL | unsafe { intrinsics::offset(self, count) } LL | unsafe { self.offset(count as isize) } | --------------------------- inside `ptr::const_ptr::::add` at $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL | - ::: $DIR/forbidden_slices.rs:74:25 + ::: $DIR/forbidden_slices.rs:75:25 | LL | from_ptr_range(ptr..ptr.add(1)) - | ---------- inside `R8` at $DIR/forbidden_slices.rs:74:25 + | ---------- inside `R8` at $DIR/forbidden_slices.rs:75:25 error[E0080]: could not evaluate static initializer --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL @@ -232,10 +232,10 @@ LL | unsafe { intrinsics::ptr_offset_from_unsigned(self, origin) } LL | unsafe { from_raw_parts(range.start, range.end.sub_ptr(range.start)) } | ------------------------------ inside `from_ptr_range::` at $SRC_DIR/core/src/slice/raw.rs:LL:COL | - ::: $DIR/forbidden_slices.rs:79:34 + ::: $DIR/forbidden_slices.rs:80:34 | LL | pub static R9: &[u32] = unsafe { from_ptr_range(&D0..(&D0 as *const u32).add(1)) }; - | ----------------------------------------------- inside `R9` at $DIR/forbidden_slices.rs:79:34 + | ----------------------------------------------- inside `R9` at $DIR/forbidden_slices.rs:80:34 error[E0080]: could not evaluate static initializer --> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL @@ -251,10 +251,10 @@ LL | unsafe { intrinsics::ptr_offset_from_unsigned(self, origin) } LL | unsafe { from_raw_parts(range.start, range.end.sub_ptr(range.start)) } | ------------------------------ inside `from_ptr_range::` at $SRC_DIR/core/src/slice/raw.rs:LL:COL | - ::: $DIR/forbidden_slices.rs:80:35 + ::: $DIR/forbidden_slices.rs:81:35 | LL | pub static R10: &[u32] = unsafe { from_ptr_range(&D0..&D0) }; - | ------------------------ inside `R10` at $DIR/forbidden_slices.rs:80:35 + | ------------------------ inside `R10` at $DIR/forbidden_slices.rs:81:35 error: aborting due to 18 previous errors diff --git a/src/test/ui/const-ptr/forbidden_slices.rs b/src/test/ui/const-ptr/forbidden_slices.rs index e2184911f422c..e0f4b0a79b42c 100644 --- a/src/test/ui/const-ptr/forbidden_slices.rs +++ b/src/test/ui/const-ptr/forbidden_slices.rs @@ -1,6 +1,7 @@ // stderr-per-bitwidth // normalize-stderr-test "alloc[0-9]+" -> "ALLOC_ID" // normalize-stderr-test "a[0-9]+\+0x" -> "A_ID+0x" +// normalize-stderr-test "─" -> "" // error-pattern: could not evaluate static initializer #![feature( slice_from_ptr_range, From df2a56a1d71368873494c8d54dd6ee33542864ea Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Wed, 14 Sep 2022 01:18:48 +0200 Subject: [PATCH 24/25] Bless pretty tests. --- src/test/pretty/dollar-crate.pp | 2 +- src/test/pretty/issue-4264.pp | 8 +++----- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/test/pretty/dollar-crate.pp b/src/test/pretty/dollar-crate.pp index 3af37955f2380..c75e4cbdcab22 100644 --- a/src/test/pretty/dollar-crate.pp +++ b/src/test/pretty/dollar-crate.pp @@ -9,5 +9,5 @@ // pp-exact:dollar-crate.pp fn main() { - { ::std::io::_print(::core::fmt::Arguments::new_v1(&["rust\n"], &[])); }; + { ::std::io::_print(::core::fmt::Arguments::from_static_str("rust\n")); }; } diff --git a/src/test/pretty/issue-4264.pp b/src/test/pretty/issue-4264.pp index 752c36a0fbc5a..a2582bfeb3079 100644 --- a/src/test/pretty/issue-4264.pp +++ b/src/test/pretty/issue-4264.pp @@ -32,12 +32,10 @@ ({ let res = ((::alloc::fmt::format as - for<'r> fn(Arguments<'r>) -> String {format})(((::core::fmt::Arguments::new_v1 + for<'r> fn(Arguments<'r>) -> String {format})(((::core::fmt::Arguments::from_static_str as - fn(&[&'static str], &[ArgumentV1]) -> Arguments {Arguments::new_v1})((&([("test" - as &str)] as [&str; 1]) as &[&str; 1]), - (&([] as [ArgumentV1; 0]) as &[ArgumentV1; 0])) as - Arguments)) as String); + fn(&'static str) -> Arguments {Arguments::from_static_str})(("test" + as &str)) as Arguments)) as String); (res as String) } as String); } as ()) From e45a26c916a305dc8f31a8465a17a22841099721 Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Wed, 14 Sep 2022 01:24:33 +0200 Subject: [PATCH 25/25] Update fmt estimated capacity tests. --- library/core/tests/fmt/mod.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/library/core/tests/fmt/mod.rs b/library/core/tests/fmt/mod.rs index 61807635813c4..b241782c57dfe 100644 --- a/library/core/tests/fmt/mod.rs +++ b/library/core/tests/fmt/mod.rs @@ -24,9 +24,11 @@ fn test_estimated_capacity() { assert_eq!(format_args!("").estimated_capacity(), 0); assert_eq!(format_args!("{}", "").estimated_capacity(), 0); assert_eq!(format_args!("Hello").estimated_capacity(), 5); - assert_eq!(format_args!("Hello, {}!", "").estimated_capacity(), 16); + #[cfg(not(bootstrap))] + assert_eq!(format_args!("Hello, {}!", "").estimated_capacity(), 0); assert_eq!(format_args!("{}, hello!", "World").estimated_capacity(), 0); - assert_eq!(format_args!("{}. 16-bytes piece", "World").estimated_capacity(), 32); + #[cfg(not(bootstrap))] + assert_eq!(format_args!("{}. 16-bytes piece", "World").estimated_capacity(), 0); } #[test]