Skip to content

Commit f894a81

Browse files
committed
Fulfill expectations after first missing-panics-doc
1 parent 6ee4f34 commit f894a81

File tree

3 files changed

+63
-70
lines changed

3 files changed

+63
-70
lines changed

clippy_lints/src/doc/missing_headers.rs

+29-57
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,13 @@ use super::{DocHeaders, MISSING_ERRORS_DOC, MISSING_PANICS_DOC, MISSING_SAFETY_D
22
use clippy_utils::diagnostics::{span_lint, span_lint_and_note};
33
use clippy_utils::macros::{is_panic, root_macro_call_first_node};
44
use clippy_utils::ty::{get_type_diagnostic_name, implements_trait_with_env, is_type_diagnostic_item};
5-
use clippy_utils::visitors::Visitable;
5+
use clippy_utils::visitors::for_each_expr;
66
use clippy_utils::{fulfill_or_allowed, 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};
7+
use rustc_hir::{BodyId, FnSig, OwnerId, Safety};
98
use rustc_lint::LateContext;
10-
use rustc_middle::hir::nested_filter::OnlyBodies;
119
use rustc_middle::ty;
1210
use rustc_span::{Span, sym};
11+
use std::ops::ControlFlow;
1312

1413
pub fn check(
1514
cx: &LateContext<'_>,
@@ -51,7 +50,7 @@ pub fn check(
5150
}
5251
if !headers.panics
5352
&& let Some(body_id) = body_id
54-
&& let Some(panic_span) = FindPanicUnwrap::find_span(cx, body_id)
53+
&& let Some(panic_span) = find_panic(cx, body_id)
5554
{
5655
span_lint_and_note(
5756
cx,
@@ -96,65 +95,38 @@ pub fn check(
9695
}
9796
}
9897

99-
struct FindPanicUnwrap<'a, 'tcx> {
100-
cx: &'a LateContext<'tcx>,
101-
panic_span: Option<Span>,
102-
typeck_results: &'tcx ty::TypeckResults<'tcx>,
103-
}
104-
105-
impl<'a, 'tcx> FindPanicUnwrap<'a, 'tcx> {
106-
pub fn find_span(cx: &'a LateContext<'tcx>, body_id: BodyId) -> Option<Span> {
107-
let mut vis = Self {
108-
cx,
109-
panic_span: None,
110-
typeck_results: cx.tcx.typeck_body(body_id),
111-
};
112-
cx.tcx.hir_body(body_id).visit(&mut vis);
113-
vis.panic_span
114-
}
115-
}
116-
117-
impl<'tcx> Visitor<'tcx> for FindPanicUnwrap<'_, 'tcx> {
118-
type NestedFilter = OnlyBodies;
119-
120-
fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
121-
if self.panic_span.is_some() {
122-
return;
123-
}
124-
125-
if let Some(macro_call) = root_macro_call_first_node(self.cx, expr) {
126-
if (is_panic(self.cx, macro_call.def_id)
98+
fn find_panic(cx: &LateContext<'_>, body_id: BodyId) -> Option<Span> {
99+
let mut panic_span = None;
100+
let typeck = cx.tcx.typeck_body(body_id);
101+
for_each_expr(cx, cx.tcx.hir_body(body_id), |expr| {
102+
if let Some(macro_call) = root_macro_call_first_node(cx, expr)
103+
&& (is_panic(cx, macro_call.def_id)
127104
|| matches!(
128-
self.cx.tcx.get_diagnostic_name(macro_call.def_id),
105+
cx.tcx.get_diagnostic_name(macro_call.def_id),
129106
Some(sym::assert_macro | sym::assert_eq_macro | sym::assert_ne_macro)
130107
))
131-
&& !self.cx.tcx.hir_is_inside_const_context(expr.hir_id)
132-
&& !fulfill_or_allowed(self.cx, MISSING_PANICS_DOC, [expr.hir_id])
133-
{
134-
self.panic_span = Some(macro_call.span);
135-
}
108+
&& !cx.tcx.hir_is_inside_const_context(expr.hir_id)
109+
&& !fulfill_or_allowed(cx, MISSING_PANICS_DOC, [expr.hir_id])
110+
&& panic_span.is_none()
111+
{
112+
panic_span = Some(macro_call.span);
136113
}
137114

138115
// check for `unwrap` and `expect` for both `Option` and `Result`
139-
if let Some(arglists) = method_chain_args(expr, &["unwrap"]).or(method_chain_args(expr, &["expect"])) {
140-
let receiver_ty = self.typeck_results.expr_ty(arglists[0].0).peel_refs();
141-
if matches!(
142-
get_type_diagnostic_name(self.cx, receiver_ty),
116+
if let Some(arglists) = method_chain_args(expr, &["unwrap"]).or_else(|| method_chain_args(expr, &["expect"]))
117+
&& let receiver_ty = typeck.expr_ty(arglists[0].0).peel_refs()
118+
&& matches!(
119+
get_type_diagnostic_name(cx, receiver_ty),
143120
Some(sym::Option | sym::Result)
144-
) && !fulfill_or_allowed(self.cx, MISSING_PANICS_DOC, [expr.hir_id])
145-
{
146-
self.panic_span = Some(expr.span);
147-
}
121+
)
122+
&& !fulfill_or_allowed(cx, MISSING_PANICS_DOC, [expr.hir_id])
123+
&& panic_span.is_none()
124+
{
125+
panic_span = Some(expr.span);
148126
}
149127

150-
// and check sub-expressions
151-
intravisit::walk_expr(self, expr);
152-
}
153-
154-
// Panics in const blocks will cause compilation to fail.
155-
fn visit_anon_const(&mut self, _: &'tcx AnonConst) {}
156-
157-
fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
158-
self.cx.tcx
159-
}
128+
// Visit all nodes to fulfill any `#[expect]`s after the first linted panic
129+
ControlFlow::<!>::Continue(())
130+
});
131+
panic_span
160132
}

tests/ui/missing_panics_doc.rs

+9
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,15 @@ pub fn expect_allow_with_error(i: Option<isize>) {
181181
i.unwrap();
182182
}
183183

184+
pub fn expect_after_error(x: Option<u32>, y: Option<u32>) {
185+
//~^ missing_panics_doc
186+
187+
let x = x.unwrap();
188+
189+
#[expect(clippy::missing_panics_doc)]
190+
let y = y.unwrap();
191+
}
192+
184193
// all function must be triggered the lint.
185194
// `pub` is required, because the lint does not consider unreachable items
186195
pub mod issue10240 {

tests/ui/missing_panics_doc.stderr

+25-13
Original file line numberDiff line numberDiff line change
@@ -97,76 +97,88 @@ LL | i.unwrap();
9797
| ^^^^^^^^^^
9898

9999
error: docs for function which may panic missing `# Panics` section
100-
--> tests/ui/missing_panics_doc.rs:187:5
100+
--> tests/ui/missing_panics_doc.rs:184:1
101+
|
102+
LL | pub fn expect_after_error(x: Option<u32>, y: Option<u32>) {
103+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
104+
|
105+
note: first possible panic found here
106+
--> tests/ui/missing_panics_doc.rs:187:13
107+
|
108+
LL | let x = x.unwrap();
109+
| ^^^^^^^^^^
110+
111+
error: docs for function which may panic missing `# Panics` section
112+
--> tests/ui/missing_panics_doc.rs:196:5
101113
|
102114
LL | pub fn option_unwrap<T>(v: &[T]) -> &T {
103115
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
104116
|
105117
note: first possible panic found here
106-
--> tests/ui/missing_panics_doc.rs:190:9
118+
--> tests/ui/missing_panics_doc.rs:199:9
107119
|
108120
LL | o.unwrap()
109121
| ^^^^^^^^^^
110122

111123
error: docs for function which may panic missing `# Panics` section
112-
--> tests/ui/missing_panics_doc.rs:193:5
124+
--> tests/ui/missing_panics_doc.rs:202:5
113125
|
114126
LL | pub fn option_expect<T>(v: &[T]) -> &T {
115127
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
116128
|
117129
note: first possible panic found here
118-
--> tests/ui/missing_panics_doc.rs:196:9
130+
--> tests/ui/missing_panics_doc.rs:205:9
119131
|
120132
LL | o.expect("passed an empty thing")
121133
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
122134

123135
error: docs for function which may panic missing `# Panics` section
124-
--> tests/ui/missing_panics_doc.rs:199:5
136+
--> tests/ui/missing_panics_doc.rs:208:5
125137
|
126138
LL | pub fn result_unwrap<T>(v: &[T]) -> &T {
127139
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
128140
|
129141
note: first possible panic found here
130-
--> tests/ui/missing_panics_doc.rs:202:9
142+
--> tests/ui/missing_panics_doc.rs:211:9
131143
|
132144
LL | res.unwrap()
133145
| ^^^^^^^^^^^^
134146

135147
error: docs for function which may panic missing `# Panics` section
136-
--> tests/ui/missing_panics_doc.rs:205:5
148+
--> tests/ui/missing_panics_doc.rs:214:5
137149
|
138150
LL | pub fn result_expect<T>(v: &[T]) -> &T {
139151
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
140152
|
141153
note: first possible panic found here
142-
--> tests/ui/missing_panics_doc.rs:208:9
154+
--> tests/ui/missing_panics_doc.rs:217:9
143155
|
144156
LL | res.expect("passed an empty thing")
145157
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
146158

147159
error: docs for function which may panic missing `# Panics` section
148-
--> tests/ui/missing_panics_doc.rs:211:5
160+
--> tests/ui/missing_panics_doc.rs:220:5
149161
|
150162
LL | pub fn last_unwrap(v: &[u32]) -> u32 {
151163
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
152164
|
153165
note: first possible panic found here
154-
--> tests/ui/missing_panics_doc.rs:213:10
166+
--> tests/ui/missing_panics_doc.rs:222:10
155167
|
156168
LL | *v.last().unwrap()
157169
| ^^^^^^^^^^^^^^^^^
158170

159171
error: docs for function which may panic missing `# Panics` section
160-
--> tests/ui/missing_panics_doc.rs:216:5
172+
--> tests/ui/missing_panics_doc.rs:225:5
161173
|
162174
LL | pub fn last_expect(v: &[u32]) -> u32 {
163175
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
164176
|
165177
note: first possible panic found here
166-
--> tests/ui/missing_panics_doc.rs:218:10
178+
--> tests/ui/missing_panics_doc.rs:227:10
167179
|
168180
LL | *v.last().expect("passed an empty thing")
169181
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
170182

171-
error: aborting due to 14 previous errors
183+
error: aborting due to 15 previous errors
172184

0 commit comments

Comments
 (0)