X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=clippy_utils%2Fsrc%2Fhigher.rs;h=ba4d50bf74469307cbfedc51e1c979c532c74d4e;hb=388a3d098358edd1f7b83f1f47f1e1e157bad067;hp=29b698e56e3c0f9e9cfb2dd49444c8769e90ad31;hpb=d9c3f0d69029cd9ffd97fe2d1b7a3701ea0ac51a;p=rust.git diff --git a/clippy_utils/src/higher.rs b/clippy_utils/src/higher.rs index 29b698e56e3..ba4d50bf744 100644 --- a/clippy_utils/src/higher.rs +++ b/clippy_utils/src/higher.rs @@ -6,33 +6,38 @@ use if_chain::if_chain; use rustc_ast::ast::{self, LitKind}; use rustc_hir as hir; -use rustc_hir::{Block, BorrowKind, Expr, ExprKind, LoopSource, Node, Pat, StmtKind, UnOp}; +use rustc_hir::{Arm, Block, BorrowKind, Expr, ExprKind, LoopSource, MatchSource, Node, Pat, StmtKind, UnOp}; use rustc_lint::LateContext; use rustc_span::{sym, ExpnKind, Span, Symbol}; /// The essential nodes of a desugared for loop as well as the entire span: /// `for pat in arg { body }` becomes `(pat, arg, body)`. Return `(pat, arg, body, span)`. pub struct ForLoop<'tcx> { + /// `for` loop item pub pat: &'tcx hir::Pat<'tcx>, + /// `IntoIterator` argument pub arg: &'tcx hir::Expr<'tcx>, + /// `for` loop body pub body: &'tcx hir::Expr<'tcx>, + /// entire `for` loop span pub span: Span, } impl<'tcx> ForLoop<'tcx> { #[inline] + /// Parses a desugared `for` loop pub fn hir(expr: &Expr<'tcx>) -> Option { if_chain! { - if let hir::ExprKind::Match(ref iterexpr, ref arms, hir::MatchSource::ForLoopDesugar) = expr.kind; + if let hir::ExprKind::Match(iterexpr, arms, hir::MatchSource::ForLoopDesugar) = expr.kind; if let Some(first_arm) = arms.get(0); - if let hir::ExprKind::Call(_, ref iterargs) = iterexpr.kind; + if let hir::ExprKind::Call(_, iterargs) = iterexpr.kind; if let Some(first_arg) = iterargs.get(0); if iterargs.len() == 1 && arms.len() == 1 && first_arm.guard.is_none(); - if let hir::ExprKind::Loop(ref block, ..) = first_arm.body.kind; + if let hir::ExprKind::Loop(block, ..) = first_arm.body.kind; if block.expr.is_none(); if let [ _, _, ref let_stmt, ref body ] = *block.stmts; - if let hir::StmtKind::Local(ref local) = let_stmt.kind; - if let hir::StmtKind::Expr(ref body_expr) = body.kind; + if let hir::StmtKind::Local(local) = let_stmt.kind; + if let hir::StmtKind::Expr(body_expr) = body.kind; then { return Some(Self { pat: &*local.pat, @@ -46,14 +51,19 @@ pub fn hir(expr: &Expr<'tcx>) -> Option { } } +/// An `if` expression without `DropTemps` pub struct If<'hir> { + /// `if` condition pub cond: &'hir Expr<'hir>, - pub r#else: Option<&'hir Expr<'hir>>, + /// `if` then expression pub then: &'hir Expr<'hir>, + /// `else` expression + pub r#else: Option<&'hir Expr<'hir>>, } impl<'hir> If<'hir> { #[inline] + /// Parses an `if` expression pub const fn hir(expr: &Expr<'hir>) -> Option { if let ExprKind::If( Expr { @@ -64,30 +74,28 @@ pub const fn hir(expr: &Expr<'hir>) -> Option { r#else, ) = expr.kind { - Some(Self { cond, r#else, then }) + Some(Self { cond, then, r#else }) } else { None } } } +/// An `if let` expression pub struct IfLet<'hir> { + /// `if let` pattern pub let_pat: &'hir Pat<'hir>, + /// `if let` scrutinee pub let_expr: &'hir Expr<'hir>, + /// `if let` then expression pub if_then: &'hir Expr<'hir>, + /// `if let` else expression pub if_else: Option<&'hir Expr<'hir>>, } impl<'hir> IfLet<'hir> { - #[inline] - pub fn ast(cx: &LateContext<'tcx>, expr: &Expr<'hir>) -> Option { - let rslt = Self::hir(expr)?; - Self::is_not_within_while_context(cx, expr)?; - Some(rslt) - } - - #[inline] - pub const fn hir(expr: &Expr<'hir>) -> Option { + /// Parses an `if let` expression + pub fn hir(cx: &LateContext<'_>, expr: &Expr<'hir>) -> Option { if let ExprKind::If( Expr { kind: ExprKind::Let(let_pat, let_expr, _), @@ -97,6 +105,20 @@ pub const fn hir(expr: &Expr<'hir>) -> Option { if_else, ) = expr.kind { + let mut iter = cx.tcx.hir().parent_iter(expr.hir_id); + if let Some((_, Node::Block(Block { stmts: [], .. }))) = iter.next() { + if let Some(( + _, + Node::Expr(Expr { + kind: ExprKind::Loop(_, _, LoopSource::While, _), + .. + }), + )) = iter.next() + { + // while loop desugar + return None; + } + } return Some(Self { let_pat, let_expr, @@ -106,32 +128,51 @@ pub const fn hir(expr: &Expr<'hir>) -> Option { } None } +} - #[inline] - fn is_not_within_while_context(cx: &LateContext<'tcx>, expr: &Expr<'hir>) -> Option<()> { - let hir = cx.tcx.hir(); - let parent = hir.get_parent_node(expr.hir_id); - let parent_parent = hir.get_parent_node(parent); - let parent_parent_node = hir.get(parent_parent); - if let Node::Expr(Expr { - kind: ExprKind::Loop(_, _, LoopSource::While, _), - .. - }) = parent_parent_node - { - return None; +/// An `if let` or `match` expression. Useful for lints that trigger on one or the other. +pub enum IfLetOrMatch<'hir> { + /// Any `match` expression + Match(&'hir Expr<'hir>, &'hir [Arm<'hir>], MatchSource), + /// scrutinee, pattern, then block, else block + IfLet( + &'hir Expr<'hir>, + &'hir Pat<'hir>, + &'hir Expr<'hir>, + Option<&'hir Expr<'hir>>, + ), +} + +impl<'hir> IfLetOrMatch<'hir> { + /// Parses an `if let` or `match` expression + pub fn parse(cx: &LateContext<'_>, expr: &Expr<'hir>) -> Option { + match expr.kind { + ExprKind::Match(expr, arms, source) => Some(Self::Match(expr, arms, source)), + _ => IfLet::hir(cx, expr).map( + |IfLet { + let_expr, + let_pat, + if_then, + if_else, + }| { Self::IfLet(let_expr, let_pat, if_then, if_else) }, + ), } - Some(()) } } +/// An `if` or `if let` expression pub struct IfOrIfLet<'hir> { + /// `if` condition that is maybe a `let` expression pub cond: &'hir Expr<'hir>, - pub r#else: Option<&'hir Expr<'hir>>, + /// `if` then expression pub then: &'hir Expr<'hir>, + /// `else` expression + pub r#else: Option<&'hir Expr<'hir>>, } impl<'hir> IfOrIfLet<'hir> { #[inline] + /// Parses an `if` or `if let` expression pub const fn hir(expr: &Expr<'hir>) -> Option { if let ExprKind::If(cond, then, r#else) = expr.kind { if let ExprKind::DropTemps(new_cond) = cond.kind { @@ -142,7 +183,7 @@ pub const fn hir(expr: &Expr<'hir>) -> Option { }); } if let ExprKind::Let(..) = cond.kind { - return Some(Self { cond, r#else, then }); + return Some(Self { cond, then, r#else }); } } None @@ -171,7 +212,7 @@ fn get_field<'c>(name: &str, fields: &'c [hir::ExprField<'_>]) -> Option<&'c hir } match expr.kind { - hir::ExprKind::Call(ref path, ref args) + hir::ExprKind::Call(path, args) if matches!( path.kind, hir::ExprKind::Path(hir::QPath::LangItem(hir::LangItem::RangeInclusiveNew, _)) @@ -183,7 +224,7 @@ fn get_field<'c>(name: &str, fields: &'c [hir::ExprField<'_>]) -> Option<&'c hir limits: ast::RangeLimits::Closed, }) }, - hir::ExprKind::Struct(ref path, ref fields, None) => match path { + hir::ExprKind::Struct(path, fields, None) => match &path { hir::QPath::LangItem(hir::LangItem::RangeFull, _) => Some(Range { start: None, end: None, @@ -229,7 +270,7 @@ impl<'a> VecArgs<'a> { /// from `vec!`. pub fn hir(cx: &LateContext<'_>, expr: &'a hir::Expr<'_>) -> Option> { if_chain! { - if let hir::ExprKind::Call(ref fun, ref args) = expr.kind; + if let hir::ExprKind::Call(fun, args) = expr.kind; if let hir::ExprKind::Path(ref qpath) = fun.kind; if is_expn_of(fun.span, "vec").is_some(); if let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id(); @@ -241,10 +282,10 @@ pub fn hir(cx: &LateContext<'_>, expr: &'a hir::Expr<'_>) -> Option> else if match_def_path(cx, fun_def_id, &paths::SLICE_INTO_VEC) && args.len() == 1 { // `vec![a, b, c]` case if_chain! { - if let hir::ExprKind::Box(ref boxed) = args[0].kind; - if let hir::ExprKind::Array(ref args) = boxed.kind; + if let hir::ExprKind::Box(boxed) = args[0].kind; + if let hir::ExprKind::Array(args) = boxed.kind; then { - return Some(VecArgs::Vec(&*args)); + return Some(VecArgs::Vec(args)); } } @@ -263,14 +304,17 @@ pub fn hir(cx: &LateContext<'_>, expr: &'a hir::Expr<'_>) -> Option> } } +/// A desugared `while` loop pub struct While<'hir> { - pub if_cond: &'hir Expr<'hir>, - pub if_then: &'hir Expr<'hir>, - pub if_else: Option<&'hir Expr<'hir>>, + /// `while` loop condition + pub condition: &'hir Expr<'hir>, + /// `while` loop body + pub body: &'hir Expr<'hir>, } impl<'hir> While<'hir> { #[inline] + /// Parses a desugared `while` loop pub const fn hir(expr: &Expr<'hir>) -> Option { if let ExprKind::Loop( Block { @@ -279,11 +323,11 @@ pub const fn hir(expr: &Expr<'hir>) -> Option { kind: ExprKind::If( Expr { - kind: ExprKind::DropTemps(if_cond), + kind: ExprKind::DropTemps(condition), .. }, - if_then, - if_else_ref, + body, + _, ), .. }), @@ -294,59 +338,53 @@ pub const fn hir(expr: &Expr<'hir>) -> Option { _, ) = expr.kind { - let if_else = *if_else_ref; - return Some(Self { - if_cond, - if_then, - if_else, - }); + return Some(Self { condition, body }); } None } } +/// A desugared `while let` loop pub struct WhileLet<'hir> { - pub if_expr: &'hir Expr<'hir>, + /// `while let` loop item pattern pub let_pat: &'hir Pat<'hir>, + /// `while let` loop scrutinee pub let_expr: &'hir Expr<'hir>, + /// `while let` loop body pub if_then: &'hir Expr<'hir>, - pub if_else: Option<&'hir Expr<'hir>>, } impl<'hir> WhileLet<'hir> { #[inline] + /// Parses a desugared `while let` loop pub const fn hir(expr: &Expr<'hir>) -> Option { if let ExprKind::Loop( Block { - expr: Some(if_expr), .. + expr: + Some(Expr { + kind: + ExprKind::If( + Expr { + kind: ExprKind::Let(let_pat, let_expr, _), + .. + }, + if_then, + _, + ), + .. + }), + .. }, _, LoopSource::While, _, ) = expr.kind { - if let Expr { - kind: - ExprKind::If( - Expr { - kind: ExprKind::Let(let_pat, let_expr, _), - .. - }, - if_then, - if_else_ref, - ), - .. - } = if_expr - { - let if_else = *if_else_ref; - return Some(Self { - if_expr, - let_pat, - let_expr, - if_then, - if_else, - }); - } + return Some(Self { + let_pat, + let_expr, + if_then, + }); } None } @@ -490,7 +528,7 @@ pub fn parse(expr: &'tcx Expr<'tcx>) -> Option { // Arguments::new_v1 [strs_ref, args] => Some((strs_ref, args, None)), // Arguments::new_v1_formatted - [strs_ref, args, fmt_expr] => Some((strs_ref, args, Some(fmt_expr))), + [strs_ref, args, fmt_expr, _unsafe_arg] => Some((strs_ref, args, Some(fmt_expr))), _ => None, }; if let ExprKind::AddrOf(BorrowKind::Ref, _, strs_arr) = strs_ref.kind; @@ -543,7 +581,7 @@ pub fn is_from_for_desugar(local: &hir::Local<'_>) -> bool { // } // ``` if_chain! { - if let Some(ref expr) = local.init; + if let Some(expr) = local.init; if let hir::ExprKind::Match(_, _, hir::MatchSource::ForLoopDesugar) = expr.kind; then { return true; @@ -564,3 +602,33 @@ pub fn is_from_for_desugar(local: &hir::Local<'_>) -> bool { false } + +/// A parsed `panic!` expansion +pub struct PanicExpn<'tcx> { + /// Span of `panic!(..)` + pub call_site: Span, + /// Inner `format_args!` expansion + pub format_args: FormatArgsExpn<'tcx>, +} + +impl PanicExpn<'tcx> { + /// Parses an expanded `panic!` invocation + pub fn parse(expr: &'tcx Expr<'tcx>) -> Option { + if_chain! { + if let ExprKind::Block(block, _) = expr.kind; + if let Some(init) = block.expr; + if let ExprKind::Call(_, [format_args]) = init.kind; + let expn_data = expr.span.ctxt().outer_expn_data(); + if let ExprKind::AddrOf(_, _, format_args) = format_args.kind; + if let Some(format_args) = FormatArgsExpn::parse(format_args); + then { + Some(PanicExpn { + call_site: expn_data.call_site, + format_args, + }) + } else { + None + } + } + } +}