33use clippy_config:: Conf ;
44use clippy_utils:: attrs:: is_doc_hidden;
55use 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} ;
76use 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} ;
118use 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
1613use pulldown_cmark:: { BrokenLink , CodeBlockKind , CowStr , Options , TagEnd } ;
1714use rustc_data_structures:: fx:: FxHashSet ;
1815use 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 } ;
2117use rustc_lint:: { EarlyContext , EarlyLintPass , LateContext , LateLintPass , LintContext } ;
22- use rustc_middle:: hir:: nested_filter;
23- use rustc_middle:: ty;
2418use 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} ;
2822use rustc_session:: impl_lint_pass;
23+ use rustc_span:: Span ;
2924use rustc_span:: edition:: Edition ;
30- use rustc_span:: { Span , sym} ;
3125use std:: ops:: Range ;
3226use url:: Url ;
3327
@@ -194,6 +188,19 @@ declare_clippy_lint! {
194188 /// }
195189 /// }
196190 /// ```
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+ /// ```
197204 #[ clippy:: version = "1.51.0" ]
198205 pub MISSING_PANICS_DOC ,
199206 pedantic,
@@ -657,20 +664,16 @@ impl<'tcx> LateLintPass<'tcx> for Documentation {
657664 self . check_private_items ,
658665 ) ;
659666 match item. kind {
660- ItemKind :: Fn { sig, body : body_id , .. } => {
667+ ItemKind :: Fn { sig, body, .. } => {
661668 if !( is_entrypoint_fn ( cx, item. owner_id . to_def_id ( ) )
662669 || item. span . in_external_macro ( cx. tcx . sess . source_map ( ) ) )
663670 {
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 ) ;
667671 missing_headers:: check (
668672 cx,
669673 item. owner_id ,
670674 sig,
671675 headers,
672- Some ( body_id) ,
673- panic_info,
676+ Some ( body) ,
674677 self . check_private_items ,
675678 ) ;
676679 }
@@ -697,32 +700,20 @@ impl<'tcx> LateLintPass<'tcx> for Documentation {
697700 if let TraitItemKind :: Fn ( sig, ..) = trait_item. kind
698701 && !trait_item. span . in_external_macro ( cx. tcx . sess . source_map ( ) )
699702 {
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 ) ;
709704 }
710705 } ,
711706 Node :: ImplItem ( impl_item) => {
712707 if let ImplItemKind :: Fn ( sig, body_id) = impl_item. kind
713708 && !impl_item. span . in_external_macro ( cx. tcx . sess . source_map ( ) )
714709 && !is_trait_impl_item ( cx, impl_item. hir_id ( ) )
715710 {
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 ) ;
719711 missing_headers:: check (
720712 cx,
721713 impl_item. owner_id ,
722714 sig,
723715 headers,
724716 Some ( body_id) ,
725- panic_span,
726717 self . check_private_items ,
727718 ) ;
728719 }
@@ -1168,71 +1159,6 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
11681159 headers
11691160}
11701161
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-
12361162#[ expect( clippy:: range_plus_one) ] // inclusive ranges aren't the same type
12371163fn looks_like_refdef ( doc : & str , range : Range < usize > ) -> Option < Range < usize > > {
12381164 if range. end < range. start {
0 commit comments