From 439ef6d76279268eb80e33afffafa22597e22776 Mon Sep 17 00:00:00 2001 From: Fabian Wolff Date: Fri, 7 May 2021 19:44:32 +0200 Subject: [PATCH 01/13] Fix suggestions for missing return type lifetime parameters --- compiler/rustc_errors/src/diagnostic.rs | 24 ++ .../rustc_resolve/src/late/diagnostics.rs | 351 ++++++++++-------- compiler/rustc_resolve/src/late/lifetimes.rs | 15 +- src/test/ui/issues/issue-84592.rs | 17 + src/test/ui/issues/issue-84592.stderr | 17 + src/test/ui/return-elided-lifetime.rs | 37 ++ src/test/ui/return-elided-lifetime.stderr | 198 ++++++++++ .../ui/suggestions/missing-lt-for-hrtb.stderr | 4 + src/tools/tidy/src/ui_tests.rs | 2 +- 9 files changed, 514 insertions(+), 151 deletions(-) create mode 100644 src/test/ui/issues/issue-84592.rs create mode 100644 src/test/ui/issues/issue-84592.stderr create mode 100644 src/test/ui/return-elided-lifetime.rs create mode 100644 src/test/ui/return-elided-lifetime.stderr diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index b2f6a0c10142d..405dd2e68c6d0 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -299,6 +299,30 @@ impl Diagnostic { self } + /// [`Diagnostic::multipart_suggestion()`] but you can set the [`SuggestionStyle`]. + pub fn multipart_suggestion_with_style( + &mut self, + msg: &str, + suggestion: Vec<(Span, String)>, + applicability: Applicability, + style: SuggestionStyle, + ) -> &mut Self { + assert!(!suggestion.is_empty()); + self.suggestions.push(CodeSuggestion { + substitutions: vec![Substitution { + parts: suggestion + .into_iter() + .map(|(span, snippet)| SubstitutionPart { snippet, span }) + .collect(), + }], + msg: msg.to_owned(), + style, + applicability, + tool_metadata: Default::default(), + }); + self + } + /// Prints out a message with for a multipart suggestion without showing the suggested code. /// /// This is intended to be used for suggestions that are obvious in what the changes need to diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 7561b3df3af70..ca4873bd51570 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -9,7 +9,7 @@ use rustc_ast::visit::FnKind; use rustc_ast::{self as ast, Expr, ExprKind, Item, ItemKind, NodeId, Path, Ty, TyKind}; use rustc_ast_pretty::pprust::path_segment_to_string; use rustc_data_structures::fx::FxHashSet; -use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder}; +use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder, SuggestionStyle}; use rustc_hir as hir; use rustc_hir::def::Namespace::{self, *}; use rustc_hir::def::{self, CtorKind, CtorOf, DefKind}; @@ -1687,12 +1687,12 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> { impl<'tcx> LifetimeContext<'_, 'tcx> { crate fn report_missing_lifetime_specifiers( &self, - span: Span, + spans: Vec, count: usize, ) -> DiagnosticBuilder<'tcx> { struct_span_err!( self.tcx.sess, - span, + spans, E0106, "missing lifetime specifier{}", pluralize!(count) @@ -1821,81 +1821,107 @@ impl<'tcx> LifetimeContext<'_, 'tcx> { crate fn add_missing_lifetime_specifiers_label( &self, err: &mut DiagnosticBuilder<'_>, - span: Span, - count: usize, + spans: Vec, + counts: Vec, lifetime_names: &FxHashSet, lifetime_spans: Vec, params: &[ElisionFailureInfo], ) { - let snippet = self.tcx.sess.source_map().span_to_snippet(span).ok(); - - err.span_label( - span, - &format!( - "expected {} lifetime parameter{}", - if count == 1 { "named".to_string() } else { count.to_string() }, - pluralize!(count) - ), - ); + let snippets: Vec> = spans + .iter() + .copied() + .map(|span| self.tcx.sess.source_map().span_to_snippet(span).ok()) + .collect(); - let suggest_existing = |err: &mut DiagnosticBuilder<'_>, - name: &str, - formatter: &dyn Fn(&str) -> String| { - if let Some(MissingLifetimeSpot::HigherRanked { span: for_span, span_type }) = - self.missing_named_lifetime_spots.iter().rev().next() - { - // When we have `struct S<'a>(&'a dyn Fn(&X) -> &X);` we want to not only suggest - // using `'a`, but also introduce the concept of HRLTs by suggesting - // `struct S<'a>(&'a dyn for<'b> Fn(&X) -> &'b X);`. (#72404) - let mut introduce_suggestion = vec![]; + for (span, count) in spans.iter().zip(counts.iter()) { + err.span_label( + span.clone(), + format!( + "expected {} lifetime parameter{}", + if *count == 1 { "named".to_string() } else { count.to_string() }, + pluralize!(*count), + ), + ); + } - let a_to_z_repeat_n = |n| { - (b'a'..=b'z').map(move |c| { - let mut s = '\''.to_string(); - s.extend(std::iter::repeat(char::from(c)).take(n)); - s - }) - }; + let suggest_existing = + |err: &mut DiagnosticBuilder<'_>, + name: &str, + formatters: &Vec String>>>| { + if let Some(MissingLifetimeSpot::HigherRanked { span: for_span, span_type }) = + self.missing_named_lifetime_spots.iter().rev().next() + { + // When we have `struct S<'a>(&'a dyn Fn(&X) -> &X);` we want to not only suggest + // using `'a`, but also introduce the concept of HRLTs by suggesting + // `struct S<'a>(&'a dyn for<'b> Fn(&X) -> &'b X);`. (#72404) + let mut introduce_suggestion = vec![]; + + let a_to_z_repeat_n = |n| { + (b'a'..=b'z').map(move |c| { + let mut s = '\''.to_string(); + s.extend(std::iter::repeat(char::from(c)).take(n)); + s + }) + }; - // If all single char lifetime names are present, we wrap around and double the chars. - let lt_name = (1..) - .flat_map(a_to_z_repeat_n) - .find(|lt| !lifetime_names.contains(&Symbol::intern(<))) - .unwrap(); - let msg = format!( - "consider making the {} lifetime-generic with a new `{}` lifetime", - span_type.descr(), - lt_name, - ); - err.note( - "for more information on higher-ranked polymorphism, visit \ + // If all single char lifetime names are present, we wrap around and double the chars. + let lt_name = (1..) + .flat_map(a_to_z_repeat_n) + .find(|lt| !lifetime_names.contains(&Symbol::intern(<))) + .unwrap(); + let msg = format!( + "consider making the {} lifetime-generic with a new `{}` lifetime", + span_type.descr(), + lt_name, + ); + err.note( + "for more information on higher-ranked polymorphism, visit \ https://doc.rust-lang.org/nomicon/hrtb.html", - ); - let for_sugg = span_type.suggestion(<_name); - for param in params { - if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(param.span) { - if snippet.starts_with('&') && !snippet.starts_with("&'") { - introduce_suggestion - .push((param.span, format!("&{} {}", lt_name, &snippet[1..]))); - } else if let Some(stripped) = snippet.strip_prefix("&'_ ") { - introduce_suggestion - .push((param.span, format!("&{} {}", lt_name, stripped))); + ); + let for_sugg = span_type.suggestion(<_name); + for param in params { + if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(param.span) + { + if snippet.starts_with('&') && !snippet.starts_with("&'") { + introduce_suggestion + .push((param.span, format!("&{} {}", lt_name, &snippet[1..]))); + } else if let Some(stripped) = snippet.strip_prefix("&'_ ") { + introduce_suggestion + .push((param.span, format!("&{} {}", lt_name, stripped))); + } } } + introduce_suggestion.push((*for_span, for_sugg)); + for (span, formatter) in spans.iter().copied().zip(formatters.iter()) { + if let Some(formatter) = formatter { + introduce_suggestion.push((span, formatter(<_name))); + } + } + err.multipart_suggestion_with_style( + &msg, + introduce_suggestion, + Applicability::MaybeIncorrect, + SuggestionStyle::ShowAlways, + ); } - introduce_suggestion.push((*for_span, for_sugg)); - introduce_suggestion.push((span, formatter(<_name))); - err.multipart_suggestion(&msg, introduce_suggestion, Applicability::MaybeIncorrect); - } - err.span_suggestion_verbose( - span, - &format!("consider using the `{}` lifetime", lifetime_names.iter().next().unwrap()), - formatter(name), - Applicability::MaybeIncorrect, - ); - }; - let suggest_new = |err: &mut DiagnosticBuilder<'_>, sugg: &str| { + let mut spans_suggs: Vec<_> = Vec::new(); + for (span, fmt) in spans.iter().copied().zip(formatters.iter()) { + if let Some(formatter) = fmt { + spans_suggs.push((span, formatter(name))); + } + } + err.multipart_suggestion_with_style( + &format!( + "consider using the `{}` lifetime", + lifetime_names.iter().next().unwrap() + ), + spans_suggs, + Applicability::MaybeIncorrect, + SuggestionStyle::ShowAlways, + ); + }; + let suggest_new = |err: &mut DiagnosticBuilder<'_>, suggs: &Vec>| { for missing in self.missing_named_lifetime_spots.iter().rev() { let mut introduce_suggestion = vec![]; let msg; @@ -1940,38 +1966,44 @@ impl<'tcx> LifetimeContext<'_, 'tcx> { (*span, span_type.suggestion("'a")) } MissingLifetimeSpot::Static => { - let (span, sugg) = match snippet.as_deref() { - Some("&") => (span.shrink_to_hi(), "'static ".to_owned()), - Some("'_") => (span, "'static".to_owned()), - Some(snippet) if !snippet.ends_with('>') => { - if snippet == "" { - ( - span, - std::iter::repeat("'static") - .take(count) - .collect::>() - .join(", "), - ) - } else { - ( - span.shrink_to_hi(), - format!( - "<{}>", + let mut spans_suggs = Vec::new(); + for ((span, snippet), count) in + spans.iter().copied().zip(snippets.iter()).zip(counts.iter().copied()) + { + let (span, sugg) = match snippet.as_deref() { + Some("&") => (span.shrink_to_hi(), "'static ".to_owned()), + Some("'_") => (span, "'static".to_owned()), + Some(snippet) if !snippet.ends_with('>') => { + if snippet == "" { + ( + span, std::iter::repeat("'static") .take(count) .collect::>() - .join(", ") - ), - ) + .join(", "), + ) + } else { + ( + span.shrink_to_hi(), + format!( + "<{}>", + std::iter::repeat("'static") + .take(count) + .collect::>() + .join(", ") + ), + ) + } } - } - _ => continue, - }; - err.span_suggestion_verbose( - span, + _ => continue, + }; + spans_suggs.push((span, sugg.to_string())); + } + err.multipart_suggestion_with_style( "consider using the `'static` lifetime", - sugg.to_string(), + spans_suggs, Applicability::MaybeIncorrect, + SuggestionStyle::ShowAlways, ); continue; } @@ -1986,8 +2018,17 @@ impl<'tcx> LifetimeContext<'_, 'tcx> { } } } - introduce_suggestion.push((span, sugg.to_string())); - err.multipart_suggestion(&msg, introduce_suggestion, Applicability::MaybeIncorrect); + for (span, sugg) in spans.iter().copied().zip(suggs.iter()) { + if let Some(sugg) = sugg { + introduce_suggestion.push((span, sugg.to_string())); + } + } + err.multipart_suggestion_with_style( + &msg, + introduce_suggestion, + Applicability::MaybeIncorrect, + SuggestionStyle::ShowAlways, + ); if should_break { break; } @@ -1995,68 +2036,86 @@ impl<'tcx> LifetimeContext<'_, 'tcx> { }; let lifetime_names: Vec<_> = lifetime_names.iter().collect(); - match (&lifetime_names[..], snippet.as_deref()) { - ([name], Some("&")) => { - suggest_existing(err, &name.as_str()[..], &|name| format!("&{} ", name)); - } - ([name], Some("'_")) => { - suggest_existing(err, &name.as_str()[..], &|n| n.to_string()); - } - ([name], Some("")) => { - suggest_existing(err, &name.as_str()[..], &|n| format!("{}, ", n).repeat(count)); - } - ([name], Some(snippet)) if !snippet.ends_with('>') => { - let f = |name: &str| { - format!( - "{}<{}>", - snippet, - std::iter::repeat(name.to_string()) - .take(count) - .collect::>() - .join(", ") - ) - }; - suggest_existing(err, &name.as_str()[..], &f); - } - ([], Some("&")) if count == 1 => { - suggest_new(err, "&'a "); - } - ([], Some("'_")) if count == 1 => { - suggest_new(err, "'a"); - } - ([], Some(snippet)) if !snippet.ends_with('>') => { - if snippet == "" { - // This happens when we have `type Bar<'a> = Foo` where we point at the space - // before `T`. We will suggest `type Bar<'a> = Foo<'a, T>`. - suggest_new( - err, - &std::iter::repeat("'a, ").take(count).collect::>().join(""), - ); - } else { - suggest_new( - err, - &format!( - "{}<{}>", - snippet, - std::iter::repeat("'a").take(count).collect::>().join(", ") - ), - ); + match &lifetime_names[..] { + [name] => { + let mut suggs: Vec String>>> = Vec::new(); + for (snippet, count) in snippets.iter().cloned().zip(counts.iter().copied()) { + if snippet == Some("&".to_string()) { + suggs.push(Some(Box::new(|name| format!("&{} ", name)))); + } else if snippet == Some("'_".to_string()) { + suggs.push(Some(Box::new(|n| n.to_string()))); + } else if snippet == Some("".to_string()) { + suggs.push(Some(Box::new(move |n| format!("{}, ", n).repeat(count)))); + } else if let Some(snippet) = snippet { + if !snippet.ends_with('>') { + suggs.push(Some(Box::new(move |name| { + format!( + "{}<{}>", + snippet, + std::iter::repeat(name.to_string()) + .take(count) + .collect::>() + .join(", ") + ) + }))); + } else { + suggs.push(None); + } + } else { + suggs.push(None); + } + } + suggest_existing(err, &name.as_str()[..], &suggs); + } + [] => { + let mut suggs: Vec> = Vec::new(); + for (snippet, count) in snippets.iter().cloned().zip(counts.iter().copied()) { + if snippet == Some("&".to_string()) { + suggs.push(Some("&'a ".to_string())); + } else if snippet == Some("'_".to_string()) { + suggs.push(Some("'a".to_string())); + } else if let Some(snippet) = snippet { + if snippet == "" { + suggs.push(Some( + std::iter::repeat("'a, ").take(count).collect::>().join(""), + )); + } else { + suggs.push(Some(format!( + "{}<{}>", + snippet, + std::iter::repeat("'a").take(count).collect::>().join(", ") + ))); + } + } else { + suggs.push(None); + } } + suggest_new(err, &suggs); } - (lts, ..) if lts.len() > 1 => { + lts if lts.len() > 1 => { err.span_note(lifetime_spans, "these named lifetimes are available to use"); - if Some("") == snippet.as_deref() { + + let mut spans_suggs: Vec<_> = Vec::new(); + for (span, snippet) in spans.iter().copied().zip(snippets.iter()) { + if Some("") == snippet.as_deref() { + spans_suggs.push((span, "'lifetime, ".to_string())); + } else if Some("&") == snippet.as_deref() { + spans_suggs.push((span, "&'lifetime ".to_string())); + } + } + + if spans_suggs.len() > 0 { // This happens when we have `Foo` where we point at the space before `T`, // but this can be confusing so we give a suggestion with placeholders. - err.span_suggestion_verbose( - span, + err.multipart_suggestion_with_style( "consider using one of the available lifetimes here", - "'lifetime, ".repeat(count), + spans_suggs, Applicability::HasPlaceholders, + SuggestionStyle::ShowAlways, ); } } - _ => {} + _ => unreachable!(), } } diff --git a/compiler/rustc_resolve/src/late/lifetimes.rs b/compiler/rustc_resolve/src/late/lifetimes.rs index 174df09cbdbb2..c81269a46b21a 100644 --- a/compiler/rustc_resolve/src/late/lifetimes.rs +++ b/compiler/rustc_resolve/src/late/lifetimes.rs @@ -2956,7 +2956,6 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { return; } - let span = lifetime_refs[0].span; let mut late_depth = 0; let mut scope = self.scope; let mut lifetime_names = FxHashSet::default(); @@ -3035,7 +3034,14 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { } }; - let mut err = self.report_missing_lifetime_specifiers(span, lifetime_refs.len()); + let mut spans: Vec<_> = lifetime_refs.iter().map(|lt| lt.span).collect(); + spans.sort(); + let mut spans_dedup = spans.clone(); + spans_dedup.dedup(); + let counts: Vec<_> = + spans_dedup.iter().map(|sp| spans.iter().filter(|nsp| *nsp == sp).count()).collect(); + + let mut err = self.report_missing_lifetime_specifiers(spans.clone(), lifetime_refs.len()); if let Some(params) = error { // If there's no lifetime available, suggest `'static`. @@ -3043,10 +3049,11 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { lifetime_names.insert(kw::StaticLifetime); } } + self.add_missing_lifetime_specifiers_label( &mut err, - span, - lifetime_refs.len(), + spans, + counts, &lifetime_names, lifetime_spans, error.unwrap_or(&[]), diff --git a/src/test/ui/issues/issue-84592.rs b/src/test/ui/issues/issue-84592.rs new file mode 100644 index 0000000000000..aa246aaa3d45e --- /dev/null +++ b/src/test/ui/issues/issue-84592.rs @@ -0,0 +1,17 @@ +/* Checks whether issue #84592 has been resolved. The issue was + * that in this example, there are two expected/missing lifetime + * parameters with *different spans*, leading to incorrect + * suggestions from rustc. + */ + +struct TwoLifetimes<'x, 'y> { + x: &'x (), + y: &'y (), +} + +fn two_lifetimes_needed(a: &(), b: &()) -> TwoLifetimes<'_, '_> { +//~^ ERROR missing lifetime specifiers [E0106] + TwoLifetimes { x: &(), y: &() } +} + +fn main() {} diff --git a/src/test/ui/issues/issue-84592.stderr b/src/test/ui/issues/issue-84592.stderr new file mode 100644 index 0000000000000..02f9241a6d2da --- /dev/null +++ b/src/test/ui/issues/issue-84592.stderr @@ -0,0 +1,17 @@ +error[E0106]: missing lifetime specifiers + --> $DIR/issue-84592.rs:12:57 + | +LL | fn two_lifetimes_needed(a: &(), b: &()) -> TwoLifetimes<'_, '_> { + | --- --- ^^ ^^ expected named lifetime parameter + | | + | expected named lifetime parameter + | + = help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from `a` or `b` +help: consider introducing a named lifetime parameter + | +LL | fn two_lifetimes_needed<'a>(a: &'a (), b: &'a ()) -> TwoLifetimes<'a, 'a> { + | ^^^^ ^^^^^^ ^^^^^^ ^^ ^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0106`. diff --git a/src/test/ui/return-elided-lifetime.rs b/src/test/ui/return-elided-lifetime.rs new file mode 100644 index 0000000000000..ca336bbb056d5 --- /dev/null +++ b/src/test/ui/return-elided-lifetime.rs @@ -0,0 +1,37 @@ +/* Checks all four scenarios possible in report_elision_failure() of + * rustc_resolve::late::lifetimes::LifetimeContext related to returning + * borrowed values, in various configurations. + */ + +fn f1() -> &i32 { loop {} } +//~^ ERROR missing lifetime specifier [E0106] +fn f1_() -> (&i32, &i32) { loop {} } +//~^ ERROR missing lifetime specifier [E0106] +//~^^ ERROR missing lifetime specifier [E0106] + +fn f2(a: i32, b: i32) -> &i32 { loop {} } +//~^ ERROR missing lifetime specifier [E0106] +fn f2_(a: i32, b: i32) -> (&i32, &i32) { loop {} } +//~^ ERROR missing lifetime specifier [E0106] +//~^^ ERROR missing lifetime specifier [E0106] + +struct S<'a, 'b> { a: &'a i32, b: &'b i32 } +fn f3(s: &S) -> &i32 { loop {} } +//~^ ERROR missing lifetime specifier [E0106] +fn f3_(s: &S, t: &S) -> (&i32, &i32) { loop {} } +//~^ ERROR missing lifetime specifier [E0106] +//~^^ ERROR missing lifetime specifier [E0106] + +fn f4<'a, 'b>(a: &'a i32, b: &'b i32) -> &i32 { loop {} } +//~^ ERROR missing lifetime specifier [E0106] +fn f4_<'a, 'b>(a: &'a i32, b: &'b i32) -> (&i32, &i32) { loop {} } +//~^ ERROR missing lifetime specifier [E0106] +//~^^ ERROR missing lifetime specifier [E0106] + +fn f5<'a>(a: &'a i32, b: &i32) -> &i32 { loop {} } +//~^ ERROR missing lifetime specifier [E0106] +fn f5_<'a>(a: &'a i32, b: &i32) -> (&i32, &i32) { loop {} } +//~^ ERROR missing lifetime specifier [E0106] +//~^^ ERROR missing lifetime specifier [E0106] + +fn main() {} diff --git a/src/test/ui/return-elided-lifetime.stderr b/src/test/ui/return-elided-lifetime.stderr new file mode 100644 index 0000000000000..888cd5e58abec --- /dev/null +++ b/src/test/ui/return-elided-lifetime.stderr @@ -0,0 +1,198 @@ +error[E0106]: missing lifetime specifier + --> $DIR/return-elided-lifetime.rs:6:12 + | +LL | fn f1() -> &i32 { loop {} } + | ^ expected named lifetime parameter + | + = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from +help: consider using the `'static` lifetime + | +LL | fn f1() -> &'static i32 { loop {} } + | ^^^^^^^^ + +error[E0106]: missing lifetime specifier + --> $DIR/return-elided-lifetime.rs:8:14 + | +LL | fn f1_() -> (&i32, &i32) { loop {} } + | ^ expected named lifetime parameter + | + = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from +help: consider using the `'static` lifetime + | +LL | fn f1_() -> (&'static i32, &i32) { loop {} } + | ^^^^^^^^ + +error[E0106]: missing lifetime specifier + --> $DIR/return-elided-lifetime.rs:8:20 + | +LL | fn f1_() -> (&i32, &i32) { loop {} } + | ^ expected named lifetime parameter + | + = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from +help: consider using the `'static` lifetime + | +LL | fn f1_() -> (&i32, &'static i32) { loop {} } + | ^^^^^^^^ + +error[E0106]: missing lifetime specifier + --> $DIR/return-elided-lifetime.rs:12:26 + | +LL | fn f2(a: i32, b: i32) -> &i32 { loop {} } + | ^ expected named lifetime parameter + | + = help: this function's return type contains a borrowed value with an elided lifetime, but the lifetime cannot be derived from the arguments +help: consider using the `'static` lifetime + | +LL | fn f2(a: i32, b: i32) -> &'static i32 { loop {} } + | ^^^^^^^^ + +error[E0106]: missing lifetime specifier + --> $DIR/return-elided-lifetime.rs:14:28 + | +LL | fn f2_(a: i32, b: i32) -> (&i32, &i32) { loop {} } + | ^ expected named lifetime parameter + | + = help: this function's return type contains a borrowed value with an elided lifetime, but the lifetime cannot be derived from the arguments +help: consider using the `'static` lifetime + | +LL | fn f2_(a: i32, b: i32) -> (&'static i32, &i32) { loop {} } + | ^^^^^^^^ + +error[E0106]: missing lifetime specifier + --> $DIR/return-elided-lifetime.rs:14:34 + | +LL | fn f2_(a: i32, b: i32) -> (&i32, &i32) { loop {} } + | ^ expected named lifetime parameter + | + = help: this function's return type contains a borrowed value with an elided lifetime, but the lifetime cannot be derived from the arguments +help: consider using the `'static` lifetime + | +LL | fn f2_(a: i32, b: i32) -> (&i32, &'static i32) { loop {} } + | ^^^^^^^^ + +error[E0106]: missing lifetime specifier + --> $DIR/return-elided-lifetime.rs:19:17 + | +LL | fn f3(s: &S) -> &i32 { loop {} } + | -- ^ expected named lifetime parameter + | + = help: this function's return type contains a borrowed value, but the signature does not say which one of `s`'s 3 lifetimes it is borrowed from +help: consider introducing a named lifetime parameter + | +LL | fn f3<'a>(s: &'a S) -> &'a i32 { loop {} } + | ^^^^ ^^^^^ ^^^ + +error[E0106]: missing lifetime specifier + --> $DIR/return-elided-lifetime.rs:21:26 + | +LL | fn f3_(s: &S, t: &S) -> (&i32, &i32) { loop {} } + | -- -- ^ expected named lifetime parameter + | + = help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from one of `s`'s 3 lifetimes or one of `t`'s 3 lifetimes +help: consider introducing a named lifetime parameter + | +LL | fn f3_<'a>(s: &'a S, t: &'a S) -> (&'a i32, &i32) { loop {} } + | ^^^^ ^^^^^ ^^^^^ ^^^ + +error[E0106]: missing lifetime specifier + --> $DIR/return-elided-lifetime.rs:21:32 + | +LL | fn f3_(s: &S, t: &S) -> (&i32, &i32) { loop {} } + | -- -- ^ expected named lifetime parameter + | + = help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from one of `s`'s 3 lifetimes or one of `t`'s 3 lifetimes +help: consider introducing a named lifetime parameter + | +LL | fn f3_<'a>(s: &'a S, t: &'a S) -> (&i32, &'a i32) { loop {} } + | ^^^^ ^^^^^ ^^^^^ ^^^ + +error[E0106]: missing lifetime specifier + --> $DIR/return-elided-lifetime.rs:25:42 + | +LL | fn f4<'a, 'b>(a: &'a i32, b: &'b i32) -> &i32 { loop {} } + | ------- ------- ^ expected named lifetime parameter + | + = help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from `a` or `b` +note: these named lifetimes are available to use + --> $DIR/return-elided-lifetime.rs:25:7 + | +LL | fn f4<'a, 'b>(a: &'a i32, b: &'b i32) -> &i32 { loop {} } + | ^^ ^^ +help: consider using one of the available lifetimes here + | +LL | fn f4<'a, 'b>(a: &'a i32, b: &'b i32) -> &'lifetime i32 { loop {} } + | ^^^^^^^^^^ + +error[E0106]: missing lifetime specifier + --> $DIR/return-elided-lifetime.rs:27:44 + | +LL | fn f4_<'a, 'b>(a: &'a i32, b: &'b i32) -> (&i32, &i32) { loop {} } + | ------- ------- ^ expected named lifetime parameter + | + = help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from `a` or `b` +note: these named lifetimes are available to use + --> $DIR/return-elided-lifetime.rs:27:8 + | +LL | fn f4_<'a, 'b>(a: &'a i32, b: &'b i32) -> (&i32, &i32) { loop {} } + | ^^ ^^ +help: consider using one of the available lifetimes here + | +LL | fn f4_<'a, 'b>(a: &'a i32, b: &'b i32) -> (&'lifetime i32, &i32) { loop {} } + | ^^^^^^^^^^ + +error[E0106]: missing lifetime specifier + --> $DIR/return-elided-lifetime.rs:27:50 + | +LL | fn f4_<'a, 'b>(a: &'a i32, b: &'b i32) -> (&i32, &i32) { loop {} } + | ------- ------- ^ expected named lifetime parameter + | + = help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from `a` or `b` +note: these named lifetimes are available to use + --> $DIR/return-elided-lifetime.rs:27:8 + | +LL | fn f4_<'a, 'b>(a: &'a i32, b: &'b i32) -> (&i32, &i32) { loop {} } + | ^^ ^^ +help: consider using one of the available lifetimes here + | +LL | fn f4_<'a, 'b>(a: &'a i32, b: &'b i32) -> (&i32, &'lifetime i32) { loop {} } + | ^^^^^^^^^^ + +error[E0106]: missing lifetime specifier + --> $DIR/return-elided-lifetime.rs:31:35 + | +LL | fn f5<'a>(a: &'a i32, b: &i32) -> &i32 { loop {} } + | ------- ---- ^ expected named lifetime parameter + | + = help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from `a` or `b` +help: consider using the `'a` lifetime + | +LL | fn f5<'a>(a: &'a i32, b: &i32) -> &'a i32 { loop {} } + | ^^^ + +error[E0106]: missing lifetime specifier + --> $DIR/return-elided-lifetime.rs:33:37 + | +LL | fn f5_<'a>(a: &'a i32, b: &i32) -> (&i32, &i32) { loop {} } + | ------- ---- ^ expected named lifetime parameter + | + = help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from `a` or `b` +help: consider using the `'a` lifetime + | +LL | fn f5_<'a>(a: &'a i32, b: &i32) -> (&'a i32, &i32) { loop {} } + | ^^^ + +error[E0106]: missing lifetime specifier + --> $DIR/return-elided-lifetime.rs:33:43 + | +LL | fn f5_<'a>(a: &'a i32, b: &i32) -> (&i32, &i32) { loop {} } + | ------- ---- ^ expected named lifetime parameter + | + = help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from `a` or `b` +help: consider using the `'a` lifetime + | +LL | fn f5_<'a>(a: &'a i32, b: &i32) -> (&i32, &'a i32) { loop {} } + | ^^^ + +error: aborting due to 15 previous errors + +For more information about this error, try `rustc --explain E0106`. diff --git a/src/test/ui/suggestions/missing-lt-for-hrtb.stderr b/src/test/ui/suggestions/missing-lt-for-hrtb.stderr index 2cb63500e48b9..a7a44b511db8d 100644 --- a/src/test/ui/suggestions/missing-lt-for-hrtb.stderr +++ b/src/test/ui/suggestions/missing-lt-for-hrtb.stderr @@ -44,6 +44,10 @@ note: these named lifetimes are available to use | LL | struct V<'a>(&'a dyn for<'b> Fn(&X) -> &X); | ^^ ^^ +help: consider using one of the available lifetimes here + | +LL | struct V<'a>(&'a dyn for<'b> Fn(&X) -> &'lifetime X); + | ^^^^^^^^^^ error[E0106]: missing lifetime specifier --> $DIR/missing-lt-for-hrtb.rs:5:41 diff --git a/src/tools/tidy/src/ui_tests.rs b/src/tools/tidy/src/ui_tests.rs index 8334bc68ae729..791489b3960a5 100644 --- a/src/tools/tidy/src/ui_tests.rs +++ b/src/tools/tidy/src/ui_tests.rs @@ -8,7 +8,7 @@ use std::path::Path; const ENTRY_LIMIT: usize = 1000; // FIXME: The following limits should be reduced eventually. const ROOT_ENTRY_LIMIT: usize = 1388; -const ISSUES_ENTRY_LIMIT: usize = 2551; +const ISSUES_ENTRY_LIMIT: usize = 2557; fn check_entries(path: &Path, bad: &mut bool) { let dirs = walkdir::WalkDir::new(&path.join("test/ui")) From 8a878f07e974232863c8c8c143970dd8a370f751 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 9 May 2021 14:56:19 +0200 Subject: [PATCH 02/13] ensure failing promoteds in const/static bodies are handled correctly --- .../const-eval/promoted_errors.noopt.stderr | 10 ++++---- .../const-eval/promoted_errors.opt.stderr | 10 ++++---- ...ted_errors.opt_with_overflow_checks.stderr | 10 ++++---- .../ui/consts/const-eval/promoted_errors.rs | 24 ++++++++++++++++++- 4 files changed, 38 insertions(+), 16 deletions(-) diff --git a/src/test/ui/consts/const-eval/promoted_errors.noopt.stderr b/src/test/ui/consts/const-eval/promoted_errors.noopt.stderr index 6f266801bdb4a..77e7d48407186 100644 --- a/src/test/ui/consts/const-eval/promoted_errors.noopt.stderr +++ b/src/test/ui/consts/const-eval/promoted_errors.noopt.stderr @@ -1,12 +1,12 @@ warning: any use of this value will cause an error - --> $DIR/promoted_errors.rs:13:5 + --> $DIR/promoted_errors.rs:15:5 | LL | 0 - 1 | ^^^^^ | | | attempt to compute `0_u32 - 1_u32`, which would overflow - | inside `overflow` at $DIR/promoted_errors.rs:13:5 - | inside `X` at $DIR/promoted_errors.rs:33:29 + | inside `overflow` at $DIR/promoted_errors.rs:15:5 + | inside `X` at $DIR/promoted_errors.rs:38:29 ... LL | / const X: () = { LL | | let _x: &'static u32 = &overflow(); @@ -18,7 +18,7 @@ LL | | }; | |__- | note: the lint level is defined here - --> $DIR/promoted_errors.rs:9:9 + --> $DIR/promoted_errors.rs:11:9 | LL | #![warn(const_err, arithmetic_overflow, unconditional_panic)] | ^^^^^^^^^ @@ -26,7 +26,7 @@ LL | #![warn(const_err, arithmetic_overflow, unconditional_panic)] = note: for more information, see issue #71800 warning: any use of this value will cause an error - --> $DIR/promoted_errors.rs:33:28 + --> $DIR/promoted_errors.rs:38:28 | LL | / const X: () = { LL | | let _x: &'static u32 = &overflow(); diff --git a/src/test/ui/consts/const-eval/promoted_errors.opt.stderr b/src/test/ui/consts/const-eval/promoted_errors.opt.stderr index 892f57bfdfc1b..6b17346e6ecd1 100644 --- a/src/test/ui/consts/const-eval/promoted_errors.opt.stderr +++ b/src/test/ui/consts/const-eval/promoted_errors.opt.stderr @@ -1,12 +1,12 @@ warning: any use of this value will cause an error - --> $DIR/promoted_errors.rs:18:5 + --> $DIR/promoted_errors.rs:20:5 | LL | 1 / 0 | ^^^^^ | | | attempt to divide `1_i32` by zero - | inside `div_by_zero1` at $DIR/promoted_errors.rs:18:5 - | inside `X` at $DIR/promoted_errors.rs:36:29 + | inside `div_by_zero1` at $DIR/promoted_errors.rs:20:5 + | inside `X` at $DIR/promoted_errors.rs:41:29 ... LL | / const X: () = { LL | | let _x: &'static u32 = &overflow(); @@ -18,7 +18,7 @@ LL | | }; | |__- | note: the lint level is defined here - --> $DIR/promoted_errors.rs:9:9 + --> $DIR/promoted_errors.rs:11:9 | LL | #![warn(const_err, arithmetic_overflow, unconditional_panic)] | ^^^^^^^^^ @@ -26,7 +26,7 @@ LL | #![warn(const_err, arithmetic_overflow, unconditional_panic)] = note: for more information, see issue #71800 warning: any use of this value will cause an error - --> $DIR/promoted_errors.rs:36:28 + --> $DIR/promoted_errors.rs:41:28 | LL | / const X: () = { LL | | let _x: &'static u32 = &overflow(); diff --git a/src/test/ui/consts/const-eval/promoted_errors.opt_with_overflow_checks.stderr b/src/test/ui/consts/const-eval/promoted_errors.opt_with_overflow_checks.stderr index 6f266801bdb4a..77e7d48407186 100644 --- a/src/test/ui/consts/const-eval/promoted_errors.opt_with_overflow_checks.stderr +++ b/src/test/ui/consts/const-eval/promoted_errors.opt_with_overflow_checks.stderr @@ -1,12 +1,12 @@ warning: any use of this value will cause an error - --> $DIR/promoted_errors.rs:13:5 + --> $DIR/promoted_errors.rs:15:5 | LL | 0 - 1 | ^^^^^ | | | attempt to compute `0_u32 - 1_u32`, which would overflow - | inside `overflow` at $DIR/promoted_errors.rs:13:5 - | inside `X` at $DIR/promoted_errors.rs:33:29 + | inside `overflow` at $DIR/promoted_errors.rs:15:5 + | inside `X` at $DIR/promoted_errors.rs:38:29 ... LL | / const X: () = { LL | | let _x: &'static u32 = &overflow(); @@ -18,7 +18,7 @@ LL | | }; | |__- | note: the lint level is defined here - --> $DIR/promoted_errors.rs:9:9 + --> $DIR/promoted_errors.rs:11:9 | LL | #![warn(const_err, arithmetic_overflow, unconditional_panic)] | ^^^^^^^^^ @@ -26,7 +26,7 @@ LL | #![warn(const_err, arithmetic_overflow, unconditional_panic)] = note: for more information, see issue #71800 warning: any use of this value will cause an error - --> $DIR/promoted_errors.rs:33:28 + --> $DIR/promoted_errors.rs:38:28 | LL | / const X: () = { LL | | let _x: &'static u32 = &overflow(); diff --git a/src/test/ui/consts/const-eval/promoted_errors.rs b/src/test/ui/consts/const-eval/promoted_errors.rs index 7840f67c216c0..5bafea1ed46bd 100644 --- a/src/test/ui/consts/const-eval/promoted_errors.rs +++ b/src/test/ui/consts/const-eval/promoted_errors.rs @@ -6,6 +6,8 @@ // build-pass // ignore-pass (test emits codegen-time warnings and verifies that they are not errors) +//! This test ensures that when we promote code that fails to evaluate, the build still succeeds. + #![warn(const_err, arithmetic_overflow, unconditional_panic)] // The only way to have promoteds that fail is in `const fn` called from `const`/`static`. @@ -29,6 +31,9 @@ const fn oob() -> i32 { [1, 2, 3][4] } +// An unused constant containing failing promoteds. +// This should work as long as `const_err` can be turned into just a warning; +// once it turns into a hard error, just remove `X`. const X: () = { let _x: &'static u32 = &overflow(); //[opt_with_overflow_checks,noopt]~^ WARN any use of this value will cause an error @@ -41,4 +46,21 @@ const X: () = { let _x: &'static i32 = &oob(); }; -fn main() {} +const fn mk_false() -> bool { false } + +// An actually used constant referencing failing promoteds in dead code. +// This needs to always work. +const Y: () = { + if mk_false() { + let _x: &'static u32 = &overflow(); + let _x: &'static i32 = &div_by_zero1(); + let _x: &'static i32 = &div_by_zero2(); + let _x: &'static i32 = &div_by_zero3(); + let _x: &'static i32 = &oob(); + } + () +}; + +fn main() { + let _y = Y; +} From 7a01160ce484325b6de27cf56400039d1b2c3721 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 9 May 2021 16:07:00 +0200 Subject: [PATCH 03/13] more erroneous-const tests --- .../ui/consts/const-eval/erroneous-const.rs | 2 + .../consts/const-eval/erroneous-const.stderr | 6 +-- .../ui/consts/const-eval/erroneous-const2.rs | 21 +++++++++++ .../consts/const-eval/erroneous-const2.stderr | 37 +++++++++++++++++++ 4 files changed, 63 insertions(+), 3 deletions(-) create mode 100644 src/test/ui/consts/const-eval/erroneous-const2.rs create mode 100644 src/test/ui/consts/const-eval/erroneous-const2.stderr diff --git a/src/test/ui/consts/const-eval/erroneous-const.rs b/src/test/ui/consts/const-eval/erroneous-const.rs index b79ce4a523f96..bee5a7cb3ba72 100644 --- a/src/test/ui/consts/const-eval/erroneous-const.rs +++ b/src/test/ui/consts/const-eval/erroneous-const.rs @@ -10,6 +10,8 @@ impl PrintName { const fn no_codegen() { if false { + // This bad constant is only used in dead code in a no-codegen function... and yet we still + // must make sure that the build fails. let _ = PrintName::::VOID; //~ERROR could not evaluate static initializer } } diff --git a/src/test/ui/consts/const-eval/erroneous-const.stderr b/src/test/ui/consts/const-eval/erroneous-const.stderr index 16ed596628bf5..7e2a60929c73d 100644 --- a/src/test/ui/consts/const-eval/erroneous-const.stderr +++ b/src/test/ui/consts/const-eval/erroneous-const.stderr @@ -27,16 +27,16 @@ LL | #![warn(const_err, unconditional_panic)] = note: for more information, see issue #71800 error[E0080]: could not evaluate static initializer - --> $DIR/erroneous-const.rs:13:17 + --> $DIR/erroneous-const.rs:15:17 | LL | let _ = PrintName::::VOID; | ^^^^^^^^^^^^^^^^^^^^ | | | referenced constant has errors - | inside `no_codegen::` at $DIR/erroneous-const.rs:13:17 + | inside `no_codegen::` at $DIR/erroneous-const.rs:15:17 ... LL | pub static FOO: () = no_codegen::(); - | ------------------- inside `FOO` at $DIR/erroneous-const.rs:17:22 + | ------------------- inside `FOO` at $DIR/erroneous-const.rs:19:22 error: aborting due to previous error; 2 warnings emitted diff --git a/src/test/ui/consts/const-eval/erroneous-const2.rs b/src/test/ui/consts/const-eval/erroneous-const2.rs new file mode 100644 index 0000000000000..aa0f093bf6293 --- /dev/null +++ b/src/test/ui/consts/const-eval/erroneous-const2.rs @@ -0,0 +1,21 @@ +//! Make sure we error on erroneous consts even if they are unused. +#![warn(const_err, unconditional_panic)] + +struct PrintName(T); +impl PrintName { + const VOID: () = [()][2]; //~WARN any use of this value will cause an error + //~^ WARN this operation will panic at runtime + //~| WARN this was previously accepted by the compiler but is being phased out +} + +pub static FOO: () = { + if false { + // This bad constant is only used in dead code in a static initializer... and yet we still + // must make sure that the build fails. + let _ = PrintName::::VOID; //~ERROR could not evaluate static initializer + } +}; + +fn main() { + FOO +} diff --git a/src/test/ui/consts/const-eval/erroneous-const2.stderr b/src/test/ui/consts/const-eval/erroneous-const2.stderr new file mode 100644 index 0000000000000..813d3ee249fb2 --- /dev/null +++ b/src/test/ui/consts/const-eval/erroneous-const2.stderr @@ -0,0 +1,37 @@ +warning: this operation will panic at runtime + --> $DIR/erroneous-const2.rs:6:22 + | +LL | const VOID: () = [()][2]; + | ^^^^^^^ index out of bounds: the length is 1 but the index is 2 + | +note: the lint level is defined here + --> $DIR/erroneous-const2.rs:2:20 + | +LL | #![warn(const_err, unconditional_panic)] + | ^^^^^^^^^^^^^^^^^^^ + +warning: any use of this value will cause an error + --> $DIR/erroneous-const2.rs:6:22 + | +LL | const VOID: () = [()][2]; + | -----------------^^^^^^^- + | | + | index out of bounds: the length is 1 but the index is 2 + | +note: the lint level is defined here + --> $DIR/erroneous-const2.rs:2:9 + | +LL | #![warn(const_err, unconditional_panic)] + | ^^^^^^^^^ + = 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[E0080]: could not evaluate static initializer + --> $DIR/erroneous-const2.rs:15:17 + | +LL | let _ = PrintName::::VOID; + | ^^^^^^^^^^^^^^^^^^^^^^ referenced constant has errors + +error: aborting due to previous error; 2 warnings emitted + +For more information about this error, try `rustc --explain E0080`. From 8f14592aa268a2712f7e229a792c92efcf5b1a85 Mon Sep 17 00:00:00 2001 From: Paul Trojahn Date: Fri, 7 May 2021 19:09:15 +0200 Subject: [PATCH 04/13] Improve "panic message is not a string literal" warning This warning always referenced panic! even in case of an assert. Related to #84656 --- compiler/rustc_lint/src/non_fmt_panic.rs | 17 ++++++++----- src/test/ui/non-fmt-panic.rs | 2 ++ src/test/ui/non-fmt-panic.stderr | 32 +++++++++++++++++++++--- 3 files changed, 42 insertions(+), 9 deletions(-) diff --git a/compiler/rustc_lint/src/non_fmt_panic.rs b/compiler/rustc_lint/src/non_fmt_panic.rs index 5a27135581747..070bc3522a453 100644 --- a/compiler/rustc_lint/src/non_fmt_panic.rs +++ b/compiler/rustc_lint/src/non_fmt_panic.rs @@ -4,7 +4,7 @@ use rustc_errors::{pluralize, Applicability}; use rustc_hir as hir; use rustc_middle::ty; use rustc_parse_format::{ParseMode, Parser, Piece}; -use rustc_span::{sym, symbol::kw, InnerSpan, Span, Symbol}; +use rustc_span::{hygiene, sym, symbol::kw, symbol::SymbolStr, InnerSpan, Span, Symbol}; declare_lint! { /// The `non_fmt_panic` lint detects `panic!(..)` invocations where the first @@ -67,7 +67,7 @@ fn check_panic<'tcx>(cx: &LateContext<'tcx>, f: &'tcx hir::Expr<'tcx>, arg: &'tc // The argument is *not* a string literal. - let (span, panic) = panic_call(cx, f); + let (span, panic, symbol_str) = panic_call(cx, f); // Find the span of the argument to `panic!()`, before expansion in the // case of `panic!(some_macro!())`. @@ -95,7 +95,7 @@ fn check_panic<'tcx>(cx: &LateContext<'tcx>, f: &'tcx hir::Expr<'tcx>, arg: &'tc } if arg_macro.map_or(false, |id| cx.tcx.is_diagnostic_item(sym::format_macro, id)) { // A case of `panic!(format!(..))`. - l.note("the panic!() macro supports formatting, so there's no need for the format!() macro here"); + l.note(format!("the {}!() macro supports formatting, so there's no need for the format!() macro here", symbol_str).as_str()); if let Some((open, close, _)) = find_delimiters(cx, arg_span) { l.multipart_suggestion( "remove the `format!(..)` macro call", @@ -160,7 +160,7 @@ fn check_panic_str<'tcx>( Parser::new(fmt.as_ref(), style, snippet.clone(), false, ParseMode::Format); let n_arguments = (&mut fmt_parser).filter(|a| matches!(a, Piece::NextArgument(_))).count(); - let (span, _) = panic_call(cx, f); + let (span, _, _) = panic_call(cx, f); if n_arguments > 0 && fmt_parser.errors.is_empty() { let arg_spans: Vec<_> = match &fmt_parser.arg_places[..] { @@ -230,7 +230,7 @@ fn find_delimiters<'tcx>(cx: &LateContext<'tcx>, span: Span) -> Option<(Span, Sp )) } -fn panic_call<'tcx>(cx: &LateContext<'tcx>, f: &'tcx hir::Expr<'tcx>) -> (Span, Symbol) { +fn panic_call<'tcx>(cx: &LateContext<'tcx>, f: &'tcx hir::Expr<'tcx>) -> (Span, Symbol, SymbolStr) { let mut expn = f.span.ctxt().outer_expn_data(); let mut panic_macro = kw::Empty; @@ -248,5 +248,10 @@ fn panic_call<'tcx>(cx: &LateContext<'tcx>, f: &'tcx hir::Expr<'tcx>) -> (Span, } } - (expn.call_site, panic_macro) + let macro_symbol = if let hygiene::ExpnKind::Macro(_, symbol) = expn.kind { + symbol + } else { + Symbol::intern("panic") + }; + (expn.call_site, panic_macro, macro_symbol.as_str()) } diff --git a/src/test/ui/non-fmt-panic.rs b/src/test/ui/non-fmt-panic.rs index c80a90b3eaaac..77390aae2d688 100644 --- a/src/test/ui/non-fmt-panic.rs +++ b/src/test/ui/non-fmt-panic.rs @@ -36,6 +36,8 @@ fn main() { panic!(a!()); //~ WARN panic message is not a string literal panic!(format!("{}", 1)); //~ WARN panic message is not a string literal + assert!(false, format!("{}", 1)); //~ WARN panic message is not a string literal + debug_assert!(false, format!("{}", 1)); //~ WARN panic message is not a string literal panic![123]; //~ WARN panic message is not a string literal panic!{123}; //~ WARN panic message is not a string literal diff --git a/src/test/ui/non-fmt-panic.stderr b/src/test/ui/non-fmt-panic.stderr index 7a333b3e76abe..3278eb5f0238e 100644 --- a/src/test/ui/non-fmt-panic.stderr +++ b/src/test/ui/non-fmt-panic.stderr @@ -213,7 +213,33 @@ LL | panic!("{}", 1); | -- -- warning: panic message is not a string literal - --> $DIR/non-fmt-panic.rs:40:12 + --> $DIR/non-fmt-panic.rs:39:20 + | +LL | assert!(false, format!("{}", 1)); + | ^^^^^^^^^^^^^^^^ + | + = note: this is no longer accepted in Rust 2021 + = note: the assert!() macro supports formatting, so there's no need for the format!() macro here +help: remove the `format!(..)` macro call + | +LL | assert!(false, "{}", 1); + | -- -- + +warning: panic message is not a string literal + --> $DIR/non-fmt-panic.rs:40:26 + | +LL | debug_assert!(false, format!("{}", 1)); + | ^^^^^^^^^^^^^^^^ + | + = note: this is no longer accepted in Rust 2021 + = note: the debug_assert!() macro supports formatting, so there's no need for the format!() macro here +help: remove the `format!(..)` macro call + | +LL | debug_assert!(false, "{}", 1); + | -- -- + +warning: panic message is not a string literal + --> $DIR/non-fmt-panic.rs:42:12 | LL | panic![123]; | ^^^ @@ -229,7 +255,7 @@ LL | std::panic::panic_any(123); | ^^^^^^^^^^^^^^^^^^^^^^ ^ warning: panic message is not a string literal - --> $DIR/non-fmt-panic.rs:41:12 + --> $DIR/non-fmt-panic.rs:43:12 | LL | panic!{123}; | ^^^ @@ -244,5 +270,5 @@ help: or use std::panic::panic_any instead LL | std::panic::panic_any(123); | ^^^^^^^^^^^^^^^^^^^^^^ ^ -warning: 18 warnings emitted +warning: 20 warnings emitted From 380bbe8d478e2252b60b79b82f285b9464227f5c Mon Sep 17 00:00:00 2001 From: ltdk Date: Sat, 8 May 2021 20:18:44 -0400 Subject: [PATCH 05/13] Make unchecked_{add,sub,mul} inherent methods unstably const --- library/core/src/lib.rs | 1 + library/core/src/num/int_macros.rs | 15 +++++++++------ library/core/src/num/uint_macros.rs | 15 +++++++++------ 3 files changed, 19 insertions(+), 12 deletions(-) diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 0e2c140c367a9..71008381475d3 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -77,6 +77,7 @@ #![feature(const_float_classify)] #![feature(const_float_bits_conv)] #![feature(const_int_unchecked_arith)] +#![feature(const_inherent_unchecked_arith)] #![feature(const_mut_refs)] #![feature(const_refs_to_cell)] #![feature(const_panic)] diff --git a/library/core/src/num/int_macros.rs b/library/core/src/num/int_macros.rs index 4b341132e31ec..47b2b30563c3a 100644 --- a/library/core/src/num/int_macros.rs +++ b/library/core/src/num/int_macros.rs @@ -412,12 +412,13 @@ macro_rules! int_impl { #[unstable( feature = "unchecked_math", reason = "niche optimization path", - issue = "none", + issue = "85122", )] #[must_use = "this returns the result of the operation, \ without modifying the original"] + #[rustc_const_unstable(feature = "const_inherent_unchecked_arith", issue = "85122")] #[inline(always)] - pub unsafe fn unchecked_add(self, rhs: Self) -> Self { + pub const unsafe fn unchecked_add(self, rhs: Self) -> Self { // SAFETY: the caller must uphold the safety contract for // `unchecked_add`. unsafe { intrinsics::unchecked_add(self, rhs) } @@ -450,12 +451,13 @@ macro_rules! int_impl { #[unstable( feature = "unchecked_math", reason = "niche optimization path", - issue = "none", + issue = "85122", )] #[must_use = "this returns the result of the operation, \ without modifying the original"] + #[rustc_const_unstable(feature = "const_inherent_unchecked_arith", issue = "85122")] #[inline(always)] - pub unsafe fn unchecked_sub(self, rhs: Self) -> Self { + pub const unsafe fn unchecked_sub(self, rhs: Self) -> Self { // SAFETY: the caller must uphold the safety contract for // `unchecked_sub`. unsafe { intrinsics::unchecked_sub(self, rhs) } @@ -488,12 +490,13 @@ macro_rules! int_impl { #[unstable( feature = "unchecked_math", reason = "niche optimization path", - issue = "none", + issue = "85122", )] #[must_use = "this returns the result of the operation, \ without modifying the original"] + #[rustc_const_unstable(feature = "const_inherent_unchecked_arith", issue = "85122")] #[inline(always)] - pub unsafe fn unchecked_mul(self, rhs: Self) -> Self { + pub const unsafe fn unchecked_mul(self, rhs: Self) -> Self { // SAFETY: the caller must uphold the safety contract for // `unchecked_mul`. unsafe { intrinsics::unchecked_mul(self, rhs) } diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs index 08d9161eff112..f9fd28b6a8c24 100644 --- a/library/core/src/num/uint_macros.rs +++ b/library/core/src/num/uint_macros.rs @@ -422,12 +422,13 @@ macro_rules! uint_impl { #[unstable( feature = "unchecked_math", reason = "niche optimization path", - issue = "none", + issue = "85122", )] #[must_use = "this returns the result of the operation, \ without modifying the original"] + #[rustc_const_unstable(feature = "const_inherent_unchecked_arith", issue = "85122")] #[inline(always)] - pub unsafe fn unchecked_add(self, rhs: Self) -> Self { + pub const unsafe fn unchecked_add(self, rhs: Self) -> Self { // SAFETY: the caller must uphold the safety contract for // `unchecked_add`. unsafe { intrinsics::unchecked_add(self, rhs) } @@ -460,12 +461,13 @@ macro_rules! uint_impl { #[unstable( feature = "unchecked_math", reason = "niche optimization path", - issue = "none", + issue = "85122", )] #[must_use = "this returns the result of the operation, \ without modifying the original"] + #[rustc_const_unstable(feature = "const_inherent_unchecked_arith", issue = "85122")] #[inline(always)] - pub unsafe fn unchecked_sub(self, rhs: Self) -> Self { + pub const unsafe fn unchecked_sub(self, rhs: Self) -> Self { // SAFETY: the caller must uphold the safety contract for // `unchecked_sub`. unsafe { intrinsics::unchecked_sub(self, rhs) } @@ -498,12 +500,13 @@ macro_rules! uint_impl { #[unstable( feature = "unchecked_math", reason = "niche optimization path", - issue = "none", + issue = "85122", )] #[must_use = "this returns the result of the operation, \ without modifying the original"] + #[rustc_const_unstable(feature = "const_inherent_unchecked_arith", issue = "85122")] #[inline(always)] - pub unsafe fn unchecked_mul(self, rhs: Self) -> Self { + pub const unsafe fn unchecked_mul(self, rhs: Self) -> Self { // SAFETY: the caller must uphold the safety contract for // `unchecked_mul`. unsafe { intrinsics::unchecked_mul(self, rhs) } From 3c0c3874fc75c730efba0702579bfeef34eecf31 Mon Sep 17 00:00:00 2001 From: Fabian Wolff Date: Sun, 9 May 2021 22:31:49 +0200 Subject: [PATCH 06/13] Implement @jackh726's suggestions --- .../rustc_resolve/src/late/diagnostics.rs | 131 ++++++++---------- compiler/rustc_resolve/src/late/lifetimes.rs | 9 +- .../ui/{issues => suggestions}/issue-84592.rs | 0 .../issue-84592.stderr | 0 src/tools/tidy/src/ui_tests.rs | 2 +- 5 files changed, 65 insertions(+), 77 deletions(-) rename src/test/ui/{issues => suggestions}/issue-84592.rs (100%) rename src/test/ui/{issues => suggestions}/issue-84592.stderr (100%) diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index ca4873bd51570..9b82d8afd2be1 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -1821,21 +1821,19 @@ impl<'tcx> LifetimeContext<'_, 'tcx> { crate fn add_missing_lifetime_specifiers_label( &self, err: &mut DiagnosticBuilder<'_>, - spans: Vec, - counts: Vec, + spans_with_counts: Vec<(Span, usize)>, lifetime_names: &FxHashSet, lifetime_spans: Vec, params: &[ElisionFailureInfo], ) { - let snippets: Vec> = spans + let snippets: Vec> = spans_with_counts .iter() - .copied() - .map(|span| self.tcx.sess.source_map().span_to_snippet(span).ok()) + .map(|(span, _)| self.tcx.sess.source_map().span_to_snippet(*span).ok()) .collect(); - for (span, count) in spans.iter().zip(counts.iter()) { + for (span, count) in &spans_with_counts { err.span_label( - span.clone(), + *span, format!( "expected {} lifetime parameter{}", if *count == 1 { "named".to_string() } else { count.to_string() }, @@ -1847,7 +1845,7 @@ impl<'tcx> LifetimeContext<'_, 'tcx> { let suggest_existing = |err: &mut DiagnosticBuilder<'_>, name: &str, - formatters: &Vec String>>>| { + formatters: Vec String>>>| { if let Some(MissingLifetimeSpot::HigherRanked { span: for_span, span_type }) = self.missing_named_lifetime_spots.iter().rev().next() { @@ -1892,9 +1890,9 @@ impl<'tcx> LifetimeContext<'_, 'tcx> { } } introduce_suggestion.push((*for_span, for_sugg)); - for (span, formatter) in spans.iter().copied().zip(formatters.iter()) { + for ((span, _), formatter) in spans_with_counts.iter().zip(formatters.iter()) { if let Some(formatter) = formatter { - introduce_suggestion.push((span, formatter(<_name))); + introduce_suggestion.push((*span, formatter(<_name))); } } err.multipart_suggestion_with_style( @@ -1905,12 +1903,12 @@ impl<'tcx> LifetimeContext<'_, 'tcx> { ); } - let mut spans_suggs: Vec<_> = Vec::new(); - for (span, fmt) in spans.iter().copied().zip(formatters.iter()) { - if let Some(formatter) = fmt { - spans_suggs.push((span, formatter(name))); - } - } + let spans_suggs: Vec<_> = formatters + .into_iter() + .filter_map(|fmt| fmt) + .zip(spans_with_counts.iter()) + .map(|(formatter, (span, _))| (*span, formatter(name))) + .collect(); err.multipart_suggestion_with_style( &format!( "consider using the `{}` lifetime", @@ -1921,7 +1919,7 @@ impl<'tcx> LifetimeContext<'_, 'tcx> { SuggestionStyle::ShowAlways, ); }; - let suggest_new = |err: &mut DiagnosticBuilder<'_>, suggs: &Vec>| { + let suggest_new = |err: &mut DiagnosticBuilder<'_>, suggs: Vec>| { for missing in self.missing_named_lifetime_spots.iter().rev() { let mut introduce_suggestion = vec![]; let msg; @@ -1967,8 +1965,8 @@ impl<'tcx> LifetimeContext<'_, 'tcx> { } MissingLifetimeSpot::Static => { let mut spans_suggs = Vec::new(); - for ((span, snippet), count) in - spans.iter().copied().zip(snippets.iter()).zip(counts.iter().copied()) + for ((span, count), snippet) in + spans_with_counts.iter().copied().zip(snippets.iter()) { let (span, sugg) = match snippet.as_deref() { Some("&") => (span.shrink_to_hi(), "'static ".to_owned()), @@ -2018,7 +2016,7 @@ impl<'tcx> LifetimeContext<'_, 'tcx> { } } } - for (span, sugg) in spans.iter().copied().zip(suggs.iter()) { + for ((span, _), sugg) in spans_with_counts.iter().copied().zip(suggs.iter()) { if let Some(sugg) = sugg { introduce_suggestion.push((span, sugg.to_string())); } @@ -2039,68 +2037,57 @@ impl<'tcx> LifetimeContext<'_, 'tcx> { match &lifetime_names[..] { [name] => { let mut suggs: Vec String>>> = Vec::new(); - for (snippet, count) in snippets.iter().cloned().zip(counts.iter().copied()) { - if snippet == Some("&".to_string()) { - suggs.push(Some(Box::new(|name| format!("&{} ", name)))); - } else if snippet == Some("'_".to_string()) { - suggs.push(Some(Box::new(|n| n.to_string()))); - } else if snippet == Some("".to_string()) { - suggs.push(Some(Box::new(move |n| format!("{}, ", n).repeat(count)))); - } else if let Some(snippet) = snippet { - if !snippet.ends_with('>') { - suggs.push(Some(Box::new(move |name| { - format!( - "{}<{}>", - snippet, - std::iter::repeat(name.to_string()) - .take(count) - .collect::>() - .join(", ") - ) - }))); - } else { - suggs.push(None); - } - } else { - suggs.push(None); - } + for (snippet, (_, count)) in snippets.iter().zip(spans_with_counts.iter().copied()) + { + suggs.push(match snippet.as_deref() { + Some("&") => Some(Box::new(|name| format!("&{} ", name))), + Some("'_") => Some(Box::new(|n| n.to_string())), + Some("") => Some(Box::new(move |n| format!("{}, ", n).repeat(count))), + Some(snippet) if !snippet.ends_with('>') => Some(Box::new(move |name| { + format!( + "{}<{}>", + snippet, + std::iter::repeat(name.to_string()) + .take(count) + .collect::>() + .join(", ") + ) + })), + _ => None, + }); } - suggest_existing(err, &name.as_str()[..], &suggs); + suggest_existing(err, &name.as_str()[..], suggs); } [] => { - let mut suggs: Vec> = Vec::new(); - for (snippet, count) in snippets.iter().cloned().zip(counts.iter().copied()) { - if snippet == Some("&".to_string()) { - suggs.push(Some("&'a ".to_string())); - } else if snippet == Some("'_".to_string()) { - suggs.push(Some("'a".to_string())); - } else if let Some(snippet) = snippet { - if snippet == "" { - suggs.push(Some( - std::iter::repeat("'a, ").take(count).collect::>().join(""), - )); - } else { - suggs.push(Some(format!( - "{}<{}>", - snippet, - std::iter::repeat("'a").take(count).collect::>().join(", ") - ))); + let mut suggs = Vec::new(); + for (snippet, (_, count)) in + snippets.iter().cloned().zip(spans_with_counts.iter().copied()) + { + suggs.push(match snippet.as_deref() { + Some("&") => Some("&'a ".to_string()), + Some("'_") => Some("'a".to_string()), + Some("") => { + Some(std::iter::repeat("'a, ").take(count).collect::>().join("")) } - } else { - suggs.push(None); - } + Some(snippet) => Some(format!( + "{}<{}>", + snippet, + std::iter::repeat("'a").take(count).collect::>().join(", "), + )), + None => None, + }); } - suggest_new(err, &suggs); + suggest_new(err, suggs); } lts if lts.len() > 1 => { err.span_note(lifetime_spans, "these named lifetimes are available to use"); let mut spans_suggs: Vec<_> = Vec::new(); - for (span, snippet) in spans.iter().copied().zip(snippets.iter()) { - if Some("") == snippet.as_deref() { - spans_suggs.push((span, "'lifetime, ".to_string())); - } else if Some("&") == snippet.as_deref() { - spans_suggs.push((span, "&'lifetime ".to_string())); + for ((span, _), snippet) in spans_with_counts.iter().copied().zip(snippets.iter()) { + match snippet.as_deref() { + Some("") => spans_suggs.push((span, "'lifetime, ".to_string())), + Some("&") => spans_suggs.push((span, "&'lifetime ".to_string())), + _ => {} } } diff --git a/compiler/rustc_resolve/src/late/lifetimes.rs b/compiler/rustc_resolve/src/late/lifetimes.rs index c81269a46b21a..e8d21af435887 100644 --- a/compiler/rustc_resolve/src/late/lifetimes.rs +++ b/compiler/rustc_resolve/src/late/lifetimes.rs @@ -3038,8 +3038,10 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { spans.sort(); let mut spans_dedup = spans.clone(); spans_dedup.dedup(); - let counts: Vec<_> = - spans_dedup.iter().map(|sp| spans.iter().filter(|nsp| *nsp == sp).count()).collect(); + let spans_with_counts: Vec<_> = spans_dedup + .into_iter() + .map(|sp| (sp, spans.iter().filter(|nsp| *nsp == &sp).count())) + .collect(); let mut err = self.report_missing_lifetime_specifiers(spans.clone(), lifetime_refs.len()); @@ -3052,8 +3054,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { self.add_missing_lifetime_specifiers_label( &mut err, - spans, - counts, + spans_with_counts, &lifetime_names, lifetime_spans, error.unwrap_or(&[]), diff --git a/src/test/ui/issues/issue-84592.rs b/src/test/ui/suggestions/issue-84592.rs similarity index 100% rename from src/test/ui/issues/issue-84592.rs rename to src/test/ui/suggestions/issue-84592.rs diff --git a/src/test/ui/issues/issue-84592.stderr b/src/test/ui/suggestions/issue-84592.stderr similarity index 100% rename from src/test/ui/issues/issue-84592.stderr rename to src/test/ui/suggestions/issue-84592.stderr diff --git a/src/tools/tidy/src/ui_tests.rs b/src/tools/tidy/src/ui_tests.rs index 791489b3960a5..8334bc68ae729 100644 --- a/src/tools/tidy/src/ui_tests.rs +++ b/src/tools/tidy/src/ui_tests.rs @@ -8,7 +8,7 @@ use std::path::Path; const ENTRY_LIMIT: usize = 1000; // FIXME: The following limits should be reduced eventually. const ROOT_ENTRY_LIMIT: usize = 1388; -const ISSUES_ENTRY_LIMIT: usize = 2557; +const ISSUES_ENTRY_LIMIT: usize = 2551; fn check_entries(path: &Path, bad: &mut bool) { let dirs = walkdir::WalkDir::new(&path.join("test/ui")) From e6b12c8e4f7817d6317ab7b981e440f260a8137e Mon Sep 17 00:00:00 2001 From: ltdk Date: Sun, 9 May 2021 17:13:22 -0400 Subject: [PATCH 07/13] Fix `Step` feature flag, make tidy lint more useful to find things like this --- library/core/src/iter/range.rs | 4 ++-- src/tools/tidy/src/features.rs | 14 ++++++++++++-- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/library/core/src/iter/range.rs b/library/core/src/iter/range.rs index 4b293c596e7af..5e39e71252f5a 100644 --- a/library/core/src/iter/range.rs +++ b/library/core/src/iter/range.rs @@ -106,7 +106,7 @@ pub unsafe trait Step: Clone + PartialOrd + Sized { /// For any `a` and `n`, where no overflow occurs: /// /// * `Step::forward_unchecked(a, n)` is equivalent to `Step::forward(a, n)` - #[unstable(feature = "unchecked_math", reason = "niche optimization path", issue = "none")] + #[unstable(feature = "step_trait_ext", reason = "recently added", issue = "42168")] unsafe fn forward_unchecked(start: Self, count: usize) -> Self { Step::forward(start, count) } @@ -178,7 +178,7 @@ pub unsafe trait Step: Clone + PartialOrd + Sized { /// For any `a` and `n`, where no overflow occurs: /// /// * `Step::backward_unchecked(a, n)` is equivalent to `Step::backward(a, n)` - #[unstable(feature = "unchecked_math", reason = "niche optimization path", issue = "none")] + #[unstable(feature = "step_trait_ext", reason = "recently added", issue = "42168")] unsafe fn backward_unchecked(start: Self, count: usize) -> Self { Step::backward(start, count) } diff --git a/src/tools/tidy/src/features.rs b/src/tools/tidy/src/features.rs index b14b5aeb57236..a7e700b935e04 100644 --- a/src/tools/tidy/src/features.rs +++ b/src/tools/tidy/src/features.rs @@ -51,6 +51,14 @@ pub struct Feature { pub has_gate_test: bool, pub tracking_issue: Option, } +impl Feature { + fn tracking_issue_display(&self) -> impl fmt::Display { + match self.tracking_issue { + None => "none".to_string(), + Some(x) => x.to_string(), + } + } +} pub type Features = HashMap; @@ -361,10 +369,12 @@ fn get_and_check_lib_features( if f.tracking_issue != s.tracking_issue && f.level != Status::Stable { tidy_error!( bad, - "{}:{}: mismatches the `issue` in {}", + "{}:{}: `issue` \"{}\" mismatches the {} `issue` of \"{}\"", file.display(), line, - display + f.tracking_issue_display(), + display, + s.tracking_issue_display(), ); } } From 74e0e45f3c32aef30ab5cdb7bf1980eb087830de Mon Sep 17 00:00:00 2001 From: Ian Jackson Date: Mon, 10 May 2021 12:56:35 +0100 Subject: [PATCH 08/13] io::Seek: Mention that seeking can fail due to buffer flush fail Signed-off-by: Ian Jackson --- library/std/src/io/mod.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index 94c70c4f267b1..110cb86498e9a 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -1663,6 +1663,8 @@ pub trait Seek { /// /// # Errors /// + /// Seeking can fail, for example becaue it might involve flushing a buffer. + /// /// Seeking to a negative offset is considered an error. #[stable(feature = "rust1", since = "1.0.0")] fn seek(&mut self, pos: SeekFrom) -> Result; From c3ca148ac05c6a07e95734f9783bd8fea2086789 Mon Sep 17 00:00:00 2001 From: Ian Jackson Date: Mon, 10 May 2021 12:58:03 +0100 Subject: [PATCH 09/13] io::Seek: Provide rewind() Signed-off-by: Ian Jackson --- library/std/src/io/mod.rs | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index 110cb86498e9a..7165b155eef98 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -1669,6 +1669,41 @@ pub trait Seek { #[stable(feature = "rust1", since = "1.0.0")] fn seek(&mut self, pos: SeekFrom) -> Result; + /// Rewind to the beginning of a stream. + /// + /// This is a convenience method, equivalent to `seek(SeekFrom::Start(0)`. + /// + /// # Errors + /// + /// Rewinding can fail, for example becaue it might involve flushing a buffer. + /// + /// # Example + /// + /// ```no_run + /// #![feature(seek_rewind)] + /// use std::io::{Read, Seek, Write}; + /// use std::fs::OpenOptions; + /// + /// let mut f = OpenOptions::new() + /// .write(true) + /// .read(true) + /// .create(true) + /// .open("foo.txt").unwrap(); + /// + /// let hello = "Hello!\n"; + /// write!(f, "{}", hello).unwrap(); + /// f.rewind().unwrap(); + /// + /// let mut buf = String::new(); + /// f.read_to_string(&mut buf).unwrap(); + /// assert_eq!(&buf, hello); + /// ``` + #[unstable(feature = "seek_rewind", issue = "none")] + fn rewind(&mut self) -> Result<()> { + self.seek(SeekFrom::Start(0))?; + Ok(()) + } + /// Returns the length of this stream (in bytes). /// /// This method is implemented using up to three seek operations. If this From debf987421c9c25cba16ab267a600b11a647b3f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= Date: Mon, 10 May 2021 15:11:01 +0300 Subject: [PATCH 10/13] :arrow_up: rust-analyzer --- src/tools/rust-analyzer | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer b/src/tools/rust-analyzer index eb741e895f1a7..fd109fb587904 160000 --- a/src/tools/rust-analyzer +++ b/src/tools/rust-analyzer @@ -1 +1 @@ -Subproject commit eb741e895f1a73420a401f2495c711afe37d9d19 +Subproject commit fd109fb587904cfecc1149e068814bfd38feb83c From 3113b6bd69127a0572a497464864db7d896f07b9 Mon Sep 17 00:00:00 2001 From: Ian Jackson Date: Mon, 10 May 2021 13:50:56 +0100 Subject: [PATCH 11/13] Fix typo in doc Co-authored-by: Mara Bos --- library/std/src/io/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index 7165b155eef98..cebed3a7c1821 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -1671,7 +1671,7 @@ pub trait Seek { /// Rewind to the beginning of a stream. /// - /// This is a convenience method, equivalent to `seek(SeekFrom::Start(0)`. + /// This is a convenience method, equivalent to `seek(SeekFrom::Start(0))`. /// /// # Errors /// From 7ae852e349eeb83243a1d1cc4c79c8997723215e Mon Sep 17 00:00:00 2001 From: Ian Jackson Date: Mon, 10 May 2021 13:55:13 +0100 Subject: [PATCH 12/13] io::Seek::rewind: Set tracking issue Signed-off-by: Ian Jackson --- library/std/src/io/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index cebed3a7c1821..9f43379aff787 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -1698,7 +1698,7 @@ pub trait Seek { /// f.read_to_string(&mut buf).unwrap(); /// assert_eq!(&buf, hello); /// ``` - #[unstable(feature = "seek_rewind", issue = "none")] + #[unstable(feature = "seek_rewind", issue = "85149")] fn rewind(&mut self) -> Result<()> { self.seek(SeekFrom::Start(0))?; Ok(()) From 2448c7698ef186ead88bd7980f2cac28c55111a8 Mon Sep 17 00:00:00 2001 From: Fabian Wolff Date: Mon, 10 May 2021 14:59:54 +0200 Subject: [PATCH 13/13] More minor fixes suggested by @jackh726 --- compiler/rustc_errors/src/diagnostic.rs | 18 +++++------------- compiler/rustc_resolve/src/late/diagnostics.rs | 6 ++++-- .../return-elided-lifetime.rs | 0 .../return-elided-lifetime.stderr | 0 4 files changed, 9 insertions(+), 15 deletions(-) rename src/test/ui/{ => suggestions}/return-elided-lifetime.rs (100%) rename src/test/ui/{ => suggestions}/return-elided-lifetime.stderr (100%) diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index 405dd2e68c6d0..14ccced2c6a56 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -283,20 +283,12 @@ impl Diagnostic { suggestion: Vec<(Span, String)>, applicability: Applicability, ) -> &mut Self { - assert!(!suggestion.is_empty()); - self.suggestions.push(CodeSuggestion { - substitutions: vec![Substitution { - parts: suggestion - .into_iter() - .map(|(span, snippet)| SubstitutionPart { snippet, span }) - .collect(), - }], - msg: msg.to_owned(), - style: SuggestionStyle::ShowCode, + self.multipart_suggestion_with_style( + msg, + suggestion, applicability, - tool_metadata: Default::default(), - }); - self + SuggestionStyle::ShowCode, + ) } /// [`Diagnostic::multipart_suggestion()`] but you can set the [`SuggestionStyle`]. diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 9b82d8afd2be1..fdde687d4866c 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -1905,9 +1905,11 @@ impl<'tcx> LifetimeContext<'_, 'tcx> { let spans_suggs: Vec<_> = formatters .into_iter() - .filter_map(|fmt| fmt) .zip(spans_with_counts.iter()) - .map(|(formatter, (span, _))| (*span, formatter(name))) + .filter_map(|(fmt, (span, _))| { + if let Some(formatter) = fmt { Some((formatter, span)) } else { None } + }) + .map(|(formatter, span)| (*span, formatter(name))) .collect(); err.multipart_suggestion_with_style( &format!( diff --git a/src/test/ui/return-elided-lifetime.rs b/src/test/ui/suggestions/return-elided-lifetime.rs similarity index 100% rename from src/test/ui/return-elided-lifetime.rs rename to src/test/ui/suggestions/return-elided-lifetime.rs diff --git a/src/test/ui/return-elided-lifetime.stderr b/src/test/ui/suggestions/return-elided-lifetime.stderr similarity index 100% rename from src/test/ui/return-elided-lifetime.stderr rename to src/test/ui/suggestions/return-elided-lifetime.stderr