11use clippy_utils:: diagnostics:: span_lint_and_then;
2+ use clippy_utils:: source:: indent_of;
23use clippy_utils:: { is_default_equivalent, peel_blocks} ;
34use rustc_errors:: Applicability ;
45use rustc_hir:: {
5- def:: { DefKind , Res } ,
6- Body , Expr , ExprKind , GenericArg , Impl , ImplItemKind , Item , ItemKind , Node , PathSegment , QPath , TyKind ,
6+ def:: { CtorKind , CtorOf , DefKind , Res } ,
7+ Body , Expr , ExprKind , GenericArg , Impl , ImplItemKind , Item , ItemKind , Node , PathSegment , QPath , Ty , TyKind ,
78} ;
89use rustc_lint:: { LateContext , LateLintPass } ;
10+ use rustc_middle:: ty:: { AdtDef , DefIdTree } ;
911use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
1012use rustc_span:: sym;
1113
@@ -61,6 +63,98 @@ fn is_path_self(e: &Expr<'_>) -> bool {
6163 }
6264}
6365
66+ fn check_struct < ' tcx > (
67+ cx : & LateContext < ' tcx > ,
68+ item : & ' tcx Item < ' _ > ,
69+ self_ty : & Ty < ' _ > ,
70+ func_expr : & Expr < ' _ > ,
71+ adt_def : AdtDef < ' _ > ,
72+ ) {
73+ if let TyKind :: Path ( QPath :: Resolved ( _, p) ) = self_ty. kind {
74+ if let Some ( PathSegment { args : Some ( a) , .. } ) = p. segments . last ( ) {
75+ for arg in a. args {
76+ if !matches ! ( arg, GenericArg :: Lifetime ( _) ) {
77+ return ;
78+ }
79+ }
80+ }
81+ }
82+ let should_emit = match peel_blocks ( func_expr) . kind {
83+ ExprKind :: Tup ( fields) => fields. iter ( ) . all ( |e| is_default_equivalent ( cx, e) ) ,
84+ ExprKind :: Call ( callee, args) if is_path_self ( callee) => args. iter ( ) . all ( |e| is_default_equivalent ( cx, e) ) ,
85+ ExprKind :: Struct ( _, fields, _) => fields. iter ( ) . all ( |ef| is_default_equivalent ( cx, ef. expr ) ) ,
86+ _ => false ,
87+ } ;
88+
89+ if should_emit {
90+ let struct_span = cx. tcx . def_span ( adt_def. did ( ) ) ;
91+ span_lint_and_then ( cx, DERIVABLE_IMPLS , item. span , "this `impl` can be derived" , |diag| {
92+ diag. span_suggestion_hidden (
93+ item. span ,
94+ "remove the manual implementation..." ,
95+ String :: new ( ) ,
96+ Applicability :: MachineApplicable ,
97+ ) ;
98+ diag. span_suggestion (
99+ struct_span. shrink_to_lo ( ) ,
100+ "...and instead derive it" ,
101+ "#[derive(Default)]\n " . to_string ( ) ,
102+ Applicability :: MachineApplicable ,
103+ ) ;
104+ } ) ;
105+ }
106+ }
107+
108+ fn check_enum < ' tcx > ( cx : & LateContext < ' tcx > , item : & ' tcx Item < ' _ > , func_expr : & Expr < ' _ > , adt_def : AdtDef < ' _ > ) {
109+ if_chain ! {
110+ if let ExprKind :: Path ( QPath :: Resolved ( None , p) ) = & peel_blocks( func_expr) . kind;
111+ if let Res :: Def ( DefKind :: Ctor ( CtorOf :: Variant , CtorKind :: Const ) , id) = p. res;
112+ if let variant_id = cx. tcx. parent( id) ;
113+ if let Some ( variant_def) = adt_def. variants( ) . iter( ) . find( |v| v. def_id == variant_id) ;
114+ if variant_def. fields. is_empty( ) ;
115+ if !variant_def. is_field_list_non_exhaustive( ) ;
116+
117+ then {
118+ let enum_span = cx. tcx. def_span( adt_def. did( ) ) ;
119+ let indent_enum = indent_of( cx, enum_span) . unwrap_or( 0 ) ;
120+ let variant_span = cx. tcx. def_span( variant_def. def_id) ;
121+ let indent_variant = indent_of( cx, variant_span) . unwrap_or( 0 ) ;
122+ span_lint_and_then(
123+ cx,
124+ DERIVABLE_IMPLS ,
125+ item. span,
126+ "this `impl` can be derived" ,
127+ |diag| {
128+ diag. span_suggestion_hidden(
129+ item. span,
130+ "remove the manual implementation..." ,
131+ String :: new( ) ,
132+ Applicability :: MachineApplicable
133+ ) ;
134+ diag. span_suggestion(
135+ enum_span. shrink_to_lo( ) ,
136+ "...and instead derive it..." ,
137+ format!(
138+ "#[derive(Default)]\n {indent}" ,
139+ indent = " " . repeat( indent_enum) ,
140+ ) ,
141+ Applicability :: MachineApplicable
142+ ) ;
143+ diag. span_suggestion(
144+ variant_span. shrink_to_lo( ) ,
145+ "...and mark the default variant" ,
146+ format!(
147+ "#[default]\n {indent}" ,
148+ indent = " " . repeat( indent_variant) ,
149+ ) ,
150+ Applicability :: MachineApplicable
151+ ) ;
152+ }
153+ ) ;
154+ }
155+ }
156+ }
157+
64158impl < ' tcx > LateLintPass < ' tcx > for DerivableImpls {
65159 fn check_item ( & mut self , cx : & LateContext < ' tcx > , item : & ' tcx Item < ' _ > ) {
66160 if_chain ! {
@@ -83,47 +177,12 @@ impl<'tcx> LateLintPass<'tcx> for DerivableImpls {
83177 if !attrs. iter( ) . any( |attr| attr. doc_str( ) . is_some( ) ) ;
84178 if let child_attrs = cx. tcx. hir( ) . attrs( impl_item_hir) ;
85179 if !child_attrs. iter( ) . any( |attr| attr. doc_str( ) . is_some( ) ) ;
86- if adt_def. is_struct( ) ;
87- then {
88- if let TyKind :: Path ( QPath :: Resolved ( _, p) ) = self_ty. kind {
89- if let Some ( PathSegment { args: Some ( a) , .. } ) = p. segments. last( ) {
90- for arg in a. args {
91- if !matches!( arg, GenericArg :: Lifetime ( _) ) {
92- return ;
93- }
94- }
95- }
96- }
97- let should_emit = match peel_blocks( func_expr) . kind {
98- ExprKind :: Tup ( fields) => fields. iter( ) . all( |e| is_default_equivalent( cx, e) ) ,
99- ExprKind :: Call ( callee, args)
100- if is_path_self( callee) => args. iter( ) . all( |e| is_default_equivalent( cx, e) ) ,
101- ExprKind :: Struct ( _, fields, _) => fields. iter( ) . all( |ef| is_default_equivalent( cx, ef. expr) ) ,
102- _ => false ,
103- } ;
104180
105- if should_emit {
106- let struct_span = cx. tcx. def_span( adt_def. did( ) ) ;
107- span_lint_and_then(
108- cx,
109- DERIVABLE_IMPLS ,
110- item. span,
111- "this `impl` can be derived" ,
112- |diag| {
113- diag. span_suggestion_hidden(
114- item. span,
115- "remove the manual implementation..." ,
116- String :: new( ) ,
117- Applicability :: MachineApplicable
118- ) ;
119- diag. span_suggestion(
120- struct_span. shrink_to_lo( ) ,
121- "...and instead derive it" ,
122- "#[derive(Default)]\n " . to_string( ) ,
123- Applicability :: MachineApplicable
124- ) ;
125- }
126- ) ;
181+ then {
182+ if adt_def. is_struct( ) {
183+ check_struct( cx, item, self_ty, func_expr, adt_def) ;
184+ } else if adt_def. is_enum( ) {
185+ check_enum( cx, item, func_expr, adt_def) ;
127186 }
128187 }
129188 }
0 commit comments