]> git.proxmox.com Git - rustc.git/blame - src/tools/clippy/clippy_utils/src/higher.rs
New upstream version 1.60.0+dfsg1
[rustc.git] / src / tools / clippy / clippy_utils / src / higher.rs
CommitLineData
3c0e092e 1//! This module contains functions that retrieve specific elements.
f20569fa
XL
2
3#![deny(clippy::missing_docs_in_private_items)]
4
3c0e092e 5use crate::ty::is_type_diagnostic_item;
5099ac24 6use crate::{is_expn_of, match_def_path, paths};
f20569fa 7use if_chain::if_chain;
136023e0 8use rustc_ast::ast::{self, LitKind};
f20569fa 9use rustc_hir as hir;
5099ac24 10use rustc_hir::{Arm, Block, Expr, ExprKind, HirId, LoopSource, MatchSource, Node, Pat, QPath};
f20569fa 11use rustc_lint::LateContext;
5099ac24 12use rustc_span::{sym, symbol, Span};
f20569fa 13
94222f64
XL
14/// The essential nodes of a desugared for loop as well as the entire span:
15/// `for pat in arg { body }` becomes `(pat, arg, body)`. Return `(pat, arg, body, span)`.
16pub struct ForLoop<'tcx> {
c295e0f8 17 /// `for` loop item
94222f64 18 pub pat: &'tcx hir::Pat<'tcx>,
c295e0f8 19 /// `IntoIterator` argument
94222f64 20 pub arg: &'tcx hir::Expr<'tcx>,
c295e0f8 21 /// `for` loop body
94222f64 22 pub body: &'tcx hir::Expr<'tcx>,
3c0e092e
XL
23 /// Compare this against `hir::Destination.target`
24 pub loop_id: HirId,
c295e0f8 25 /// entire `for` loop span
94222f64 26 pub span: Span,
f20569fa
XL
27}
28
94222f64 29impl<'tcx> ForLoop<'tcx> {
c295e0f8 30 /// Parses a desugared `for` loop
94222f64
XL
31 pub fn hir(expr: &Expr<'tcx>) -> Option<Self> {
32 if_chain! {
3c0e092e
XL
33 if let hir::ExprKind::DropTemps(e) = expr.kind;
34 if let hir::ExprKind::Match(iterexpr, [arm], hir::MatchSource::ForLoopDesugar) = e.kind;
35 if let hir::ExprKind::Call(_, [arg]) = iterexpr.kind;
36 if let hir::ExprKind::Loop(block, ..) = arm.body.kind;
37 if let [stmt] = &*block.stmts;
38 if let hir::StmtKind::Expr(e) = stmt.kind;
39 if let hir::ExprKind::Match(_, [_, some_arm], _) = e.kind;
40 if let hir::PatKind::Struct(_, [field], _) = some_arm.pat.kind;
94222f64
XL
41 then {
42 return Some(Self {
3c0e092e
XL
43 pat: field.pat,
44 arg,
45 body: some_arm.body,
46 loop_id: arm.body.hir_id,
47 span: expr.span.ctxt().outer_expn_data().call_site,
94222f64
XL
48 });
49 }
50 }
51 None
f20569fa 52 }
94222f64 53}
f20569fa 54
c295e0f8 55/// An `if` expression without `DropTemps`
94222f64 56pub struct If<'hir> {
c295e0f8 57 /// `if` condition
94222f64 58 pub cond: &'hir Expr<'hir>,
c295e0f8 59 /// `if` then expression
94222f64 60 pub then: &'hir Expr<'hir>,
c295e0f8
XL
61 /// `else` expression
62 pub r#else: Option<&'hir Expr<'hir>>,
f20569fa
XL
63}
64
94222f64
XL
65impl<'hir> If<'hir> {
66 #[inline]
c295e0f8 67 /// Parses an `if` expression
94222f64
XL
68 pub const fn hir(expr: &Expr<'hir>) -> Option<Self> {
69 if let ExprKind::If(
70 Expr {
71 kind: ExprKind::DropTemps(cond),
72 ..
73 },
74 then,
75 r#else,
76 ) = expr.kind
77 {
c295e0f8 78 Some(Self { cond, then, r#else })
94222f64
XL
79 } else {
80 None
f20569fa
XL
81 }
82 }
94222f64 83}
f20569fa 84
c295e0f8 85/// An `if let` expression
94222f64 86pub struct IfLet<'hir> {
c295e0f8 87 /// `if let` pattern
94222f64 88 pub let_pat: &'hir Pat<'hir>,
c295e0f8 89 /// `if let` scrutinee
94222f64 90 pub let_expr: &'hir Expr<'hir>,
c295e0f8 91 /// `if let` then expression
94222f64 92 pub if_then: &'hir Expr<'hir>,
c295e0f8 93 /// `if let` else expression
94222f64
XL
94 pub if_else: Option<&'hir Expr<'hir>>,
95}
96
97impl<'hir> IfLet<'hir> {
c295e0f8 98 /// Parses an `if let` expression
94222f64
XL
99 pub fn hir(cx: &LateContext<'_>, expr: &Expr<'hir>) -> Option<Self> {
100 if let ExprKind::If(
101 Expr {
a2a8927a
XL
102 kind:
103 ExprKind::Let(hir::Let {
104 pat: let_pat,
105 init: let_expr,
106 ..
107 }),
94222f64
XL
108 ..
109 },
110 if_then,
111 if_else,
112 ) = expr.kind
113 {
c295e0f8 114 let mut iter = cx.tcx.hir().parent_iter(expr.hir_id);
94222f64 115 if let Some((_, Node::Block(Block { stmts: [], .. }))) = iter.next() {
c295e0f8
XL
116 if let Some((
117 _,
118 Node::Expr(Expr {
119 kind: ExprKind::Loop(_, _, LoopSource::While, _),
120 ..
121 }),
122 )) = iter.next()
123 {
94222f64
XL
124 // while loop desugar
125 return None;
126 }
127 }
128 return Some(Self {
129 let_pat,
130 let_expr,
131 if_then,
132 if_else,
133 });
134 }
135 None
f20569fa 136 }
94222f64 137}
f20569fa 138
c295e0f8
XL
139/// An `if let` or `match` expression. Useful for lints that trigger on one or the other.
140pub enum IfLetOrMatch<'hir> {
141 /// Any `match` expression
142 Match(&'hir Expr<'hir>, &'hir [Arm<'hir>], MatchSource),
143 /// scrutinee, pattern, then block, else block
144 IfLet(
145 &'hir Expr<'hir>,
146 &'hir Pat<'hir>,
147 &'hir Expr<'hir>,
148 Option<&'hir Expr<'hir>>,
149 ),
150}
151
152impl<'hir> IfLetOrMatch<'hir> {
153 /// Parses an `if let` or `match` expression
154 pub fn parse(cx: &LateContext<'_>, expr: &Expr<'hir>) -> Option<Self> {
155 match expr.kind {
156 ExprKind::Match(expr, arms, source) => Some(Self::Match(expr, arms, source)),
157 _ => IfLet::hir(cx, expr).map(
158 |IfLet {
159 let_expr,
160 let_pat,
161 if_then,
162 if_else,
163 }| { Self::IfLet(let_expr, let_pat, if_then, if_else) },
164 ),
165 }
166 }
167}
168
169/// An `if` or `if let` expression
94222f64 170pub struct IfOrIfLet<'hir> {
c295e0f8 171 /// `if` condition that is maybe a `let` expression
94222f64 172 pub cond: &'hir Expr<'hir>,
c295e0f8 173 /// `if` then expression
94222f64 174 pub then: &'hir Expr<'hir>,
c295e0f8
XL
175 /// `else` expression
176 pub r#else: Option<&'hir Expr<'hir>>,
f20569fa
XL
177}
178
94222f64
XL
179impl<'hir> IfOrIfLet<'hir> {
180 #[inline]
c295e0f8 181 /// Parses an `if` or `if let` expression
94222f64
XL
182 pub const fn hir(expr: &Expr<'hir>) -> Option<Self> {
183 if let ExprKind::If(cond, then, r#else) = expr.kind {
184 if let ExprKind::DropTemps(new_cond) = cond.kind {
185 return Some(Self {
186 cond: new_cond,
187 r#else,
188 then,
189 });
190 }
191 if let ExprKind::Let(..) = cond.kind {
c295e0f8 192 return Some(Self { cond, then, r#else });
94222f64 193 }
f20569fa 194 }
94222f64 195 None
f20569fa 196 }
f20569fa
XL
197}
198
94222f64
XL
199/// Represent a range akin to `ast::ExprKind::Range`.
200#[derive(Debug, Copy, Clone)]
201pub struct Range<'a> {
202 /// The lower bound of the range, or `None` for ranges such as `..X`.
203 pub start: Option<&'a hir::Expr<'a>>,
204 /// The upper bound of the range, or `None` for ranges such as `X..`.
205 pub end: Option<&'a hir::Expr<'a>>,
206 /// Whether the interval is open or closed.
207 pub limits: ast::RangeLimits,
208}
209
210impl<'a> Range<'a> {
211 /// Higher a `hir` range to something similar to `ast::ExprKind::Range`.
212 pub fn hir(expr: &'a hir::Expr<'_>) -> Option<Range<'a>> {
213 /// Finds the field named `name` in the field. Always return `Some` for
214 /// convenience.
215 fn get_field<'c>(name: &str, fields: &'c [hir::ExprField<'_>]) -> Option<&'c hir::Expr<'c>> {
216 let expr = &fields.iter().find(|field| field.ident.name.as_str() == name)?.expr;
217 Some(expr)
218 }
219
220 match expr.kind {
c295e0f8 221 hir::ExprKind::Call(path, args)
94222f64
XL
222 if matches!(
223 path.kind,
a2a8927a 224 hir::ExprKind::Path(hir::QPath::LangItem(hir::LangItem::RangeInclusiveNew, ..))
94222f64
XL
225 ) =>
226 {
227 Some(Range {
228 start: Some(&args[0]),
229 end: Some(&args[1]),
230 limits: ast::RangeLimits::Closed,
231 })
232 },
c295e0f8 233 hir::ExprKind::Struct(path, fields, None) => match &path {
a2a8927a 234 hir::QPath::LangItem(hir::LangItem::RangeFull, ..) => Some(Range {
94222f64
XL
235 start: None,
236 end: None,
237 limits: ast::RangeLimits::HalfOpen,
238 }),
a2a8927a 239 hir::QPath::LangItem(hir::LangItem::RangeFrom, ..) => Some(Range {
94222f64
XL
240 start: Some(get_field("start", fields)?),
241 end: None,
242 limits: ast::RangeLimits::HalfOpen,
243 }),
a2a8927a 244 hir::QPath::LangItem(hir::LangItem::Range, ..) => Some(Range {
94222f64
XL
245 start: Some(get_field("start", fields)?),
246 end: Some(get_field("end", fields)?),
247 limits: ast::RangeLimits::HalfOpen,
248 }),
a2a8927a 249 hir::QPath::LangItem(hir::LangItem::RangeToInclusive, ..) => Some(Range {
94222f64
XL
250 start: None,
251 end: Some(get_field("end", fields)?),
252 limits: ast::RangeLimits::Closed,
253 }),
a2a8927a 254 hir::QPath::LangItem(hir::LangItem::RangeTo, ..) => Some(Range {
94222f64
XL
255 start: None,
256 end: Some(get_field("end", fields)?),
257 limits: ast::RangeLimits::HalfOpen,
258 }),
259 _ => None,
260 },
261 _ => None,
f20569fa
XL
262 }
263 }
f20569fa
XL
264}
265
266/// Represent the pre-expansion arguments of a `vec!` invocation.
267pub enum VecArgs<'a> {
268 /// `vec![elem; len]`
269 Repeat(&'a hir::Expr<'a>, &'a hir::Expr<'a>),
270 /// `vec![a, b, c]`
271 Vec(&'a [hir::Expr<'a>]),
272}
273
94222f64
XL
274impl<'a> VecArgs<'a> {
275 /// Returns the arguments of the `vec!` macro if this expression was expanded
276 /// from `vec!`.
277 pub fn hir(cx: &LateContext<'_>, expr: &'a hir::Expr<'_>) -> Option<VecArgs<'a>> {
278 if_chain! {
c295e0f8 279 if let hir::ExprKind::Call(fun, args) = expr.kind;
94222f64
XL
280 if let hir::ExprKind::Path(ref qpath) = fun.kind;
281 if is_expn_of(fun.span, "vec").is_some();
282 if let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id();
283 then {
284 return if match_def_path(cx, fun_def_id, &paths::VEC_FROM_ELEM) && args.len() == 2 {
285 // `vec![elem; size]` case
286 Some(VecArgs::Repeat(&args[0], &args[1]))
5099ac24 287 } else if match_def_path(cx, fun_def_id, &paths::SLICE_INTO_VEC) && args.len() == 1 {
94222f64
XL
288 // `vec![a, b, c]` case
289 if_chain! {
c295e0f8
XL
290 if let hir::ExprKind::Box(boxed) = args[0].kind;
291 if let hir::ExprKind::Array(args) = boxed.kind;
94222f64 292 then {
c295e0f8 293 return Some(VecArgs::Vec(args));
94222f64
XL
294 }
295 }
f20569fa 296
94222f64 297 None
5099ac24 298 } else if match_def_path(cx, fun_def_id, &paths::VEC_NEW) && args.is_empty() {
94222f64 299 Some(VecArgs::Vec(&[]))
5099ac24 300 } else {
94222f64
XL
301 None
302 };
f20569fa 303 }
94222f64
XL
304 }
305
306 None
307 }
308}
309
c295e0f8 310/// A desugared `while` loop
94222f64 311pub struct While<'hir> {
c295e0f8
XL
312 /// `while` loop condition
313 pub condition: &'hir Expr<'hir>,
314 /// `while` loop body
315 pub body: &'hir Expr<'hir>,
94222f64
XL
316}
317
318impl<'hir> While<'hir> {
319 #[inline]
c295e0f8 320 /// Parses a desugared `while` loop
94222f64
XL
321 pub const fn hir(expr: &Expr<'hir>) -> Option<Self> {
322 if let ExprKind::Loop(
323 Block {
324 expr:
325 Some(Expr {
326 kind:
327 ExprKind::If(
328 Expr {
c295e0f8 329 kind: ExprKind::DropTemps(condition),
94222f64
XL
330 ..
331 },
c295e0f8
XL
332 body,
333 _,
94222f64
XL
334 ),
335 ..
336 }),
337 ..
338 },
339 _,
340 LoopSource::While,
341 _,
342 ) = expr.kind
343 {
c295e0f8 344 return Some(Self { condition, body });
94222f64
XL
345 }
346 None
347 }
348}
349
c295e0f8 350/// A desugared `while let` loop
94222f64 351pub struct WhileLet<'hir> {
c295e0f8 352 /// `while let` loop item pattern
94222f64 353 pub let_pat: &'hir Pat<'hir>,
c295e0f8 354 /// `while let` loop scrutinee
94222f64 355 pub let_expr: &'hir Expr<'hir>,
c295e0f8 356 /// `while let` loop body
94222f64 357 pub if_then: &'hir Expr<'hir>,
94222f64
XL
358}
359
360impl<'hir> WhileLet<'hir> {
361 #[inline]
c295e0f8 362 /// Parses a desugared `while let` loop
94222f64
XL
363 pub const fn hir(expr: &Expr<'hir>) -> Option<Self> {
364 if let ExprKind::Loop(
365 Block {
c295e0f8
XL
366 expr:
367 Some(Expr {
368 kind:
369 ExprKind::If(
370 Expr {
a2a8927a
XL
371 kind:
372 ExprKind::Let(hir::Let {
373 pat: let_pat,
374 init: let_expr,
375 ..
376 }),
c295e0f8
XL
377 ..
378 },
379 if_then,
380 _,
381 ),
382 ..
383 }),
384 ..
94222f64
XL
385 },
386 _,
387 LoopSource::While,
388 _,
389 ) = expr.kind
390 {
c295e0f8
XL
391 return Some(Self {
392 let_pat,
393 let_expr,
394 if_then,
395 });
f20569fa 396 }
94222f64 397 None
f20569fa 398 }
94222f64 399}
f20569fa 400
94222f64
XL
401/// Converts a hir binary operator to the corresponding `ast` type.
402#[must_use]
403pub fn binop(op: hir::BinOpKind) -> ast::BinOpKind {
404 match op {
405 hir::BinOpKind::Eq => ast::BinOpKind::Eq,
406 hir::BinOpKind::Ge => ast::BinOpKind::Ge,
407 hir::BinOpKind::Gt => ast::BinOpKind::Gt,
408 hir::BinOpKind::Le => ast::BinOpKind::Le,
409 hir::BinOpKind::Lt => ast::BinOpKind::Lt,
410 hir::BinOpKind::Ne => ast::BinOpKind::Ne,
411 hir::BinOpKind::Or => ast::BinOpKind::Or,
412 hir::BinOpKind::Add => ast::BinOpKind::Add,
413 hir::BinOpKind::And => ast::BinOpKind::And,
414 hir::BinOpKind::BitAnd => ast::BinOpKind::BitAnd,
415 hir::BinOpKind::BitOr => ast::BinOpKind::BitOr,
416 hir::BinOpKind::BitXor => ast::BinOpKind::BitXor,
417 hir::BinOpKind::Div => ast::BinOpKind::Div,
418 hir::BinOpKind::Mul => ast::BinOpKind::Mul,
419 hir::BinOpKind::Rem => ast::BinOpKind::Rem,
420 hir::BinOpKind::Shl => ast::BinOpKind::Shl,
421 hir::BinOpKind::Shr => ast::BinOpKind::Shr,
422 hir::BinOpKind::Sub => ast::BinOpKind::Sub,
423 }
f20569fa
XL
424}
425
3c0e092e
XL
426/// A parsed `Vec` initialization expression
427#[derive(Clone, Copy)]
428pub enum VecInitKind {
429 /// `Vec::new()`
430 New,
431 /// `Vec::default()` or `Default::default()`
432 Default,
433 /// `Vec::with_capacity(123)`
434 WithLiteralCapacity(u64),
435 /// `Vec::with_capacity(slice.len())`
436 WithExprCapacity(HirId),
437}
438
439/// Checks if given expression is an initialization of `Vec` and returns its kind.
440pub fn get_vec_init_kind<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option<VecInitKind> {
441 if let ExprKind::Call(func, args) = expr.kind {
442 match func.kind {
443 ExprKind::Path(QPath::TypeRelative(ty, name))
444 if is_type_diagnostic_item(cx, cx.typeck_results().node_type(ty.hir_id), sym::Vec) =>
445 {
446 if name.ident.name == sym::new {
447 return Some(VecInitKind::New);
448 } else if name.ident.name == symbol::kw::Default {
449 return Some(VecInitKind::Default);
450 } else if name.ident.name.as_str() == "with_capacity" {
451 let arg = args.get(0)?;
452 if_chain! {
453 if let ExprKind::Lit(lit) = &arg.kind;
454 if let LitKind::Int(num, _) = lit.node;
455 then {
5099ac24 456 return Some(VecInitKind::WithLiteralCapacity(num.try_into().ok()?));
3c0e092e
XL
457 }
458 }
459 return Some(VecInitKind::WithExprCapacity(arg.hir_id));
460 }
461 },
462 ExprKind::Path(QPath::Resolved(_, path))
463 if match_def_path(cx, path.res.opt_def_id()?, &paths::DEFAULT_TRAIT_METHOD)
464 && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym::Vec) =>
465 {
466 return Some(VecInitKind::Default);
467 },
468 _ => (),
469 }
470 }
471 None
472}