11use crate :: utils:: {
2- attr_by_name, attrs:: is_proc_macro, is_must_use_ty, is_trait_impl_item, iter_input_pats , match_def_path ,
3- must_use_attr, qpath_res, return_ty, snippet, snippet_opt, span_lint, span_lint_and_help , span_lint_and_then ,
4- trait_ref_of_method, type_is_unsafe_function,
2+ attr_by_name, attrs:: is_proc_macro, is_must_use_ty, is_trait_impl_item, is_type_diagnostic_item , iter_input_pats ,
3+ last_path_segment , match_def_path , must_use_attr, qpath_res, return_ty, snippet, snippet_opt, span_lint,
4+ span_lint_and_help , span_lint_and_then , trait_ref_of_method, type_is_unsafe_function,
55} ;
6+ use if_chain:: if_chain;
67use rustc_ast:: ast:: Attribute ;
78use rustc_data_structures:: fx:: FxHashSet ;
89use rustc_errors:: Applicability ;
@@ -16,6 +17,7 @@ use rustc_middle::ty::{self, Ty};
1617use rustc_session:: { declare_tool_lint, impl_lint_pass} ;
1718use rustc_span:: source_map:: Span ;
1819use rustc_target:: spec:: abi:: Abi ;
20+ use rustc_typeck:: hir_ty_to_ty;
1921
2022declare_clippy_lint ! {
2123 /// **What it does:** Checks for functions with too many parameters.
@@ -169,6 +171,52 @@ declare_clippy_lint! {
169171 "function or method that could take a `#[must_use]` attribute"
170172}
171173
174+ declare_clippy_lint ! {
175+ /// **What it does:** Checks for public functions that return a `Result`
176+ /// with an `Err` type of `()`. It suggests using a custom type that
177+ /// implements [`std::error::Error`].
178+ ///
179+ /// **Why is this bad?** Unit does not implement `Error` and carries no
180+ /// further information about what went wrong.
181+ ///
182+ /// **Known problems:** Of course, this lint assumes that `Result` is used
183+ /// for a fallible operation (which is after all the intended use). However
184+ /// code may opt to (mis)use it as a basic two-variant-enum. In that case,
185+ /// the suggestion is misguided, and the code should use a custom enum
186+ /// instead.
187+ ///
188+ /// **Examples:**
189+ /// ```rust
190+ /// pub fn read_u8() -> Result<u8, ()> { Err(()) }
191+ /// ```
192+ /// should become
193+ /// ```rust,should_panic
194+ /// use std::fmt;
195+ ///
196+ /// #[derive(Debug)]
197+ /// pub struct EndOfStream;
198+ ///
199+ /// impl fmt::Display for EndOfStream {
200+ /// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
201+ /// write!(f, "End of Stream")
202+ /// }
203+ /// }
204+ ///
205+ /// impl std::error::Error for EndOfStream { }
206+ ///
207+ /// pub fn read_u8() -> Result<u8, EndOfStream> { Err(EndOfStream) }
208+ ///# fn main() {
209+ ///# read_u8().unwrap();
210+ ///# }
211+ /// ```
212+ ///
213+ /// Note that there are crates that simplify creating the error type, e.g.
214+ /// [`thiserror`](https://docs.rs/thiserror).
215+ pub RESULT_UNIT_ERR ,
216+ style,
217+ "public function returning `Result` with an `Err` type of `()`"
218+ }
219+
172220#[ derive( Copy , Clone ) ]
173221pub struct Functions {
174222 threshold : u64 ,
@@ -188,6 +236,7 @@ impl_lint_pass!(Functions => [
188236 MUST_USE_UNIT ,
189237 DOUBLE_MUST_USE ,
190238 MUST_USE_CANDIDATE ,
239+ RESULT_UNIT_ERR ,
191240] ) ;
192241
193242impl < ' tcx > LateLintPass < ' tcx > for Functions {
@@ -233,15 +282,16 @@ impl<'tcx> LateLintPass<'tcx> for Functions {
233282 fn check_item ( & mut self , cx : & LateContext < ' tcx > , item : & ' tcx hir:: Item < ' _ > ) {
234283 let attr = must_use_attr ( & item. attrs ) ;
235284 if let hir:: ItemKind :: Fn ( ref sig, ref _generics, ref body_id) = item. kind {
285+ let is_public = cx. access_levels . is_exported ( item. hir_id ) ;
286+ let fn_header_span = item. span . with_hi ( sig. decl . output . span ( ) . hi ( ) ) ;
287+ if is_public {
288+ check_result_unit_err ( cx, & sig. decl , item. span , fn_header_span) ;
289+ }
236290 if let Some ( attr) = attr {
237- let fn_header_span = item. span . with_hi ( sig. decl . output . span ( ) . hi ( ) ) ;
238291 check_needless_must_use ( cx, & sig. decl , item. hir_id , item. span , fn_header_span, attr) ;
239292 return ;
240293 }
241- if cx. access_levels . is_exported ( item. hir_id )
242- && !is_proc_macro ( cx. sess ( ) , & item. attrs )
243- && attr_by_name ( & item. attrs , "no_mangle" ) . is_none ( )
244- {
294+ if is_public && !is_proc_macro ( cx. sess ( ) , & item. attrs ) && attr_by_name ( & item. attrs , "no_mangle" ) . is_none ( ) {
245295 check_must_use_candidate (
246296 cx,
247297 & sig. decl ,
@@ -257,11 +307,15 @@ impl<'tcx> LateLintPass<'tcx> for Functions {
257307
258308 fn check_impl_item ( & mut self , cx : & LateContext < ' tcx > , item : & ' tcx hir:: ImplItem < ' _ > ) {
259309 if let hir:: ImplItemKind :: Fn ( ref sig, ref body_id) = item. kind {
310+ let is_public = cx. access_levels . is_exported ( item. hir_id ) ;
311+ let fn_header_span = item. span . with_hi ( sig. decl . output . span ( ) . hi ( ) ) ;
312+ if is_public && trait_ref_of_method ( cx, item. hir_id ) . is_none ( ) {
313+ check_result_unit_err ( cx, & sig. decl , item. span , fn_header_span) ;
314+ }
260315 let attr = must_use_attr ( & item. attrs ) ;
261316 if let Some ( attr) = attr {
262- let fn_header_span = item. span . with_hi ( sig. decl . output . span ( ) . hi ( ) ) ;
263317 check_needless_must_use ( cx, & sig. decl , item. hir_id , item. span , fn_header_span, attr) ;
264- } else if cx . access_levels . is_exported ( item . hir_id )
318+ } else if is_public
265319 && !is_proc_macro ( cx. sess ( ) , & item. attrs )
266320 && trait_ref_of_method ( cx, item. hir_id ) . is_none ( )
267321 {
@@ -284,18 +338,21 @@ impl<'tcx> LateLintPass<'tcx> for Functions {
284338 if sig. header . abi == Abi :: Rust {
285339 self . check_arg_number ( cx, & sig. decl , item. span . with_hi ( sig. decl . output . span ( ) . hi ( ) ) ) ;
286340 }
341+ let is_public = cx. access_levels . is_exported ( item. hir_id ) ;
342+ let fn_header_span = item. span . with_hi ( sig. decl . output . span ( ) . hi ( ) ) ;
343+ if is_public {
344+ check_result_unit_err ( cx, & sig. decl , item. span , fn_header_span) ;
345+ }
287346
288347 let attr = must_use_attr ( & item. attrs ) ;
289348 if let Some ( attr) = attr {
290- let fn_header_span = item. span . with_hi ( sig. decl . output . span ( ) . hi ( ) ) ;
291349 check_needless_must_use ( cx, & sig. decl , item. hir_id , item. span , fn_header_span, attr) ;
292350 }
293351 if let hir:: TraitFn :: Provided ( eid) = * eid {
294352 let body = cx. tcx . hir ( ) . body ( eid) ;
295353 Self :: check_raw_ptr ( cx, sig. header . unsafety , & sig. decl , body, item. hir_id ) ;
296354
297- if attr. is_none ( ) && cx. access_levels . is_exported ( item. hir_id ) && !is_proc_macro ( cx. sess ( ) , & item. attrs )
298- {
355+ if attr. is_none ( ) && is_public && !is_proc_macro ( cx. sess ( ) , & item. attrs ) {
299356 check_must_use_candidate (
300357 cx,
301358 & sig. decl ,
@@ -411,6 +468,29 @@ impl<'tcx> Functions {
411468 }
412469}
413470
471+ fn check_result_unit_err ( cx : & LateContext < ' _ > , decl : & hir:: FnDecl < ' _ > , item_span : Span , fn_header_span : Span ) {
472+ if_chain ! {
473+ if !in_external_macro( cx. sess( ) , item_span) ;
474+ if let hir:: FnRetTy :: Return ( ref ty) = decl. output;
475+ if let hir:: TyKind :: Path ( ref qpath) = ty. kind;
476+ if is_type_diagnostic_item( cx, hir_ty_to_ty( cx. tcx, ty) , sym!( result_type) ) ;
477+ if let Some ( ref args) = last_path_segment( qpath) . args;
478+ if let [ _, hir:: GenericArg :: Type ( ref err_ty) ] = args. args;
479+ if let hir:: TyKind :: Tup ( t) = err_ty. kind;
480+ if t. is_empty( ) ;
481+ then {
482+ span_lint_and_help(
483+ cx,
484+ RESULT_UNIT_ERR ,
485+ fn_header_span,
486+ "this returns a `Result<_, ()>" ,
487+ None ,
488+ "use a custom Error type instead" ,
489+ ) ;
490+ }
491+ }
492+ }
493+
414494fn check_needless_must_use (
415495 cx : & LateContext < ' _ > ,
416496 decl : & hir:: FnDecl < ' _ > ,
0 commit comments