1#![deny(clippy::missing_docs_in_private_items)]
4
5use crate::consts::{ConstEvalCtxt, Constant};
6use crate::is_expn_of;
7use crate::ty::is_type_diagnostic_item;
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, sym, 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 if let Some((
123 _,
124 Node::Expr(Expr {
125 kind: ExprKind::Loop(_, _, LoopSource::While, _),
126 ..
127 }),
128 )) = iter.next()
129 {
130 return None;
132 }
133 }
134 return Some(Self {
135 let_pat,
136 let_expr,
137 if_then,
138 if_else,
139 let_span,
140 });
141 }
142 None
143 }
144}
145
146#[derive(Debug)]
148pub enum IfLetOrMatch<'hir> {
149 Match(&'hir Expr<'hir>, &'hir [Arm<'hir>], MatchSource),
151 IfLet(
153 &'hir Expr<'hir>,
154 &'hir Pat<'hir>,
155 &'hir Expr<'hir>,
156 Option<&'hir Expr<'hir>>,
157 Span,
160 ),
161}
162
163impl<'hir> IfLetOrMatch<'hir> {
164 pub fn parse(cx: &LateContext<'_>, expr: &Expr<'hir>) -> Option<Self> {
166 match expr.kind {
167 ExprKind::Match(expr, arms, source) => Some(Self::Match(expr, arms, source)),
168 _ => IfLet::hir(cx, expr).map(
169 |IfLet {
170 let_expr,
171 let_pat,
172 if_then,
173 if_else,
174 let_span,
175 }| { Self::IfLet(let_expr, let_pat, if_then, if_else, let_span) },
176 ),
177 }
178 }
179}
180
181pub struct IfOrIfLet<'hir> {
183 pub cond: &'hir Expr<'hir>,
185 pub then: &'hir Expr<'hir>,
187 pub r#else: Option<&'hir Expr<'hir>>,
189}
190
191impl<'hir> IfOrIfLet<'hir> {
192 #[inline]
193 pub const fn hir(expr: &Expr<'hir>) -> Option<Self> {
195 if let ExprKind::If(cond, then, r#else) = expr.kind {
196 if let ExprKind::DropTemps(new_cond) = cond.kind {
197 return Some(Self {
198 cond: new_cond,
199 then,
200 r#else,
201 });
202 }
203 if let ExprKind::Let(..) = cond.kind {
204 return Some(Self { cond, then, r#else });
205 }
206 }
207 None
208 }
209}
210
211#[derive(Debug, Copy, Clone)]
213pub struct Range<'a> {
214 pub start: Option<&'a Expr<'a>>,
216 pub end: Option<&'a Expr<'a>>,
218 pub limits: ast::RangeLimits,
220}
221
222impl<'a> Range<'a> {
223 #[allow(clippy::similar_names)]
225 pub fn hir(expr: &'a Expr<'_>) -> Option<Range<'a>> {
226 match expr.kind {
227 ExprKind::Call(path, [arg1, arg2])
228 if matches!(
229 path.kind,
230 ExprKind::Path(QPath::LangItem(hir::LangItem::RangeInclusiveNew, ..))
231 ) =>
232 {
233 Some(Range {
234 start: Some(arg1),
235 end: Some(arg2),
236 limits: ast::RangeLimits::Closed,
237 })
238 },
239 ExprKind::Struct(path, fields, StructTailExpr::None) => match (path, fields) {
240 (QPath::LangItem(hir::LangItem::RangeFull, ..), []) => Some(Range {
241 start: None,
242 end: None,
243 limits: ast::RangeLimits::HalfOpen,
244 }),
245 (QPath::LangItem(hir::LangItem::RangeFrom, ..), [field]) if field.ident.name == sym::start => {
246 Some(Range {
247 start: Some(field.expr),
248 end: None,
249 limits: ast::RangeLimits::HalfOpen,
250 })
251 },
252 (QPath::LangItem(hir::LangItem::Range, ..), [field1, field2]) => {
253 let (start, end) = match (field1.ident.name, field2.ident.name) {
254 (sym::start, sym::end) => (field1.expr, field2.expr),
255 (sym::end, sym::start) => (field2.expr, field1.expr),
256 _ => return None,
257 };
258 Some(Range {
259 start: Some(start),
260 end: Some(end),
261 limits: ast::RangeLimits::HalfOpen,
262 })
263 },
264 (QPath::LangItem(hir::LangItem::RangeToInclusive, ..), [field]) if field.ident.name == sym::end => {
265 Some(Range {
266 start: None,
267 end: Some(field.expr),
268 limits: ast::RangeLimits::Closed,
269 })
270 },
271 (QPath::LangItem(hir::LangItem::RangeTo, ..), [field]) if field.ident.name == sym::end => Some(Range {
272 start: None,
273 end: Some(field.expr),
274 limits: ast::RangeLimits::HalfOpen,
275 }),
276 _ => None,
277 },
278 _ => None,
279 }
280 }
281}
282
283pub enum VecArgs<'a> {
285 Repeat(&'a Expr<'a>, &'a Expr<'a>),
287 Vec(&'a [Expr<'a>]),
289}
290
291impl<'a> VecArgs<'a> {
292 pub fn hir(cx: &LateContext<'_>, expr: &'a Expr<'_>) -> Option<VecArgs<'a>> {
295 if let ExprKind::Call(fun, args) = expr.kind
296 && let ExprKind::Path(ref qpath) = fun.kind
297 && is_expn_of(fun.span, "vec").is_some()
298 && let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id()
299 {
300 return if cx.tcx.is_diagnostic_item(sym::vec_from_elem, fun_def_id) && args.len() == 2 {
301 Some(VecArgs::Repeat(&args[0], &args[1]))
303 } else if cx.tcx.is_diagnostic_item(sym::slice_into_vec, fun_def_id) && args.len() == 1 {
304 if let ExprKind::Call(_, [arg]) = &args[0].kind
306 && let ExprKind::Array(args) = arg.kind
307 {
308 Some(VecArgs::Vec(args))
309 } else {
310 None
311 }
312 } else if cx.tcx.is_diagnostic_item(sym::vec_new, fun_def_id) && args.is_empty() {
313 Some(VecArgs::Vec(&[]))
314 } else {
315 None
316 };
317 }
318
319 None
320 }
321}
322
323pub struct While<'hir> {
325 pub condition: &'hir Expr<'hir>,
327 pub body: &'hir Expr<'hir>,
329 pub span: Span,
331}
332
333impl<'hir> While<'hir> {
334 #[inline]
335 pub const fn hir(expr: &Expr<'hir>) -> Option<Self> {
337 if let ExprKind::Loop(
338 Block {
339 expr:
340 Some(Expr {
341 kind:
342 ExprKind::If(
343 Expr {
344 kind: ExprKind::DropTemps(condition),
345 ..
346 },
347 body,
348 _,
349 ),
350 ..
351 }),
352 ..
353 },
354 _,
355 LoopSource::While,
356 span,
357 ) = expr.kind
358 {
359 return Some(Self { condition, body, span });
360 }
361 None
362 }
363}
364
365pub struct WhileLet<'hir> {
367 pub let_pat: &'hir Pat<'hir>,
369 pub let_expr: &'hir Expr<'hir>,
371 pub if_then: &'hir Expr<'hir>,
373 pub label: Option<ast::Label>,
374 pub let_span: Span,
377}
378
379impl<'hir> WhileLet<'hir> {
380 #[inline]
381 pub const fn hir(expr: &Expr<'hir>) -> Option<Self> {
383 if let ExprKind::Loop(
384 &Block {
385 expr:
386 Some(&Expr {
387 kind:
388 ExprKind::If(
389 &Expr {
390 kind:
391 ExprKind::Let(&hir::LetExpr {
392 pat: let_pat,
393 init: let_expr,
394 span: let_span,
395 ..
396 }),
397 ..
398 },
399 if_then,
400 _,
401 ),
402 ..
403 }),
404 ..
405 },
406 label,
407 LoopSource::While,
408 _,
409 ) = expr.kind
410 {
411 return Some(Self {
412 let_pat,
413 let_expr,
414 if_then,
415 label,
416 let_span,
417 });
418 }
419 None
420 }
421}
422
423#[must_use]
425pub fn binop(op: hir::BinOpKind) -> ast::BinOpKind {
426 match op {
427 hir::BinOpKind::Eq => ast::BinOpKind::Eq,
428 hir::BinOpKind::Ge => ast::BinOpKind::Ge,
429 hir::BinOpKind::Gt => ast::BinOpKind::Gt,
430 hir::BinOpKind::Le => ast::BinOpKind::Le,
431 hir::BinOpKind::Lt => ast::BinOpKind::Lt,
432 hir::BinOpKind::Ne => ast::BinOpKind::Ne,
433 hir::BinOpKind::Or => ast::BinOpKind::Or,
434 hir::BinOpKind::Add => ast::BinOpKind::Add,
435 hir::BinOpKind::And => ast::BinOpKind::And,
436 hir::BinOpKind::BitAnd => ast::BinOpKind::BitAnd,
437 hir::BinOpKind::BitOr => ast::BinOpKind::BitOr,
438 hir::BinOpKind::BitXor => ast::BinOpKind::BitXor,
439 hir::BinOpKind::Div => ast::BinOpKind::Div,
440 hir::BinOpKind::Mul => ast::BinOpKind::Mul,
441 hir::BinOpKind::Rem => ast::BinOpKind::Rem,
442 hir::BinOpKind::Shl => ast::BinOpKind::Shl,
443 hir::BinOpKind::Shr => ast::BinOpKind::Shr,
444 hir::BinOpKind::Sub => ast::BinOpKind::Sub,
445 }
446}
447
448#[derive(Clone, Copy)]
450pub enum VecInitKind {
451 New,
453 Default,
455 WithConstCapacity(u128),
457 WithExprCapacity(HirId),
459}
460
461pub fn get_vec_init_kind<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option<VecInitKind> {
463 if let ExprKind::Call(func, args) = expr.kind {
464 match func.kind {
465 ExprKind::Path(QPath::TypeRelative(ty, name))
466 if is_type_diagnostic_item(cx, cx.typeck_results().node_type(ty.hir_id), sym::Vec) =>
467 {
468 if name.ident.name == sym::new {
469 return Some(VecInitKind::New);
470 } else if name.ident.name == symbol::kw::Default {
471 return Some(VecInitKind::Default);
472 } else if name.ident.name.as_str() == "with_capacity" {
473 let arg = args.first()?;
474 return match ConstEvalCtxt::new(cx).eval_simple(arg) {
475 Some(Constant::Int(num)) => Some(VecInitKind::WithConstCapacity(num)),
476 _ => Some(VecInitKind::WithExprCapacity(arg.hir_id)),
477 };
478 }
479 },
480 ExprKind::Path(QPath::Resolved(_, path))
481 if cx.tcx.is_diagnostic_item(sym::default_fn, path.res.opt_def_id()?)
482 && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym::Vec) =>
483 {
484 return Some(VecInitKind::Default);
485 },
486 _ => (),
487 }
488 }
489 None
490}