]>
Commit | Line | Data |
---|---|---|
3c0e092e | 1 | //! This module contains functions that retrieve specific elements. |
f20569fa XL |
2 | |
3 | #![deny(clippy::missing_docs_in_private_items)] | |
4 | ||
3c0e092e | 5 | use crate::ty::is_type_diagnostic_item; |
5099ac24 | 6 | use crate::{is_expn_of, match_def_path, paths}; |
f20569fa | 7 | use if_chain::if_chain; |
136023e0 | 8 | use rustc_ast::ast::{self, LitKind}; |
f20569fa | 9 | use rustc_hir as hir; |
5099ac24 | 10 | use rustc_hir::{Arm, Block, Expr, ExprKind, HirId, LoopSource, MatchSource, Node, Pat, QPath}; |
f20569fa | 11 | use rustc_lint::LateContext; |
5099ac24 | 12 | use 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)`. | |
16 | pub 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 | 29 | impl<'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 | 56 | pub 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 |
65 | impl<'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 | 86 | pub 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 | ||
97 | impl<'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. |
140 | pub 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 | ||
152 | impl<'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 | 170 | pub 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 |
179 | impl<'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)] | |
201 | pub 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 | ||
210 | impl<'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. | |
267 | pub 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 |
274 | impl<'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 | 311 | pub 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 | ||
318 | impl<'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 | 351 | pub 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 | ||
360 | impl<'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] | |
403 | pub 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)] | |
428 | pub 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. | |
440 | pub 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 | } |