Suggest standalone doctest for non-local impl defs
This commit is contained in:
parent
8217b412a2
commit
ab0e72781f
6 changed files with 121 additions and 38 deletions
|
@ -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);
|
||||
|
|
|
@ -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(()),
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue