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