提交 266f4521 编写于 作者: B bors

Auto merge of #85150 - Dylan-DPC:rollup-q26gbx3, r=Dylan-DPC

Rollup of 6 pull requests

Successful merges:

 - #85050 (Fix suggestions for missing return type lifetime specifiers)
 - #85075 (Improve "panic message is not a string literal" warning)
 - #85096 (Make unchecked_{add,sub,mul} inherent methods unstably const)
 - #85112 (ensure failing promoteds in const/static bodies are handled correctly)
 - #85146 (Provide io::Seek::rewind)
 - #85147 ( rust-analyzer)

Failed merges:

r? `@ghost`
`@rustbot` modify labels: rollup
......@@ -282,6 +282,22 @@ pub fn multipart_suggestion(
msg: &str,
suggestion: Vec<(Span, String)>,
applicability: Applicability,
) -> &mut Self {
self.multipart_suggestion_with_style(
msg,
suggestion,
applicability,
SuggestionStyle::ShowCode,
)
}
/// [`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 {
......@@ -292,7 +308,7 @@ pub fn multipart_suggestion(
.collect(),
}],
msg: msg.to_owned(),
style: SuggestionStyle::ShowCode,
style,
applicability,
tool_metadata: Default::default(),
});
......
......@@ -4,7 +4,7 @@
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())
}
......@@ -9,7 +9,7 @@
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 @@ fn suggest_using_enum_variant(
impl<'tcx> LifetimeContext<'_, 'tcx> {
crate fn report_missing_lifetime_specifiers(
&self,
span: Span,
spans: Vec<Span>,
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_with_counts: Vec<(Span, usize)>,
lifetime_names: &FxHashSet<Symbol>,
lifetime_spans: Vec<Span>,
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<Option<String>> = spans_with_counts
.iter()
.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_with_counts {
err.span_label(
*span,
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<Option<Box<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![];
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(&lt)))
.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(&lt)))
.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(&lt_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(&lt_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_with_counts.iter().zip(formatters.iter()) {
if let Some(formatter) = formatter {
introduce_suggestion.push((*span, formatter(&lt_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(&lt_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 spans_suggs: Vec<_> = formatters
.into_iter()
.zip(spans_with_counts.iter())
.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!(
"consider using the `{}` lifetime",
lifetime_names.iter().next().unwrap()
),
spans_suggs,
Applicability::MaybeIncorrect,
SuggestionStyle::ShowAlways,
);
};
let suggest_new = |err: &mut DiagnosticBuilder<'_>, suggs: Vec<Option<String>>| {
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::<Vec<_>>()
.join(", "),
)
} else {
(
span.shrink_to_hi(),
format!(
"<{}>",
let mut spans_suggs = Vec::new();
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()),
Some("'_") => (span, "'static".to_owned()),
Some(snippet) if !snippet.ends_with('>') => {
if snippet == "" {
(
span,
std::iter::repeat("'static")
.take(count)
.collect::<Vec<_>>()
.join(", ")
),
)
.join(", "),
)
} else {
(
span.shrink_to_hi(),
format!(
"<{}>",
std::iter::repeat("'static")
.take(count)
.collect::<Vec<_>>()
.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_with_counts.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,75 @@ 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::<Vec<_>>()
.join(", ")
)
};
suggest_existing(err, &name.as_str()[..], &f);
}
([], Some("&")) if count == 1 => {
suggest_new(err, "&'a ");
}
([], Some("'_")) if count == 1 => {
suggest_new(err, "'a");
match &lifetime_names[..] {
[name] => {
let mut suggs: Vec<Option<Box<dyn Fn(&str) -> String>>> = Vec::new();
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::<Vec<_>>()
.join(", ")
)
})),
_ => None,
});
}
suggest_existing(err, &name.as_str()[..], suggs);
}
([], Some(snippet)) if !snippet.ends_with('>') => {
if snippet == "" {
// This happens when we have `type Bar<'a> = Foo<T>` 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::<Vec<_>>().join(""),
);
} else {
suggest_new(
err,
&format!(
[] => {
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::<Vec<_>>().join(""))
}
Some(snippet) => Some(format!(
"{}<{}>",
snippet,
std::iter::repeat("'a").take(count).collect::<Vec<_>>().join(", ")
),
);
std::iter::repeat("'a").take(count).collect::<Vec<_>>().join(", "),
)),
None => 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_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())),
_ => {}
}
}
if spans_suggs.len() > 0 {
// This happens when we have `Foo<T>` 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!(),
}
}
......
......@@ -2956,7 +2956,6 @@ fn resolve_elided_lifetimes(&mut self, lifetime_refs: &[&'tcx hir::Lifetime]) {
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,16 @@ fn resolve_elided_lifetimes(&mut self, lifetime_refs: &[&'tcx hir::Lifetime]) {
}
};
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 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());
if let Some(params) = error {
// If there's no lifetime available, suggest `'static`.
......@@ -3043,10 +3051,10 @@ fn resolve_elided_lifetimes(&mut self, lifetime_refs: &[&'tcx hir::Lifetime]) {
lifetime_names.insert(kw::StaticLifetime);
}
}
self.add_missing_lifetime_specifiers_label(
&mut err,
span,
lifetime_refs.len(),
spans_with_counts,
&lifetime_names,
lifetime_spans,
error.unwrap_or(&[]),
......
......@@ -106,7 +106,7 @@ fn forward(start: Self, count: usize) -> Self {
/// 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 @@ fn backward(start: Self, count: usize) -> Self {
/// 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)
}
......
......@@ -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)]
......
......@@ -412,12 +412,13 @@ pub const fn checked_add(self, rhs: Self) -> Option<Self> {
#[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 @@ pub const fn checked_sub(self, rhs: Self) -> Option<Self> {
#[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 @@ pub const fn checked_mul(self, rhs: Self) -> Option<Self> {
#[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) }
......
......@@ -422,12 +422,13 @@ pub const fn checked_add(self, rhs: Self) -> Option<Self> {
#[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 @@ pub const fn checked_sub(self, rhs: Self) -> Option<Self> {
#[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 @@ pub const fn checked_mul(self, rhs: Self) -> Option<Self> {
#[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) }
......
......@@ -1663,10 +1663,47 @@ 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<u64>;
/// 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 = "85149")]
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
......
......@@ -10,6 +10,8 @@ impl<T> PrintName<T> {
const fn no_codegen<T>() {
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::<T>::VOID; //~ERROR could not evaluate static initializer
}
}
......
......@@ -27,16 +27,16 @@ LL | #![warn(const_err, unconditional_panic)]
= note: for more information, see issue #71800 <https://github.com/rust-lang/rust/issues/71800>
error[E0080]: could not evaluate static initializer
--> $DIR/erroneous-const.rs:13:17
--> $DIR/erroneous-const.rs:15:17
|
LL | let _ = PrintName::<T>::VOID;
| ^^^^^^^^^^^^^^^^^^^^
| |
| referenced constant has errors
| inside `no_codegen::<i32>` at $DIR/erroneous-const.rs:13:17
| inside `no_codegen::<i32>` at $DIR/erroneous-const.rs:15:17
...
LL | pub static FOO: () = no_codegen::<i32>();
| ------------------- 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
......
//! Make sure we error on erroneous consts even if they are unused.
#![warn(const_err, unconditional_panic)]
struct PrintName<T>(T);
impl<T> PrintName<T> {
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::<i32>::VOID; //~ERROR could not evaluate static initializer
}
};
fn main() {
FOO
}
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 <https://github.com/rust-lang/rust/issues/71800>
error[E0080]: could not evaluate static initializer
--> $DIR/erroneous-const2.rs:15:17
|
LL | let _ = PrintName::<i32>::VOID;
| ^^^^^^^^^^^^^^^^^^^^^^ referenced constant has errors
error: aborting due to previous error; 2 warnings emitted
For more information about this error, try `rustc --explain E0080`.
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 <https://github.com/rust-lang/rust/issues/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();
......
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 <https://github.com/rust-lang/rust/issues/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();
......
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 <https://github.com/rust-lang/rust/issues/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();
......
......@@ -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 fn oob() -> i32 {
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;
}
......@@ -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
......
......@@ -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
/* 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() {}
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`.
......@@ -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
......
/* 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() {}
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`.
Subproject commit eb741e895f1a73420a401f2495c711afe37d9d19
Subproject commit fd109fb587904cfecc1149e068814bfd38feb83c
......@@ -51,6 +51,14 @@ pub struct Feature {
pub has_gate_test: bool,
pub tracking_issue: Option<NonZeroU32>,
}
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<String, Feature>;
......@@ -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(),
);
}
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册