]> git.lizzy.rs Git - rust.git/blobdiff - src/tools/clippy/clippy_utils/src/visitors.rs
Auto merge of #102755 - pcc:data-local-tmp, r=Mark-Simulacrum
[rust.git] / src / tools / clippy / clippy_utils / src / visitors.rs
index 232d571902b6c20a8f84684e7ca1ca5e88206f47..d4294f18fd5019c0eed128e9321f8caca80b0109 100644 (file)
@@ -5,14 +5,13 @@
 use rustc_hir::def::{CtorKind, DefKind, Res};
 use rustc_hir::intravisit::{self, walk_block, walk_expr, Visitor};
 use rustc_hir::{
-    Arm, Block, BlockCheckMode, Body, BodyId, Expr, ExprKind, HirId, ItemId, ItemKind, Let, Pat, QPath, Stmt, UnOp,
-    UnsafeSource, Unsafety,
+    AnonConst, Arm, Block, BlockCheckMode, Body, BodyId, Expr, ExprKind, HirId, ItemId, ItemKind, Let, Pat, QPath,
+    Stmt, UnOp, UnsafeSource, Unsafety,
 };
 use rustc_lint::LateContext;
-use rustc_middle::hir::map::Map;
 use rustc_middle::hir::nested_filter;
 use rustc_middle::ty::adjustment::Adjust;
-use rustc_middle::ty::{self, Ty, TypeckResults};
+use rustc_middle::ty::{self, Ty, TyCtxt, TypeckResults};
 use rustc_span::Span;
 
 mod internal {
@@ -48,6 +47,26 @@ fn descend(&self) -> bool {
     }
 }
 
+/// A type which can be visited.
+pub trait Visitable<'tcx> {
+    /// Calls the corresponding `visit_*` function on the visitor.
+    fn visit<V: Visitor<'tcx>>(self, visitor: &mut V);
+}
+macro_rules! visitable_ref {
+    ($t:ident, $f:ident) => {
+        impl<'tcx> Visitable<'tcx> for &'tcx $t<'tcx> {
+            fn visit<V: Visitor<'tcx>>(self, visitor: &mut V) {
+                visitor.$f(self);
+            }
+        }
+    };
+}
+visitable_ref!(Arm, visit_arm);
+visitable_ref!(Block, visit_block);
+visitable_ref!(Body, visit_body);
+visitable_ref!(Expr, visit_expr);
+visitable_ref!(Stmt, visit_stmt);
+
 /// Calls the given function once for each expression contained. This does not enter any bodies or
 /// nested items.
 pub fn for_each_expr<'tcx, B, C: Continue>(
@@ -82,57 +101,63 @@ fn visit_nested_item(&mut self, _: ItemId) {}
     v.res
 }
 
-/// Convenience method for creating a `Visitor` with just `visit_expr` overridden and nested
-/// bodies (i.e. closures) are visited.
-/// If the callback returns `true`, the expr just provided to the callback is walked.
-#[must_use]
-pub fn expr_visitor<'tcx>(cx: &LateContext<'tcx>, f: impl FnMut(&'tcx Expr<'tcx>) -> bool) -> impl Visitor<'tcx> {
-    struct V<'tcx, F> {
-        hir: Map<'tcx>,
+/// Calls the given function once for each expression contained. This will enter bodies, but not
+/// nested items.
+pub fn for_each_expr_with_closures<'tcx, B, C: Continue>(
+    cx: &LateContext<'tcx>,
+    node: impl Visitable<'tcx>,
+    f: impl FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B, C>,
+) -> Option<B> {
+    struct V<'tcx, B, F> {
+        tcx: TyCtxt<'tcx>,
         f: F,
+        res: Option<B>,
     }
-    impl<'tcx, F: FnMut(&'tcx Expr<'tcx>) -> bool> Visitor<'tcx> for V<'tcx, F> {
+    impl<'tcx, B, C: Continue, F: FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B, C>> Visitor<'tcx> for V<'tcx, B, F> {
         type NestedFilter = nested_filter::OnlyBodies;
         fn nested_visit_map(&mut self) -> Self::Map {
-            self.hir
+            self.tcx.hir()
         }
 
-        fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
-            if (self.f)(expr) {
-                walk_expr(self, expr);
+        fn visit_expr(&mut self, e: &'tcx Expr<'tcx>) {
+            if self.res.is_some() {
+                return;
             }
-        }
-    }
-    V { hir: cx.tcx.hir(), f }
-}
-
-/// Convenience method for creating a `Visitor` with just `visit_expr` overridden and nested
-/// bodies (i.e. closures) are not visited.
-/// If the callback returns `true`, the expr just provided to the callback is walked.
-#[must_use]
-pub fn expr_visitor_no_bodies<'tcx>(f: impl FnMut(&'tcx Expr<'tcx>) -> bool) -> impl Visitor<'tcx> {
-    struct V<F>(F);
-    impl<'tcx, F: FnMut(&'tcx Expr<'tcx>) -> bool> Visitor<'tcx> for V<F> {
-        fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
-            if (self.0)(e) {
-                walk_expr(self, e);
+            match (self.f)(e) {
+                ControlFlow::Continue(c) if c.descend() => walk_expr(self, e),
+                ControlFlow::Break(b) => self.res = Some(b),
+                ControlFlow::Continue(_) => (),
             }
         }
+
+        // Only walk closures
+        fn visit_anon_const(&mut self, _: &'tcx AnonConst) {}
+        // Avoid unnecessary `walk_*` calls.
+        fn visit_ty(&mut self, _: &'tcx hir::Ty<'tcx>) {}
+        fn visit_pat(&mut self, _: &'tcx Pat<'tcx>) {}
+        fn visit_qpath(&mut self, _: &'tcx QPath<'tcx>, _: HirId, _: Span) {}
+        // Avoid monomorphising all `visit_*` functions.
+        fn visit_nested_item(&mut self, _: ItemId) {}
     }
-    V(f)
+    let mut v = V {
+        tcx: cx.tcx,
+        f,
+        res: None,
+    };
+    node.visit(&mut v);
+    v.res
 }
 
 /// returns `true` if expr contains match expr desugared from try
 fn contains_try(expr: &hir::Expr<'_>) -> bool {
-    let mut found = false;
-    expr_visitor_no_bodies(|e| {
-        if !found {
-            found = matches!(e.kind, hir::ExprKind::Match(_, _, hir::MatchSource::TryDesugar));
+    for_each_expr(expr, |e| {
+        if matches!(e.kind, hir::ExprKind::Match(_, _, hir::MatchSource::TryDesugar)) {
+            ControlFlow::Break(())
+        } else {
+            ControlFlow::Continue(())
         }
-        !found
     })
-    .visit_expr(expr);
-    found
+    .is_some()
 }
 
 pub fn find_all_ret_expressions<'hir, F>(_cx: &LateContext<'_>, expr: &'hir hir::Expr<'hir>, callback: F) -> bool
@@ -228,68 +253,29 @@ fn visit_expr(&mut self, expr: &'hir hir::Expr<'_>) {
     }
 }
 
-/// A type which can be visited.
-pub trait Visitable<'tcx> {
-    /// Calls the corresponding `visit_*` function on the visitor.
-    fn visit<V: Visitor<'tcx>>(self, visitor: &mut V);
-}
-macro_rules! visitable_ref {
-    ($t:ident, $f:ident) => {
-        impl<'tcx> Visitable<'tcx> for &'tcx $t<'tcx> {
-            fn visit<V: Visitor<'tcx>>(self, visitor: &mut V) {
-                visitor.$f(self);
-            }
-        }
-    };
-}
-visitable_ref!(Arm, visit_arm);
-visitable_ref!(Block, visit_block);
-visitable_ref!(Body, visit_body);
-visitable_ref!(Expr, visit_expr);
-visitable_ref!(Stmt, visit_stmt);
-
-// impl<'tcx, I: IntoIterator> Visitable<'tcx> for I
-// where
-//     I::Item: Visitable<'tcx>,
-// {
-//     fn visit<V: Visitor<'tcx>>(self, visitor: &mut V) {
-//         for x in self {
-//             x.visit(visitor);
-//         }
-//     }
-// }
-
 /// Checks if the given resolved path is used in the given body.
 pub fn is_res_used(cx: &LateContext<'_>, res: Res, body: BodyId) -> bool {
-    let mut found = false;
-    expr_visitor(cx, |e| {
-        if found {
-            return false;
-        }
-
+    for_each_expr_with_closures(cx, cx.tcx.hir().body(body).value, |e| {
         if let ExprKind::Path(p) = &e.kind {
             if cx.qpath_res(p, e.hir_id) == res {
-                found = true;
+                return ControlFlow::Break(());
             }
         }
-        !found
+        ControlFlow::Continue(())
     })
-    .visit_expr(cx.tcx.hir().body(body).value);
-    found
+    .is_some()
 }
 
 /// Checks if the given local is used.
 pub fn is_local_used<'tcx>(cx: &LateContext<'tcx>, visitable: impl Visitable<'tcx>, id: HirId) -> bool {
-    let mut is_used = false;
-    let mut visitor = expr_visitor(cx, |expr| {
-        if !is_used {
-            is_used = path_to_local_id(expr, id);
+    for_each_expr_with_closures(cx, visitable, |e| {
+        if path_to_local_id(e, id) {
+            ControlFlow::Break(())
+        } else {
+            ControlFlow::Continue(())
         }
-        !is_used
-    });
-    visitable.visit(&mut visitor);
-    drop(visitor);
-    is_used
+    })
+    .is_some()
 }
 
 /// Checks if the given expression is a constant.