@@ -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,58 @@ 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: For `let x = 1`, if the `HirId` of `x` is provided, the `Expr` `1` is returned.
177
+ /// Note: If you have an expression that references a binding `x`, use `path_to_local` to get the
178
+ /// canonical binding `HirId`.
179
+ pub fn find_binding_init < ' tcx > ( cx : & LateContext < ' tcx > , hir_id : HirId ) -> Option < & ' tcx Expr < ' tcx > > {
180
+ let hir = cx. tcx . hir ( ) ;
181
+ if_chain ! {
182
+ if let Some ( Node :: Binding ( pat) ) = hir. find( hir_id) ;
183
+ if matches!( pat. kind, PatKind :: Binding ( BindingAnnotation :: Unannotated , ..) ) ;
184
+ let parent = hir. get_parent_node( hir_id) ;
185
+ if let Some ( Node :: Local ( local) ) = hir. find( parent) ;
186
+ then {
187
+ return local. init;
188
+ }
189
+ }
190
+ None
191
+ }
192
+
141
193
/// Returns `true` if the given `NodeId` is inside a constant context
142
194
///
143
195
/// # Example
0 commit comments