1#![deny(clippy::missing_docs_in_private_items)]
4
5use crate::consts::{ConstEvalCtxt, Constant};
6use crate::ty::is_type_diagnostic_item;
7use crate::{is_expn_of, sym};
8
9use rustc_ast::ast;
10use rustc_hir as hir;
11use rustc_hir::{Arm, Block, Expr, ExprKind, HirId, LoopSource, MatchSource, Node, Pat, QPath, StructTailExpr};
12use rustc_lint::LateContext;
13use rustc_span::{Span, symbol};
14
15pub struct ForLoop<'tcx> {
18 pub pat: &'tcx Pat<'tcx>,
20 pub arg: &'tcx Expr<'tcx>,
22 pub body: &'tcx Expr<'tcx>,
24 pub loop_id: HirId,
26 pub span: Span,
28 pub label: Option<ast::Label>,
30}
31
32impl<'tcx> ForLoop<'tcx> {
33 pub fn hir(expr: &Expr<'tcx>) -> Option<Self> {
35 if let ExprKind::DropTemps(e) = expr.kind
36 && let ExprKind::Match(iterexpr, [arm], MatchSource::ForLoopDesugar) = e.kind
37 && let ExprKind::Call(_, [arg]) = iterexpr.kind
38 && let ExprKind::Loop(block, label, ..) = arm.body.kind
39 && let [stmt] = block.stmts
40 && let hir::StmtKind::Expr(e) = stmt.kind
41 && let ExprKind::Match(_, [_, some_arm], _) = e.kind
42 && let hir::PatKind::Struct(_, [field], _) = some_arm.pat.kind
43 {
44 return Some(Self {
45 pat: field.pat,
46 arg,
47 body: some_arm.body,
48 loop_id: arm.body.hir_id,
49 span: expr.span.ctxt().outer_expn_data().call_site,
50 label,
51 });
52 }
53 None
54 }
55}
56
57pub struct If<'hir> {
59 pub cond: &'hir Expr<'hir>,
61 pub then: &'hir Expr<'hir>,
63 pub r#else: Option<&'hir Expr<'hir>>,
65}
66
67impl<'hir> If<'hir> {
68 #[inline]
69 pub const fn hir(expr: &Expr<'hir>) -> Option<Self> {
71 if let ExprKind::If(
72 Expr {
73 kind: ExprKind::DropTemps(cond),
74 ..
75 },
76 then,
77 r#else,
78 ) = expr.kind
79 {
80 Some(Self { cond, then, r#else })
81 } else {
82 None
83 }
84 }
85}
86
87pub struct IfLet<'hir> {
89 pub let_pat: &'hir Pat<'hir>,
91 pub let_expr: &'hir Expr<'hir>,
93 pub if_then: &'hir Expr<'hir>,
95 pub if_else: Option<&'hir Expr<'hir>>,
97 pub let_span: Span,
100}
101
102impl<'hir> IfLet<'hir> {
103 pub fn hir(cx: &LateContext<'_>, expr: &Expr<'hir>) -> Option<Self> {
105 if let ExprKind::If(
106 &Expr {
107 kind:
108 ExprKind::Let(&hir::LetExpr {
109 pat: let_pat,
110 init: let_expr,
111 span: let_span,
112 ..
113 }),
114 ..
115 },
116 if_then,
117 if_else,
118 ) = expr.kind
119 {
120 let mut iter = cx.tcx.hir_parent_iter(expr.hir_id);
121 if let Some((_, Node::Block(Block { stmts: [], .. }))) = iter.next()
122 && let Some((
123 _,
124 Node::Expr(Expr {
125 kind: ExprKind::Loop(_, _, LoopSource::While, _),
126 ..
127 }),
128 )) = iter.next()
129 {
130 return None;
132 }
133 return Some(Self {
134 let_pat,
135 let_expr,
136 if_then,
137 if_else,
138 let_span,
139 });
140 }
141 None
142 }
143}
144
145#[derive(Debug)]
147pub enum IfLetOrMatch<'hir> {
148 Match(&'hir Expr<'hir>, &'hir [Arm<'hir>], MatchSource),
150 IfLet(
152 &'hir Expr<'hir>,
153 &'hir Pat<'hir>,
154 &'hir Expr<'hir>,
155 Option<&'hir Expr<'hir>>,
156 Span,
159 ),
160}
161
162impl<'hir> IfLetOrMatch<'hir> {
163 pub fn parse(cx: &LateContext<'_>, expr: &Expr<'hir>) -> Option<Self> {
165 match expr.kind {
166 ExprKind::Match(expr, arms, source) => Some(Self::Match(expr, arms, source)),
167 _ => IfLet::hir(cx, expr).map(
168 |IfLet {
169 let_expr,
170 let_pat,
171 if_then,
172 if_else,
173 let_span,
174 }| { Self::IfLet(let_expr, let_pat, if_then, if_else, let_span) },
175 ),
176 }
177 }
178
179 pub fn scrutinee(&self) -> &'hir Expr<'hir> {
180 match self {
181 Self::Match(scrutinee, _, _) | Self::IfLet(scrutinee, _, _, _, _) => scrutinee,
182 }
183 }
184}
185
186pub struct IfOrIfLet<'hir> {
188 pub cond: &'hir Expr<'hir>,
190 pub then: &'hir Expr<'hir>,
192 pub r#else: Option<&'hir Expr<'hir>>,
194}
195
196impl<'hir> IfOrIfLet<'hir> {
197 #[inline]
198 pub const fn hir(expr: &Expr<'hir>) -> Option<Self> {
200 if let ExprKind::If(cond, then, r#else) = expr.kind {
201 if let ExprKind::DropTemps(new_cond) = cond.kind {
202 return Some(Self {
203 cond: new_cond,
204 then,
205 r#else,
206 });
207 }
208 if let ExprKind::Let(..) = cond.kind {
209 return Some(Self { cond, then, r#else });
210 }
211 }
212 None
213 }
214}
215
216#[derive(Debug, Copy, Clone)]
218pub struct Range<'a> {
219 pub start: Option<&'a Expr<'a>>,
221 pub end: Option<&'a Expr<'a>>,
223 pub limits: ast::RangeLimits,
225}
226
227impl<'a> Range<'a> {
228 #[allow(clippy::similar_names)]
230 pub fn hir(expr: &'a Expr<'_>) -> Option<Range<'a>> {
231 match expr.kind {
232 ExprKind::Call(path, [arg1, arg2])
233 if matches!(
234 path.kind,
235 ExprKind::Path(QPath::LangItem(hir::LangItem::RangeInclusiveNew, ..))
236 ) =>
237 {
238 Some(Range {
239 start: Some(arg1),
240 end: Some(arg2),
241 limits: ast::RangeLimits::Closed,
242 })
243 },
244 ExprKind::Struct(path, fields, StructTailExpr::None) => match (path, fields) {
245 (QPath::LangItem(hir::LangItem::RangeFull, ..), []) => Some(Range {
246 start: None,
247 end: None,
248 limits: ast::RangeLimits::HalfOpen,
249 }),
250 (QPath::LangItem(hir::LangItem::RangeFrom, ..), [field]) if field.ident.name == sym::start => {
251 Some(Range {
252 start: Some(field.expr),
253 end: None,
254 limits: ast::RangeLimits::HalfOpen,
255 })
256 },
257 (QPath::LangItem(hir::LangItem::Range, ..), [field1, field2]) => {
258 let (start, end) = match (field1.ident.name, field2.ident.name) {
259 (sym::start, sym::end) => (field1.expr, field2.expr),
260 (sym::end, sym::start) => (field2.expr, field1.expr),
261 _ => return None,
262 };
263 Some(Range {
264 start: Some(start),
265 end: Some(end),
266 limits: ast::RangeLimits::HalfOpen,
267 })
268 },
269 (QPath::LangItem(hir::LangItem::RangeToInclusive, ..), [field]) if field.ident.name == sym::end => {
270 Some(Range {
271 start: None,
272 end: Some(field.expr),
273 limits: ast::RangeLimits::Closed,
274 })
275 },
276 (QPath::LangItem(hir::LangItem::RangeTo, ..), [field]) if field.ident.name == sym::end => Some(Range {
277 start: None,
278 end: Some(field.expr),
279 limits: ast::RangeLimits::HalfOpen,
280 }),
281 _ => None,
282 },
283 _ => None,
284 }
285 }
286}
287
288pub enum VecArgs<'a> {
290 Repeat(&'a Expr<'a>, &'a Expr<'a>),
292 Vec(&'a [Expr<'a>]),
294}
295
296impl<'a> VecArgs<'a> {
297 pub fn hir(cx: &LateContext<'_>, expr: &'a Expr<'_>) -> Option<VecArgs<'a>> {
300 if let ExprKind::Call(fun, args) = expr.kind
301 && let ExprKind::Path(ref qpath) = fun.kind
302 && is_expn_of(fun.span, sym::vec).is_some()
303 && let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id()
304 {
305 return if cx.tcx.is_diagnostic_item(sym::vec_from_elem, fun_def_id) && args.len() == 2 {
306 Some(VecArgs::Repeat(&args[0], &args[1]))
308 } else if cx.tcx.is_diagnostic_item(sym::slice_into_vec, fun_def_id) && args.len() == 1 {
309 if let ExprKind::Call(_, [arg]) = &args[0].kind
311 && let ExprKind::Array(args) = arg.kind
312 {
313 Some(VecArgs::Vec(args))
314 } else {
315 None
316 }
317 } else if cx.tcx.is_diagnostic_item(sym::vec_new, fun_def_id) && args.is_empty() {
318 Some(VecArgs::Vec(&[]))
319 } else {
320 None
321 };
322 }
323
324 None
325 }
326}
327
328pub struct While<'hir> {
330 pub condition: &'hir Expr<'hir>,
332 pub body: &'hir Expr<'hir>,
334 pub span: Span,
336}
337
338impl<'hir> While<'hir> {
339 #[inline]
340 pub const fn hir(expr: &Expr<'hir>) -> Option<Self> {
342 if let ExprKind::Loop(
343 Block {
344 expr:
345 Some(Expr {
346 kind:
347 ExprKind::If(
348 Expr {
349 kind: ExprKind::DropTemps(condition),
350 ..
351 },
352 body,
353 _,
354 ),
355 ..
356 }),
357 ..
358 },
359 _,
360 LoopSource::While,
361 span,
362 ) = expr.kind
363 {
364 return Some(Self { condition, body, span });
365 }
366 None
367 }
368}
369
370pub struct WhileLet<'hir> {
372 pub let_pat: &'hir Pat<'hir>,
374 pub let_expr: &'hir Expr<'hir>,
376 pub if_then: &'hir Expr<'hir>,
378 pub label: Option<ast::Label>,
379 pub let_span: Span,
382}
383
384impl<'hir> WhileLet<'hir> {
385 #[inline]
386 pub const fn hir(expr: &Expr<'hir>) -> Option<Self> {
388 if let ExprKind::Loop(
389 &Block {
390 expr:
391 Some(&Expr {
392 kind:
393 ExprKind::If(
394 &Expr {
395 kind:
396 ExprKind::Let(&hir::LetExpr {
397 pat: let_pat,
398 init: let_expr,
399 span: let_span,
400 ..
401 }),
402 ..
403 },
404 if_then,
405 _,
406 ),
407 ..
408 }),
409 ..
410 },
411 label,
412 LoopSource::While,
413 _,
414 ) = expr.kind
415 {
416 return Some(Self {
417 let_pat,
418 let_expr,
419 if_then,
420 label,
421 let_span,
422 });
423 }
424 None
425 }
426}
427
428#[must_use]
430pub fn binop(op: hir::BinOpKind) -> ast::BinOpKind {
431 match op {
432 hir::BinOpKind::Eq => ast::BinOpKind::Eq,
433 hir::BinOpKind::Ge => ast::BinOpKind::Ge,
434 hir::BinOpKind::Gt => ast::BinOpKind::Gt,
435 hir::BinOpKind::Le => ast::BinOpKind::Le,
436 hir::BinOpKind::Lt => ast::BinOpKind::Lt,
437 hir::BinOpKind::Ne => ast::BinOpKind::Ne,
438 hir::BinOpKind::Or => ast::BinOpKind::Or,
439 hir::BinOpKind::Add => ast::BinOpKind::Add,
440 hir::BinOpKind::And => ast::BinOpKind::And,
441 hir::BinOpKind::BitAnd => ast::BinOpKind::BitAnd,
442 hir::BinOpKind::BitOr => ast::BinOpKind::BitOr,
443 hir::BinOpKind::BitXor => ast::BinOpKind::BitXor,
444 hir::BinOpKind::Div => ast::BinOpKind::Div,
445 hir::BinOpKind::Mul => ast::BinOpKind::Mul,
446 hir::BinOpKind::Rem => ast::BinOpKind::Rem,
447 hir::BinOpKind::Shl => ast::BinOpKind::Shl,
448 hir::BinOpKind::Shr => ast::BinOpKind::Shr,
449 hir::BinOpKind::Sub => ast::BinOpKind::Sub,
450 }
451}
452
453#[derive(Clone, Copy)]
455pub enum VecInitKind {
456 New,
458 Default,
460 WithConstCapacity(u128),
462 WithExprCapacity(HirId),
464}
465
466pub fn get_vec_init_kind<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option<VecInitKind> {
468 if let ExprKind::Call(func, args) = expr.kind {
469 match func.kind {
470 ExprKind::Path(QPath::TypeRelative(ty, name))
471 if is_type_diagnostic_item(cx, cx.typeck_results().node_type(ty.hir_id), sym::Vec) =>
472 {
473 if name.ident.name == sym::new {
474 return Some(VecInitKind::New);
475 } else if name.ident.name == symbol::kw::Default {
476 return Some(VecInitKind::Default);
477 } else if name.ident.name == sym::with_capacity {
478 let arg = args.first()?;
479 return match ConstEvalCtxt::new(cx).eval_simple(arg) {
480 Some(Constant::Int(num)) => Some(VecInitKind::WithConstCapacity(num)),
481 _ => Some(VecInitKind::WithExprCapacity(arg.hir_id)),
482 };
483 }
484 },
485 ExprKind::Path(QPath::Resolved(_, path))
486 if cx.tcx.is_diagnostic_item(sym::default_fn, path.res.opt_def_id()?)
487 && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym::Vec) =>
488 {
489 return Some(VecInitKind::Default);
490 },
491 _ => (),
492 }
493 }
494 None
495}