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} ;
@@ -8,7 +8,10 @@ use rustc_span::source_map::Spanned;
88use if_chain:: if_chain;
99
1010use 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+ } ;
1215
1316declare_clippy_lint ! {
1417 /// **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 {
173176 }
174177}
175178
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+
176199// Max length a b"foo" string can take
177200const MAX_LENGTH_BYTE_STRING_LIT : usize = 32 ;
178201
179- declare_lint_pass ! ( StringLitAsBytes => [ STRING_LIT_AS_BYTES ] ) ;
202+ declare_lint_pass ! ( StringLitAsBytes => [ STRING_LIT_AS_BYTES , STRING_FROM_UTF8_AS_BYTES ] ) ;
180203
181204impl < ' tcx > LateLintPass < ' tcx > for StringLitAsBytes {
182205 fn check_expr ( & mut self , cx : & LateContext < ' tcx > , e : & ' tcx Expr < ' _ > ) {
183206 use crate :: utils:: { snippet, snippet_with_applicability} ;
184207 use rustc_ast:: LitKind ;
185208
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+
186248 if_chain ! {
187249 if let ExprKind :: MethodCall ( path, _, args, _) = & e. kind;
188250 if path. ident. name == sym!( as_bytes) ;
0 commit comments