From: bjorn3 Date: Fri, 29 Apr 2022 16:48:58 +0000 (+0200) Subject: Make rustc_parse_format compile on stable X-Git-Url: https://git.lizzy.rs/?a=commitdiff_plain;h=d33140d2dc95c33bc8281b476048c923476c8b73;p=rust.git Make rustc_parse_format compile on stable This allows it to be used by lightweight formatting systems and may allow it to be used by rust-analyzer. --- diff --git a/Cargo.lock b/Cargo.lock index ef4800a2261..94832a24409 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4171,7 +4171,6 @@ name = "rustc_parse_format" version = "0.0.0" dependencies = [ "rustc_lexer", - "rustc_span", ] [[package]] diff --git a/compiler/rustc_builtin_macros/src/asm.rs b/compiler/rustc_builtin_macros/src/asm.rs index e9e3307ca95..c95d7147176 100644 --- a/compiler/rustc_builtin_macros/src/asm.rs +++ b/compiler/rustc_builtin_macros/src/asm.rs @@ -626,7 +626,7 @@ fn expand_preparsed_asm(ecx: &mut ExtCtxt<'_>, args: AsmArgs) -> Option, args: AsmArgs) -> Option, args: AsmArgs) -> Option { @@ -699,14 +702,21 @@ fn expand_preparsed_asm(ecx: &mut ExtCtxt<'_>, args: AsmArgs) -> Option match args.named_args.get(&name) { - Some(&idx) => Some(idx), - None => { - let msg = format!("there is no argument named `{}`", name); - ecx.struct_span_err(template_span.from_inner(span), &msg).emit(); - None + parse::ArgumentNamed(name, span) => { + match args.named_args.get(&Symbol::intern(name)) { + Some(&idx) => Some(idx), + None => { + let msg = format!("there is no argument named `{}`", name); + ecx.struct_span_err( + template_span + .from_inner(InnerSpan::new(span.start, span.end)), + &msg, + ) + .emit(); + None + } } - }, + } }; let mut chars = arg.format.ty.chars(); @@ -715,7 +725,7 @@ fn expand_preparsed_asm(ecx: &mut ExtCtxt<'_>, args: AsmArgs) -> Option, args: AsmArgs) -> Option usize { fn resolve_name_inplace(&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: Symbol| *self.names.get(&s).unwrap_or(&0); + let lookup = |s: &str| *self.names.get(&Symbol::intern(s)).unwrap_or(&0); match *p { parse::String(_) => {} @@ -276,7 +276,9 @@ fn verify_piece(&mut self, p: &parse::Piece<'_>) { // it's written second, so it should come after width/precision. let pos = match arg.position { parse::ArgumentIs(i) | parse::ArgumentImplicitlyIs(i) => Exact(i), - parse::ArgumentNamed(s, span) => Named(s, span), + parse::ArgumentNamed(s, span) => { + Named(Symbol::intern(s), InnerSpan::new(span.start, span.end)) + } }; let ty = Placeholder(match arg.format.ty { @@ -291,7 +293,10 @@ fn verify_piece(&mut self, p: &parse::Piece<'_>) { "X" => "UpperHex", _ => { let fmtsp = self.fmtsp; - let sp = arg.format.ty_span.map(|sp| fmtsp.from_inner(sp)); + 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), @@ -340,14 +345,17 @@ fn verify_piece(&mut self, p: &parse::Piece<'_>) { } } - fn verify_count(&mut self, c: parse::Count) { + fn verify_count(&mut self, c: parse::Count<'_>) { match c { parse::CountImplied | parse::CountIs(..) => {} parse::CountIsParam(i) => { self.verify_arg_type(Exact(i), Count); } parse::CountIsName(s, span) => { - self.verify_arg_type(Named(s, span), Count); + self.verify_arg_type( + Named(Symbol::intern(s), InnerSpan::new(span.start, span.end)), + Count, + ); } } } @@ -425,7 +433,7 @@ fn report_invalid_references(&self, numbered_position_args: bool) { for fmt in &self.arg_with_formatting { if let Some(span) = fmt.precision_span { - let span = self.fmtsp.from_inner(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( @@ -471,7 +479,7 @@ fn report_invalid_references(&self, numbered_position_args: bool) { } } if let Some(span) = fmt.width_span { - let span = self.fmtsp.from_inner(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( @@ -610,7 +618,7 @@ 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 { + 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); @@ -1033,7 +1041,7 @@ pub fn expand_preparsed_format_args( if !parser.errors.is_empty() { let err = parser.errors.remove(0); let sp = if efmt_kind_is_lit { - fmt_span.from_inner(err.span) + fmt_span.from_inner(InnerSpan::new(err.span.start, err.span.end)) } else { // The format string could be another macro invocation, e.g.: // format!(concat!("abc", "{}"), 4); @@ -1052,14 +1060,18 @@ pub fn expand_preparsed_format_args( } if let Some((label, span)) = err.secondary_label { if efmt_kind_is_lit { - e.span_label(fmt_span.from_inner(span), label); + e.span_label(fmt_span.from_inner(InnerSpan::new(span.start, span.end)), label); } } e.emit(); return DummyResult::raw_expr(sp, true); } - let arg_spans = parser.arg_places.iter().map(|span| fmt_span.from_inner(*span)).collect(); + let arg_spans = parser + .arg_places + .iter() + .map(|span| fmt_span.from_inner(InnerSpan::new(span.start, span.end))) + .collect(); let named_pos: FxHashSet = names.values().cloned().collect(); diff --git a/compiler/rustc_lint/src/non_fmt_panic.rs b/compiler/rustc_lint/src/non_fmt_panic.rs index 71769fceec1..4e7aeca9ce1 100644 --- a/compiler/rustc_lint/src/non_fmt_panic.rs +++ b/compiler/rustc_lint/src/non_fmt_panic.rs @@ -254,7 +254,10 @@ fn check_panic_str<'tcx>( if n_arguments > 0 && fmt_parser.errors.is_empty() { let arg_spans: Vec<_> = match &fmt_parser.arg_places[..] { [] => vec![fmt_span], - v => v.iter().map(|span| fmt_span.from_inner(*span)).collect(), + v => v + .iter() + .map(|span| fmt_span.from_inner(InnerSpan::new(span.start, span.end))) + .collect(), }; cx.struct_span_lint(NON_FMT_PANICS, arg_spans, |lint| { let mut l = lint.build(match n_arguments { diff --git a/compiler/rustc_parse_format/Cargo.toml b/compiler/rustc_parse_format/Cargo.toml index aa1714e820f..fcc68b3a219 100644 --- a/compiler/rustc_parse_format/Cargo.toml +++ b/compiler/rustc_parse_format/Cargo.toml @@ -4,5 +4,4 @@ version = "0.0.0" edition = "2021" [dependencies] -rustc_span = { path = "../rustc_span" } rustc_lexer = { path = "../rustc_lexer" } diff --git a/compiler/rustc_parse_format/src/lib.rs b/compiler/rustc_parse_format/src/lib.rs index 00bac26a16a..f6fa19030ac 100644 --- a/compiler/rustc_parse_format/src/lib.rs +++ b/compiler/rustc_parse_format/src/lib.rs @@ -9,8 +9,8 @@ html_playground_url = "https://play.rust-lang.org/", test(attr(deny(warnings))) )] -#![feature(nll)] -#![feature(bool_to_option)] +// We want to be able to build this crate with a stable compiler, so no +// `#![feature]` attributes should be added. pub use Alignment::*; pub use Count::*; @@ -22,7 +22,19 @@ use std::str; use std::string; -use rustc_span::{InnerSpan, Symbol}; +// Note: copied from rustc_span +/// Range inside of a `Span` used for diagnostics when we only have access to relative positions. +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub struct InnerSpan { + pub start: usize, + pub end: usize, +} + +impl InnerSpan { + pub fn new(start: usize, end: usize) -> InnerSpan { + InnerSpan { start, end } + } +} /// The type of format string that we are parsing. #[derive(Copy, Clone, Debug, Eq, PartialEq)] @@ -57,7 +69,7 @@ pub enum Piece<'a> { #[derive(Copy, Clone, Debug, PartialEq)] pub struct Argument<'a> { /// Where to find this argument - pub position: Position, + pub position: Position<'a>, /// How to format the argument pub format: FormatSpec<'a>, } @@ -72,11 +84,11 @@ pub struct FormatSpec<'a> { /// Packed version of various flags provided. pub flags: u32, /// The integer precision to use. - pub precision: Count, + pub precision: Count<'a>, /// The span of the precision formatting flag (for diagnostics). pub precision_span: Option, /// The string width requested for the resulting format. - pub width: Count, + pub width: Count<'a>, /// The span of the width formatting flag (for diagnostics). pub width_span: Option, /// The descriptor string representing the name of the format desired for @@ -89,16 +101,16 @@ pub struct FormatSpec<'a> { /// Enum describing where an argument for a format can be located. #[derive(Copy, Clone, Debug, PartialEq)] -pub enum Position { +pub enum Position<'a> { /// The argument is implied to be located at an index ArgumentImplicitlyIs(usize), /// The argument is located at a specific index given in the format ArgumentIs(usize), /// The argument has a name. - ArgumentNamed(Symbol, InnerSpan), + ArgumentNamed(&'a str, InnerSpan), } -impl Position { +impl Position<'_> { pub fn index(&self) -> Option { match self { ArgumentIs(i) | ArgumentImplicitlyIs(i) => Some(*i), @@ -143,11 +155,11 @@ pub enum Flag { /// A count is used for the precision and width parameters of an integer, and /// can reference either an argument or a literal integer. #[derive(Copy, Clone, Debug, PartialEq)] -pub enum Count { +pub enum Count<'a> { /// The count is specified explicitly. CountIs(usize), /// The count is specified by the argument with the given name. - CountIsName(Symbol, InnerSpan), + CountIsName(&'a str, InnerSpan), /// The count is specified by the argument at the given index. CountIsParam(usize), /// The count is implied and cannot be explicitly specified. @@ -489,7 +501,7 @@ fn argument(&mut self) -> Argument<'a> { /// integer index of an argument, a named argument, or a blank string. /// Returns `Some(parsed_position)` if the position is not implicitly /// consuming a macro argument, `None` if it's the case. - fn position(&mut self) -> Option { + fn position(&mut self) -> Option> { if let Some(i) = self.integer() { Some(ArgumentIs(i)) } else { @@ -498,7 +510,7 @@ fn position(&mut self) -> Option { let word = self.word(); let end = start + word.len(); let span = self.to_span_index(start).to(self.to_span_index(end)); - Some(ArgumentNamed(Symbol::intern(word), span)) + Some(ArgumentNamed(word, span)) } // This is an `ArgumentNext`. @@ -651,7 +663,7 @@ fn inline_asm(&mut self) -> FormatSpec<'a> { /// Parses a `Count` parameter at the current position. This does not check /// for 'CountIsNextParam' because that is only used in precision, not /// width. - fn count(&mut self, start: usize) -> (Count, Option) { + fn count(&mut self, start: usize) -> (Count<'a>, Option) { if let Some(i) = self.integer() { if let Some(end) = self.consume_pos('$') { let span = self.to_span_index(start).to(self.to_span_index(end + 1)); @@ -667,7 +679,7 @@ fn count(&mut self, start: usize) -> (Count, Option) { (CountImplied, None) } else if let Some(end) = self.consume_pos('$') { let span = self.to_span_index(start + 1).to(self.to_span_index(end)); - (CountIsName(Symbol::intern(word), span), None) + (CountIsName(word, span), None) } else { self.cur = tmp; (CountImplied, None) @@ -723,7 +735,7 @@ fn integer(&mut self) -> Option { break; } } - found.then_some(cur) + if found { Some(cur) } else { None } } } diff --git a/compiler/rustc_parse_format/src/tests.rs b/compiler/rustc_parse_format/src/tests.rs index 6c960fdc72b..c9667922ee7 100644 --- a/compiler/rustc_parse_format/src/tests.rs +++ b/compiler/rustc_parse_format/src/tests.rs @@ -144,93 +144,91 @@ fn format_align_fill() { } #[test] fn format_counts() { - rustc_span::create_default_session_globals_then(|| { - same( - "{:10x}", - &[NextArgument(Argument { - position: ArgumentImplicitlyIs(0), - format: FormatSpec { - fill: None, - align: AlignUnknown, - flags: 0, - precision: CountImplied, - width: CountIs(10), - precision_span: None, - width_span: None, - ty: "x", - ty_span: None, - }, - })], - ); - same( - "{:10$.10x}", - &[NextArgument(Argument { - position: ArgumentImplicitlyIs(0), - format: FormatSpec { - fill: None, - align: AlignUnknown, - flags: 0, - precision: CountIs(10), - width: CountIsParam(10), - precision_span: None, - width_span: Some(InnerSpan::new(3, 6)), - ty: "x", - ty_span: None, - }, - })], - ); - same( - "{:.*x}", - &[NextArgument(Argument { - position: ArgumentImplicitlyIs(1), - format: FormatSpec { - fill: None, - align: AlignUnknown, - flags: 0, - precision: CountIsParam(0), - width: CountImplied, - precision_span: Some(InnerSpan::new(3, 5)), - width_span: None, - ty: "x", - ty_span: None, - }, - })], - ); - same( - "{:.10$x}", - &[NextArgument(Argument { - position: ArgumentImplicitlyIs(0), - format: FormatSpec { - fill: None, - align: AlignUnknown, - flags: 0, - precision: CountIsParam(10), - width: CountImplied, - precision_span: Some(InnerSpan::new(3, 7)), - width_span: None, - ty: "x", - ty_span: None, - }, - })], - ); - same( - "{:a$.b$?}", - &[NextArgument(Argument { - position: ArgumentImplicitlyIs(0), - format: FormatSpec { - fill: None, - align: AlignUnknown, - flags: 0, - precision: CountIsName(Symbol::intern("b"), InnerSpan::new(6, 7)), - width: CountIsName(Symbol::intern("a"), InnerSpan::new(4, 4)), - precision_span: None, - width_span: None, - ty: "?", - ty_span: None, - }, - })], - ); - }); + same( + "{:10x}", + &[NextArgument(Argument { + position: ArgumentImplicitlyIs(0), + format: FormatSpec { + fill: None, + align: AlignUnknown, + flags: 0, + precision: CountImplied, + width: CountIs(10), + precision_span: None, + width_span: None, + ty: "x", + ty_span: None, + }, + })], + ); + same( + "{:10$.10x}", + &[NextArgument(Argument { + position: ArgumentImplicitlyIs(0), + format: FormatSpec { + fill: None, + align: AlignUnknown, + flags: 0, + precision: CountIs(10), + width: CountIsParam(10), + precision_span: None, + width_span: Some(InnerSpan::new(3, 6)), + ty: "x", + ty_span: None, + }, + })], + ); + same( + "{:.*x}", + &[NextArgument(Argument { + position: ArgumentImplicitlyIs(1), + format: FormatSpec { + fill: None, + align: AlignUnknown, + flags: 0, + precision: CountIsParam(0), + width: CountImplied, + precision_span: Some(InnerSpan::new(3, 5)), + width_span: None, + ty: "x", + ty_span: None, + }, + })], + ); + same( + "{:.10$x}", + &[NextArgument(Argument { + position: ArgumentImplicitlyIs(0), + format: FormatSpec { + fill: None, + align: AlignUnknown, + flags: 0, + precision: CountIsParam(10), + width: CountImplied, + precision_span: Some(InnerSpan::new(3, 7)), + width_span: None, + ty: "x", + ty_span: None, + }, + })], + ); + same( + "{:a$.b$?}", + &[NextArgument(Argument { + position: ArgumentImplicitlyIs(0), + format: FormatSpec { + fill: None, + align: AlignUnknown, + flags: 0, + precision: CountIsName("b", InnerSpan::new(6, 7)), + width: CountIsName("a", InnerSpan::new(4, 4)), + precision_span: None, + width_span: None, + ty: "?", + ty_span: None, + }, + })], + ); } #[test] fn format_flags() { diff --git a/compiler/rustc_trait_selection/src/traits/on_unimplemented.rs b/compiler/rustc_trait_selection/src/traits/on_unimplemented.rs index ce0e0a21ff5..c266eec25aa 100644 --- a/compiler/rustc_trait_selection/src/traits/on_unimplemented.rs +++ b/compiler/rustc_trait_selection/src/traits/on_unimplemented.rs @@ -304,42 +304,40 @@ fn verify( match token { Piece::String(_) => (), // Normal string, no need to check it Piece::NextArgument(a) => match a.position { - // `{Self}` is allowed - Position::ArgumentNamed(s, _) if s == kw::SelfUpper => (), - // `{ThisTraitsName}` is allowed - Position::ArgumentNamed(s, _) if s == trait_name => (), - // `{from_method}` is allowed - Position::ArgumentNamed(s, _) if s == sym::from_method => (), - // `{from_desugaring}` is allowed - Position::ArgumentNamed(s, _) if s == sym::from_desugaring => (), - // `{ItemContext}` is allowed - Position::ArgumentNamed(s, _) if s == sym::ItemContext => (), - // `{integral}` and `{integer}` and `{float}` are allowed - Position::ArgumentNamed(s, _) - if s == sym::integral || s == sym::integer_ || s == sym::float => - { - () - } - // So is `{A}` if A is a type parameter Position::ArgumentNamed(s, _) => { - match generics.params.iter().find(|param| param.name == s) { - Some(_) => (), - None => { - let reported = struct_span_err!( - tcx.sess, - span, - E0230, - "there is no parameter `{}` on {}", - s, - if trait_def_id == item_def_id { - format!("trait `{}`", trait_name) - } else { - "impl".to_string() - } - ) - .emit(); - result = Err(reported); - } + match Symbol::intern(s) { + // `{Self}` is allowed + kw::SelfUpper => (), + // `{ThisTraitsName}` is allowed + s if s == trait_name => (), + // `{from_method}` is allowed + sym::from_method => (), + // `{from_desugaring}` is allowed + sym::from_desugaring => (), + // `{ItemContext}` is allowed + sym::ItemContext => (), + // `{integral}` and `{integer}` and `{float}` are allowed + sym::integral | sym::integer_ | sym::float => (), + // So is `{A}` if A is a type parameter + s => match generics.params.iter().find(|param| param.name == s) { + Some(_) => (), + None => { + let reported = struct_span_err!( + tcx.sess, + span, + E0230, + "there is no parameter `{}` on {}", + s, + if trait_def_id == item_def_id { + format!("trait `{}`", trait_name) + } else { + "impl".to_string() + } + ) + .emit(); + result = Err(reported); + } + }, } } // `{:1}` and `{}` are not to be used @@ -392,34 +390,37 @@ pub fn format( .map(|p| match p { Piece::String(s) => s, Piece::NextArgument(a) => match a.position { - Position::ArgumentNamed(s, _) => match generic_map.get(&s) { - Some(val) => val, - None if s == name => &trait_str, - None => { - if let Some(val) = options.get(&s) { - val - } else if s == sym::from_desugaring || s == sym::from_method { - // don't break messages using these two arguments incorrectly - &empty_string - } else if s == sym::ItemContext { - &item_context - } else if s == sym::integral { - "{integral}" - } else if s == sym::integer_ { - "{integer}" - } else if s == sym::float { - "{float}" - } else { - bug!( - "broken on_unimplemented {:?} for {:?}: \ + Position::ArgumentNamed(s, _) => { + let s = Symbol::intern(s); + match generic_map.get(&s) { + Some(val) => val, + None if s == name => &trait_str, + None => { + if let Some(val) = options.get(&s) { + val + } else if s == sym::from_desugaring || s == sym::from_method { + // don't break messages using these two arguments incorrectly + &empty_string + } else if s == sym::ItemContext { + &item_context + } else if s == sym::integral { + "{integral}" + } else if s == sym::integer_ { + "{integer}" + } else if s == sym::float { + "{float}" + } else { + bug!( + "broken on_unimplemented {:?} for {:?}: \ no argument matching {:?}", - self.0, - trait_ref, - s - ) + self.0, + trait_ref, + s + ) + } } } - }, + } _ => bug!("broken on_unimplemented {:?} - bad format arg", self.0), }, }) diff --git a/src/tools/clippy/clippy_lints/src/write.rs b/src/tools/clippy/clippy_lints/src/write.rs index f3d818cc348..54b93a20a05 100644 --- a/src/tools/clippy/clippy_lints/src/write.rs +++ b/src/tools/clippy/clippy_lints/src/write.rs @@ -13,7 +13,7 @@ use rustc_parse::parser; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::symbol::{kw, Symbol}; -use rustc_span::{sym, BytePos, Span, DUMMY_SP}; +use rustc_span::{sym, BytePos, InnerSpan, Span, DUMMY_SP}; declare_clippy_lint! { /// ### What it does @@ -454,6 +454,7 @@ fn push(&mut self, arg: rustc_parse_format::Argument<'_>, span: Span) { } }, ArgumentNamed(n, _) => { + let n = Symbol::intern(n); if let Some(x) = self.named.iter_mut().find(|x| x.0 == n) { match x.1.as_slice() { // A non-empty format string has been seen already. @@ -495,7 +496,7 @@ fn parse_fmt_string(&self, cx: &EarlyContext<'_>, str_lit: &StrLit) -> Option