2020-08-27 23:18:05 +00:00
use crate ::utils ::{ is_expn_of , is_type_diagnostic_item , return_ty , span_lint_and_then } ;
use if_chain ::if_chain ;
use rustc_hir as hir ;
use rustc_lint ::{ LateContext , LateLintPass } ;
use rustc_middle ::hir ::map ::Map ;
use rustc_session ::{ declare_lint_pass , declare_tool_lint } ;
use rustc_span ::Span ;
declare_clippy_lint! {
2020-08-27 23:55:23 +00:00
/// **What it does:** Checks for usage of `panic!`, `unimplemented!`, `todo!` or `unreachable!` in a function of type result.
2020-08-27 23:18:05 +00:00
///
2020-08-27 23:55:23 +00:00
/// **Why is this bad?** For some codebases, it is desirable for functions of type result to return an error instead of crashing. Hence unimplemented, panic and unreachable should be avoided.
2020-08-27 23:18:05 +00:00
///
/// **Known problems:** None.
///
/// **Example:**
///
/// ```rust
2020-08-27 23:55:23 +00:00
/// fn result_with_panic() -> Result<bool, String>
2020-08-27 23:18:05 +00:00
/// {
/// panic!("error");
/// }
/// ```
pub PANIC_IN_RESULT ,
restriction ,
2020-08-27 23:55:23 +00:00
" functions of type `Result<..>` that contain `panic!()`, `todo!()` or `unreachable()` or `unimplemented()` "
2020-08-27 23:18:05 +00:00
}
declare_lint_pass! ( PanicInResult = > [ PANIC_IN_RESULT ] ) ;
impl < ' tcx > LateLintPass < ' tcx > for PanicInResult {
fn check_impl_item ( & mut self , cx : & LateContext < ' tcx > , impl_item : & ' tcx hir ::ImplItem < '_ > ) {
if_chain! {
// first check if it's a method or function
if let hir ::ImplItemKind ::Fn ( ref _signature , _ ) = impl_item . kind ;
// checking if its return type is `result` or `option`
2020-08-27 23:55:23 +00:00
if is_type_diagnostic_item ( cx , return_ty ( cx , impl_item . hir_id ) , sym! ( result_type ) ) ;
2020-08-27 23:18:05 +00:00
then {
lint_impl_body ( cx , impl_item . span , impl_item ) ;
}
}
}
}
use rustc_hir ::intravisit ::{ self , NestedVisitorMap , Visitor } ;
use rustc_hir ::{ Expr , ImplItemKind } ;
struct FindPanicUnimplementedUnreachable {
result : Vec < Span > ,
}
impl < ' tcx > Visitor < ' tcx > for FindPanicUnimplementedUnreachable {
type Map = Map < ' tcx > ;
fn visit_expr ( & mut self , expr : & ' tcx Expr < '_ > ) {
2020-08-27 23:55:23 +00:00
if is_expn_of ( expr . span , " unimplemented " ) . is_some ( )
| | is_expn_of ( expr . span , " unreachable " ) . is_some ( )
| | is_expn_of ( expr . span , " panic " ) . is_some ( )
| | is_expn_of ( expr . span , " todo " ) . is_some ( )
{
2020-08-27 23:18:05 +00:00
self . result . push ( expr . span ) ;
}
// and check sub-expressions
intravisit ::walk_expr ( self , expr ) ;
}
fn nested_visit_map ( & mut self ) -> NestedVisitorMap < Self ::Map > {
NestedVisitorMap ::None
}
}
fn lint_impl_body < ' tcx > ( cx : & LateContext < ' tcx > , impl_span : Span , impl_item : & ' tcx hir ::ImplItem < '_ > ) {
if_chain! {
if let ImplItemKind ::Fn ( _ , body_id ) = impl_item . kind ;
then {
let body = cx . tcx . hir ( ) . body ( body_id ) ;
let mut fpu = FindPanicUnimplementedUnreachable {
result : Vec ::new ( ) ,
} ;
fpu . visit_expr ( & body . value ) ;
// if we've found one, lint
if ! fpu . result . is_empty ( ) {
span_lint_and_then (
cx ,
PANIC_IN_RESULT ,
impl_span ,
2020-08-27 23:55:23 +00:00
" used unimplemented, unreachable, todo or panic in a function that returns result " ,
2020-08-27 23:18:05 +00:00
move | diag | {
diag . help (
2020-08-27 23:55:23 +00:00
" unimplemented, unreachable, todo or panic should not be used in a function that returns result " ) ;
2020-08-27 23:18:05 +00:00
diag . span_note ( fpu . result , " will cause the application to crash. " ) ;
} ) ;
}
}
}
}