2016-04-14 11:13:15 -05:00
use rustc ::hir ::* ;
2016-04-07 10:46:48 -05:00
use rustc ::hir ::map ::Node ::NodeItem ;
2016-02-20 10:35:07 -06:00
use rustc ::lint ::* ;
2016-03-27 13:59:02 -05:00
use rustc ::ty ::TypeVariants ;
2016-02-20 14:15:05 -06:00
use syntax ::ast ::LitKind ;
2016-04-14 11:13:15 -05:00
use utils ::paths ;
2016-02-22 10:54:46 -06:00
use utils ::{ is_expn_of , match_path , match_type , span_lint , walk_ptrs_ty } ;
2016-02-20 10:35:07 -06:00
2016-02-21 06:21:04 -06:00
/// **What it does:** This lints about use of `format!("string literal with no argument")` and
2016-02-22 10:54:46 -06:00
/// `format!("{}", foo)` where `foo` is a string.
2016-02-20 10:35:07 -06:00
///
2016-02-22 10:54:46 -06:00
/// **Why is this bad?** There is no point of doing that. `format!("too")` can be replaced by `"foo".to_owned()` if you really need a `String`. The even worse `&format!("foo")` is often
/// encountered in the wild. `format!("{}", foo)` can be replaced by `foo.clone()` if `foo: String`
/// or `foo.to_owned()` is `foo: &str`.
2016-02-20 10:35:07 -06:00
///
/// **Known problems:** None.
///
2016-02-21 06:21:04 -06:00
/// **Examples:** `format!("foo")` and `format!("{}", foo)`
2016-02-20 10:35:07 -06:00
declare_lint! {
pub USELESS_FORMAT ,
Warn ,
" useless use of `format!` "
}
#[ derive(Copy, Clone, Debug) ]
pub struct FormatMacLint ;
impl LintPass for FormatMacLint {
fn get_lints ( & self ) -> LintArray {
lint_array! [ USELESS_FORMAT ]
}
}
impl LateLintPass for FormatMacLint {
fn check_expr ( & mut self , cx : & LateContext , expr : & Expr ) {
2016-02-20 14:15:05 -06:00
if let Some ( span ) = is_expn_of ( cx , expr . span , " format " ) {
match expr . node {
// `format!("{}", foo)` expansion
ExprCall ( ref fun , ref args ) = > {
if_let_chain! { [
let ExprPath ( _ , ref path ) = fun . node ,
args . len ( ) = = 2 ,
2016-04-14 11:13:15 -05:00
match_path ( path , & paths ::FMT_ARGUMENTS_NEWV1 ) ,
2016-02-20 14:15:05 -06:00
// ensure the format string is `"{..}"` with only one argument and no text
check_static_str ( cx , & args [ 0 ] ) ,
// ensure the format argument is `{}` ie. Display with no fancy option
2016-02-22 10:54:46 -06:00
check_arg_is_display ( cx , & args [ 1 ] )
2016-02-20 14:15:05 -06:00
] , {
2016-02-20 14:20:56 -06:00
span_lint ( cx , USELESS_FORMAT , span , " useless use of `format!` " ) ;
2016-02-20 14:15:05 -06:00
} }
2016-02-20 10:35:07 -06:00
}
2016-02-20 14:15:05 -06:00
// `format!("foo")` expansion contains `match () { () => [], }`
ExprMatch ( ref matchee , _ , _ ) = > {
if let ExprTup ( ref tup ) = matchee . node {
if tup . is_empty ( ) {
2016-02-20 14:20:56 -06:00
span_lint ( cx , USELESS_FORMAT , span , " useless use of `format!` " ) ;
2016-02-20 14:15:05 -06:00
}
}
}
_ = > ( ) ,
2016-02-20 10:35:07 -06:00
}
}
}
}
2016-02-20 14:15:05 -06:00
/// Checks if the expressions matches
/// ```
/// { static __STATIC_FMTSTR: &[""] = _; __STATIC_FMTSTR }
/// ```
fn check_static_str ( cx : & LateContext , expr : & Expr ) -> bool {
if_let_chain! { [
let ExprBlock ( ref block ) = expr . node ,
block . stmts . len ( ) = = 1 ,
let StmtDecl ( ref decl , _ ) = block . stmts [ 0 ] . node ,
let DeclItem ( ref decl ) = decl . node ,
let Some ( NodeItem ( decl ) ) = cx . tcx . map . find ( decl . id ) ,
decl . name . as_str ( ) = = " __STATIC_FMTSTR " ,
let ItemStatic ( _ , _ , ref expr ) = decl . node ,
let ExprAddrOf ( _ , ref expr ) = expr . node , // &[""]
let ExprVec ( ref expr ) = expr . node ,
expr . len ( ) = = 1 ,
let ExprLit ( ref lit ) = expr [ 0 ] . node ,
let LitKind ::Str ( ref lit , _ ) = lit . node ,
lit . is_empty ( )
] , {
return true ;
} }
false
}
/// Checks if the expressions matches
/// ```
/// &match (&42,) {
/// (__arg0,) => [::std::fmt::ArgumentV1::new(__arg0, ::std::fmt::Display::fmt)],
/// })
/// ```
2016-02-22 10:54:46 -06:00
fn check_arg_is_display ( cx : & LateContext , expr : & Expr ) -> bool {
2016-02-20 14:15:05 -06:00
if_let_chain! { [
let ExprAddrOf ( _ , ref expr ) = expr . node ,
let ExprMatch ( _ , ref arms , _ ) = expr . node ,
arms . len ( ) = = 1 ,
2016-02-22 10:54:46 -06:00
arms [ 0 ] . pats . len ( ) = = 1 ,
2016-05-27 07:24:28 -05:00
let PatKind ::Tuple ( ref pat , None ) = arms [ 0 ] . pats [ 0 ] . node ,
2016-02-22 10:54:46 -06:00
pat . len ( ) = = 1 ,
2016-02-20 14:15:05 -06:00
let ExprVec ( ref exprs ) = arms [ 0 ] . body . node ,
exprs . len ( ) = = 1 ,
let ExprCall ( _ , ref args ) = exprs [ 0 ] . node ,
args . len ( ) = = 2 ,
let ExprPath ( None , ref path ) = args [ 1 ] . node ,
2016-04-14 11:13:15 -05:00
match_path ( path , & paths ::DISPLAY_FMT_METHOD )
2016-02-20 14:15:05 -06:00
] , {
2016-02-22 10:54:46 -06:00
let ty = walk_ptrs_ty ( cx . tcx . pat_ty ( & pat [ 0 ] ) ) ;
2016-04-14 11:13:15 -05:00
return ty . sty = = TypeVariants ::TyStr | | match_type ( cx , ty , & paths ::STRING ) ;
2016-02-20 14:15:05 -06:00
} }
false
}