@@ -62,10 +62,9 @@ use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
62
62
use rustc_hir:: def_id:: { DefId , LOCAL_CRATE } ;
63
63
use rustc_hir:: intravisit:: { self , NestedVisitorMap , Visitor } ;
64
64
use rustc_hir:: {
65
- def, Arm , Block , Body , Constness , CrateItem , Expr , ExprKind , FnDecl , ForeignItem , GenericArgs , GenericParam , HirId ,
66
- Impl , ImplItem , ImplItemKind , Item , ItemKind , LangItem , Lifetime , Local , MacroDef , MatchSource , Node , Param , Pat ,
67
- PatKind , Path , PathSegment , QPath , Stmt , StructField , TraitItem , TraitItemKind , TraitRef , TyKind , Unsafety ,
68
- Variant , Visibility ,
65
+ def, Arm , BindingAnnotation , Block , Body , Constness , CrateItem , Expr , ExprKind , FnDecl , ForeignItem , GenericArgs , GenericParam , HirId , Impl ,
66
+ ImplItem , ImplItemKind , Item , ItemKind , LangItem , Lifetime , Local , MacroDef , MatchSource , Node , Param , Pat , PatKind , Path , PathSegment , QPath ,
67
+ Stmt , StructField , TraitItem , TraitItemKind , TraitRef , TyKind , Unsafety , Variant , Visibility ,
69
68
} ;
70
69
use rustc_infer:: infer:: TyCtxtInferExt ;
71
70
use rustc_lint:: { LateContext , Level , Lint , LintContext } ;
@@ -138,6 +137,62 @@ pub fn differing_macro_contexts(lhs: Span, rhs: Span) -> bool {
138
137
rhs. ctxt ( ) != lhs. ctxt ( )
139
138
}
140
139
140
+ /// If the given expression is a local binding, find the initializer expression.
141
+ /// If that initializer expression is another local binding, find its initializer again.
142
+ /// This process repeats as long as possible (but usually no more than once). Initializer
143
+ /// expressions with adjustments are ignored. If this is not desired, use [`find_binding_init`]
144
+ /// instead.
145
+ ///
146
+ /// Examples:
147
+ /// ```ignore
148
+ /// let abc = 1;
149
+ /// // ^ output
150
+ /// let def = abc;
151
+ /// dbg!(def)
152
+ /// // ^^^ input
153
+ ///
154
+ /// // or...
155
+ /// let abc = 1;
156
+ /// let def = abc + 2;
157
+ /// // ^^^^^^^ output
158
+ /// dbg!(def)
159
+ /// // ^^^ input
160
+ /// ```
161
+ pub fn expr_or_init < ' a , ' b , ' tcx : ' b > ( cx : & LateContext < ' tcx > , mut expr : & ' a Expr < ' b > ) -> & ' a Expr < ' b > {
162
+ while let Some ( init) = path_to_local ( expr)
163
+ . and_then ( |id| find_binding_init ( cx, id) )
164
+ . filter ( |init| cx. typeck_results ( ) . expr_adjustments ( init) . is_empty ( ) )
165
+ {
166
+ expr = init;
167
+ }
168
+ expr
169
+ }
170
+
171
+ /// Finds the initializer expression for a local binding. Returns `None` if the binding is mutable.
172
+ /// By only considering immutable bindings, we guarantee that the returned expression represents the
173
+ /// value of the binding wherever it is referenced.
174
+ ///
175
+ /// Example:
176
+ /// ```ignore
177
+ /// let abc = 1;
178
+ /// // ^ output
179
+ /// dbg!(abc)
180
+ /// // ^^^ input
181
+ /// ```
182
+ pub fn find_binding_init < ' tcx > ( cx : & LateContext < ' tcx > , hir_id : HirId ) -> Option < & ' tcx Expr < ' tcx > > {
183
+ let hir = cx. tcx . hir ( ) ;
184
+ if_chain ! {
185
+ if let Some ( Node :: Binding ( pat) ) = hir. find( hir_id) ;
186
+ if matches!( pat. kind, PatKind :: Binding ( BindingAnnotation :: Unannotated , ..) ) ;
187
+ let parent = hir. get_parent_node( hir_id) ;
188
+ if let Some ( Node :: Local ( local) ) = hir. find( parent) ;
189
+ then {
190
+ return local. init;
191
+ }
192
+ }
193
+ None
194
+ }
195
+
141
196
/// Returns `true` if the given `NodeId` is inside a constant context
142
197
///
143
198
/// # Example
0 commit comments