1- use crate :: utils:: { match_qpath, match_trait_method, paths, snippet, span_lint_and_then} ;
1+ use crate :: utils:: { in_constant , match_qpath, match_trait_method, paths, snippet, span_lint_and_then} ;
22use if_chain:: if_chain;
33use rustc_ast:: ast:: LitKind ;
44use rustc_errors:: Applicability ;
5- use rustc_hir:: { Arm , Expr , ExprKind , MatchSource , PatKind , QPath } ;
5+ use rustc_hir:: { Arm , Expr , ExprKind , HirId , MatchSource , PatKind , QPath } ;
66use rustc_lint:: { LateContext , LateLintPass } ;
7+ use rustc_middle:: ty;
8+ use rustc_mir:: const_eval:: is_const_fn;
79use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
10+ use rustc_span:: source_map:: Symbol ;
811
912declare_clippy_lint ! {
1013 /// **What it does:** Lint for redundant pattern matching over `Result` or
@@ -64,26 +67,37 @@ fn find_sugg_for_if_let<'a, 'tcx>(
6467 arms : & [ Arm < ' _ > ] ,
6568 keyword : & ' static str ,
6669) {
70+ fn find_suggestion ( cx : & LateContext < ' _ , ' _ > , hir_id : HirId , path : & QPath < ' _ > ) -> Option < & ' static str > {
71+ if match_qpath ( path, & paths:: RESULT_OK ) && can_suggest ( cx, hir_id, sym ! ( result_type) , "is_ok" ) {
72+ return Some ( "is_ok()" ) ;
73+ }
74+ if match_qpath ( path, & paths:: RESULT_ERR ) && can_suggest ( cx, hir_id, sym ! ( result_type) , "is_err" ) {
75+ return Some ( "is_err()" ) ;
76+ }
77+ if match_qpath ( path, & paths:: OPTION_SOME ) && can_suggest ( cx, hir_id, sym ! ( option_type) , "is_some" ) {
78+ return Some ( "is_some()" ) ;
79+ }
80+ if match_qpath ( path, & paths:: OPTION_NONE ) && can_suggest ( cx, hir_id, sym ! ( option_type) , "is_none" ) {
81+ return Some ( "is_none()" ) ;
82+ }
83+ None
84+ }
85+
86+ let hir_id = expr. hir_id ;
6787 let good_method = match arms[ 0 ] . pat . kind {
6888 PatKind :: TupleStruct ( ref path, ref patterns, _) if patterns. len ( ) == 1 => {
6989 if let PatKind :: Wild = patterns[ 0 ] . kind {
70- if match_qpath ( path, & paths:: RESULT_OK ) {
71- "is_ok()"
72- } else if match_qpath ( path, & paths:: RESULT_ERR ) {
73- "is_err()"
74- } else if match_qpath ( path, & paths:: OPTION_SOME ) {
75- "is_some()"
76- } else {
77- return ;
78- }
90+ find_suggestion ( cx, hir_id, path)
7991 } else {
80- return ;
92+ None
8193 }
8294 } ,
83-
84- PatKind :: Path ( ref path) if match_qpath ( path, & paths:: OPTION_NONE ) => "is_none()" ,
85-
86- _ => return ,
95+ PatKind :: Path ( ref path) => find_suggestion ( cx, hir_id, path) ,
96+ _ => None ,
97+ } ;
98+ let good_method = match good_method {
99+ Some ( method) => method,
100+ None => return ,
87101 } ;
88102
89103 // check that `while_let_on_iterator` lint does not trigger
@@ -128,6 +142,7 @@ fn find_sugg_for_match<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_
128142 if arms. len ( ) == 2 {
129143 let node_pair = ( & arms[ 0 ] . pat . kind , & arms[ 1 ] . pat . kind ) ;
130144
145+ let hir_id = expr. hir_id ;
131146 let found_good_method = match node_pair {
132147 (
133148 PatKind :: TupleStruct ( ref path_left, ref patterns_left, _) ,
@@ -142,6 +157,8 @@ fn find_sugg_for_match<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_
142157 & paths:: RESULT_ERR ,
143158 "is_ok()" ,
144159 "is_err()" ,
160+ || can_suggest ( cx, hir_id, sym ! ( result_type) , "is_ok" ) ,
161+ || can_suggest ( cx, hir_id, sym ! ( result_type) , "is_err" ) ,
145162 )
146163 } else {
147164 None
@@ -160,6 +177,8 @@ fn find_sugg_for_match<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_
160177 & paths:: OPTION_NONE ,
161178 "is_some()" ,
162179 "is_none()" ,
180+ || can_suggest ( cx, hir_id, sym ! ( option_type) , "is_some" ) ,
181+ || can_suggest ( cx, hir_id, sym ! ( option_type) , "is_none" ) ,
163182 )
164183 } else {
165184 None
@@ -188,6 +207,7 @@ fn find_sugg_for_match<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_
188207 }
189208}
190209
210+ #[ allow( clippy:: too_many_arguments) ]
191211fn find_good_method_for_match < ' a > (
192212 arms : & [ Arm < ' _ > ] ,
193213 path_left : & QPath < ' _ > ,
@@ -196,6 +216,8 @@ fn find_good_method_for_match<'a>(
196216 expected_right : & [ & str ] ,
197217 should_be_left : & ' a str ,
198218 should_be_right : & ' a str ,
219+ can_suggest_left : impl Fn ( ) -> bool ,
220+ can_suggest_right : impl Fn ( ) -> bool ,
199221) -> Option < & ' a str > {
200222 let body_node_pair = if match_qpath ( path_left, expected_left) && match_qpath ( path_right, expected_right) {
201223 ( & ( * arms[ 0 ] . body ) . kind , & ( * arms[ 1 ] . body ) . kind )
@@ -207,10 +229,32 @@ fn find_good_method_for_match<'a>(
207229
208230 match body_node_pair {
209231 ( ExprKind :: Lit ( ref lit_left) , ExprKind :: Lit ( ref lit_right) ) => match ( & lit_left. node , & lit_right. node ) {
210- ( LitKind :: Bool ( true ) , LitKind :: Bool ( false ) ) => Some ( should_be_left) ,
211- ( LitKind :: Bool ( false ) , LitKind :: Bool ( true ) ) => Some ( should_be_right) ,
232+ ( LitKind :: Bool ( true ) , LitKind :: Bool ( false ) ) if can_suggest_left ( ) => Some ( should_be_left) ,
233+ ( LitKind :: Bool ( false ) , LitKind :: Bool ( true ) ) if can_suggest_right ( ) => Some ( should_be_right) ,
212234 _ => None ,
213235 } ,
214236 _ => None ,
215237 }
216238}
239+
240+ fn can_suggest ( cx : & LateContext < ' _ , ' _ > , hir_id : HirId , diag_item : Symbol , name : & str ) -> bool {
241+ if !in_constant ( cx, hir_id) {
242+ return true ;
243+ }
244+
245+ // Avoid suggesting calls to non-`const fn`s in const contexts, see #5697.
246+ cx. tcx
247+ . get_diagnostic_item ( diag_item)
248+ . and_then ( |def_id| {
249+ cx. tcx . inherent_impls ( def_id) . iter ( ) . find_map ( |imp| {
250+ cx. tcx
251+ . associated_items ( * imp)
252+ . in_definition_order ( )
253+ . find_map ( |item| match item. kind {
254+ ty:: AssocKind :: Fn if item. ident . name . as_str ( ) == name => Some ( item. def_id ) ,
255+ _ => None ,
256+ } )
257+ } )
258+ } )
259+ . map_or ( false , |def_id| is_const_fn ( cx. tcx , def_id) )
260+ }
0 commit comments