2020-03-05 02:13:57 +00:00
use crate ::utils ::{ match_type , paths , span_lint_and_help } ;
use if_chain ::if_chain ;
use rustc_hir ::{ Expr , ExprKind , QPath } ;
use rustc_lint ::{ LateContext , LateLintPass } ;
use rustc_session ::{ declare_lint_pass , declare_tool_lint } ;
declare_clippy_lint! {
/// **What it does:** Checks for use of File::read_to_end and File::read_to_string.
///
/// **Why is this bad?** `fs::{read, read_to_string}` provide the same functionality when `buf` is empty with fewer imports and no intermediate values.
2020-03-10 09:41:24 +00:00
/// See also: [fs::read docs](https://doc.rust-lang.org/std/fs/fn.read.html), [fs::read_to_string docs](https://doc.rust-lang.org/std/fs/fn.read_to_string.html)
2020-06-09 14:36:01 +00:00
///
2020-03-05 02:13:57 +00:00
/// **Known problems:** None.
///
/// **Example:**
///
2020-03-10 09:41:24 +00:00
/// ```rust,no_run
/// # use std::io::Read;
/// # use std::fs::File;
/// let mut f = File::open("foo.txt").unwrap();
2020-03-05 02:13:57 +00:00
/// let mut bytes = Vec::new();
2020-03-10 09:41:24 +00:00
/// f.read_to_end(&mut bytes).unwrap();
2020-03-05 02:13:57 +00:00
/// ```
/// Can be written more concisely as
2020-03-10 09:41:24 +00:00
/// ```rust,no_run
/// # use std::fs;
/// let mut bytes = fs::read("foo.txt").unwrap();
2020-03-05 02:13:57 +00:00
/// ```
pub VERBOSE_FILE_READS ,
2020-03-26 14:01:03 +00:00
restriction ,
2020-03-05 02:13:57 +00:00
" use of `File::read_to_end` or `File::read_to_string` "
}
declare_lint_pass! ( VerboseFileReads = > [ VERBOSE_FILE_READS ] ) ;
2020-06-25 20:41:36 +00:00
impl < ' tcx > LateLintPass < ' tcx > for VerboseFileReads {
fn check_expr ( & mut self , cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' tcx > ) {
2020-03-05 02:13:57 +00:00
if is_file_read_to_end ( cx , expr ) {
span_lint_and_help (
cx ,
VERBOSE_FILE_READS ,
expr . span ,
2020-03-10 09:41:24 +00:00
" use of `File::read_to_end` " ,
2020-04-18 10:28:29 +00:00
None ,
2020-03-10 09:41:24 +00:00
" consider using `fs::read` instead " ,
2020-03-05 02:13:57 +00:00
) ;
} else if is_file_read_to_string ( cx , expr ) {
span_lint_and_help (
cx ,
VERBOSE_FILE_READS ,
expr . span ,
2020-03-10 09:41:24 +00:00
" use of `File::read_to_string` " ,
2020-04-18 10:28:29 +00:00
None ,
2020-03-10 09:41:24 +00:00
" consider using `fs::read_to_string` instead " ,
2020-03-05 02:13:57 +00:00
)
}
}
}
2020-06-25 20:41:36 +00:00
fn is_file_read_to_end < ' tcx > ( cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' tcx > ) -> bool {
2020-03-05 02:13:57 +00:00
if_chain! {
2020-06-09 21:44:04 +00:00
if let ExprKind ::MethodCall ( method_name , _ , exprs , _ ) = expr . kind ;
2020-03-05 02:13:57 +00:00
if method_name . ident . as_str ( ) = = " read_to_end " ;
if let ExprKind ::Path ( QPath ::Resolved ( None , _ ) ) = & exprs [ 0 ] . kind ;
2020-07-17 08:47:04 +00:00
let ty = cx . typeck_results ( ) . expr_ty ( & exprs [ 0 ] ) ;
2020-03-05 02:13:57 +00:00
if match_type ( cx , ty , & paths ::FILE ) ;
then {
return true
}
}
false
}
2020-06-25 20:41:36 +00:00
fn is_file_read_to_string < ' tcx > ( cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' tcx > ) -> bool {
2020-03-05 02:13:57 +00:00
if_chain! {
2020-06-09 21:44:04 +00:00
if let ExprKind ::MethodCall ( method_name , _ , exprs , _ ) = expr . kind ;
2020-03-05 02:13:57 +00:00
if method_name . ident . as_str ( ) = = " read_to_string " ;
if let ExprKind ::Path ( QPath ::Resolved ( None , _ ) ) = & exprs [ 0 ] . kind ;
2020-07-17 08:47:04 +00:00
let ty = cx . typeck_results ( ) . expr_ty ( & exprs [ 0 ] ) ;
2020-03-05 02:13:57 +00:00
if match_type ( cx , ty , & paths ::FILE ) ;
then {
return true
}
}
false
}