about summary refs log tree commit diff
path: root/compiler/rustc_lint/src/non_local_def.rs
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_lint/src/non_local_def.rs')
-rw-r--r--compiler/rustc_lint/src/non_local_def.rs214
1 files changed, 179 insertions, 35 deletions
diff --git a/compiler/rustc_lint/src/non_local_def.rs b/compiler/rustc_lint/src/non_local_def.rs
index 87ee5f53628..42b03f47a5b 100644
--- a/compiler/rustc_lint/src/non_local_def.rs
+++ b/compiler/rustc_lint/src/non_local_def.rs
@@ -1,3 +1,6 @@
+use rustc_errors::MultiSpan;
+use rustc_hir::intravisit::{self, Visitor};
+use rustc_hir::HirId;
 use rustc_hir::{def::DefKind, Body, Item, ItemKind, Node, TyKind};
 use rustc_hir::{Path, QPath};
 use rustc_infer::infer::InferCtxt;
@@ -7,12 +10,13 @@ use rustc_middle::ty::{EarlyBinder, TraitRef, TypeSuperFoldable};
 use rustc_session::{declare_lint, impl_lint_pass};
 use rustc_span::def_id::{DefId, LOCAL_CRATE};
 use rustc_span::Span;
-use rustc_span::{sym, symbol::kw, ExpnKind, MacroKind};
+use rustc_span::{sym, symbol::kw, ExpnKind, MacroKind, Symbol};
 use rustc_trait_selection::infer::TyCtxtInferExt;
 use rustc_trait_selection::traits::error_reporting::ambiguity::{
     compute_applicable_impls_for_diagnostics, CandidateSource,
 };
 
+use crate::fluent_generated as fluent;
 use crate::lints::{NonLocalDefinitionsCargoUpdateNote, NonLocalDefinitionsDiag};
 use crate::{LateContext, LateLintPass, LintContext};
 
@@ -134,35 +138,8 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions {
                 };
 
                 // Part 1: Is the Self type local?
-                let self_ty_has_local_parent = match impl_.self_ty.kind {
-                    TyKind::Path(QPath::Resolved(_, ty_path)) => {
-                        path_has_local_parent(ty_path, cx, parent, parent_parent)
-                    }
-                    TyKind::TraitObject([principle_poly_trait_ref, ..], _, _) => {
-                        path_has_local_parent(
-                            principle_poly_trait_ref.trait_ref.path,
-                            cx,
-                            parent,
-                            parent_parent,
-                        )
-                    }
-                    TyKind::TraitObject([], _, _)
-                    | TyKind::InferDelegation(_, _)
-                    | TyKind::Slice(_)
-                    | TyKind::Array(_, _)
-                    | TyKind::Ptr(_)
-                    | TyKind::Ref(_, _)
-                    | TyKind::BareFn(_)
-                    | TyKind::Never
-                    | TyKind::Tup(_)
-                    | TyKind::Path(_)
-                    | TyKind::Pat(..)
-                    | TyKind::AnonAdt(_)
-                    | TyKind::OpaqueDef(_, _, _)
-                    | TyKind::Typeof(_)
-                    | TyKind::Infer
-                    | TyKind::Err(_) => false,
-                };
+                let self_ty_has_local_parent =
+                    ty_has_local_parent(&impl_.self_ty.kind, cx, parent, parent_parent);
 
                 if self_ty_has_local_parent {
                     return;
@@ -202,8 +179,7 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions {
                 // Get the span of the parent const item ident (if it's a not a const anon).
                 //
                 // Used to suggest changing the const item to a const anon.
-                let span_for_const_anon_suggestion = if self.body_depth == 1
-                    && parent_def_kind == DefKind::Const
+                let span_for_const_anon_suggestion = if parent_def_kind == DefKind::Const
                     && parent_opt_item_name != Some(kw::Underscore)
                     && let Some(parent) = parent.as_local()
                     && let Node::Item(item) = cx.tcx.hir_node_by_def_id(parent)
@@ -215,9 +191,76 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions {
                     None
                 };
 
+                let mut collector = PathCollector { paths: Vec::new() };
+                collector.visit_ty(&impl_.self_ty);
+                if let Some(of_trait) = &impl_.of_trait {
+                    collector.visit_trait_ref(of_trait);
+                }
+                collector.visit_generics(&impl_.generics);
+
+                let mut may_move: Vec<Span> = collector
+                    .paths
+                    .into_iter()
+                    .filter_map(|path| {
+                        if let Some(did) = path.res.opt_def_id()
+                            && did_has_local_parent(did, cx.tcx, parent, parent_parent)
+                        {
+                            Some(cx.tcx.def_span(did))
+                        } else {
+                            None
+                        }
+                    })
+                    .collect();
+                may_move.sort();
+                may_move.dedup();
+
+                let const_anon = matches!(parent_def_kind, DefKind::Const | DefKind::Static { .. })
+                    .then_some(span_for_const_anon_suggestion);
+
+                let may_remove = match &impl_.self_ty.kind {
+                    TyKind::Ptr(mut_ty) | TyKind::Ref(_, mut_ty)
+                        if ty_has_local_parent(&mut_ty.ty.kind, cx, parent, parent_parent) =>
+                    {
+                        let type_ =
+                            if matches!(impl_.self_ty.kind, TyKind::Ptr(_)) { "*" } else { "&" };
+                        let part = format!("{}{}", type_, mut_ty.mutbl.prefix_str());
+                        Some((impl_.self_ty.span.shrink_to_lo().until(mut_ty.ty.span), part))
+                    }
+                    _ => None,
+                };
+
+                let impl_span = item.span.shrink_to_lo().to(impl_.self_ty.span);
+                let mut ms = MultiSpan::from_span(impl_span);
+
+                let (self_ty_span, self_ty_str) =
+                    self_ty_kind_for_diagnostic(&impl_.self_ty, cx.tcx);
+
+                ms.push_span_label(
+                    self_ty_span,
+                    fluent::lint_non_local_definitions_self_ty_not_local,
+                );
+                let of_trait_str = if let Some(of_trait) = &impl_.of_trait {
+                    ms.push_span_label(
+                        path_span_without_args(&of_trait.path),
+                        fluent::lint_non_local_definitions_of_trait_not_local,
+                    );
+                    Some(path_name_to_string(&of_trait.path))
+                } else {
+                    None
+                };
+                let move_to = if may_move.is_empty() {
+                    ms.push_span_label(
+                        cx.tcx.def_span(parent),
+                        fluent::lint_non_local_definitions_impl_move_help,
+                    );
+                    None
+                } else {
+                    Some((cx.tcx.def_span(parent), may_move))
+                };
+
                 cx.emit_span_lint(
                     NON_LOCAL_DEFINITIONS,
-                    item.span,
+                    ms,
                     NonLocalDefinitionsDiag::Impl {
                         depth: self.body_depth,
                         body_kind_descr: cx.tcx.def_kind_descr(parent_def_kind, parent),
@@ -225,7 +268,12 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions {
                             .map(|s| s.to_ident_string())
                             .unwrap_or_else(|| "<unnameable>".to_string()),
                         cargo_update: cargo_update(),
-                        const_anon: span_for_const_anon_suggestion,
+                        const_anon,
+                        self_ty_str,
+                        of_trait_str,
+                        move_to,
+                        may_remove,
+                        has_trait: impl_.of_trait.is_some(),
                     },
                 )
             }
@@ -250,7 +298,6 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions {
                         cargo_update: cargo_update(),
                         help: (!is_at_toplevel_doctest).then_some(()),
                         doctest_help: is_at_toplevel_doctest.then_some(()),
-                        notes: (),
                     },
                 )
             }
@@ -343,6 +390,54 @@ impl<'a, 'tcx, F: FnMut(DefId) -> bool> TypeFolder<TyCtxt<'tcx>>
     }
 }
 
+/// Simple hir::Path collector
+struct PathCollector<'tcx> {
+    paths: Vec<Path<'tcx>>,
+}
+
+impl<'tcx> Visitor<'tcx> for PathCollector<'tcx> {
+    fn visit_path(&mut self, path: &Path<'tcx>, _id: HirId) {
+        self.paths.push(path.clone()); // need to clone, bc of the restricted lifetime
+        intravisit::walk_path(self, path)
+    }
+}
+
+/// Given a `Ty` we check if the (outermost) type is local.
+fn ty_has_local_parent(
+    ty_kind: &TyKind<'_>,
+    cx: &LateContext<'_>,
+    impl_parent: DefId,
+    impl_parent_parent: Option<DefId>,
+) -> bool {
+    match ty_kind {
+        TyKind::Path(QPath::Resolved(_, ty_path)) => {
+            path_has_local_parent(ty_path, cx, impl_parent, impl_parent_parent)
+        }
+        TyKind::TraitObject([principle_poly_trait_ref, ..], _, _) => path_has_local_parent(
+            principle_poly_trait_ref.trait_ref.path,
+            cx,
+            impl_parent,
+            impl_parent_parent,
+        ),
+        TyKind::TraitObject([], _, _)
+        | TyKind::InferDelegation(_, _)
+        | TyKind::Slice(_)
+        | TyKind::Array(_, _)
+        | TyKind::Ptr(_)
+        | TyKind::Ref(_, _)
+        | TyKind::BareFn(_)
+        | TyKind::Never
+        | TyKind::Tup(_)
+        | TyKind::Path(_)
+        | TyKind::Pat(..)
+        | TyKind::AnonAdt(_)
+        | TyKind::OpaqueDef(_, _, _)
+        | TyKind::Typeof(_)
+        | TyKind::Infer
+        | TyKind::Err(_) => false,
+    }
+}
+
 /// Given a path and a parent impl def id, this checks if the if parent resolution
 /// def id correspond to the def id of the parent impl definition.
 ///
@@ -384,3 +479,52 @@ fn did_has_local_parent(
             false
         }
 }
+
+/// Return for a given `Path` the span until the last args
+fn path_span_without_args(path: &Path<'_>) -> Span {
+    if let Some(args) = &path.segments.last().unwrap().args {
+        path.span.until(args.span_ext)
+    } else {
+        path.span
+    }
+}
+
+/// Return a "error message-able" ident for the last segment of the `Path`
+fn path_name_to_string(path: &Path<'_>) -> String {
+    path.segments.last().unwrap().ident.name.to_ident_string()
+}
+
+/// Compute the `Span` and visual representation for the `Self` we want to point at;
+/// It follows part of the actual logic of non-local, and if possible return the least
+/// amount possible for the span and representation.
+fn self_ty_kind_for_diagnostic(ty: &rustc_hir::Ty<'_>, tcx: TyCtxt<'_>) -> (Span, String) {
+    match ty.kind {
+        TyKind::Path(QPath::Resolved(_, ty_path)) => (
+            path_span_without_args(ty_path),
+            ty_path
+                .res
+                .opt_def_id()
+                .map(|did| tcx.opt_item_name(did))
+                .flatten()
+                .as_ref()
+                .map(|s| Symbol::as_str(s))
+                .unwrap_or("<unnameable>")
+                .to_string(),
+        ),
+        TyKind::TraitObject([principle_poly_trait_ref, ..], _, _) => {
+            let path = &principle_poly_trait_ref.trait_ref.path;
+            (
+                path_span_without_args(path),
+                path.res
+                    .opt_def_id()
+                    .map(|did| tcx.opt_item_name(did))
+                    .flatten()
+                    .as_ref()
+                    .map(|s| Symbol::as_str(s))
+                    .unwrap_or("<unnameable>")
+                    .to_string(),
+            )
+        }
+        _ => (ty.span, rustc_hir_pretty::ty_to_string(&tcx, ty)),
+    }
+}