Merge commit 'bf1c6f9871' into clippyup
This commit is contained in:
parent
fcde7683fe
commit
cdb555f4fc
74 changed files with 2147 additions and 593 deletions
|
|
@ -82,7 +82,7 @@ fn identify_some_pure_patterns(expr: &Expr<'_>) -> bool {
|
|||
/// Identify some potentially computationally expensive patterns.
|
||||
/// This function is named so to stress that its implementation is non-exhaustive.
|
||||
/// It returns FNs and FPs.
|
||||
fn identify_some_potentially_expensive_patterns<'a, 'tcx>(cx: &'a LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
|
||||
fn identify_some_potentially_expensive_patterns<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
|
||||
// Searches an expression for method calls or function calls that aren't ctors
|
||||
struct FunCallFinder<'a, 'tcx> {
|
||||
cx: &'a LateContext<'tcx>,
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ use crate::utils::{is_expn_of, match_def_path, paths};
|
|||
use if_chain::if_chain;
|
||||
use rustc_ast::ast;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::{BorrowKind, Expr, ExprKind, StmtKind, UnOp};
|
||||
use rustc_lint::LateContext;
|
||||
|
||||
/// Converts a hir binary operator to the corresponding `ast` type.
|
||||
|
|
@ -241,3 +242,56 @@ pub fn vec_macro<'e>(cx: &LateContext<'_>, expr: &'e hir::Expr<'_>) -> Option<Ve
|
|||
|
||||
None
|
||||
}
|
||||
|
||||
/// Extract args from an assert-like macro.
|
||||
/// Currently working with:
|
||||
/// - `assert!`, `assert_eq!` and `assert_ne!`
|
||||
/// - `debug_assert!`, `debug_assert_eq!` and `debug_assert_ne!`
|
||||
/// For example:
|
||||
/// `assert!(expr)` will return Some([expr])
|
||||
/// `debug_assert_eq!(a, b)` will return Some([a, b])
|
||||
pub fn extract_assert_macro_args<'tcx>(e: &'tcx Expr<'tcx>) -> Option<Vec<&'tcx Expr<'tcx>>> {
|
||||
/// Try to match the AST for a pattern that contains a match, for example when two args are
|
||||
/// compared
|
||||
fn ast_matchblock(matchblock_expr: &'tcx Expr<'tcx>) -> Option<Vec<&Expr<'_>>> {
|
||||
if_chain! {
|
||||
if let ExprKind::Match(ref headerexpr, _, _) = &matchblock_expr.kind;
|
||||
if let ExprKind::Tup([lhs, rhs]) = &headerexpr.kind;
|
||||
if let ExprKind::AddrOf(BorrowKind::Ref, _, lhs) = lhs.kind;
|
||||
if let ExprKind::AddrOf(BorrowKind::Ref, _, rhs) = rhs.kind;
|
||||
then {
|
||||
return Some(vec![lhs, rhs]);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
if let ExprKind::Block(ref block, _) = e.kind {
|
||||
if block.stmts.len() == 1 {
|
||||
if let StmtKind::Semi(ref matchexpr) = block.stmts[0].kind {
|
||||
// macros with unique arg: `{debug_}assert!` (e.g., `debug_assert!(some_condition)`)
|
||||
if_chain! {
|
||||
if let ExprKind::Match(ref ifclause, _, _) = matchexpr.kind;
|
||||
if let ExprKind::DropTemps(ref droptmp) = ifclause.kind;
|
||||
if let ExprKind::Unary(UnOp::UnNot, condition) = droptmp.kind;
|
||||
then {
|
||||
return Some(vec![condition]);
|
||||
}
|
||||
}
|
||||
|
||||
// debug macros with two args: `debug_assert_{ne, eq}` (e.g., `assert_ne!(a, b)`)
|
||||
if_chain! {
|
||||
if let ExprKind::Block(ref matchblock,_) = matchexpr.kind;
|
||||
if let Some(ref matchblock_expr) = matchblock.expr;
|
||||
then {
|
||||
return ast_matchblock(matchblock_expr);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if let Some(matchblock_expr) = block.expr {
|
||||
// macros with two args: `assert_{ne, eq}` (e.g., `assert_ne!(a, b)`)
|
||||
return ast_matchblock(&matchblock_expr);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
|
|
|||
|
|
@ -708,7 +708,7 @@ fn reindent_multiline_inner(s: &str, ignore_first: bool, indent: Option<usize>,
|
|||
}
|
||||
|
||||
/// Gets the parent expression, if any –- this is useful to constrain a lint.
|
||||
pub fn get_parent_expr<'c>(cx: &'c LateContext<'_>, e: &Expr<'_>) -> Option<&'c Expr<'c>> {
|
||||
pub fn get_parent_expr<'tcx>(cx: &LateContext<'tcx>, e: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
|
||||
let map = &cx.tcx.hir();
|
||||
let hir_id = e.hir_id;
|
||||
let parent_id = map.get_parent_node(hir_id);
|
||||
|
|
|
|||
|
|
@ -13,8 +13,10 @@ use rustc_span::{BytePos, Pos};
|
|||
use std::borrow::Cow;
|
||||
use std::convert::TryInto;
|
||||
use std::fmt::Display;
|
||||
use std::ops::{Add, Neg, Not, Sub};
|
||||
|
||||
/// A helper type to build suggestion correctly handling parenthesis.
|
||||
#[derive(Clone, PartialEq)]
|
||||
pub enum Sugg<'a> {
|
||||
/// An expression that never needs parenthesis such as `1337` or `[0; 42]`.
|
||||
NonParen(Cow<'a, str>),
|
||||
|
|
@ -25,8 +27,12 @@ pub enum Sugg<'a> {
|
|||
BinOp(AssocOp, Cow<'a, str>),
|
||||
}
|
||||
|
||||
/// Literal constant `0`, for convenience.
|
||||
pub const ZERO: Sugg<'static> = Sugg::NonParen(Cow::Borrowed("0"));
|
||||
/// Literal constant `1`, for convenience.
|
||||
pub const ONE: Sugg<'static> = Sugg::NonParen(Cow::Borrowed("1"));
|
||||
/// a constant represents an empty string, for convenience.
|
||||
pub const EMPTY: Sugg<'static> = Sugg::NonParen(Cow::Borrowed(""));
|
||||
|
||||
impl Display for Sugg<'_> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
|
||||
|
|
@ -269,21 +275,60 @@ impl<'a> Sugg<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> std::ops::Add<Sugg<'b>> for Sugg<'a> {
|
||||
// Copied from the rust standart library, and then edited
|
||||
macro_rules! forward_binop_impls_to_ref {
|
||||
(impl $imp:ident, $method:ident for $t:ty, type Output = $o:ty) => {
|
||||
impl $imp<$t> for &$t {
|
||||
type Output = $o;
|
||||
|
||||
fn $method(self, other: $t) -> $o {
|
||||
$imp::$method(self, &other)
|
||||
}
|
||||
}
|
||||
|
||||
impl $imp<&$t> for $t {
|
||||
type Output = $o;
|
||||
|
||||
fn $method(self, other: &$t) -> $o {
|
||||
$imp::$method(&self, other)
|
||||
}
|
||||
}
|
||||
|
||||
impl $imp for $t {
|
||||
type Output = $o;
|
||||
|
||||
fn $method(self, other: $t) -> $o {
|
||||
$imp::$method(&self, &other)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl Add for &Sugg<'_> {
|
||||
type Output = Sugg<'static>;
|
||||
fn add(self, rhs: Sugg<'b>) -> Sugg<'static> {
|
||||
make_binop(ast::BinOpKind::Add, &self, &rhs)
|
||||
fn add(self, rhs: &Sugg<'_>) -> Sugg<'static> {
|
||||
make_binop(ast::BinOpKind::Add, self, rhs)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> std::ops::Sub<Sugg<'b>> for Sugg<'a> {
|
||||
impl Sub for &Sugg<'_> {
|
||||
type Output = Sugg<'static>;
|
||||
fn sub(self, rhs: Sugg<'b>) -> Sugg<'static> {
|
||||
make_binop(ast::BinOpKind::Sub, &self, &rhs)
|
||||
fn sub(self, rhs: &Sugg<'_>) -> Sugg<'static> {
|
||||
make_binop(ast::BinOpKind::Sub, self, rhs)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> std::ops::Not for Sugg<'a> {
|
||||
forward_binop_impls_to_ref!(impl Add, add for Sugg<'_>, type Output = Sugg<'static>);
|
||||
forward_binop_impls_to_ref!(impl Sub, sub for Sugg<'_>, type Output = Sugg<'static>);
|
||||
|
||||
impl Neg for Sugg<'_> {
|
||||
type Output = Sugg<'static>;
|
||||
fn neg(self) -> Sugg<'static> {
|
||||
make_unop("-", self)
|
||||
}
|
||||
}
|
||||
|
||||
impl Not for Sugg<'_> {
|
||||
type Output = Sugg<'static>;
|
||||
fn not(self) -> Sugg<'static> {
|
||||
make_unop("!", self)
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
use crate::utils;
|
||||
use crate::utils::match_var;
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def::Res;
|
||||
use rustc_hir::intravisit;
|
||||
use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
|
||||
use rustc_hir::{Expr, HirId, Path};
|
||||
use rustc_hir::{Expr, ExprKind, HirId, Path};
|
||||
use rustc_infer::infer::TyCtxtInferExt;
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::hir::map::Map;
|
||||
|
|
@ -174,3 +175,50 @@ impl<'a, 'tcx> intravisit::Visitor<'tcx> for BindingUsageFinder<'a, 'tcx> {
|
|||
intravisit::NestedVisitorMap::OnlyBodies(self.cx.tcx.hir())
|
||||
}
|
||||
}
|
||||
|
||||
struct ReturnBreakContinueMacroVisitor {
|
||||
seen_return_break_continue: bool,
|
||||
}
|
||||
|
||||
impl ReturnBreakContinueMacroVisitor {
|
||||
fn new() -> ReturnBreakContinueMacroVisitor {
|
||||
ReturnBreakContinueMacroVisitor {
|
||||
seen_return_break_continue: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> Visitor<'tcx> for ReturnBreakContinueMacroVisitor {
|
||||
type Map = Map<'tcx>;
|
||||
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
|
||||
NestedVisitorMap::None
|
||||
}
|
||||
|
||||
fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) {
|
||||
if self.seen_return_break_continue {
|
||||
// No need to look farther if we've already seen one of them
|
||||
return;
|
||||
}
|
||||
match &ex.kind {
|
||||
ExprKind::Ret(..) | ExprKind::Break(..) | ExprKind::Continue(..) => {
|
||||
self.seen_return_break_continue = true;
|
||||
},
|
||||
// Something special could be done here to handle while or for loop
|
||||
// desugaring, as this will detect a break if there's a while loop
|
||||
// or a for loop inside the expression.
|
||||
_ => {
|
||||
if utils::in_macro(ex.span) {
|
||||
self.seen_return_break_continue = true;
|
||||
} else {
|
||||
rustc_hir::intravisit::walk_expr(self, ex);
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn contains_return_break_continue_macro(expression: &Expr<'_>) -> bool {
|
||||
let mut recursive_visitor = ReturnBreakContinueMacroVisitor::new();
|
||||
recursive_visitor.visit_expr(expression);
|
||||
recursive_visitor.seen_return_break_continue
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue