1
1
use rustc_errors:: Applicability ;
2
- use rustc_hir:: { BinOpKind , Expr , ExprKind } ;
2
+ use rustc_hir:: { BinOpKind , BorrowKind , Expr , ExprKind , LangItem , QPath } ;
3
3
use rustc_lint:: { LateContext , LateLintPass , LintContext } ;
4
4
use rustc_middle:: lint:: in_external_macro;
5
5
use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
@@ -9,7 +9,10 @@ use rustc_span::sym;
9
9
use if_chain:: if_chain;
10
10
11
11
use crate :: utils:: SpanlessEq ;
12
- use crate :: utils:: { get_parent_expr, is_allowed, is_type_diagnostic_item, span_lint, span_lint_and_sugg} ;
12
+ use crate :: utils:: {
13
+ get_parent_expr, is_allowed, is_type_diagnostic_item, match_function_call, method_calls, paths, span_lint,
14
+ span_lint_and_sugg,
15
+ } ;
13
16
14
17
declare_clippy_lint ! {
15
18
/// **What it does:** Checks for string appends of the form `x = x + y` (without
@@ -174,16 +177,75 @@ fn is_add(cx: &LateContext<'_>, src: &Expr<'_>, target: &Expr<'_>) -> bool {
174
177
}
175
178
}
176
179
180
+ declare_clippy_lint ! {
181
+ /// **What it does:** Check if the string is transformed to byte array and casted back to string.
182
+ ///
183
+ /// **Why is this bad?** It's unnecessary, the string can be used directly.
184
+ ///
185
+ /// **Known problems:** None
186
+ ///
187
+ /// **Example:**
188
+ /// ```rust
189
+ /// let _ = std::str::from_utf8(&"Hello World!".as_bytes()[6..11]).unwrap();
190
+ /// ```
191
+ /// could be written as
192
+ /// ```rust
193
+ /// let _ = &"Hello World!"[6..11];
194
+ /// ```
195
+ pub STRING_FROM_UTF8_AS_BYTES ,
196
+ complexity,
197
+ "casting string slices to byte slices and back"
198
+ }
199
+
177
200
// Max length a b"foo" string can take
178
201
const MAX_LENGTH_BYTE_STRING_LIT : usize = 32 ;
179
202
180
- declare_lint_pass ! ( StringLitAsBytes => [ STRING_LIT_AS_BYTES ] ) ;
203
+ declare_lint_pass ! ( StringLitAsBytes => [ STRING_LIT_AS_BYTES , STRING_FROM_UTF8_AS_BYTES ] ) ;
181
204
182
205
impl < ' tcx > LateLintPass < ' tcx > for StringLitAsBytes {
183
206
fn check_expr ( & mut self , cx : & LateContext < ' tcx > , e : & ' tcx Expr < ' _ > ) {
184
207
use crate :: utils:: { snippet, snippet_with_applicability} ;
185
208
use rustc_ast:: LitKind ;
186
209
210
+ if_chain ! {
211
+ // Find std::str::converts::from_utf8
212
+ if let Some ( args) = match_function_call( cx, e, & paths:: STR_FROM_UTF8 ) ;
213
+
214
+ // Find string::as_bytes
215
+ if let ExprKind :: AddrOf ( BorrowKind :: Ref , _, ref args) = args[ 0 ] . kind;
216
+ if let ExprKind :: Index ( ref left, ref right) = args. kind;
217
+ let ( method_names, expressions, _) = method_calls( left, 1 ) ;
218
+ if method_names. len( ) == 1 ;
219
+ if expressions. len( ) == 1 ;
220
+ if expressions[ 0 ] . len( ) == 1 ;
221
+ if method_names[ 0 ] == sym!( as_bytes) ;
222
+
223
+ // Check for slicer
224
+ if let ExprKind :: Struct ( ref path, _, _) = right. kind;
225
+ if let QPath :: LangItem ( LangItem :: Range , _) = path;
226
+
227
+ then {
228
+ let mut applicability = Applicability :: MachineApplicable ;
229
+ let string_expression = & expressions[ 0 ] [ 0 ] ;
230
+
231
+ let snippet_app = snippet_with_applicability(
232
+ cx,
233
+ string_expression. span, ".." ,
234
+ & mut applicability,
235
+ ) ;
236
+
237
+ span_lint_and_sugg(
238
+ cx,
239
+ STRING_FROM_UTF8_AS_BYTES ,
240
+ e. span,
241
+ "calling a slice of `as_bytes()` with `from_utf8` should be not necessary" ,
242
+ "try" ,
243
+ format!( "Some(&{}[{}])" , snippet_app, snippet( cx, right. span, ".." ) ) ,
244
+ applicability
245
+ )
246
+ }
247
+ }
248
+
187
249
if_chain ! {
188
250
if let ExprKind :: MethodCall ( path, _, args, _) = & e. kind;
189
251
if path. ident. name == sym!( as_bytes) ;
0 commit comments