3
3
use clippy_config:: Conf ;
4
4
use clippy_utils:: attrs:: is_doc_hidden;
5
5
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} ;
7
6
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} ;
11
8
use pulldown_cmark:: Event :: {
12
9
Code , DisplayMath , End , FootnoteReference , HardBreak , Html , InlineHtml , InlineMath , Rule , SoftBreak , Start ,
13
10
TaskListMarker , Text ,
@@ -16,18 +13,15 @@ use pulldown_cmark::Tag::{BlockQuote, CodeBlock, FootnoteDefinition, Heading, It
16
13
use pulldown_cmark:: { BrokenLink , CodeBlockKind , CowStr , Options , TagEnd } ;
17
14
use rustc_data_structures:: fx:: FxHashSet ;
18
15
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 } ;
21
17
use rustc_lint:: { EarlyContext , EarlyLintPass , LateContext , LateLintPass , LintContext } ;
22
- use rustc_middle:: hir:: nested_filter;
23
- use rustc_middle:: ty;
24
18
use rustc_resolve:: rustdoc:: {
25
19
DocFragment , add_doc_fragment, attrs_to_doc_fragments, main_body_opts, source_span_for_markdown_range,
26
20
span_of_fragments,
27
21
} ;
28
22
use rustc_session:: impl_lint_pass;
23
+ use rustc_span:: Span ;
29
24
use rustc_span:: edition:: Edition ;
30
- use rustc_span:: { Span , sym} ;
31
25
use std:: ops:: Range ;
32
26
use url:: Url ;
33
27
@@ -194,6 +188,19 @@ declare_clippy_lint! {
194
188
/// }
195
189
/// }
196
190
/// ```
191
+ ///
192
+ /// Individual panics within a function can be ignored with `#[expect]` or
193
+ /// `#[allow]`:
194
+ ///
195
+ /// ```no_run
196
+ /// # use std::num::NonZeroUsize;
197
+ /// pub fn will_not_panic(x: usize) {
198
+ /// #[expect(clippy::missing_panics_doc, reason = "infallible")]
199
+ /// let y = NonZeroUsize::new(1).unwrap();
200
+ ///
201
+ /// // If any panics are added in the future the lint will still catch them
202
+ /// }
203
+ /// ```
197
204
#[ clippy:: version = "1.51.0" ]
198
205
pub MISSING_PANICS_DOC ,
199
206
pedantic,
@@ -657,20 +664,16 @@ impl<'tcx> LateLintPass<'tcx> for Documentation {
657
664
self . check_private_items ,
658
665
) ;
659
666
match item. kind {
660
- ItemKind :: Fn { sig, body : body_id , .. } => {
667
+ ItemKind :: Fn { sig, body, .. } => {
661
668
if !( is_entrypoint_fn ( cx, item. owner_id . to_def_id ( ) )
662
669
|| item. span . in_external_macro ( cx. tcx . sess . source_map ( ) ) )
663
670
{
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 ) ;
667
671
missing_headers:: check (
668
672
cx,
669
673
item. owner_id ,
670
674
sig,
671
675
headers,
672
- Some ( body_id) ,
673
- panic_info,
676
+ Some ( body) ,
674
677
self . check_private_items ,
675
678
) ;
676
679
}
@@ -697,32 +700,20 @@ impl<'tcx> LateLintPass<'tcx> for Documentation {
697
700
if let TraitItemKind :: Fn ( sig, ..) = trait_item. kind
698
701
&& !trait_item. span . in_external_macro ( cx. tcx . sess . source_map ( ) )
699
702
{
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
- ) ;
703
+ missing_headers:: check ( cx, trait_item. owner_id , sig, headers, None , self . check_private_items ) ;
709
704
}
710
705
} ,
711
706
Node :: ImplItem ( impl_item) => {
712
707
if let ImplItemKind :: Fn ( sig, body_id) = impl_item. kind
713
708
&& !impl_item. span . in_external_macro ( cx. tcx . sess . source_map ( ) )
714
709
&& !is_trait_impl_item ( cx, impl_item. hir_id ( ) )
715
710
{
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 ) ;
719
711
missing_headers:: check (
720
712
cx,
721
713
impl_item. owner_id ,
722
714
sig,
723
715
headers,
724
716
Some ( body_id) ,
725
- panic_span,
726
717
self . check_private_items ,
727
718
) ;
728
719
}
@@ -1168,71 +1159,6 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
1168
1159
headers
1169
1160
}
1170
1161
1171
- struct FindPanicUnwrap < ' a , ' tcx > {
1172
- cx : & ' a LateContext < ' tcx > ,
1173
- is_const : bool ,
1174
- panic_span : Option < Span > ,
1175
- typeck_results : & ' tcx ty:: TypeckResults < ' tcx > ,
1176
- }
1177
-
1178
- impl < ' a , ' tcx > FindPanicUnwrap < ' a , ' tcx > {
1179
- pub fn find_span (
1180
- cx : & ' a LateContext < ' tcx > ,
1181
- typeck_results : & ' tcx ty:: TypeckResults < ' tcx > ,
1182
- body : impl Visitable < ' tcx > ,
1183
- ) -> Option < ( Span , bool ) > {
1184
- let mut vis = Self {
1185
- cx,
1186
- is_const : false ,
1187
- panic_span : None ,
1188
- typeck_results,
1189
- } ;
1190
- body. visit ( & mut vis) ;
1191
- vis. panic_span . map ( |el| ( el, vis. is_const ) )
1192
- }
1193
- }
1194
-
1195
- impl < ' tcx > Visitor < ' tcx > for FindPanicUnwrap < ' _ , ' tcx > {
1196
- type NestedFilter = nested_filter:: OnlyBodies ;
1197
-
1198
- fn visit_expr ( & mut self , expr : & ' tcx Expr < ' _ > ) {
1199
- if self . panic_span . is_some ( ) {
1200
- return ;
1201
- }
1202
-
1203
- if let Some ( macro_call) = root_macro_call_first_node ( self . cx , expr)
1204
- && ( is_panic ( self . cx , macro_call. def_id )
1205
- || matches ! (
1206
- self . cx. tcx. item_name( macro_call. def_id) . as_str( ) ,
1207
- "assert" | "assert_eq" | "assert_ne"
1208
- ) )
1209
- {
1210
- self . is_const = self . cx . tcx . hir_is_inside_const_context ( expr. hir_id ) ;
1211
- self . panic_span = Some ( macro_call. span ) ;
1212
- }
1213
-
1214
- // check for `unwrap` and `expect` for both `Option` and `Result`
1215
- if let Some ( arglists) = method_chain_args ( expr, & [ "unwrap" ] ) . or ( method_chain_args ( expr, & [ "expect" ] ) ) {
1216
- let receiver_ty = self . typeck_results . expr_ty ( arglists[ 0 ] . 0 ) . peel_refs ( ) ;
1217
- if is_type_diagnostic_item ( self . cx , receiver_ty, sym:: Option )
1218
- || is_type_diagnostic_item ( self . cx , receiver_ty, sym:: Result )
1219
- {
1220
- self . panic_span = Some ( expr. span ) ;
1221
- }
1222
- }
1223
-
1224
- // and check sub-expressions
1225
- intravisit:: walk_expr ( self , expr) ;
1226
- }
1227
-
1228
- // Panics in const blocks will cause compilation to fail.
1229
- fn visit_anon_const ( & mut self , _: & ' tcx AnonConst ) { }
1230
-
1231
- fn maybe_tcx ( & mut self ) -> Self :: MaybeTyCtxt {
1232
- self . cx . tcx
1233
- }
1234
- }
1235
-
1236
1162
#[ expect( clippy:: range_plus_one) ] // inclusive ranges aren't the same type
1237
1163
fn looks_like_refdef ( doc : & str , range : Range < usize > ) -> Option < Range < usize > > {
1238
1164
if range. end < range. start {
0 commit comments