X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=compiler%2Frustc_resolve%2Fsrc%2Flate%2Fdiagnostics.rs;h=673b2e3a55a35f22df2ef43d5cd4f9f043251487;hb=49c82f31a85f04a709810de4ccfb8ba765c1377b;hp=a5243bf8ac3f8ee86f4703e73713487cc9bc063f;hpb=579d26876ddc5426be32cca1a026b6fd7fc5be24;p=rust.git diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index a5243bf8ac3..673b2e3a55a 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -1,16 +1,17 @@ use crate::diagnostics::{ImportSuggestion, LabelSuggestion, TypoSuggestion}; use crate::late::lifetimes::{ElisionFailureInfo, LifetimeContext}; use crate::late::{AliasPossibility, LateResolutionVisitor, RibKind}; -use crate::late::{LifetimeBinderKind, LifetimeRibKind}; +use crate::late::{LifetimeBinderKind, LifetimeRibKind, LifetimeUseSet}; use crate::path_names_to_string; -use crate::{Finalize, Module, ModuleKind, ModuleOrUniformRoot}; +use crate::{Module, ModuleKind, ModuleOrUniformRoot}; use crate::{PathResult, PathSource, Segment}; -use rustc_ast::visit::FnKind; +use rustc_ast::visit::{FnCtxt, FnKind, LifetimeCtxt}; use rustc_ast::{ self as ast, AssocItemKind, Expr, ExprKind, GenericParam, GenericParamKind, Item, ItemKind, NodeId, Path, Ty, TyKind, }; +use rustc_ast_lowering::ResolverAstLowering; use rustc_ast_pretty::pprust::path_segment_to_string; use rustc_data_structures::fx::FxHashSet; use rustc_errors::{ @@ -22,6 +23,7 @@ use rustc_hir::def::{self, CtorKind, CtorOf, DefKind}; use rustc_hir::def_id::{DefId, CRATE_DEF_ID, LOCAL_CRATE}; use rustc_hir::PrimTy; +use rustc_session::lint; use rustc_session::parse::feature_err; use rustc_span::edition::Edition; use rustc_span::hygiene::MacroKind; @@ -57,13 +59,13 @@ fn action(&self) -> &'static str { } } -crate enum MissingLifetimeSpot<'tcx> { +pub(crate) enum MissingLifetimeSpot<'tcx> { Generics(&'tcx hir::Generics<'tcx>), HigherRanked { span: Span, span_type: ForLifetimeSpanType }, Static, } -crate enum ForLifetimeSpanType { +pub(crate) enum ForLifetimeSpanType { BoundEmpty, BoundTail, TypeEmpty, @@ -71,14 +73,14 @@ fn action(&self) -> &'static str { } impl ForLifetimeSpanType { - crate fn descr(&self) -> &'static str { + pub(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 { + pub(crate) fn suggestion(&self, sugg: &str) -> String { match self { Self::BoundEmpty | Self::TypeEmpty => format!("for<{}> ", sugg), Self::BoundTail | Self::TypeTail => format!(", {}", sugg), @@ -144,15 +146,22 @@ pub(crate) fn smart_resolve_report_errors( let is_enum_variant = &|res| matches!(res, Res::Def(DefKind::Variant, _)); // Make the base error. + struct BaseError<'a> { + msg: String, + fallback_label: String, + span: Span, + could_be_expr: bool, + suggestion: Option<(Span, &'a str, String)>, + } let mut expected = source.descr_expected(); let path_str = Segment::names_to_string(path); let item_str = path.last().unwrap().ident; - let (base_msg, fallback_label, base_span, could_be_expr) = if let Some(res) = res { - ( - format!("expected {}, found {} `{}`", expected, res.descr(), path_str), - format!("not a {}", expected), + let base_error = if let Some(res) = res { + BaseError { + msg: format!("expected {}, found {} `{}`", expected, res.descr(), path_str), + fallback_label: format!("not a {expected}"), span, - match res { + could_be_expr: match res { Res::Def(DefKind::Fn, _) => { // Verify whether this is a fn call or an Fn used as a type. self.r @@ -171,45 +180,78 @@ pub(crate) fn smart_resolve_report_errors( | Res::Local(_) => true, _ => false, }, - ) + suggestion: None, + } } else { let item_span = path.last().unwrap().ident.span; - let (mod_prefix, mod_str) = if path.len() == 1 { - (String::new(), "this scope".to_string()) + let (mod_prefix, mod_str, suggestion) = if path.len() == 1 { + debug!(?self.diagnostic_metadata.current_impl_items); + debug!(?self.diagnostic_metadata.current_function); + let suggestion = if let Some(items) = self.diagnostic_metadata.current_impl_items + && let Some((fn_kind, _)) = self.diagnostic_metadata.current_function + && self.current_trait_ref.is_none() + && let Some(FnCtxt::Assoc(_)) = fn_kind.ctxt() + && let Some(item) = items.iter().find(|i| { + if let AssocItemKind::Fn(fn_) = &i.kind + && !fn_.sig.decl.has_self() + && i.ident.name == item_str.name + { + debug!(?item_str.name); + debug!(?fn_.sig.decl.inputs); + return true + } + false + }) + { + Some(( + item_span, + "consider using the associated function", + format!("Self::{}", item.ident) + )) + } else { + None + }; + (String::new(), "this scope".to_string(), suggestion) } else if path.len() == 2 && path[0].ident.name == kw::PathRoot { if self.r.session.edition() > Edition::Edition2015 { // In edition 2018 onwards, the `::foo` syntax may only pull from the extern prelude // which overrides all other expectations of item type expected = "crate"; - (String::new(), "the list of imported crates".to_string()) + (String::new(), "the list of imported crates".to_string(), None) } else { - (String::new(), "the crate root".to_string()) + (String::new(), "the crate root".to_string(), None) } } else if path.len() == 2 && path[0].ident.name == kw::Crate { - (String::new(), "the crate root".to_string()) + (String::new(), "the crate root".to_string(), None) } else { let mod_path = &path[..path.len() - 1]; - let mod_prefix = match self.resolve_path(mod_path, Some(TypeNS), Finalize::No) { + let mod_prefix = match self.resolve_path(mod_path, Some(TypeNS), None) { PathResult::Module(ModuleOrUniformRoot::Module(module)) => module.res(), _ => None, } .map_or_else(String::new, |res| format!("{} ", res.descr())); - (mod_prefix, format!("`{}`", Segment::names_to_string(mod_path))) + (mod_prefix, format!("`{}`", Segment::names_to_string(mod_path)), None) }; - ( - format!("cannot find {} `{}` in {}{}", expected, item_str, mod_prefix, mod_str), - if path_str == "async" && expected.starts_with("struct") { + BaseError { + msg: format!("cannot find {expected} `{item_str}` in {mod_prefix}{mod_str}"), + fallback_label: if path_str == "async" && expected.starts_with("struct") { "`async` blocks are only allowed in Rust 2018 or later".to_string() } else { - format!("not found in {}", mod_str) + format!("not found in {mod_str}") }, - item_span, - false, - ) + span: item_span, + could_be_expr: false, + suggestion, + } }; let code = source.error_code(res.is_some()); - let mut err = self.r.session.struct_span_err_with_code(base_span, &base_msg, code); + let mut err = + self.r.session.struct_span_err_with_code(base_error.span, &base_error.msg, code); + + if let Some(sugg) = base_error.suggestion { + err.span_suggestion_verbose(sugg.0, sugg.1, sugg.2, Applicability::MaybeIncorrect); + } if let Some(span) = self.diagnostic_metadata.current_block_could_be_bare_struct_literal { err.multipart_suggestion( @@ -269,7 +311,7 @@ pub(crate) fn smart_resolve_report_errors( } } - self.detect_assoct_type_constraint_meant_as_path(base_span, &mut err); + self.detect_assoct_type_constraint_meant_as_path(base_error.span, &mut err); // Emit special messages for unresolved `Self` and `self`. if is_self_type(path, ns) { @@ -471,7 +513,7 @@ pub(crate) fn smart_resolve_report_errors( source, res, &path_str, - &fallback_label, + &base_error.fallback_label, ) { // We do this to avoid losing a secondary span when we override the main error span. self.r.add_typo_suggestion(&mut err, typo_sugg, ident_span); @@ -479,8 +521,9 @@ pub(crate) fn smart_resolve_report_errors( } } - let is_macro = base_span.from_expansion() && base_span.desugaring_kind().is_none(); - if !self.type_ascription_suggestion(&mut err, base_span) { + let is_macro = + base_error.span.from_expansion() && base_error.span.desugaring_kind().is_none(); + if !self.type_ascription_suggestion(&mut err, base_error.span) { let mut fallback = false; if let ( PathSource::Trait(AliasPossibility::Maybe), @@ -493,7 +536,7 @@ pub(crate) fn smart_resolve_report_errors( let spans: Vec = bounds .iter() .map(|bound| bound.span()) - .filter(|&sp| sp != base_span) + .filter(|&sp| sp != base_error.span) .collect(); let start_span = bounds.iter().map(|bound| bound.span()).next().unwrap(); @@ -515,7 +558,7 @@ pub(crate) fn smart_resolve_report_errors( multi_span.push_span_label(sp, msg); } multi_span.push_span_label( - base_span, + base_error.span, "expected this type to be a trait...".to_string(), ); err.span_help( @@ -525,14 +568,14 @@ pub(crate) fn smart_resolve_report_errors( ); if bounds.iter().all(|bound| match bound { ast::GenericBound::Outlives(_) => true, - ast::GenericBound::Trait(tr, _) => tr.span == base_span, + ast::GenericBound::Trait(tr, _) => tr.span == base_error.span, }) { let mut sugg = vec![]; - if base_span != start_span { - sugg.push((start_span.until(base_span), String::new())); + if base_error.span != start_span { + sugg.push((start_span.until(base_error.span), String::new())); } - if base_span != end_span { - sugg.push((base_span.shrink_to_hi().to(end_span), String::new())); + if base_error.span != end_span { + sugg.push((base_error.span.shrink_to_hi().to(end_span), String::new())); } err.multipart_suggestion( @@ -550,7 +593,7 @@ pub(crate) fn smart_resolve_report_errors( fallback = true; match self.diagnostic_metadata.current_let_binding { Some((pat_sp, Some(ty_sp), None)) - if ty_sp.contains(base_span) && could_be_expr => + if ty_sp.contains(base_error.span) && base_error.could_be_expr => { err.span_suggestion_short( pat_sp.between(ty_sp), @@ -568,7 +611,7 @@ pub(crate) fn smart_resolve_report_errors( } if fallback { // Fallback label. - err.span_label(base_span, fallback_label); + err.span_label(base_error.span, base_error.fallback_label); } } if let Some(err_code) = &err.code { @@ -648,7 +691,7 @@ fn get_single_associated_item( if let crate::PathSource::TraitItem(_) = source { let mod_path = &path[..path.len() - 1]; if let PathResult::Module(ModuleOrUniformRoot::Module(module)) = - self.resolve_path(mod_path, None, Finalize::No) + self.resolve_path(mod_path, None, None) { let resolutions = self.r.resolutions(module).borrow(); let targets: Vec<_> = @@ -1178,7 +1221,7 @@ fn smart_resolve_context_dependent_help( /// Given the target `ident` and `kind`, search for the similarly named associated item /// in `self.current_trait_ref`. - crate fn find_similarly_named_assoc_item( + pub(crate) fn find_similarly_named_assoc_item( &mut self, ident: Symbol, kind: &AssocItemKind, @@ -1362,7 +1405,7 @@ fn lookup_typo_candidate( // Search in module. let mod_path = &path[..path.len() - 1]; if let PathResult::Module(ModuleOrUniformRoot::Module(module)) = - self.resolve_path(mod_path, Some(TypeNS), Finalize::No) + self.resolve_path(mod_path, Some(TypeNS), None) { self.r.add_module_candidates(module, &mut names, &filter_fn); } @@ -1686,7 +1729,7 @@ fn suggest_using_enum_variant( } } - crate fn report_missing_type_error( + pub(crate) fn report_missing_type_error( &self, path: &[Segment], ) -> Option<(Span, &'static str, String, Applicability)> { @@ -1766,7 +1809,7 @@ fn suggest_using_enum_variant( /// Given the target `label`, search the `rib_index`th label rib for similarly named labels, /// optionally returning the closest match and whether it is reachable. - crate fn suggestion_for_label_in_rib( + pub(crate) fn suggestion_for_label_in_rib( &self, rib_index: usize, label: Ident, @@ -1791,7 +1834,77 @@ fn suggest_using_enum_variant( }) } - crate fn emit_undeclared_lifetime_error( + pub(crate) fn maybe_report_lifetime_uses( + &mut self, + generics_span: Span, + params: &[ast::GenericParam], + ) { + for (param_index, param) in params.iter().enumerate() { + let GenericParamKind::Lifetime = param.kind else { continue }; + + let def_id = self.r.local_def_id(param.id); + + let use_set = self.lifetime_uses.remove(&def_id); + debug!( + "Use set for {:?}({:?} at {:?}) is {:?}", + def_id, param.ident, param.ident.span, use_set + ); + + let deletion_span = || { + if params.len() == 1 { + // if sole lifetime, remove the entire `<>` brackets + generics_span + } else if param_index == 0 { + // if removing within `<>` brackets, we also want to + // delete a leading or trailing comma as appropriate + param.span().to(params[param_index + 1].span().shrink_to_lo()) + } else { + // if removing within `<>` brackets, we also want to + // delete a leading or trailing comma as appropriate + params[param_index - 1].span().shrink_to_hi().to(param.span()) + } + }; + match use_set { + Some(LifetimeUseSet::Many) => {} + Some(LifetimeUseSet::One { use_span, use_ctxt }) => { + debug!(?param.ident, ?param.ident.span, ?use_span); + + let elidable = matches!(use_ctxt, LifetimeCtxt::Rptr); + + let deletion_span = deletion_span(); + self.r.lint_buffer.buffer_lint_with_diagnostic( + lint::builtin::SINGLE_USE_LIFETIMES, + param.id, + param.ident.span, + &format!("lifetime parameter `{}` only used once", param.ident), + lint::BuiltinLintDiagnostics::SingleUseLifetime { + param_span: param.ident.span, + use_span: Some((use_span, elidable)), + deletion_span, + }, + ); + } + None => { + debug!(?param.ident, ?param.ident.span); + + let deletion_span = deletion_span(); + self.r.lint_buffer.buffer_lint_with_diagnostic( + lint::builtin::UNUSED_LIFETIMES, + param.id, + param.ident.span, + &format!("lifetime parameter `{}` never used", param.ident), + lint::BuiltinLintDiagnostics::SingleUseLifetime { + param_span: param.ident.span, + use_span: None, + deletion_span, + }, + ); + } + } + } + } + + pub(crate) fn emit_undeclared_lifetime_error( &self, lifetime_ref: &ast::Lifetime, outer_lifetime_ref: Option, @@ -1822,7 +1935,7 @@ fn suggest_using_enum_variant( for rib in self.lifetime_ribs.iter().rev() { match rib.kind { - LifetimeRibKind::Generics { parent: _, span, kind } => { + LifetimeRibKind::Generics { binder: _, span, kind } => { if !span.can_be_used_for_suggestions() && suggest_note { suggest_note = false; // Avoid displaying the same help multiple times. err.span_label( @@ -1887,7 +2000,7 @@ fn suggest_using_enum_variant( err.emit(); } - crate fn emit_non_static_lt_in_const_generic_error(&self, lifetime_ref: &ast::Lifetime) { + pub(crate) fn emit_non_static_lt_in_const_generic_error(&self, lifetime_ref: &ast::Lifetime) { struct_span_err!( self.r.session, lifetime_ref.ident.span, @@ -1905,7 +2018,10 @@ fn suggest_using_enum_variant( /// Non-static lifetimes are prohibited in anonymous constants under `min_const_generics`. /// This function will emit an error if `generic_const_exprs` is not enabled, the body identified by /// `body_id` is an anonymous constant and `lifetime_ref` is non-static. - crate fn maybe_emit_forbidden_non_static_lifetime_error(&self, lifetime_ref: &ast::Lifetime) { + pub(crate) fn maybe_emit_forbidden_non_static_lifetime_error( + &self, + lifetime_ref: &ast::Lifetime, + ) { let feature_active = self.r.session.features_untracked().generic_const_exprs; if !feature_active { feature_err( @@ -1920,7 +2036,7 @@ fn suggest_using_enum_variant( } impl<'tcx> LifetimeContext<'_, 'tcx> { - crate fn report_missing_lifetime_specifiers( + pub(crate) fn report_missing_lifetime_specifiers( &self, spans: Vec, count: usize, @@ -1935,7 +2051,7 @@ impl<'tcx> LifetimeContext<'_, 'tcx> { } /// Returns whether to add `'static` lifetime to the suggested lifetime list. - crate fn report_elision_failure( + pub(crate) fn report_elision_failure( &mut self, diag: &mut Diagnostic, params: &[ElisionFailureInfo], @@ -2013,7 +2129,10 @@ impl<'tcx> LifetimeContext<'_, 'tcx> { } } - crate fn is_trait_ref_fn_scope(&mut self, trait_ref: &'tcx hir::PolyTraitRef<'tcx>) -> bool { + pub(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(), @@ -2034,7 +2153,7 @@ impl<'tcx> LifetimeContext<'_, 'tcx> { false } - crate fn add_missing_lifetime_specifiers_label( + pub(crate) fn add_missing_lifetime_specifiers_label( &self, err: &mut Diagnostic, mut spans_with_counts: Vec<(Span, usize)>,