@@ -38,73 +38,87 @@ declare_lint_pass!(NoopMethodCall => [NOOP_METHOD_CALL]);
3838
3939impl < ' tcx > LateLintPass < ' tcx > for NoopMethodCall {
4040 fn check_expr ( & mut self , cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' _ > ) {
41- // We only care about method calls
42- if let ExprKind :: MethodCall ( call, _, elements, _) = expr. kind {
43- // Get the `DefId` only when dealing with an `AssocFn`
44- if let Some ( ( DefKind :: AssocFn , did) ) =
45- cx. typeck_results ( ) . type_dependent_def ( expr. hir_id )
46- {
47- // Check that we're dealing with a trait method
48- if let Some ( trait_id) = cx. tcx . trait_of_item ( did) {
49- // Check we're dealing with one of the traits we care about
50- if ![ sym:: Clone , sym:: Deref , sym:: Borrow ]
41+ // We only care about method calls.
42+ let ( call, elements) = match expr. kind {
43+ ExprKind :: MethodCall ( call, _, elements, _) => ( call, elements) ,
44+ _ => return ,
45+ } ;
46+ // We only care about method calls corresponding to the `Clone`, `Deref` and `Borrow`
47+ // traits and ignore any other method call.
48+ let ( trait_id, did) = match cx. typeck_results ( ) . type_dependent_def ( expr. hir_id ) {
49+ // Verify we are dealing with a method/associated function.
50+ Some ( ( DefKind :: AssocFn , did) ) => match cx. tcx . trait_of_item ( did) {
51+ // Check that we're dealing with a trait method for one of the traits we care about.
52+ Some ( trait_id)
53+ if [ sym:: Clone , sym:: Deref , sym:: Borrow ]
5154 . iter ( )
52- . any ( |s| cx. tcx . is_diagnostic_item ( * s, trait_id) )
53- {
54- return ;
55- }
56-
57- let substs = cx. typeck_results ( ) . node_substs ( expr. hir_id ) ;
58- // We can't resolve on types that require monomorphization,
59- // so check that we don't need to perfom substitution
60- if !substs. needs_subst ( ) {
61- let param_env = cx. tcx . param_env ( trait_id) ;
62- // Resolve the trait method instance
63- if let Ok ( Some ( i) ) = ty:: Instance :: resolve ( cx. tcx , param_env, did, substs) {
64- // Check that it implements the noop diagnostic
65- for ( s, peel_ref) in [
66- ( sym:: noop_method_borrow, true ) ,
67- ( sym:: noop_method_clone, false ) ,
68- ( sym:: noop_method_deref, true ) ,
69- ]
70- . iter ( )
71- {
72- if cx. tcx . is_diagnostic_item ( * s, i. def_id ( ) ) {
73- let method = & call. ident . name ;
74- let receiver = & elements[ 0 ] ;
75- let receiver_ty = cx. typeck_results ( ) . expr_ty ( receiver) ;
76- let receiver_ty = match receiver_ty. kind ( ) {
77- // Remove one borrow from the receiver as all the trait methods
78- // we care about here have a `&self` receiver.
79- ty:: Ref ( _, ty, _) if * peel_ref => ty,
80- _ => receiver_ty,
81- } ;
82- let expr_ty = cx. typeck_results ( ) . expr_ty_adjusted ( expr) ;
83- if receiver_ty != expr_ty {
84- return ;
85- }
86- let expr_span = expr. span ;
87- let note = format ! (
88- "the type `{:?}` which `{}` is being called on is the same as \
89- the type returned from `{}`, so the method call does not do \
90- anything and can be removed",
91- receiver_ty, method, method,
92- ) ;
93-
94- let span = expr_span. with_lo ( receiver. span . hi ( ) ) ;
95- cx. struct_span_lint ( NOOP_METHOD_CALL , span, |lint| {
96- let method = & call. ident . name ;
97- let message = format ! ( "call to `.{}()` on a reference in this situation does nothing" , & method) ;
98- lint. build ( & message)
99- . span_label ( span, "unnecessary method call" )
100- . note ( & note)
101- . emit ( )
102- } ) ;
103- }
104- }
105- }
106- }
55+ . any ( |s| cx. tcx . is_diagnostic_item ( * s, trait_id) ) =>
56+ {
57+ ( trait_id, did)
10758 }
59+ _ => return ,
60+ } ,
61+ _ => return ,
62+ } ;
63+ let substs = cx. typeck_results ( ) . node_substs ( expr. hir_id ) ;
64+ if substs. needs_subst ( ) {
65+ // We can't resolve on types that require monomorphization, so we don't handle them if
66+ // we need to perfom substitution.
67+ return ;
68+ }
69+ let param_env = cx. tcx . param_env ( trait_id) ;
70+ // Resolve the trait method instance.
71+ let i = match ty:: Instance :: resolve ( cx. tcx , param_env, did, substs) {
72+ Ok ( Some ( i) ) => i,
73+ _ => return ,
74+ } ;
75+ // (Re)check that it implements the noop diagnostic.
76+ for ( s, peel_ref) in [
77+ ( sym:: noop_method_borrow, true ) ,
78+ ( sym:: noop_method_clone, false ) ,
79+ ( sym:: noop_method_deref, true ) ,
80+ ]
81+ . iter ( )
82+ {
83+ if cx. tcx . is_diagnostic_item ( * s, i. def_id ( ) ) {
84+ let method = & call. ident . name ;
85+ let receiver = & elements[ 0 ] ;
86+ let receiver_ty = cx. typeck_results ( ) . expr_ty ( receiver) ;
87+ let receiver_ty = match receiver_ty. kind ( ) {
88+ // Remove one borrow from the receiver if appropriate to positively verify that
89+ // the receiver `&self` type and the return type are the same, depending on the
90+ // involved trait being checked.
91+ ty:: Ref ( _, ty, _) if * peel_ref => ty,
92+ // When it comes to `Clone` we need to check the `receiver_ty` directly.
93+ // FIXME: we must come up with a better strategy for this.
94+ _ => receiver_ty,
95+ } ;
96+ let expr_ty = cx. typeck_results ( ) . expr_ty_adjusted ( expr) ;
97+ if receiver_ty != expr_ty {
98+ // This lint will only trigger if the receiver type and resulting expression \
99+ // type are the same, implying that the method call is unnecessary.
100+ return ;
101+ }
102+ let expr_span = expr. span ;
103+ let note = format ! (
104+ "the type `{:?}` which `{}` is being called on is the same as \
105+ the type returned from `{}`, so the method call does not do \
106+ anything and can be removed",
107+ receiver_ty, method, method,
108+ ) ;
109+
110+ let span = expr_span. with_lo ( receiver. span . hi ( ) ) ;
111+ cx. struct_span_lint ( NOOP_METHOD_CALL , span, |lint| {
112+ let method = & call. ident . name ;
113+ let message = format ! (
114+ "call to `.{}()` on a reference in this situation does nothing" ,
115+ & method,
116+ ) ;
117+ lint. build ( & message)
118+ . span_label ( span, "unnecessary method call" )
119+ . note ( & note)
120+ . emit ( )
121+ } ) ;
108122 }
109123 }
110124 }
0 commit comments