mirror of https://github.com/rust-lang/rust.git
Move code to `diagnostics.rs`
This commit is contained in:
parent
49f9bf897b
commit
1beac2b774
|
@ -8,6 +8,7 @@ use rustc_ast_pretty::pprust;
|
|||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder};
|
||||
use rustc_feature::BUILTIN_ATTRIBUTES;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def::Namespace::{self, *};
|
||||
use rustc_hir::def::{self, CtorKind, CtorOf, DefKind, NonMacroAttrKind};
|
||||
use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX, LOCAL_CRATE};
|
||||
|
@ -19,7 +20,7 @@ use syntax::ast::{self, Ident, Path};
|
|||
use syntax::util::lev_distance::find_best_match_for_name;
|
||||
|
||||
use crate::imports::{ImportDirective, ImportDirectiveSubclass, ImportResolver};
|
||||
use crate::lifetimes::{ElisionFailureInfo, MissingLifetimeSpot};
|
||||
use crate::lifetimes::{ElisionFailureInfo, LifetimeContext};
|
||||
use crate::path_names_to_string;
|
||||
use crate::{AmbiguityError, AmbiguityErrorMisc, AmbiguityKind};
|
||||
use crate::{BindingError, CrateLint, HasGenericParams, LegacyScope, Module, ModuleOrUniformRoot};
|
||||
|
@ -48,6 +49,40 @@ crate struct ImportSuggestion {
|
|||
pub path: Path,
|
||||
}
|
||||
|
||||
crate enum MissingLifetimeSpot<'tcx> {
|
||||
Generics(&'tcx hir::Generics<'tcx>),
|
||||
HigherRanked { span: Span, span_type: ForLifetimeSpanType },
|
||||
}
|
||||
|
||||
crate enum ForLifetimeSpanType {
|
||||
BoundEmpty,
|
||||
BoundTail,
|
||||
TypeEmpty,
|
||||
TypeTail,
|
||||
}
|
||||
|
||||
impl ForLifetimeSpanType {
|
||||
crate fn descr(&self) -> &'static str {
|
||||
match self {
|
||||
Self::BoundEmpty | Self::BoundTail => "bound",
|
||||
Self::TypeEmpty | Self::TypeTail => "type",
|
||||
}
|
||||
}
|
||||
|
||||
crate fn suggestion(&self, sugg: &str) -> String {
|
||||
match self {
|
||||
Self::BoundEmpty | Self::TypeEmpty => format!("for<{}> ", sugg),
|
||||
Self::BoundTail | Self::TypeTail => format!(", {}", sugg),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> Into<MissingLifetimeSpot<'tcx>> for &'tcx hir::Generics<'tcx> {
|
||||
fn into(self) -> MissingLifetimeSpot<'tcx> {
|
||||
MissingLifetimeSpot::Generics(self)
|
||||
}
|
||||
}
|
||||
|
||||
/// Adjust the impl span so that just the `impl` keyword is taken by removing
|
||||
/// everything after `<` (`"impl<T> Iterator for A<T> {}" -> "impl"`) and
|
||||
/// everything after the first whitespace (`"impl Iterator for A" -> "impl"`).
|
||||
|
@ -1457,104 +1492,185 @@ crate fn show_candidates(
|
|||
}
|
||||
}
|
||||
|
||||
crate fn report_missing_lifetime_specifiers(
|
||||
sess: &Session,
|
||||
span: Span,
|
||||
count: usize,
|
||||
) -> DiagnosticBuilder<'_> {
|
||||
struct_span_err!(sess, span, E0106, "missing lifetime specifier{}", pluralize!(count))
|
||||
}
|
||||
impl<'tcx> LifetimeContext<'_, 'tcx> {
|
||||
crate fn report_missing_lifetime_specifiers(
|
||||
&self,
|
||||
span: Span,
|
||||
count: usize,
|
||||
) -> DiagnosticBuilder<'tcx> {
|
||||
struct_span_err!(
|
||||
self.tcx.sess,
|
||||
span,
|
||||
E0106,
|
||||
"missing lifetime specifier{}",
|
||||
pluralize!(count)
|
||||
)
|
||||
}
|
||||
|
||||
crate fn add_missing_lifetime_specifiers_label(
|
||||
err: &mut DiagnosticBuilder<'_>,
|
||||
source_map: &SourceMap,
|
||||
span: Span,
|
||||
count: usize,
|
||||
lifetime_names: &FxHashSet<ast::Ident>,
|
||||
snippet: Option<&str>,
|
||||
missing_named_lifetime_spots: &[MissingLifetimeSpot<'_>],
|
||||
params: &[ElisionFailureInfo],
|
||||
) {
|
||||
if count > 1 {
|
||||
err.span_label(span, format!("expected {} lifetime parameters", count));
|
||||
} else {
|
||||
let suggest_existing = |err: &mut DiagnosticBuilder<'_>, sugg| {
|
||||
err.span_suggestion(
|
||||
span,
|
||||
"consider using the named lifetime",
|
||||
sugg,
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
};
|
||||
let suggest_new = |err: &mut DiagnosticBuilder<'_>, sugg: &str| {
|
||||
err.span_label(span, "expected named lifetime parameter");
|
||||
|
||||
for missing in missing_named_lifetime_spots.iter().rev() {
|
||||
let mut introduce_suggestion = vec![];
|
||||
let msg;
|
||||
let should_break;
|
||||
introduce_suggestion.push(match missing {
|
||||
MissingLifetimeSpot::Generics(generics) => {
|
||||
msg = "consider introducing a named lifetime parameter".to_string();
|
||||
should_break = true;
|
||||
match &generics.params {
|
||||
[] => (generics.span, "<'a>".to_string()),
|
||||
[param, ..] => (param.span.shrink_to_lo(), "'a, ".to_string()),
|
||||
}
|
||||
}
|
||||
MissingLifetimeSpot::HigherRanked { span, span_type } => {
|
||||
msg = format!(
|
||||
"consider making the {} lifetime-generic with a new `'a` lifetime",
|
||||
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");
|
||||
for missing in &self.missing_named_lifetime_spots {
|
||||
match missing {
|
||||
MissingLifetimeSpot::Generics(generics) => {
|
||||
let (span, sugg) = match &generics.params {
|
||||
[] => (generics.span, format!("<{}>", lifetime_ref)),
|
||||
[param, ..] => (param.span.shrink_to_lo(), format!("{}, ", lifetime_ref)),
|
||||
};
|
||||
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(),
|
||||
);
|
||||
should_break = false;
|
||||
err.note(
|
||||
"for more information on higher-ranked polymorphism, visit \
|
||||
https://doc.rust-lang.org/nomicon/hrtb.html",
|
||||
);
|
||||
(*span, span_type.suggestion("'a"))
|
||||
}
|
||||
});
|
||||
for param in params {
|
||||
if let Ok(snippet) = source_map.span_to_snippet(param.span) {
|
||||
if snippet.starts_with("&") && !snippet.starts_with("&'") {
|
||||
introduce_suggestion
|
||||
.push((param.span, format!("&'a {}", &snippet[1..])));
|
||||
} else if snippet.starts_with("&'_ ") {
|
||||
introduce_suggestion
|
||||
.push((param.span, format!("&'a {}", &snippet[4..])));
|
||||
}
|
||||
}
|
||||
}
|
||||
introduce_suggestion.push((span, sugg.to_string()));
|
||||
err.multipart_suggestion(&msg, introduce_suggestion, Applicability::MaybeIncorrect);
|
||||
if should_break {
|
||||
break;
|
||||
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();
|
||||
}
|
||||
|
||||
crate fn is_trait_ref_fn_scope(&mut self, trait_ref: &'tcx hir::PolyTraitRef<'tcx>) -> bool {
|
||||
if let def::Res::Def(_, did) = trait_ref.trait_ref.path.res {
|
||||
if [
|
||||
self.tcx.lang_items().fn_once_trait(),
|
||||
self.tcx.lang_items().fn_trait(),
|
||||
self.tcx.lang_items().fn_mut_trait(),
|
||||
]
|
||||
.contains(&Some(did))
|
||||
{
|
||||
let (span, span_type) = match &trait_ref.bound_generic_params {
|
||||
[] => (trait_ref.span.shrink_to_lo(), ForLifetimeSpanType::BoundEmpty),
|
||||
[.., bound] => (bound.span.shrink_to_hi(), ForLifetimeSpanType::BoundTail),
|
||||
};
|
||||
self.missing_named_lifetime_spots
|
||||
.push(MissingLifetimeSpot::HigherRanked { span, span_type });
|
||||
return true;
|
||||
}
|
||||
};
|
||||
false
|
||||
}
|
||||
|
||||
match (lifetime_names.len(), lifetime_names.iter().next(), snippet) {
|
||||
(1, Some(name), Some("&")) => {
|
||||
suggest_existing(err, format!("&{} ", name));
|
||||
}
|
||||
(1, Some(name), Some("'_")) => {
|
||||
suggest_existing(err, name.to_string());
|
||||
}
|
||||
(1, Some(name), Some(snippet)) if !snippet.ends_with(">") => {
|
||||
suggest_existing(err, format!("{}<{}>", snippet, name));
|
||||
}
|
||||
(0, _, Some("&")) => {
|
||||
suggest_new(err, "&'a ");
|
||||
}
|
||||
(0, _, Some("'_")) => {
|
||||
suggest_new(err, "'a");
|
||||
}
|
||||
(0, _, Some(snippet)) if !snippet.ends_with(">") => {
|
||||
suggest_new(err, &format!("{}<'a>", snippet));
|
||||
}
|
||||
_ => {
|
||||
err.span_label(span, "expected lifetime parameter");
|
||||
crate fn add_missing_lifetime_specifiers_label(
|
||||
&self,
|
||||
err: &mut DiagnosticBuilder<'_>,
|
||||
span: Span,
|
||||
count: usize,
|
||||
lifetime_names: &FxHashSet<ast::Ident>,
|
||||
params: &[ElisionFailureInfo],
|
||||
) {
|
||||
if count > 1 {
|
||||
err.span_label(span, format!("expected {} lifetime parameters", count));
|
||||
} else {
|
||||
let snippet = self.tcx.sess.source_map().span_to_snippet(span).ok();
|
||||
let suggest_existing = |err: &mut DiagnosticBuilder<'_>, sugg| {
|
||||
err.span_suggestion(
|
||||
span,
|
||||
"consider using the named lifetime",
|
||||
sugg,
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
};
|
||||
let suggest_new = |err: &mut DiagnosticBuilder<'_>, sugg: &str| {
|
||||
err.span_label(span, "expected named lifetime parameter");
|
||||
|
||||
for missing in self.missing_named_lifetime_spots.iter().rev() {
|
||||
let mut introduce_suggestion = vec![];
|
||||
let msg;
|
||||
let should_break;
|
||||
introduce_suggestion.push(match missing {
|
||||
MissingLifetimeSpot::Generics(generics) => {
|
||||
msg = "consider introducing a named lifetime parameter".to_string();
|
||||
should_break = true;
|
||||
match &generics.params {
|
||||
[] => (generics.span, "<'a>".to_string()),
|
||||
[param, ..] => (param.span.shrink_to_lo(), "'a, ".to_string()),
|
||||
}
|
||||
}
|
||||
MissingLifetimeSpot::HigherRanked { span, span_type } => {
|
||||
msg = format!(
|
||||
"consider making the {} lifetime-generic with a new `'a` lifetime",
|
||||
span_type.descr(),
|
||||
);
|
||||
should_break = false;
|
||||
err.note(
|
||||
"for more information on higher-ranked polymorphism, visit \
|
||||
https://doc.rust-lang.org/nomicon/hrtb.html",
|
||||
);
|
||||
(*span, span_type.suggestion("'a"))
|
||||
}
|
||||
});
|
||||
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!("&'a {}", &snippet[1..])));
|
||||
} else if snippet.starts_with("&'_ ") {
|
||||
introduce_suggestion
|
||||
.push((param.span, format!("&'a {}", &snippet[4..])));
|
||||
}
|
||||
}
|
||||
}
|
||||
introduce_suggestion.push((span, sugg.to_string()));
|
||||
err.multipart_suggestion(
|
||||
&msg,
|
||||
introduce_suggestion,
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
if should_break {
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
match (
|
||||
lifetime_names.len(),
|
||||
lifetime_names.iter().next(),
|
||||
snippet.as_ref().map(|s| s.as_str()),
|
||||
) {
|
||||
(1, Some(name), Some("&")) => {
|
||||
suggest_existing(err, format!("&{} ", name));
|
||||
}
|
||||
(1, Some(name), Some("'_")) => {
|
||||
suggest_existing(err, name.to_string());
|
||||
}
|
||||
(1, Some(name), Some(snippet)) if !snippet.ends_with(">") => {
|
||||
suggest_existing(err, format!("{}<{}>", snippet, name));
|
||||
}
|
||||
(0, _, Some("&")) => {
|
||||
suggest_new(err, "&'a ");
|
||||
}
|
||||
(0, _, Some("'_")) => {
|
||||
suggest_new(err, "'a");
|
||||
}
|
||||
(0, _, Some(snippet)) if !snippet.ends_with(">") => {
|
||||
suggest_new(err, &format!("{}<'a>", snippet));
|
||||
}
|
||||
_ => {
|
||||
err.span_label(span, "expected lifetime parameter");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,9 +5,7 @@
|
|||
//! used between functions, and they operate in a purely top-down
|
||||
//! way. Therefore, we break lifetime name resolution into a separate pass.
|
||||
|
||||
use crate::diagnostics::{
|
||||
add_missing_lifetime_specifiers_label, report_missing_lifetime_specifiers,
|
||||
};
|
||||
use crate::diagnostics::{ForLifetimeSpanType, MissingLifetimeSpot};
|
||||
use rustc::hir::map::Map;
|
||||
use rustc::lint;
|
||||
use rustc::middle::resolve_lifetime::*;
|
||||
|
@ -153,42 +151,8 @@ struct NamedRegionMap {
|
|||
object_lifetime_defaults: HirIdMap<Vec<ObjectLifetimeDefault>>,
|
||||
}
|
||||
|
||||
crate enum MissingLifetimeSpot<'tcx> {
|
||||
Generics(&'tcx hir::Generics<'tcx>),
|
||||
HigherRanked { span: Span, span_type: ForLifetimeSpanType },
|
||||
}
|
||||
|
||||
crate enum ForLifetimeSpanType {
|
||||
BoundEmpty,
|
||||
BoundTail,
|
||||
TypeEmpty,
|
||||
TypeTail,
|
||||
}
|
||||
|
||||
impl ForLifetimeSpanType {
|
||||
crate fn descr(&self) -> &'static str {
|
||||
match self {
|
||||
Self::BoundEmpty | Self::BoundTail => "bound",
|
||||
Self::TypeEmpty | Self::TypeTail => "type",
|
||||
}
|
||||
}
|
||||
|
||||
crate fn suggestion(&self, sugg: &str) -> String {
|
||||
match self {
|
||||
Self::BoundEmpty | Self::TypeEmpty => format!("for<{}> ", sugg),
|
||||
Self::BoundTail | Self::TypeTail => format!(", {}", sugg),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> Into<MissingLifetimeSpot<'tcx>> for &'tcx hir::Generics<'tcx> {
|
||||
fn into(self) -> MissingLifetimeSpot<'tcx> {
|
||||
MissingLifetimeSpot::Generics(self)
|
||||
}
|
||||
}
|
||||
|
||||
struct LifetimeContext<'a, 'tcx> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
crate struct LifetimeContext<'a, 'tcx> {
|
||||
crate tcx: TyCtxt<'tcx>,
|
||||
map: &'a mut NamedRegionMap,
|
||||
scope: ScopeRef<'a>,
|
||||
|
||||
|
@ -220,7 +184,7 @@ struct LifetimeContext<'a, 'tcx> {
|
|||
|
||||
/// When encountering an undefined named lifetime, we will suggest introducing it in these
|
||||
/// places.
|
||||
missing_named_lifetime_spots: Vec<MissingLifetimeSpot<'tcx>>,
|
||||
crate missing_named_lifetime_spots: Vec<MissingLifetimeSpot<'tcx>>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -1879,49 +1843,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
|
|||
|
||||
self.insert_lifetime(lifetime_ref, def);
|
||||
} else {
|
||||
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");
|
||||
for missing in &self.missing_named_lifetime_spots {
|
||||
match missing {
|
||||
MissingLifetimeSpot::Generics(generics) => {
|
||||
let (span, sugg) = match &generics.params {
|
||||
[] => (generics.span, format!("<{}>", lifetime_ref)),
|
||||
[param, ..] => {
|
||||
(param.span.shrink_to_lo(), format!("{}, ", lifetime_ref))
|
||||
}
|
||||
};
|
||||
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();
|
||||
self.emit_undeclared_lifetime_error(lifetime_ref);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2461,7 +2383,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
|
|||
}
|
||||
};
|
||||
|
||||
let mut err = report_missing_lifetime_specifiers(self.tcx.sess, span, lifetime_refs.len());
|
||||
let mut err = self.report_missing_lifetime_specifiers(span, lifetime_refs.len());
|
||||
let mut add_label = true;
|
||||
|
||||
if let Some(params) = error {
|
||||
|
@ -2470,14 +2392,11 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
if add_label {
|
||||
add_missing_lifetime_specifiers_label(
|
||||
self.add_missing_lifetime_specifiers_label(
|
||||
&mut err,
|
||||
self.tcx.sess.source_map(),
|
||||
span,
|
||||
lifetime_refs.len(),
|
||||
&lifetime_names,
|
||||
self.tcx.sess.source_map().span_to_snippet(span).ok().as_ref().map(|s| s.as_str()),
|
||||
&self.missing_named_lifetime_spots,
|
||||
error.map(|p| &p[..]).unwrap_or(&[]),
|
||||
);
|
||||
}
|
||||
|
@ -2827,27 +2746,6 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
|
|||
let old_value = self.map.defs.remove(&lifetime_ref.hir_id);
|
||||
assert_eq!(old_value, Some(bad_def));
|
||||
}
|
||||
|
||||
fn is_trait_ref_fn_scope(&mut self, trait_ref: &'tcx hir::PolyTraitRef<'tcx>) -> bool {
|
||||
if let Res::Def(_, did) = trait_ref.trait_ref.path.res {
|
||||
if [
|
||||
self.tcx.lang_items().fn_once_trait(),
|
||||
self.tcx.lang_items().fn_trait(),
|
||||
self.tcx.lang_items().fn_mut_trait(),
|
||||
]
|
||||
.contains(&Some(did))
|
||||
{
|
||||
let (span, span_type) = match &trait_ref.bound_generic_params {
|
||||
[] => (trait_ref.span.shrink_to_lo(), ForLifetimeSpanType::BoundEmpty),
|
||||
[.., bound] => (bound.span.shrink_to_hi(), ForLifetimeSpanType::BoundTail),
|
||||
};
|
||||
self.missing_named_lifetime_spots
|
||||
.push(MissingLifetimeSpot::HigherRanked { span, span_type });
|
||||
return true;
|
||||
}
|
||||
};
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// Detects late-bound lifetimes and inserts them into
|
||||
|
|
Loading…
Reference in New Issue