Report undeclared lifetimes on AST.

This commit is contained in:
Camille GILLOT 2022-03-06 12:02:13 +01:00
parent 4cfceeabdc
commit fc9f25531a
5 changed files with 833 additions and 396 deletions

View File

@ -484,7 +484,7 @@ enum ParenthesizedGenericArgs {
/// an "elided" or "underscore" lifetime name. In the future, we probably want to move /// an "elided" or "underscore" lifetime name. In the future, we probably want to move
/// everything into HIR lowering. /// everything into HIR lowering.
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug)]
enum AnonymousLifetimeMode { pub enum AnonymousLifetimeMode {
/// For **Modern** cases, create a new anonymous region parameter /// For **Modern** cases, create a new anonymous region parameter
/// and reference that. /// and reference that.
/// ///
@ -2017,16 +2017,16 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
}); });
let param_name = match lt.name { let param_name = match lt.name {
hir::LifetimeName::Param(param_name) => param_name, hir::LifetimeName::Param(param_name) => param_name,
hir::LifetimeName::Implicit(_) hir::LifetimeName::Implicit(_) | hir::LifetimeName::Underscore => {
| hir::LifetimeName::Underscore hir::ParamName::Plain(lt.name.ident())
| hir::LifetimeName::Static => hir::ParamName::Plain(lt.name.ident()), }
hir::LifetimeName::ImplicitObjectLifetimeDefault => { hir::LifetimeName::ImplicitObjectLifetimeDefault => {
self.sess.diagnostic().span_bug( self.sess.diagnostic().span_bug(
param.ident.span, param.ident.span,
"object-lifetime-default should not occur here", "object-lifetime-default should not occur here",
); );
} }
hir::LifetimeName::Error => ParamName::Error, hir::LifetimeName::Static | hir::LifetimeName::Error => ParamName::Error,
}; };
let kind = let kind =
@ -2404,20 +2404,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
/// Report an error on illegal use of `'_` or a `&T` with no explicit lifetime; /// Report an error on illegal use of `'_` or a `&T` with no explicit lifetime;
/// return an "error lifetime". /// return an "error lifetime".
fn new_error_lifetime(&mut self, id: Option<NodeId>, span: Span) -> hir::Lifetime { fn new_error_lifetime(&mut self, id: Option<NodeId>, span: Span) -> hir::Lifetime {
let (id, msg, label) = match id { let id = id.unwrap_or_else(|| self.resolver.next_node_id());
Some(id) => (id, "`'_` cannot be used here", "`'_` is a reserved lifetime name"),
None => (
self.resolver.next_node_id(),
"`&` without an explicit lifetime name cannot be used here",
"explicit lifetime name needed here",
),
};
let mut err = struct_span_err!(self.sess, span, E0637, "{}", msg,);
err.span_label(span, label);
err.emit();
self.new_named_lifetime(id, span, hir::LifetimeName::Error) self.new_named_lifetime(id, span, hir::LifetimeName::Error)
} }

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,7 @@
use crate::diagnostics::{ImportSuggestion, LabelSuggestion, TypoSuggestion}; use crate::diagnostics::{ImportSuggestion, LabelSuggestion, TypoSuggestion};
use crate::late::lifetimes::{ElisionFailureInfo, LifetimeContext}; use crate::late::lifetimes::{ElisionFailureInfo, LifetimeContext};
use crate::late::{AliasPossibility, LateResolutionVisitor, RibKind}; use crate::late::{AliasPossibility, LateResolutionVisitor, RibKind};
use crate::late::{LifetimeBinderKind, LifetimeRibKind};
use crate::path_names_to_string; use crate::path_names_to_string;
use crate::{Finalize, Module, ModuleKind, ModuleOrUniformRoot}; use crate::{Finalize, Module, ModuleKind, ModuleOrUniformRoot};
use crate::{PathResult, PathSource, Segment}; use crate::{PathResult, PathSource, Segment};
@ -1793,6 +1794,102 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
(*ident, within_scope) (*ident, within_scope)
}) })
} }
crate fn emit_undeclared_lifetime_error(
&self,
lifetime_ref: &ast::Lifetime,
outer_lifetime_ref: Option<Ident>,
) {
debug_assert_ne!(lifetime_ref.ident.name, kw::UnderscoreLifetime);
let mut err = if let Some(outer) = outer_lifetime_ref {
let mut err = struct_span_err!(
self.r.session,
lifetime_ref.ident.span,
E0401,
"can't use generic parameters from outer item",
);
err.span_label(lifetime_ref.ident.span, "use of generic parameter from outer item");
err.span_label(outer.span, "lifetime parameter from outer item");
err
} else {
let mut err = struct_span_err!(
self.r.session,
lifetime_ref.ident.span,
E0261,
"use of undeclared lifetime name `{}`",
lifetime_ref.ident
);
err.span_label(lifetime_ref.ident.span, "undeclared lifetime");
err
};
let mut suggest_note = true;
for rib in self.lifetime_ribs.iter().rev() {
match rib.kind {
LifetimeRibKind::Generics { span, kind } => {
if !span.can_be_used_for_suggestions() && suggest_note {
suggest_note = false; // Avoid displaying the same help multiple times.
err.span_label(
span,
&format!(
"lifetime `{}` is missing in item created through this procedural macro",
lifetime_ref.ident,
),
);
continue;
}
let higher_ranked = matches!(
kind,
LifetimeBinderKind::BareFnType
| LifetimeBinderKind::PolyTrait
| LifetimeBinderKind::WhereBound
);
let (span, sugg) = if span.is_empty() {
let sugg = format!(
"{}<{}>{}",
if higher_ranked { "for" } else { "" },
lifetime_ref.ident,
if higher_ranked { " " } else { "" },
);
(span, sugg)
} else {
let span =
self.r.session.source_map().span_through_char(span, '<').shrink_to_hi();
let sugg = format!("{}, ", lifetime_ref.ident);
(span, sugg)
};
if higher_ranked {
err.span_suggestion(
span,
&format!(
"consider making the {} lifetime-generic with a new `{}` lifetime",
kind.descr(),
lifetime_ref
),
sugg,
Applicability::MaybeIncorrect,
);
err.note_once(
"for more information on higher-ranked polymorphism, visit \
https://doc.rust-lang.org/nomicon/hrtb.html",
);
} else {
err.span_suggestion(
span,
&format!("consider introducing lifetime `{}` here", lifetime_ref.ident),
sugg,
Applicability::MaybeIncorrect,
);
}
}
LifetimeRibKind::Item => break,
_ => {}
}
}
err.emit();
}
} }
impl<'tcx> LifetimeContext<'_, 'tcx> { impl<'tcx> LifetimeContext<'_, 'tcx> {
@ -1810,67 +1907,6 @@ impl<'tcx> LifetimeContext<'_, 'tcx> {
) )
} }
crate fn emit_undeclared_lifetime_error(&self, lifetime_ref: &hir::Lifetime) {
let mut err = struct_span_err!(
self.tcx.sess,
lifetime_ref.span,
E0261,
"use of undeclared lifetime name `{}`",
lifetime_ref
);
err.span_label(lifetime_ref.span, "undeclared lifetime");
let mut suggested_spans = vec![];
for missing in &self.missing_named_lifetime_spots {
match missing {
MissingLifetimeSpot::Generics(generics) => {
let (span, sugg) = if let Some(param) = generics.params.iter().find(|p| {
!matches!(
p.kind,
hir::GenericParamKind::Type { synthetic: true, .. }
| hir::GenericParamKind::Lifetime {
kind: hir::LifetimeParamKind::Elided,
}
)
}) {
(param.span.shrink_to_lo(), format!("{}, ", lifetime_ref))
} else {
(generics.span, format!("<{}>", lifetime_ref))
};
if suggested_spans.contains(&span) {
continue;
}
suggested_spans.push(span);
if span.can_be_used_for_suggestions() {
err.span_suggestion(
span,
&format!("consider introducing lifetime `{}` here", lifetime_ref),
sugg,
Applicability::MaybeIncorrect,
);
}
}
MissingLifetimeSpot::HigherRanked { span, span_type } => {
err.span_suggestion(
*span,
&format!(
"consider making the {} lifetime-generic with a new `{}` lifetime",
span_type.descr(),
lifetime_ref
),
span_type.suggestion(&lifetime_ref.to_string()),
Applicability::MaybeIncorrect,
);
err.note(
"for more information on higher-ranked polymorphism, visit \
https://doc.rust-lang.org/nomicon/hrtb.html",
);
}
_ => {}
}
}
err.emit();
}
/// Returns whether to add `'static` lifetime to the suggested lifetime list. /// Returns whether to add `'static` lifetime to the suggested lifetime list.
crate fn report_elision_failure( crate fn report_elision_failure(
&mut self, &mut self,

View File

@ -2312,7 +2312,10 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
self.insert_lifetime(lifetime_ref, def); self.insert_lifetime(lifetime_ref, def);
} else { } else {
self.emit_undeclared_lifetime_error(lifetime_ref); self.tcx.sess.delay_span_bug(
lifetime_ref.span,
&format!("Could not resolve {:?} in scope {:#?}", lifetime_ref, self.scope,),
);
} }
} }
@ -3119,18 +3122,10 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
if let hir::ParamName::Plain(_) = lifetime_i_name { if let hir::ParamName::Plain(_) = lifetime_i_name {
let name = lifetime_i_name.ident().name; let name = lifetime_i_name.ident().name;
if name == kw::UnderscoreLifetime || name == kw::StaticLifetime { if name == kw::UnderscoreLifetime || name == kw::StaticLifetime {
let mut err = struct_span_err!( self.tcx.sess.delay_span_bug(
self.tcx.sess,
lifetime_i.span, lifetime_i.span,
E0262, &format!("invalid lifetime parameter name: `{}`", lifetime_i.name.ident()),
"invalid lifetime parameter name: `{}`",
lifetime_i.name.ident(),
); );
err.span_label(
lifetime_i.span,
format!("{} is a reserved lifetime name", name),
);
err.emit();
} }
} }

View File

@ -11,6 +11,7 @@
#![feature(drain_filter)] #![feature(drain_filter)]
#![feature(bool_to_option)] #![feature(bool_to_option)]
#![feature(crate_visibility_modifier)] #![feature(crate_visibility_modifier)]
#![feature(if_let_guard)]
#![feature(let_chains)] #![feature(let_chains)]
#![feature(let_else)] #![feature(let_else)]
#![feature(never_type)] #![feature(never_type)]
@ -1358,9 +1359,17 @@ impl<'a> Resolver<'a> {
} }
pub fn next_node_id(&mut self) -> NodeId { pub fn next_node_id(&mut self) -> NodeId {
let next = let start = self.next_node_id;
self.next_node_id.as_u32().checked_add(1).expect("input too large; ran out of NodeIds"); let next = start.as_u32().checked_add(1).expect("input too large; ran out of NodeIds");
mem::replace(&mut self.next_node_id, ast::NodeId::from_u32(next)) self.next_node_id = ast::NodeId::from_u32(next);
start
}
pub fn next_node_ids(&mut self, count: usize) -> std::ops::Range<NodeId> {
let start = self.next_node_id;
let end = start.as_usize().checked_add(count).expect("input too large; ran out of NodeIds");
self.next_node_id = ast::NodeId::from_usize(end);
start..self.next_node_id
} }
pub fn lint_buffer(&mut self) -> &mut LintBuffer { pub fn lint_buffer(&mut self) -> &mut LintBuffer {