@@ -5,7 +5,6 @@ use crate::utils::{
55use if_chain:: if_chain;
66use rustc:: hir:: * ;
77use rustc:: lint:: { LateContext , LateLintPass , LintArray , LintContext , LintPass } ;
8- use rustc:: ty;
98use rustc:: { declare_lint_pass, declare_tool_lint} ;
109use rustc_errors:: Applicability ;
1110use syntax:: ast:: LitKind ;
@@ -38,56 +37,16 @@ declare_lint_pass!(UselessFormat => [USELESS_FORMAT]);
3837
3938impl < ' a , ' tcx > LateLintPass < ' a , ' tcx > for UselessFormat {
4039 fn check_expr ( & mut self , cx : & LateContext < ' a , ' tcx > , expr : & ' tcx Expr ) {
41- if let Some ( span) = is_expn_of ( expr. span , "format" ) {
42- if span. from_expansion ( ) {
43- return ;
44- }
45- match expr. node {
46- // `format!("{}", foo)` expansion
47- ExprKind :: Call ( ref fun, ref args) => {
48- if_chain ! {
49- if let ExprKind :: Path ( ref qpath) = fun. node;
50- if let Some ( fun_def_id) = resolve_node( cx, qpath, fun. hir_id) . opt_def_id( ) ;
51- let new_v1 = match_def_path( cx, fun_def_id, & paths:: FMT_ARGUMENTS_NEWV1 ) ;
52- let new_v1_fmt = match_def_path( cx,
53- fun_def_id,
54- & paths:: FMT_ARGUMENTS_NEWV1FORMATTED
55- ) ;
56- if new_v1 || new_v1_fmt;
57- if check_single_piece( & args[ 0 ] ) ;
58- if let Some ( format_arg) = get_single_string_arg( cx, & args[ 1 ] ) ;
59- if new_v1 || check_unformatted( & args[ 2 ] ) ;
60- if let ExprKind :: AddrOf ( _, ref format_arg) = format_arg. node;
61- then {
62- let ( message, sugg) = if_chain! {
63- if let ExprKind :: MethodCall ( ref path, _, _) = format_arg. node;
64- if path. ident. as_interned_str( ) . as_symbol( ) == sym!( to_string) ;
65- then {
66- ( "`to_string()` is enough" ,
67- snippet( cx, format_arg. span, "<arg>" ) . to_string( ) )
68- } else {
69- ( "consider using .to_string()" ,
70- format!( "{}.to_string()" , snippet( cx, format_arg. span, "<arg>" ) ) )
71- }
72- } ;
40+ let span = match is_expn_of ( expr. span , "format" ) {
41+ Some ( s) if !s. from_expansion ( ) => s,
42+ _ => return ,
43+ } ;
7344
74- span_useless_format( cx, span, message, sugg) ;
75- }
76- }
77- } ,
78- // `format!("foo")` expansion contains `match () { () => [], }`
79- ExprKind :: Match ( ref matchee, _, _) => {
80- if let ExprKind :: Tup ( ref tup) = matchee. node {
81- if tup. is_empty ( ) {
82- let actual_snippet = snippet ( cx, expr. span , "<expr>" ) . to_string ( ) ;
83- let actual_snippet = actual_snippet. replace ( "{{}}" , "{}" ) ;
84- let sugg = format ! ( "{}.to_string()" , actual_snippet) ;
85- span_useless_format ( cx, span, "consider using .to_string()" , sugg) ;
86- }
87- }
88- } ,
89- _ => ( ) ,
90- }
45+ // Operate on the only argument of `alloc::fmt::format`.
46+ if let Some ( sugg) = on_new_v1 ( cx, expr) {
47+ span_useless_format ( cx, span, "consider using .to_string()" , sugg) ;
48+ } else if let Some ( sugg) = on_new_v1_fmt ( cx, expr) {
49+ span_useless_format ( cx, span, "consider using .to_string()" , sugg) ;
9150 }
9251 }
9352}
@@ -111,56 +70,102 @@ fn span_useless_format<T: LintContext>(cx: &T, span: Span, help: &str, mut sugg:
11170 } ) ;
11271}
11372
114- /// Checks if the expressions matches `&[""]`
115- fn check_single_piece ( expr : & Expr ) -> bool {
73+ fn on_argumentv1_new < ' a , ' tcx > ( cx : & LateContext < ' a , ' tcx > , expr : & ' tcx Expr , arms : & ' a [ Arm ] ) -> Option < String > {
11674 if_chain ! {
117- if let ExprKind :: AddrOf ( _, ref expr) = expr. node; // &[""]
118- if let ExprKind :: Array ( ref exprs) = expr. node; // [""]
119- if exprs. len( ) == 1 ;
120- if let ExprKind :: Lit ( ref lit) = exprs[ 0 ] . node;
121- if let LitKind :: Str ( ref lit, _) = lit. node;
75+ if let ExprKind :: AddrOf ( _, ref format_args) = expr. node;
76+ if let ExprKind :: Array ( ref elems) = arms[ 0 ] . body. node;
77+ if elems. len( ) == 1 ;
78+ if let ExprKind :: Call ( ref fun, ref args) = elems[ 0 ] . node;
79+ if let ExprKind :: Path ( ref qpath) = fun. node;
80+ if let Some ( did) = resolve_node( cx, qpath, fun. hir_id) . opt_def_id( ) ;
81+ if match_def_path( cx, did, & paths:: FMT_ARGUMENTV1_NEW ) ;
82+ // matches `core::fmt::Display::fmt`
83+ if args. len( ) == 2 ;
84+ if let ExprKind :: Path ( ref qpath) = args[ 1 ] . node;
85+ if let Some ( did) = resolve_node( cx, qpath, args[ 1 ] . hir_id) . opt_def_id( ) ;
86+ if match_def_path( cx, did, & paths:: DISPLAY_FMT_METHOD ) ;
87+ if arms[ 0 ] . pats. len( ) == 1 ;
88+ // check `(arg0,)` in match block
89+ if let PatKind :: Tuple ( ref pats, None ) = arms[ 0 ] . pats[ 0 ] . node;
90+ if pats. len( ) == 1 ;
12291 then {
123- return lit. as_str( ) . is_empty( ) ;
92+ let ty = walk_ptrs_ty( cx. tables. pat_ty( & pats[ 0 ] ) ) ;
93+ if ty. sty != rustc:: ty:: Str && !match_type( cx, ty, & paths:: STRING ) {
94+ return None ;
95+ }
96+ if let ExprKind :: Lit ( ref lit) = format_args. node {
97+ if let LitKind :: Str ( ref s, _) = lit. node {
98+ return Some ( format!( "{:?}.to_string()" , s. as_str( ) ) ) ;
99+ }
100+ } else {
101+ let snip = snippet( cx, format_args. span, "<arg>" ) ;
102+ if let ExprKind :: MethodCall ( ref path, _, _) = format_args. node {
103+ if path. ident. name == sym!( to_string) {
104+ return Some ( format!( "{}" , snip) ) ;
105+ }
106+ } else if let ExprKind :: Binary ( ..) = format_args. node {
107+ return Some ( format!( "{}" , snip) ) ;
108+ }
109+ return Some ( format!( "{}.to_string()" , snip) ) ;
110+ }
124111 }
125112 }
126-
127- false
113+ None
128114}
129115
130- /// Checks if the expressions matches
131- /// ```rust,ignore
132- /// &match (&"arg",) {
133- /// (__arg0,) => [::std::fmt::ArgumentV1::new(__arg0,
134- /// ::std::fmt::Display::fmt)],
135- /// }
136- /// ```
137- /// and that the type of `__arg0` is `&str` or `String`,
138- /// then returns the span of first element of the matched tuple.
139- fn get_single_string_arg < ' a > ( cx : & LateContext < ' _ , ' _ > , expr : & ' a Expr ) -> Option < & ' a Expr > {
116+ fn on_new_v1 < ' a , ' tcx > ( cx : & LateContext < ' a , ' tcx > , expr : & ' tcx Expr ) -> Option < String > {
140117 if_chain ! {
141- if let ExprKind :: AddrOf ( _, ref expr) = expr. node;
142- if let ExprKind :: Match ( ref match_expr, ref arms, _) = expr. node;
143- if arms. len( ) == 1 ;
144- if arms[ 0 ] . pats. len( ) == 1 ;
145- if let PatKind :: Tuple ( ref pat, None ) = arms[ 0 ] . pats[ 0 ] . node;
146- if pat. len( ) == 1 ;
147- if let ExprKind :: Array ( ref exprs) = arms[ 0 ] . body. node;
148- if exprs. len( ) == 1 ;
149- if let ExprKind :: Call ( _, ref args) = exprs[ 0 ] . node;
118+ if let ExprKind :: Call ( ref fun, ref args) = expr. node;
150119 if args. len( ) == 2 ;
151- if let ExprKind :: Path ( ref qpath) = args[ 1 ] . node;
152- if let Some ( fun_def_id) = resolve_node( cx, qpath, args[ 1 ] . hir_id) . opt_def_id( ) ;
153- if match_def_path( cx, fun_def_id, & paths:: DISPLAY_FMT_METHOD ) ;
120+ if let ExprKind :: Path ( ref qpath) = fun. node;
121+ if let Some ( did) = resolve_node( cx, qpath, fun. hir_id) . opt_def_id( ) ;
122+ if match_def_path( cx, did, & paths:: FMT_ARGUMENTS_NEW_V1 ) ;
123+ // Argument 1 in `new_v1()`
124+ if let ExprKind :: AddrOf ( _, ref arr) = args[ 0 ] . node;
125+ if let ExprKind :: Array ( ref pieces) = arr. node;
126+ if pieces. len( ) == 1 ;
127+ if let ExprKind :: Lit ( ref lit) = pieces[ 0 ] . node;
128+ if let LitKind :: Str ( ref s, _) = lit. node;
129+ // Argument 2 in `new_v1()`
130+ if let ExprKind :: AddrOf ( _, ref arg1) = args[ 1 ] . node;
131+ if let ExprKind :: Match ( ref matchee, ref arms, MatchSource :: Normal ) = arg1. node;
132+ if arms. len( ) == 1 ;
133+ if let ExprKind :: Tup ( ref tup) = matchee. node;
154134 then {
155- let ty = walk_ptrs_ty ( cx . tables . pat_ty ( & pat [ 0 ] ) ) ;
156- if ty . sty == ty :: Str || match_type ( cx , ty , & paths :: STRING ) {
157- if let ExprKind :: Tup ( ref values ) = match_expr . node {
158- return Some ( & values [ 0 ] ) ;
159- }
135+ // `format!("foo")` expansion contains `match () { () => [], }`
136+ if tup . is_empty ( ) {
137+ return Some ( format! ( "{:?}.to_string()" , s . as_str ( ) ) ) ;
138+ } else if s . as_str ( ) . is_empty ( ) {
139+ return on_argumentv1_new ( cx , & tup [ 0 ] , arms ) ;
160140 }
161141 }
162142 }
143+ None
144+ }
163145
146+ fn on_new_v1_fmt < ' a , ' tcx > ( cx : & LateContext < ' a , ' tcx > , expr : & ' tcx Expr ) -> Option < String > {
147+ if_chain ! {
148+ if let ExprKind :: Call ( ref fun, ref args) = expr. node;
149+ if args. len( ) == 3 ;
150+ if let ExprKind :: Path ( ref qpath) = fun. node;
151+ if let Some ( did) = resolve_node( cx, qpath, fun. hir_id) . opt_def_id( ) ;
152+ if match_def_path( cx, did, & paths:: FMT_ARGUMENTS_NEW_V1_FORMATTED ) ;
153+ if check_unformatted( & args[ 2 ] ) ;
154+ // Argument 1 in `new_v1_formatted()`
155+ if let ExprKind :: AddrOf ( _, ref arr) = args[ 0 ] . node;
156+ if let ExprKind :: Array ( ref pieces) = arr. node;
157+ if pieces. len( ) == 1 ;
158+ if let ExprKind :: Lit ( ref lit) = pieces[ 0 ] . node;
159+ if let LitKind :: Str ( ..) = lit. node;
160+ // Argument 2 in `new_v1_formatted()`
161+ if let ExprKind :: AddrOf ( _, ref arg1) = args[ 1 ] . node;
162+ if let ExprKind :: Match ( ref matchee, ref arms, MatchSource :: Normal ) = arg1. node;
163+ if arms. len( ) == 1 ;
164+ if let ExprKind :: Tup ( ref tup) = matchee. node;
165+ then {
166+ return on_argumentv1_new( cx, & tup[ 0 ] , arms) ;
167+ }
168+ }
164169 None
165170}
166171
@@ -169,6 +174,7 @@ fn get_single_string_arg<'a>(cx: &LateContext<'_, '_>, expr: &'a Expr) -> Option
169174/// &[_ {
170175/// format: _ {
171176/// width: _::Implied,
177+ /// precision: _::Implied,
172178/// ...
173179/// },
174180/// ...,
@@ -179,15 +185,17 @@ fn check_unformatted(expr: &Expr) -> bool {
179185 if let ExprKind :: AddrOf ( _, ref expr) = expr. node;
180186 if let ExprKind :: Array ( ref exprs) = expr. node;
181187 if exprs. len( ) == 1 ;
188+ // struct `core::fmt::rt::v1::Argument`
182189 if let ExprKind :: Struct ( _, ref fields, _) = exprs[ 0 ] . node;
183190 if let Some ( format_field) = fields. iter( ) . find( |f| f. ident. name == sym!( format) ) ;
191+ // struct `core::fmt::rt::v1::FormatSpec`
184192 if let ExprKind :: Struct ( _, ref fields, _) = format_field. expr. node;
185- if let Some ( width_field) = fields. iter( ) . find( |f| f. ident. name == sym!( width) ) ;
186- if let ExprKind :: Path ( ref width_qpath) = width_field. expr. node;
187- if last_path_segment( width_qpath) . ident. name == sym!( Implied ) ;
188193 if let Some ( precision_field) = fields. iter( ) . find( |f| f. ident. name == sym!( precision) ) ;
189194 if let ExprKind :: Path ( ref precision_path) = precision_field. expr. node;
190195 if last_path_segment( precision_path) . ident. name == sym!( Implied ) ;
196+ if let Some ( width_field) = fields. iter( ) . find( |f| f. ident. name == sym!( width) ) ;
197+ if let ExprKind :: Path ( ref width_qpath) = width_field. expr. node;
198+ if last_path_segment( width_qpath) . ident. name == sym!( Implied ) ;
191199 then {
192200 return true ;
193201 }
0 commit comments