Suggest standalone doctest for non-local impl defs

This commit is contained in:
Urgau 2024-06-13 17:08:04 +02:00
parent 8217b412a2
commit ab0e72781f
6 changed files with 121 additions and 38 deletions

View file

@ -1358,6 +1358,7 @@ pub enum NonLocalDefinitionsDiag {
cargo_update: Option<NonLocalDefinitionsCargoUpdateNote>,
const_anon: Option<Option<Span>>,
move_to: Option<(Span, Vec<Span>)>,
doctest: bool,
may_remove: Option<(Span, String)>,
has_trait: bool,
self_ty_str: String,
@ -1383,6 +1384,7 @@ impl<'a> LintDiagnostic<'a, ()> for NonLocalDefinitionsDiag {
cargo_update,
const_anon,
move_to,
doctest,
may_remove,
has_trait,
self_ty_str,
@ -1411,6 +1413,9 @@ impl<'a> LintDiagnostic<'a, ()> for NonLocalDefinitionsDiag {
}
diag.span_help(ms, fluent::lint_non_local_definitions_impl_move_help);
}
if doctest {
diag.help(fluent::lint_doctest);
}
if let Some((span, part)) = may_remove {
diag.arg("may_remove_part", part);

View file

@ -111,6 +111,12 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions {
}
};
// determining if we are in a doctest context can't currently be determined
// by the code itself (there are no specific attributes), but fortunately rustdoc
// sets a perma-unstable env var for libtest so we just reuse that for now
let is_at_toplevel_doctest =
|| self.body_depth == 2 && std::env::var("UNSTABLE_RUSTDOC_TEST_PATH").is_ok();
match item.kind {
ItemKind::Impl(impl_) => {
// The RFC states:
@ -191,29 +197,6 @@ 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);
@ -248,14 +231,44 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions {
} 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
let (doctest, move_to) = if is_at_toplevel_doctest() {
(true, None)
} else {
Some((cx.tcx.def_span(parent), may_move))
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 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))
};
(false, move_to)
};
cx.emit_span_lint(
@ -272,6 +285,7 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions {
self_ty_str,
of_trait_str,
move_to,
doctest,
may_remove,
has_trait: impl_.of_trait.is_some(),
},
@ -280,12 +294,6 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions {
ItemKind::Macro(_macro, MacroKind::Bang)
if cx.tcx.has_attr(item.owner_id.def_id, sym::macro_export) =>
{
// determining we if are in a doctest context can't currently be determined
// by the code it-self (no specific attrs), but fortunatly rustdoc sets a
// perma-unstable env for libtest so we just re-use that env for now
let is_at_toplevel_doctest =
self.body_depth == 2 && std::env::var("UNSTABLE_RUSTDOC_TEST_PATH").is_ok();
cx.emit_span_lint(
NON_LOCAL_DEFINITIONS,
item.span,
@ -296,8 +304,8 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions {
.map(|s| s.to_ident_string())
.unwrap_or_else(|| "<unnameable>".to_string()),
cargo_update: cargo_update(),
help: (!is_at_toplevel_doctest).then_some(()),
doctest_help: is_at_toplevel_doctest.then_some(()),
help: (!is_at_toplevel_doctest()).then_some(()),
doctest_help: is_at_toplevel_doctest().then_some(()),
},
)
}