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-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 ,
complexity ,
" use of `File::read_to_end` or `File::read_to_string` "
}
declare_lint_pass! ( VerboseFileReads = > [ VERBOSE_FILE_READS ] ) ;
impl < ' a , ' tcx > LateLintPass < ' a , ' tcx > for VerboseFileReads {
fn check_expr ( & mut self , cx : & LateContext < ' a , ' tcx > , expr : & ' tcx Expr < ' tcx > ) {
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` " ,
" 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` " ,
" consider using `fs::read_to_string` instead " ,
2020-03-05 02:13:57 +00:00
)
}
}
}
fn is_file_read_to_end < ' a , ' tcx > ( cx : & LateContext < ' a , ' tcx > , expr : & ' tcx Expr < ' tcx > ) -> bool {
if_chain! {
if let ExprKind ::MethodCall ( method_name , _ , exprs ) = expr . kind ;
if method_name . ident . as_str ( ) = = " read_to_end " ;
if let ExprKind ::Path ( QPath ::Resolved ( None , _ ) ) = & exprs [ 0 ] . kind ;
let ty = cx . tables . expr_ty ( & exprs [ 0 ] ) ;
if match_type ( cx , ty , & paths ::FILE ) ;
then {
return true
}
}
false
}
fn is_file_read_to_string < ' a , ' tcx > ( cx : & LateContext < ' a , ' tcx > , expr : & ' tcx Expr < ' tcx > ) -> bool {
if_chain! {
if let ExprKind ::MethodCall ( method_name , _ , exprs ) = expr . kind ;
if method_name . ident . as_str ( ) = = " read_to_string " ;
if let ExprKind ::Path ( QPath ::Resolved ( None , _ ) ) = & exprs [ 0 ] . kind ;
let ty = cx . tables . expr_ty ( & exprs [ 0 ] ) ;
if match_type ( cx , ty , & paths ::FILE ) ;
then {
return true
}
}
false
}