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