@@ -215,11 +215,41 @@ declare_clippy_lint! {
215215 "redundant allocation"
216216}
217217
218+ declare_clippy_lint ! {
219+ /// **What it does:** Checks for Rc<T> and Arc<T> when T is a mutable buffer type such as String or Vec
220+ ///
221+ /// **Why is this bad?** Expressions such as Rc<String> have no advantage over Rc<str>, since
222+ /// it is larger and involves an extra level of indirection, and doesn't implement Borrow<str>.
223+ ///
224+ /// While mutating a buffer type would still be possible with Rc::get_mut(), it only
225+ /// works if there are no additional references yet, which defeats the purpose of
226+ /// enclosing it in a shared ownership type. Instead, additionally wrapping the inner
227+ /// type with an interior mutable container (such as RefCell or Mutex) would normally
228+ /// be used.
229+ ///
230+ /// **Known problems:** None.
231+ ///
232+ /// **Example:**
233+ /// ```rust,ignore
234+ /// # use std::rc::Rc;
235+ /// fn foo(interned: Rc<String>) { ... }
236+ /// ```
237+ ///
238+ /// Better:
239+ ///
240+ /// ```rust,ignore
241+ /// fn foo(interned: Rc<str>) { ... }
242+ /// ```
243+ pub RC_BUFFER ,
244+ perf,
245+ "shared ownership of a buffer type"
246+ }
247+
218248pub struct Types {
219249 vec_box_size_threshold : u64 ,
220250}
221251
222- impl_lint_pass ! ( Types => [ BOX_VEC , VEC_BOX , OPTION_OPTION , LINKEDLIST , BORROWED_BOX , REDUNDANT_ALLOCATION ] ) ;
252+ impl_lint_pass ! ( Types => [ BOX_VEC , VEC_BOX , OPTION_OPTION , LINKEDLIST , BORROWED_BOX , REDUNDANT_ALLOCATION , RC_BUFFER ] ) ;
223253
224254impl < ' tcx > LateLintPass < ' tcx > for Types {
225255 fn check_fn ( & mut self , cx : & LateContext < ' _ > , _: FnKind < ' _ > , decl : & FnDecl < ' _ > , _: & Body < ' _ > , _: Span , id : HirId ) {
@@ -272,6 +302,19 @@ fn match_type_parameter(cx: &LateContext<'_>, qpath: &QPath<'_>, path: &[&str])
272302 None
273303}
274304
305+ fn match_buffer_type ( cx : & LateContext < ' _ > , qpath : & QPath < ' _ > ) -> Option < & ' static str > {
306+ if match_type_parameter ( cx, qpath, & paths:: STRING ) . is_some ( ) {
307+ return Some ( "str" ) ;
308+ }
309+ if match_type_parameter ( cx, qpath, & paths:: OS_STRING ) . is_some ( ) {
310+ return Some ( "std::ffi::OsStr" ) ;
311+ }
312+ if match_type_parameter ( cx, qpath, & paths:: PATH_BUF ) . is_some ( ) {
313+ return Some ( "std::path::Path" ) ;
314+ }
315+ None
316+ }
317+
275318fn match_borrows_parameter ( _cx : & LateContext < ' _ > , qpath : & QPath < ' _ > ) -> Option < Span > {
276319 let last = last_path_segment ( qpath) ;
277320 if_chain ! {
@@ -385,6 +428,45 @@ impl Types {
385428 ) ;
386429 return ; // don't recurse into the type
387430 }
431+ if let Some ( alternate) = match_buffer_type ( cx, qpath) {
432+ span_lint_and_sugg (
433+ cx,
434+ RC_BUFFER ,
435+ hir_ty. span ,
436+ "usage of `Rc<T>` when T is a buffer type" ,
437+ "try" ,
438+ format ! ( "Rc<{}>" , alternate) ,
439+ Applicability :: MachineApplicable ,
440+ ) ;
441+ return ; // don't recurse into the type
442+ }
443+ if match_type_parameter ( cx, qpath, & paths:: VEC ) . is_some ( ) {
444+ let vec_ty = match & last_path_segment ( qpath) . args . unwrap ( ) . args [ 0 ] {
445+ GenericArg :: Type ( ty) => match & ty. kind {
446+ TyKind :: Path ( qpath) => qpath,
447+ _ => return ,
448+ } ,
449+ _ => return ,
450+ } ;
451+ let inner_span = match & last_path_segment ( & vec_ty) . args . unwrap ( ) . args [ 0 ] {
452+ GenericArg :: Type ( ty) => ty. span ,
453+ _ => return ,
454+ } ;
455+ let mut applicability = Applicability :: MachineApplicable ;
456+ span_lint_and_sugg (
457+ cx,
458+ RC_BUFFER ,
459+ hir_ty. span ,
460+ "usage of `Rc<T>` when T is a buffer type" ,
461+ "try" ,
462+ format ! (
463+ "Rc<[{}]>" ,
464+ snippet_with_applicability( cx, inner_span, ".." , & mut applicability)
465+ ) ,
466+ Applicability :: MachineApplicable ,
467+ ) ;
468+ return ; // don't recurse into the type
469+ }
388470 if let Some ( span) = match_borrows_parameter ( cx, qpath) {
389471 let mut applicability = Applicability :: MachineApplicable ;
390472 span_lint_and_sugg (
@@ -398,6 +480,46 @@ impl Types {
398480 ) ;
399481 return ; // don't recurse into the type
400482 }
483+ } else if cx. tcx . is_diagnostic_item ( sym:: Arc , def_id) {
484+ if let Some ( alternate) = match_buffer_type ( cx, qpath) {
485+ span_lint_and_sugg (
486+ cx,
487+ RC_BUFFER ,
488+ hir_ty. span ,
489+ "usage of `Arc<T>` when T is a buffer type" ,
490+ "try" ,
491+ format ! ( "Arc<{}>" , alternate) ,
492+ Applicability :: MachineApplicable ,
493+ ) ;
494+ return ; // don't recurse into the type
495+ }
496+ if match_type_parameter ( cx, qpath, & paths:: VEC ) . is_some ( ) {
497+ let vec_ty = match & last_path_segment ( qpath) . args . unwrap ( ) . args [ 0 ] {
498+ GenericArg :: Type ( ty) => match & ty. kind {
499+ TyKind :: Path ( qpath) => qpath,
500+ _ => return ,
501+ } ,
502+ _ => return ,
503+ } ;
504+ let inner_span = match & last_path_segment ( & vec_ty) . args . unwrap ( ) . args [ 0 ] {
505+ GenericArg :: Type ( ty) => ty. span ,
506+ _ => return ,
507+ } ;
508+ let mut applicability = Applicability :: MachineApplicable ;
509+ span_lint_and_sugg (
510+ cx,
511+ RC_BUFFER ,
512+ hir_ty. span ,
513+ "usage of `Arc<T>` when T is a buffer type" ,
514+ "try" ,
515+ format ! (
516+ "Arc<[{}]>" ,
517+ snippet_with_applicability( cx, inner_span, ".." , & mut applicability)
518+ ) ,
519+ Applicability :: MachineApplicable ,
520+ ) ;
521+ return ; // don't recurse into the type
522+ }
401523 } else if cx. tcx . is_diagnostic_item ( sym ! ( vec_type) , def_id) {
402524 if_chain ! {
403525 // Get the _ part of Vec<_>
0 commit comments