Skip to content

Commit 5c25f07

Browse files
committed
Move FindPanicUnwrap to missing_headers.rs
1 parent 6166f60 commit 5c25f07

File tree

2 files changed

+79
-99
lines changed

2 files changed

+79
-99
lines changed

clippy_lints/src/doc/missing_headers.rs

Lines changed: 73 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
use super::{DocHeaders, MISSING_ERRORS_DOC, MISSING_PANICS_DOC, MISSING_SAFETY_DOC, UNNECESSARY_SAFETY_DOC};
22
use clippy_utils::diagnostics::{span_lint, span_lint_and_note};
3+
use clippy_utils::macros::{is_panic, root_macro_call_first_node};
34
use clippy_utils::ty::{implements_trait_with_env, is_type_diagnostic_item};
4-
use clippy_utils::{is_doc_hidden, return_ty};
5-
use rustc_hir::{BodyId, FnSig, OwnerId, Safety};
5+
use clippy_utils::visitors::Visitable;
6+
use clippy_utils::{is_doc_hidden, method_chain_args, return_ty};
7+
use rustc_hir::intravisit::{self, Visitor};
8+
use rustc_hir::{AnonConst, BodyId, Expr, FnSig, OwnerId, Safety};
69
use rustc_lint::LateContext;
10+
use rustc_middle::hir::nested_filter::OnlyBodies;
711
use rustc_middle::ty;
812
use rustc_span::{Span, sym};
913

@@ -13,7 +17,6 @@ pub fn check(
1317
sig: FnSig<'_>,
1418
headers: DocHeaders,
1519
body_id: Option<BodyId>,
16-
panic_info: Option<(Span, bool)>,
1720
check_private_items: bool,
1821
) {
1922
if !check_private_items && !cx.effective_visibilities.is_exported(owner_id.def_id) {
@@ -46,13 +49,16 @@ pub fn check(
4649
),
4750
_ => (),
4851
}
49-
if !headers.panics && panic_info.is_some_and(|el| !el.1) {
52+
if !headers.panics
53+
&& let Some(body_id) = body_id
54+
&& let Some((panic_span, false)) = FindPanicUnwrap::find_span(cx, body_id)
55+
{
5056
span_lint_and_note(
5157
cx,
5258
MISSING_PANICS_DOC,
5359
span,
5460
"docs for function which may panic missing `# Panics` section",
55-
panic_info.map(|el| el.0),
61+
Some(panic_span),
5662
"first possible panic found here",
5763
);
5864
}
@@ -89,3 +95,65 @@ pub fn check(
8995
}
9096
}
9197
}
98+
99+
struct FindPanicUnwrap<'a, 'tcx> {
100+
cx: &'a LateContext<'tcx>,
101+
is_const: bool,
102+
panic_span: Option<Span>,
103+
typeck_results: &'tcx ty::TypeckResults<'tcx>,
104+
}
105+
106+
impl<'a, 'tcx> FindPanicUnwrap<'a, 'tcx> {
107+
pub fn find_span(cx: &'a LateContext<'tcx>, body_id: BodyId) -> Option<(Span, bool)> {
108+
let mut vis = Self {
109+
cx,
110+
is_const: false,
111+
panic_span: None,
112+
typeck_results: cx.tcx.typeck_body(body_id),
113+
};
114+
cx.tcx.hir_body(body_id).visit(&mut vis);
115+
vis.panic_span.map(|el| (el, vis.is_const))
116+
}
117+
}
118+
119+
impl<'tcx> Visitor<'tcx> for FindPanicUnwrap<'_, 'tcx> {
120+
type NestedFilter = OnlyBodies;
121+
122+
fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
123+
if self.panic_span.is_some() {
124+
return;
125+
}
126+
127+
if let Some(macro_call) = root_macro_call_first_node(self.cx, expr) {
128+
if is_panic(self.cx, macro_call.def_id)
129+
|| matches!(
130+
self.cx.tcx.item_name(macro_call.def_id).as_str(),
131+
"assert" | "assert_eq" | "assert_ne"
132+
)
133+
{
134+
self.is_const = self.cx.tcx.hir_is_inside_const_context(expr.hir_id);
135+
self.panic_span = Some(macro_call.span);
136+
}
137+
}
138+
139+
// check for `unwrap` and `expect` for both `Option` and `Result`
140+
if let Some(arglists) = method_chain_args(expr, &["unwrap"]).or(method_chain_args(expr, &["expect"])) {
141+
let receiver_ty = self.typeck_results.expr_ty(arglists[0].0).peel_refs();
142+
if is_type_diagnostic_item(self.cx, receiver_ty, sym::Option)
143+
|| is_type_diagnostic_item(self.cx, receiver_ty, sym::Result)
144+
{
145+
self.panic_span = Some(expr.span);
146+
}
147+
}
148+
149+
// and check sub-expressions
150+
intravisit::walk_expr(self, expr);
151+
}
152+
153+
// Panics in const blocks will cause compilation to fail.
154+
fn visit_anon_const(&mut self, _: &'tcx AnonConst) {}
155+
156+
fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
157+
self.cx.tcx
158+
}
159+
}

clippy_lints/src/doc/mod.rs

Lines changed: 6 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,8 @@
33
use clippy_config::Conf;
44
use clippy_utils::attrs::is_doc_hidden;
55
use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_then};
6-
use clippy_utils::macros::{is_panic, root_macro_call_first_node};
76
use clippy_utils::source::snippet_opt;
8-
use clippy_utils::ty::is_type_diagnostic_item;
9-
use clippy_utils::visitors::Visitable;
10-
use clippy_utils::{is_entrypoint_fn, is_trait_impl_item, method_chain_args};
7+
use clippy_utils::{is_entrypoint_fn, is_trait_impl_item};
118
use pulldown_cmark::Event::{
129
Code, DisplayMath, End, FootnoteReference, HardBreak, Html, InlineHtml, InlineMath, Rule, SoftBreak, Start,
1310
TaskListMarker, Text,
@@ -16,18 +13,15 @@ use pulldown_cmark::Tag::{BlockQuote, CodeBlock, FootnoteDefinition, Heading, It
1613
use pulldown_cmark::{BrokenLink, CodeBlockKind, CowStr, Options, TagEnd};
1714
use rustc_data_structures::fx::FxHashSet;
1815
use rustc_errors::Applicability;
19-
use rustc_hir::intravisit::{self, Visitor};
20-
use rustc_hir::{AnonConst, Attribute, Expr, ImplItemKind, ItemKind, Node, Safety, TraitItemKind};
16+
use rustc_hir::{Attribute, ImplItemKind, ItemKind, Node, Safety, TraitItemKind};
2117
use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext};
22-
use rustc_middle::hir::nested_filter;
23-
use rustc_middle::ty;
2418
use rustc_resolve::rustdoc::{
2519
DocFragment, add_doc_fragment, attrs_to_doc_fragments, main_body_opts, source_span_for_markdown_range,
2620
span_of_fragments,
2721
};
2822
use rustc_session::impl_lint_pass;
23+
use rustc_span::Span;
2924
use rustc_span::edition::Edition;
30-
use rustc_span::{Span, sym};
3125
use std::ops::Range;
3226
use url::Url;
3327

@@ -657,20 +651,16 @@ impl<'tcx> LateLintPass<'tcx> for Documentation {
657651
self.check_private_items,
658652
);
659653
match item.kind {
660-
ItemKind::Fn { sig, body: body_id, .. } => {
654+
ItemKind::Fn { sig, body, .. } => {
661655
if !(is_entrypoint_fn(cx, item.owner_id.to_def_id())
662656
|| item.span.in_external_macro(cx.tcx.sess.source_map()))
663657
{
664-
let body = cx.tcx.hir_body(body_id);
665-
666-
let panic_info = FindPanicUnwrap::find_span(cx, cx.tcx.typeck(item.owner_id), body.value);
667658
missing_headers::check(
668659
cx,
669660
item.owner_id,
670661
sig,
671662
headers,
672-
Some(body_id),
673-
panic_info,
663+
Some(body),
674664
self.check_private_items,
675665
);
676666
}
@@ -697,32 +687,20 @@ impl<'tcx> LateLintPass<'tcx> for Documentation {
697687
if let TraitItemKind::Fn(sig, ..) = trait_item.kind
698688
&& !trait_item.span.in_external_macro(cx.tcx.sess.source_map())
699689
{
700-
missing_headers::check(
701-
cx,
702-
trait_item.owner_id,
703-
sig,
704-
headers,
705-
None,
706-
None,
707-
self.check_private_items,
708-
);
690+
missing_headers::check(cx, trait_item.owner_id, sig, headers, None, self.check_private_items);
709691
}
710692
},
711693
Node::ImplItem(impl_item) => {
712694
if let ImplItemKind::Fn(sig, body_id) = impl_item.kind
713695
&& !impl_item.span.in_external_macro(cx.tcx.sess.source_map())
714696
&& !is_trait_impl_item(cx, impl_item.hir_id())
715697
{
716-
let body = cx.tcx.hir_body(body_id);
717-
718-
let panic_span = FindPanicUnwrap::find_span(cx, cx.tcx.typeck(impl_item.owner_id), body.value);
719698
missing_headers::check(
720699
cx,
721700
impl_item.owner_id,
722701
sig,
723702
headers,
724703
Some(body_id),
725-
panic_span,
726704
self.check_private_items,
727705
);
728706
}
@@ -1169,72 +1147,6 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
11691147
headers
11701148
}
11711149

1172-
struct FindPanicUnwrap<'a, 'tcx> {
1173-
cx: &'a LateContext<'tcx>,
1174-
is_const: bool,
1175-
panic_span: Option<Span>,
1176-
typeck_results: &'tcx ty::TypeckResults<'tcx>,
1177-
}
1178-
1179-
impl<'a, 'tcx> FindPanicUnwrap<'a, 'tcx> {
1180-
pub fn find_span(
1181-
cx: &'a LateContext<'tcx>,
1182-
typeck_results: &'tcx ty::TypeckResults<'tcx>,
1183-
body: impl Visitable<'tcx>,
1184-
) -> Option<(Span, bool)> {
1185-
let mut vis = Self {
1186-
cx,
1187-
is_const: false,
1188-
panic_span: None,
1189-
typeck_results,
1190-
};
1191-
body.visit(&mut vis);
1192-
vis.panic_span.map(|el| (el, vis.is_const))
1193-
}
1194-
}
1195-
1196-
impl<'tcx> Visitor<'tcx> for FindPanicUnwrap<'_, 'tcx> {
1197-
type NestedFilter = nested_filter::OnlyBodies;
1198-
1199-
fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
1200-
if self.panic_span.is_some() {
1201-
return;
1202-
}
1203-
1204-
if let Some(macro_call) = root_macro_call_first_node(self.cx, expr) {
1205-
if is_panic(self.cx, macro_call.def_id)
1206-
|| matches!(
1207-
self.cx.tcx.item_name(macro_call.def_id).as_str(),
1208-
"assert" | "assert_eq" | "assert_ne"
1209-
)
1210-
{
1211-
self.is_const = self.cx.tcx.hir_is_inside_const_context(expr.hir_id);
1212-
self.panic_span = Some(macro_call.span);
1213-
}
1214-
}
1215-
1216-
// check for `unwrap` and `expect` for both `Option` and `Result`
1217-
if let Some(arglists) = method_chain_args(expr, &["unwrap"]).or(method_chain_args(expr, &["expect"])) {
1218-
let receiver_ty = self.typeck_results.expr_ty(arglists[0].0).peel_refs();
1219-
if is_type_diagnostic_item(self.cx, receiver_ty, sym::Option)
1220-
|| is_type_diagnostic_item(self.cx, receiver_ty, sym::Result)
1221-
{
1222-
self.panic_span = Some(expr.span);
1223-
}
1224-
}
1225-
1226-
// and check sub-expressions
1227-
intravisit::walk_expr(self, expr);
1228-
}
1229-
1230-
// Panics in const blocks will cause compilation to fail.
1231-
fn visit_anon_const(&mut self, _: &'tcx AnonConst) {}
1232-
1233-
fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
1234-
self.cx.tcx
1235-
}
1236-
}
1237-
12381150
#[expect(clippy::range_plus_one)] // inclusive ranges aren't the same type
12391151
fn looks_like_refdef(doc: &str, range: Range<usize>) -> Option<Range<usize>> {
12401152
if range.end < range.start {

0 commit comments

Comments
 (0)