]> git.proxmox.com Git - rustc.git/blame - src/tools/clippy/clippy_utils/src/eager_or_lazy.rs
New upstream version 1.53.0+dfsg1
[rustc.git] / src / tools / clippy / clippy_utils / src / eager_or_lazy.rs
CommitLineData
f20569fa
XL
1//! Utilities for evaluating whether eagerly evaluated expressions can be made lazy and vice versa.
2//!
3//! Things to consider:
4//! - has the expression side-effects?
5//! - is the expression computationally expensive?
6//!
7//! See lints:
8//! - unnecessary-lazy-evaluations
9//! - or-fun-call
10//! - option-if-let-else
11
cdc7bbd5
XL
12use crate::is_ctor_or_promotable_const_function;
13use crate::ty::is_type_diagnostic_item;
f20569fa
XL
14use rustc_hir::def::{DefKind, Res};
15
16use rustc_hir::intravisit;
17use rustc_hir::intravisit::{NestedVisitorMap, Visitor};
18
19use rustc_hir::{Block, Expr, ExprKind, Path, QPath};
20use rustc_lint::LateContext;
21use rustc_middle::hir::map::Map;
22use rustc_span::sym;
23
24/// Is the expr pure (is it free from side-effects)?
25/// This function is named so to stress that it isn't exhaustive and returns FNs.
26fn identify_some_pure_patterns(expr: &Expr<'_>) -> bool {
27 match expr.kind {
28 ExprKind::Lit(..) | ExprKind::ConstBlock(..) | ExprKind::Path(..) | ExprKind::Field(..) => true,
29 ExprKind::AddrOf(_, _, addr_of_expr) => identify_some_pure_patterns(addr_of_expr),
30 ExprKind::Tup(tup_exprs) => tup_exprs.iter().all(|expr| identify_some_pure_patterns(expr)),
31 ExprKind::Struct(_, fields, expr) => {
32 fields.iter().all(|f| identify_some_pure_patterns(f.expr))
33 && expr.map_or(true, |e| identify_some_pure_patterns(e))
34 },
35 ExprKind::Call(
36 &Expr {
37 kind:
38 ExprKind::Path(QPath::Resolved(
39 _,
40 Path {
41 res: Res::Def(DefKind::Ctor(..) | DefKind::Variant, ..),
42 ..
43 },
44 )),
45 ..
46 },
47 args,
48 ) => args.iter().all(|expr| identify_some_pure_patterns(expr)),
49 ExprKind::Block(
50 &Block {
51 stmts,
52 expr: Some(expr),
53 ..
54 },
55 _,
56 ) => stmts.is_empty() && identify_some_pure_patterns(expr),
57 ExprKind::Box(..)
58 | ExprKind::Array(..)
59 | ExprKind::Call(..)
60 | ExprKind::MethodCall(..)
61 | ExprKind::Binary(..)
62 | ExprKind::Unary(..)
63 | ExprKind::Cast(..)
64 | ExprKind::Type(..)
65 | ExprKind::DropTemps(..)
66 | ExprKind::Loop(..)
67 | ExprKind::If(..)
68 | ExprKind::Match(..)
69 | ExprKind::Closure(..)
70 | ExprKind::Block(..)
71 | ExprKind::Assign(..)
72 | ExprKind::AssignOp(..)
73 | ExprKind::Index(..)
74 | ExprKind::Break(..)
75 | ExprKind::Continue(..)
76 | ExprKind::Ret(..)
77 | ExprKind::InlineAsm(..)
78 | ExprKind::LlvmInlineAsm(..)
79 | ExprKind::Repeat(..)
80 | ExprKind::Yield(..)
81 | ExprKind::Err => false,
82 }
83}
84
85/// Identify some potentially computationally expensive patterns.
86/// This function is named so to stress that its implementation is non-exhaustive.
87/// It returns FNs and FPs.
88fn identify_some_potentially_expensive_patterns<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
89 // Searches an expression for method calls or function calls that aren't ctors
90 struct FunCallFinder<'a, 'tcx> {
91 cx: &'a LateContext<'tcx>,
92 found: bool,
93 }
94
95 impl<'a, 'tcx> intravisit::Visitor<'tcx> for FunCallFinder<'a, 'tcx> {
96 type Map = Map<'tcx>;
97
98 fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
99 let call_found = match &expr.kind {
100 // ignore enum and struct constructors
101 ExprKind::Call(..) => !is_ctor_or_promotable_const_function(self.cx, expr),
102 ExprKind::Index(obj, _) => {
103 let ty = self.cx.typeck_results().expr_ty(obj);
104 is_type_diagnostic_item(self.cx, ty, sym::hashmap_type)
105 || is_type_diagnostic_item(self.cx, ty, sym::BTreeMap)
106 },
107 ExprKind::MethodCall(..) => true,
108 _ => false,
109 };
110
111 if call_found {
112 self.found |= true;
113 }
114
115 if !self.found {
116 intravisit::walk_expr(self, expr);
117 }
118 }
119
120 fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
121 NestedVisitorMap::None
122 }
123 }
124
125 let mut finder = FunCallFinder { cx, found: false };
126 finder.visit_expr(expr);
127 finder.found
128}
129
130pub fn is_eagerness_candidate<'a, 'tcx>(cx: &'a LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
131 !identify_some_potentially_expensive_patterns(cx, expr) && identify_some_pure_patterns(expr)
132}
133
134pub fn is_lazyness_candidate<'a, 'tcx>(cx: &'a LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
135 identify_some_potentially_expensive_patterns(cx, expr)
136}