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.
This commit is contained in:
bjorn3 2022-04-29 18:48:58 +02:00
parent 683c582c1e
commit d33140d2dc
9 changed files with 231 additions and 191 deletions

View File

@ -4171,7 +4171,6 @@ name = "rustc_parse_format"
version = "0.0.0" version = "0.0.0"
dependencies = [ dependencies = [
"rustc_lexer", "rustc_lexer",
"rustc_span",
] ]
[[package]] [[package]]

View File

@ -626,7 +626,7 @@ fn expand_preparsed_asm(ecx: &mut ExtCtxt<'_>, args: AsmArgs) -> Option<ast::Inl
if !parser.errors.is_empty() { if !parser.errors.is_empty() {
let err = parser.errors.remove(0); let err = parser.errors.remove(0);
let err_sp = template_span.from_inner(err.span); let err_sp = template_span.from_inner(InnerSpan::new(err.span.start, err.span.end));
let msg = &format!("invalid asm template string: {}", err.description); let msg = &format!("invalid asm template string: {}", err.description);
let mut e = ecx.struct_span_err(err_sp, msg); let mut e = ecx.struct_span_err(err_sp, msg);
e.span_label(err_sp, err.label + " in asm template string"); e.span_label(err_sp, err.label + " in asm template string");
@ -634,7 +634,7 @@ fn expand_preparsed_asm(ecx: &mut ExtCtxt<'_>, args: AsmArgs) -> Option<ast::Inl
e.note(&note); e.note(&note);
} }
if let Some((label, span)) = err.secondary_label { if let Some((label, span)) = err.secondary_label {
let err_sp = template_span.from_inner(span); let err_sp = template_span.from_inner(InnerSpan::new(span.start, span.end));
e.span_label(err_sp, label); e.span_label(err_sp, label);
} }
e.emit(); e.emit();
@ -643,7 +643,10 @@ fn expand_preparsed_asm(ecx: &mut ExtCtxt<'_>, args: AsmArgs) -> Option<ast::Inl
curarg = parser.curarg; curarg = parser.curarg;
let mut arg_spans = parser.arg_places.iter().map(|span| template_span.from_inner(*span)); let mut arg_spans = parser
.arg_places
.iter()
.map(|span| template_span.from_inner(InnerSpan::new(span.start, span.end)));
for piece in unverified_pieces { for piece in unverified_pieces {
match piece { match piece {
parse::Piece::String(s) => { parse::Piece::String(s) => {
@ -699,14 +702,21 @@ fn expand_preparsed_asm(ecx: &mut ExtCtxt<'_>, args: AsmArgs) -> Option<ast::Inl
Some(idx) Some(idx)
} }
} }
parse::ArgumentNamed(name, span) => match args.named_args.get(&name) { parse::ArgumentNamed(name, span) => {
Some(&idx) => Some(idx), match args.named_args.get(&Symbol::intern(name)) {
None => { Some(&idx) => Some(idx),
let msg = format!("there is no argument named `{}`", name); None => {
ecx.struct_span_err(template_span.from_inner(span), &msg).emit(); let msg = format!("there is no argument named `{}`", name);
None ecx.struct_span_err(
template_span
.from_inner(InnerSpan::new(span.start, span.end)),
&msg,
)
.emit();
None
}
} }
}, }
}; };
let mut chars = arg.format.ty.chars(); let mut chars = arg.format.ty.chars();
@ -715,7 +725,7 @@ fn expand_preparsed_asm(ecx: &mut ExtCtxt<'_>, args: AsmArgs) -> Option<ast::Inl
let span = arg let span = arg
.format .format
.ty_span .ty_span
.map(|sp| template_sp.from_inner(sp)) .map(|sp| template_sp.from_inner(InnerSpan::new(sp.start, sp.end)))
.unwrap_or(template_sp); .unwrap_or(template_sp);
ecx.struct_span_err( ecx.struct_span_err(
span, span,
@ -741,7 +751,12 @@ fn expand_preparsed_asm(ecx: &mut ExtCtxt<'_>, args: AsmArgs) -> Option<ast::Inl
let template_num_lines = 1 + template_str.matches('\n').count(); let template_num_lines = 1 + template_str.matches('\n').count();
line_spans.extend(std::iter::repeat(template_sp).take(template_num_lines)); line_spans.extend(std::iter::repeat(template_sp).take(template_num_lines));
} else { } else {
line_spans.extend(parser.line_spans.iter().map(|span| template_span.from_inner(*span))); line_spans.extend(
parser
.line_spans
.iter()
.map(|span| template_span.from_inner(InnerSpan::new(span.start, span.end))),
);
}; };
} }

View File

@ -242,7 +242,7 @@ impl<'a, 'b> Context<'a, 'b> {
fn resolve_name_inplace(&self, p: &mut parse::Piece<'_>) { fn resolve_name_inplace(&self, p: &mut parse::Piece<'_>) {
// NOTE: the `unwrap_or` branch is needed in case of invalid format // NOTE: the `unwrap_or` branch is needed in case of invalid format
// arguments, e.g., `format_args!("{foo}")`. // 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 { match *p {
parse::String(_) => {} parse::String(_) => {}
@ -276,7 +276,9 @@ impl<'a, 'b> Context<'a, 'b> {
// it's written second, so it should come after width/precision. // it's written second, so it should come after width/precision.
let pos = match arg.position { let pos = match arg.position {
parse::ArgumentIs(i) | parse::ArgumentImplicitlyIs(i) => Exact(i), 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 { let ty = Placeholder(match arg.format.ty {
@ -291,7 +293,10 @@ impl<'a, 'b> Context<'a, 'b> {
"X" => "UpperHex", "X" => "UpperHex",
_ => { _ => {
let fmtsp = self.fmtsp; 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( let mut err = self.ecx.struct_span_err(
sp.unwrap_or(fmtsp), sp.unwrap_or(fmtsp),
&format!("unknown format trait `{}`", arg.format.ty), &format!("unknown format trait `{}`", arg.format.ty),
@ -340,14 +345,17 @@ impl<'a, 'b> Context<'a, 'b> {
} }
} }
fn verify_count(&mut self, c: parse::Count) { fn verify_count(&mut self, c: parse::Count<'_>) {
match c { match c {
parse::CountImplied | parse::CountIs(..) => {} parse::CountImplied | parse::CountIs(..) => {}
parse::CountIsParam(i) => { parse::CountIsParam(i) => {
self.verify_arg_type(Exact(i), Count); self.verify_arg_type(Exact(i), Count);
} }
parse::CountIsName(s, span) => { 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 @@ impl<'a, 'b> Context<'a, 'b> {
for fmt in &self.arg_with_formatting { for fmt in &self.arg_with_formatting {
if let Some(span) = fmt.precision_span { 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 { match fmt.precision {
parse::CountIsParam(pos) if pos > self.num_args() => { parse::CountIsParam(pos) if pos > self.num_args() => {
e.span_label( e.span_label(
@ -471,7 +479,7 @@ impl<'a, 'b> Context<'a, 'b> {
} }
} }
if let Some(span) = fmt.width_span { 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 { match fmt.width {
parse::CountIsParam(pos) if pos > self.num_args() => { parse::CountIsParam(pos) if pos > self.num_args() => {
e.span_label( e.span_label(
@ -610,7 +618,7 @@ impl<'a, 'b> Context<'a, 'b> {
ecx.std_path(&[sym::fmt, sym::rt, sym::v1, s]) ecx.std_path(&[sym::fmt, sym::rt, sym::v1, s])
} }
fn build_count(&self, c: parse::Count) -> P<ast::Expr> { fn build_count(&self, c: parse::Count<'_>) -> P<ast::Expr> {
let sp = self.macsp; let sp = self.macsp;
let count = |c, arg| { let count = |c, arg| {
let mut path = Context::rtpath(self.ecx, sym::Count); let mut path = Context::rtpath(self.ecx, sym::Count);
@ -1033,7 +1041,7 @@ pub fn expand_preparsed_format_args(
if !parser.errors.is_empty() { if !parser.errors.is_empty() {
let err = parser.errors.remove(0); let err = parser.errors.remove(0);
let sp = if efmt_kind_is_lit { 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 { } else {
// The format string could be another macro invocation, e.g.: // The format string could be another macro invocation, e.g.:
// format!(concat!("abc", "{}"), 4); // format!(concat!("abc", "{}"), 4);
@ -1052,14 +1060,18 @@ pub fn expand_preparsed_format_args(
} }
if let Some((label, span)) = err.secondary_label { if let Some((label, span)) = err.secondary_label {
if efmt_kind_is_lit { 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(); e.emit();
return DummyResult::raw_expr(sp, true); 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<usize> = names.values().cloned().collect(); let named_pos: FxHashSet<usize> = names.values().cloned().collect();

View File

@ -254,7 +254,10 @@ fn check_panic_str<'tcx>(
if n_arguments > 0 && fmt_parser.errors.is_empty() { if n_arguments > 0 && fmt_parser.errors.is_empty() {
let arg_spans: Vec<_> = match &fmt_parser.arg_places[..] { let arg_spans: Vec<_> = match &fmt_parser.arg_places[..] {
[] => vec![fmt_span], [] => 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| { cx.struct_span_lint(NON_FMT_PANICS, arg_spans, |lint| {
let mut l = lint.build(match n_arguments { let mut l = lint.build(match n_arguments {

View File

@ -4,5 +4,4 @@ version = "0.0.0"
edition = "2021" edition = "2021"
[dependencies] [dependencies]
rustc_span = { path = "../rustc_span" }
rustc_lexer = { path = "../rustc_lexer" } rustc_lexer = { path = "../rustc_lexer" }

View File

@ -9,8 +9,8 @@
html_playground_url = "https://play.rust-lang.org/", html_playground_url = "https://play.rust-lang.org/",
test(attr(deny(warnings))) test(attr(deny(warnings)))
)] )]
#![feature(nll)] // We want to be able to build this crate with a stable compiler, so no
#![feature(bool_to_option)] // `#![feature]` attributes should be added.
pub use Alignment::*; pub use Alignment::*;
pub use Count::*; pub use Count::*;
@ -22,7 +22,19 @@ use std::iter;
use std::str; use std::str;
use std::string; 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. /// The type of format string that we are parsing.
#[derive(Copy, Clone, Debug, Eq, PartialEq)] #[derive(Copy, Clone, Debug, Eq, PartialEq)]
@ -57,7 +69,7 @@ pub enum Piece<'a> {
#[derive(Copy, Clone, Debug, PartialEq)] #[derive(Copy, Clone, Debug, PartialEq)]
pub struct Argument<'a> { pub struct Argument<'a> {
/// Where to find this argument /// Where to find this argument
pub position: Position, pub position: Position<'a>,
/// How to format the argument /// How to format the argument
pub format: FormatSpec<'a>, pub format: FormatSpec<'a>,
} }
@ -72,11 +84,11 @@ pub struct FormatSpec<'a> {
/// Packed version of various flags provided. /// Packed version of various flags provided.
pub flags: u32, pub flags: u32,
/// The integer precision to use. /// The integer precision to use.
pub precision: Count, pub precision: Count<'a>,
/// The span of the precision formatting flag (for diagnostics). /// The span of the precision formatting flag (for diagnostics).
pub precision_span: Option<InnerSpan>, pub precision_span: Option<InnerSpan>,
/// The string width requested for the resulting format. /// The string width requested for the resulting format.
pub width: Count, pub width: Count<'a>,
/// The span of the width formatting flag (for diagnostics). /// The span of the width formatting flag (for diagnostics).
pub width_span: Option<InnerSpan>, pub width_span: Option<InnerSpan>,
/// The descriptor string representing the name of the format desired for /// 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. /// Enum describing where an argument for a format can be located.
#[derive(Copy, Clone, Debug, PartialEq)] #[derive(Copy, Clone, Debug, PartialEq)]
pub enum Position { pub enum Position<'a> {
/// The argument is implied to be located at an index /// The argument is implied to be located at an index
ArgumentImplicitlyIs(usize), ArgumentImplicitlyIs(usize),
/// The argument is located at a specific index given in the format /// The argument is located at a specific index given in the format
ArgumentIs(usize), ArgumentIs(usize),
/// The argument has a name. /// The argument has a name.
ArgumentNamed(Symbol, InnerSpan), ArgumentNamed(&'a str, InnerSpan),
} }
impl Position { impl Position<'_> {
pub fn index(&self) -> Option<usize> { pub fn index(&self) -> Option<usize> {
match self { match self {
ArgumentIs(i) | ArgumentImplicitlyIs(i) => Some(*i), 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 /// A count is used for the precision and width parameters of an integer, and
/// can reference either an argument or a literal integer. /// can reference either an argument or a literal integer.
#[derive(Copy, Clone, Debug, PartialEq)] #[derive(Copy, Clone, Debug, PartialEq)]
pub enum Count { pub enum Count<'a> {
/// The count is specified explicitly. /// The count is specified explicitly.
CountIs(usize), CountIs(usize),
/// The count is specified by the argument with the given name. /// 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. /// The count is specified by the argument at the given index.
CountIsParam(usize), CountIsParam(usize),
/// The count is implied and cannot be explicitly specified. /// The count is implied and cannot be explicitly specified.
@ -489,7 +501,7 @@ impl<'a> Parser<'a> {
/// integer index of an argument, a named argument, or a blank string. /// integer index of an argument, a named argument, or a blank string.
/// Returns `Some(parsed_position)` if the position is not implicitly /// Returns `Some(parsed_position)` if the position is not implicitly
/// consuming a macro argument, `None` if it's the case. /// consuming a macro argument, `None` if it's the case.
fn position(&mut self) -> Option<Position> { fn position(&mut self) -> Option<Position<'a>> {
if let Some(i) = self.integer() { if let Some(i) = self.integer() {
Some(ArgumentIs(i)) Some(ArgumentIs(i))
} else { } else {
@ -498,7 +510,7 @@ impl<'a> Parser<'a> {
let word = self.word(); let word = self.word();
let end = start + word.len(); let end = start + word.len();
let span = self.to_span_index(start).to(self.to_span_index(end)); 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`. // This is an `ArgumentNext`.
@ -651,7 +663,7 @@ impl<'a> Parser<'a> {
/// Parses a `Count` parameter at the current position. This does not check /// Parses a `Count` parameter at the current position. This does not check
/// for 'CountIsNextParam' because that is only used in precision, not /// for 'CountIsNextParam' because that is only used in precision, not
/// width. /// width.
fn count(&mut self, start: usize) -> (Count, Option<InnerSpan>) { fn count(&mut self, start: usize) -> (Count<'a>, Option<InnerSpan>) {
if let Some(i) = self.integer() { if let Some(i) = self.integer() {
if let Some(end) = self.consume_pos('$') { if let Some(end) = self.consume_pos('$') {
let span = self.to_span_index(start).to(self.to_span_index(end + 1)); let span = self.to_span_index(start).to(self.to_span_index(end + 1));
@ -667,7 +679,7 @@ impl<'a> Parser<'a> {
(CountImplied, None) (CountImplied, None)
} else if let Some(end) = self.consume_pos('$') { } else if let Some(end) = self.consume_pos('$') {
let span = self.to_span_index(start + 1).to(self.to_span_index(end)); 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 { } else {
self.cur = tmp; self.cur = tmp;
(CountImplied, None) (CountImplied, None)
@ -723,7 +735,7 @@ impl<'a> Parser<'a> {
break; break;
} }
} }
found.then_some(cur) if found { Some(cur) } else { None }
} }
} }

View File

@ -144,93 +144,91 @@ fn format_align_fill() {
} }
#[test] #[test]
fn format_counts() { fn format_counts() {
rustc_span::create_default_session_globals_then(|| { same(
same( "{:10x}",
"{:10x}", &[NextArgument(Argument {
&[NextArgument(Argument { position: ArgumentImplicitlyIs(0),
position: ArgumentImplicitlyIs(0), format: FormatSpec {
format: FormatSpec { fill: None,
fill: None, align: AlignUnknown,
align: AlignUnknown, flags: 0,
flags: 0, precision: CountImplied,
precision: CountImplied, width: CountIs(10),
width: CountIs(10), precision_span: None,
precision_span: None, width_span: None,
width_span: None, ty: "x",
ty: "x", ty_span: None,
ty_span: None, },
}, })],
})], );
); same(
same( "{:10$.10x}",
"{:10$.10x}", &[NextArgument(Argument {
&[NextArgument(Argument { position: ArgumentImplicitlyIs(0),
position: ArgumentImplicitlyIs(0), format: FormatSpec {
format: FormatSpec { fill: None,
fill: None, align: AlignUnknown,
align: AlignUnknown, flags: 0,
flags: 0, precision: CountIs(10),
precision: CountIs(10), width: CountIsParam(10),
width: CountIsParam(10), precision_span: None,
precision_span: None, width_span: Some(InnerSpan::new(3, 6)),
width_span: Some(InnerSpan::new(3, 6)), ty: "x",
ty: "x", ty_span: None,
ty_span: None, },
}, })],
})], );
); same(
same( "{:.*x}",
"{:.*x}", &[NextArgument(Argument {
&[NextArgument(Argument { position: ArgumentImplicitlyIs(1),
position: ArgumentImplicitlyIs(1), format: FormatSpec {
format: FormatSpec { fill: None,
fill: None, align: AlignUnknown,
align: AlignUnknown, flags: 0,
flags: 0, precision: CountIsParam(0),
precision: CountIsParam(0), width: CountImplied,
width: CountImplied, precision_span: Some(InnerSpan::new(3, 5)),
precision_span: Some(InnerSpan::new(3, 5)), width_span: None,
width_span: None, ty: "x",
ty: "x", ty_span: None,
ty_span: None, },
}, })],
})], );
); same(
same( "{:.10$x}",
"{:.10$x}", &[NextArgument(Argument {
&[NextArgument(Argument { position: ArgumentImplicitlyIs(0),
position: ArgumentImplicitlyIs(0), format: FormatSpec {
format: FormatSpec { fill: None,
fill: None, align: AlignUnknown,
align: AlignUnknown, flags: 0,
flags: 0, precision: CountIsParam(10),
precision: CountIsParam(10), width: CountImplied,
width: CountImplied, precision_span: Some(InnerSpan::new(3, 7)),
precision_span: Some(InnerSpan::new(3, 7)), width_span: None,
width_span: None, ty: "x",
ty: "x", ty_span: None,
ty_span: None, },
}, })],
})], );
); same(
same( "{:a$.b$?}",
"{:a$.b$?}", &[NextArgument(Argument {
&[NextArgument(Argument { position: ArgumentImplicitlyIs(0),
position: ArgumentImplicitlyIs(0), format: FormatSpec {
format: FormatSpec { fill: None,
fill: None, align: AlignUnknown,
align: AlignUnknown, flags: 0,
flags: 0, precision: CountIsName("b", InnerSpan::new(6, 7)),
precision: CountIsName(Symbol::intern("b"), InnerSpan::new(6, 7)), width: CountIsName("a", InnerSpan::new(4, 4)),
width: CountIsName(Symbol::intern("a"), InnerSpan::new(4, 4)), precision_span: None,
precision_span: None, width_span: None,
width_span: None, ty: "?",
ty: "?", ty_span: None,
ty_span: None, },
}, })],
})], );
);
});
} }
#[test] #[test]
fn format_flags() { fn format_flags() {

View File

@ -304,42 +304,40 @@ impl<'tcx> OnUnimplementedFormatString {
match token { match token {
Piece::String(_) => (), // Normal string, no need to check it Piece::String(_) => (), // Normal string, no need to check it
Piece::NextArgument(a) => match a.position { 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, _) => { Position::ArgumentNamed(s, _) => {
match generics.params.iter().find(|param| param.name == s) { match Symbol::intern(s) {
Some(_) => (), // `{Self}` is allowed
None => { kw::SelfUpper => (),
let reported = struct_span_err!( // `{ThisTraitsName}` is allowed
tcx.sess, s if s == trait_name => (),
span, // `{from_method}` is allowed
E0230, sym::from_method => (),
"there is no parameter `{}` on {}", // `{from_desugaring}` is allowed
s, sym::from_desugaring => (),
if trait_def_id == item_def_id { // `{ItemContext}` is allowed
format!("trait `{}`", trait_name) sym::ItemContext => (),
} else { // `{integral}` and `{integer}` and `{float}` are allowed
"impl".to_string() 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) {
.emit(); Some(_) => (),
result = Err(reported); 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 // `{:1}` and `{}` are not to be used
@ -392,34 +390,37 @@ impl<'tcx> OnUnimplementedFormatString {
.map(|p| match p { .map(|p| match p {
Piece::String(s) => s, Piece::String(s) => s,
Piece::NextArgument(a) => match a.position { Piece::NextArgument(a) => match a.position {
Position::ArgumentNamed(s, _) => match generic_map.get(&s) { Position::ArgumentNamed(s, _) => {
Some(val) => val, let s = Symbol::intern(s);
None if s == name => &trait_str, match generic_map.get(&s) {
None => { Some(val) => val,
if let Some(val) = options.get(&s) { None if s == name => &trait_str,
val None => {
} else if s == sym::from_desugaring || s == sym::from_method { if let Some(val) = options.get(&s) {
// don't break messages using these two arguments incorrectly val
&empty_string } else if s == sym::from_desugaring || s == sym::from_method {
} else if s == sym::ItemContext { // don't break messages using these two arguments incorrectly
&item_context &empty_string
} else if s == sym::integral { } else if s == sym::ItemContext {
"{integral}" &item_context
} else if s == sym::integer_ { } else if s == sym::integral {
"{integer}" "{integral}"
} else if s == sym::float { } else if s == sym::integer_ {
"{float}" "{integer}"
} else { } else if s == sym::float {
bug!( "{float}"
"broken on_unimplemented {:?} for {:?}: \ } else {
bug!(
"broken on_unimplemented {:?} for {:?}: \
no argument matching {:?}", no argument matching {:?}",
self.0, self.0,
trait_ref, trait_ref,
s s
) )
}
} }
} }
}, }
_ => bug!("broken on_unimplemented {:?} - bad format arg", self.0), _ => bug!("broken on_unimplemented {:?} - bad format arg", self.0),
}, },
}) })

View File

@ -13,7 +13,7 @@ use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
use rustc_parse::parser; use rustc_parse::parser;
use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::symbol::{kw, Symbol}; 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! { declare_clippy_lint! {
/// ### What it does /// ### What it does
@ -454,6 +454,7 @@ impl SimpleFormatArgs {
} }
}, },
ArgumentNamed(n, _) => { ArgumentNamed(n, _) => {
let n = Symbol::intern(n);
if let Some(x) = self.named.iter_mut().find(|x| x.0 == n) { if let Some(x) = self.named.iter_mut().find(|x| x.0 == n) {
match x.1.as_slice() { match x.1.as_slice() {
// A non-empty format string has been seen already. // A non-empty format string has been seen already.
@ -495,7 +496,7 @@ impl Write {
let span = parser let span = parser
.arg_places .arg_places
.last() .last()
.map_or(DUMMY_SP, |&x| str_lit.span.from_inner(x)); .map_or(DUMMY_SP, |&x| str_lit.span.from_inner(InnerSpan::new(x.start, x.end)));
if !self.in_debug_impl && arg.format.ty == "?" { if !self.in_debug_impl && arg.format.ty == "?" {
// FIXME: modify rustc's fmt string parser to give us the current span // FIXME: modify rustc's fmt string parser to give us the current span