1 //! Utilities for evaluating whether eagerly evaluated expressions can be made lazy and vice versa.
3 //! Things to consider:
4 //! - has the expression side-effects?
5 //! - is the expression computationally expensive?
8 //! - unnecessary-lazy-evaluations
10 //! - option-if-let-else
12 use crate::{is_ctor_or_promotable_const_function, is_type_diagnostic_item, match_type, paths};
13 use rustc_hir::def::{DefKind, Res};
15 use rustc_hir::intravisit;
16 use rustc_hir::intravisit::{NestedVisitorMap, Visitor};
18 use rustc_hir::{Block, Expr, ExprKind, Path, QPath};
19 use rustc_lint::LateContext;
20 use rustc_middle::hir::map::Map;
23 /// Is the expr pure (is it free from side-effects)?
24 /// This function is named so to stress that it isn't exhaustive and returns FNs.
25 fn identify_some_pure_patterns(expr: &Expr<'_>) -> bool {
27 ExprKind::Lit(..) | ExprKind::ConstBlock(..) | ExprKind::Path(..) | ExprKind::Field(..) => true,
28 ExprKind::AddrOf(_, _, addr_of_expr) => identify_some_pure_patterns(addr_of_expr),
29 ExprKind::Tup(tup_exprs) => tup_exprs.iter().all(|expr| identify_some_pure_patterns(expr)),
30 ExprKind::Struct(_, fields, expr) => {
31 fields.iter().all(|f| identify_some_pure_patterns(f.expr))
32 && expr.map_or(true, |e| identify_some_pure_patterns(e))
37 ExprKind::Path(QPath::Resolved(
40 res: Res::Def(DefKind::Ctor(..) | DefKind::Variant, ..),
47 ) => args.iter().all(|expr| identify_some_pure_patterns(expr)),
55 ) => stmts.is_empty() && identify_some_pure_patterns(expr),
59 | ExprKind::MethodCall(..)
60 | ExprKind::Binary(..)
64 | ExprKind::DropTemps(..)
68 | ExprKind::Closure(..)
70 | ExprKind::Assign(..)
71 | ExprKind::AssignOp(..)
74 | ExprKind::Continue(..)
76 | ExprKind::InlineAsm(..)
77 | ExprKind::LlvmInlineAsm(..)
78 | ExprKind::Repeat(..)
80 | ExprKind::Err => false,
84 /// Identify some potentially computationally expensive patterns.
85 /// This function is named so to stress that its implementation is non-exhaustive.
86 /// It returns FNs and FPs.
87 fn identify_some_potentially_expensive_patterns<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
88 // Searches an expression for method calls or function calls that aren't ctors
89 struct FunCallFinder<'a, 'tcx> {
90 cx: &'a LateContext<'tcx>,
94 impl<'a, 'tcx> intravisit::Visitor<'tcx> for FunCallFinder<'a, 'tcx> {
97 fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
98 let call_found = match &expr.kind {
99 // ignore enum and struct constructors
100 ExprKind::Call(..) => !is_ctor_or_promotable_const_function(self.cx, expr),
101 ExprKind::Index(obj, _) => {
102 let ty = self.cx.typeck_results().expr_ty(obj);
103 is_type_diagnostic_item(self.cx, ty, sym::hashmap_type)
104 || match_type(self.cx, ty, &paths::BTREEMAP)
106 ExprKind::MethodCall(..) => true,
115 intravisit::walk_expr(self, expr);
119 fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
120 NestedVisitorMap::None
124 let mut finder = FunCallFinder { cx, found: false };
125 finder.visit_expr(expr);
129 pub fn is_eagerness_candidate<'a, 'tcx>(cx: &'a LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
130 !identify_some_potentially_expensive_patterns(cx, expr) && identify_some_pure_patterns(expr)
133 pub fn is_lazyness_candidate<'a, 'tcx>(cx: &'a LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
134 identify_some_potentially_expensive_patterns(cx, expr)