11use rustc_errors:: Applicability ;
2- use rustc_hir:: { BinOpKind , Expr , ExprKind } ;
2+ use rustc_hir:: { BinOpKind , BorrowKind , Expr , ExprKind , LangItem , QPath } ;
33use rustc_lint:: { LateContext , LateLintPass , LintContext } ;
44use rustc_middle:: lint:: in_external_macro;
55use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
@@ -9,7 +9,10 @@ use rustc_span::sym;
99use if_chain:: if_chain;
1010
1111use 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+ } ;
1316
1417declare_clippy_lint ! {
1518 /// **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 {
174177 }
175178}
176179
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+
177200// Max length a b"foo" string can take
178201const MAX_LENGTH_BYTE_STRING_LIT : usize = 32 ;
179202
180- declare_lint_pass ! ( StringLitAsBytes => [ STRING_LIT_AS_BYTES ] ) ;
203+ declare_lint_pass ! ( StringLitAsBytes => [ STRING_LIT_AS_BYTES , STRING_FROM_UTF8_AS_BYTES ] ) ;
181204
182205impl < ' tcx > LateLintPass < ' tcx > for StringLitAsBytes {
183206 fn check_expr ( & mut self , cx : & LateContext < ' tcx > , e : & ' tcx Expr < ' _ > ) {
184207 use crate :: utils:: { snippet, snippet_with_applicability} ;
185208 use rustc_ast:: LitKind ;
186209
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+
187249 if_chain ! {
188250 if let ExprKind :: MethodCall ( path, _, args, _) = & e. kind;
189251 if path. ident. name == sym!( as_bytes) ;
0 commit comments