Merge remote-tracking branch 'upstream/master' into doc-markdown
This commit is contained in:
commit
fb943fbe86
320 changed files with 10022 additions and 5208 deletions
|
|
@ -10,6 +10,17 @@ use rustc_ast::{self as ast, *};
|
|||
use rustc_span::symbol::Ident;
|
||||
use std::mem;
|
||||
|
||||
pub mod ident_iter;
|
||||
pub use ident_iter::IdentIter;
|
||||
|
||||
pub fn is_useless_with_eq_exprs(kind: BinOpKind) -> bool {
|
||||
use BinOpKind::*;
|
||||
matches!(
|
||||
kind,
|
||||
Sub | Div | Eq | Lt | Le | Gt | Ge | Ne | And | Or | BitXor | BitAnd | BitOr
|
||||
)
|
||||
}
|
||||
|
||||
/// Checks if each element in the first slice is contained within the latter as per `eq_fn`.
|
||||
pub fn unordered_over<X>(left: &[X], right: &[X], mut eq_fn: impl FnMut(&X, &X) -> bool) -> bool {
|
||||
left.len() == right.len() && left.iter().all(|l| right.iter().any(|r| eq_fn(l, r)))
|
||||
|
|
@ -396,8 +407,15 @@ pub fn eq_use_tree_kind(l: &UseTreeKind, r: &UseTreeKind) -> bool {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn eq_anon_const(l: &AnonConst, r: &AnonConst) -> bool {
|
||||
eq_expr(&l.value, &r.value)
|
||||
}
|
||||
|
||||
pub fn eq_defaultness(l: Defaultness, r: Defaultness) -> bool {
|
||||
matches!((l, r), (Defaultness::Final, Defaultness::Final) | (Defaultness::Default(_), Defaultness::Default(_)))
|
||||
matches!(
|
||||
(l, r),
|
||||
(Defaultness::Final, Defaultness::Final) | (Defaultness::Default(_), Defaultness::Default(_))
|
||||
)
|
||||
}
|
||||
|
||||
pub fn eq_vis(l: &Visibility, r: &Visibility) -> bool {
|
||||
|
|
@ -483,7 +501,18 @@ pub fn eq_generic_param(l: &GenericParam, r: &GenericParam) -> bool {
|
|||
&& match (&l.kind, &r.kind) {
|
||||
(Lifetime, Lifetime) => true,
|
||||
(Type { default: l }, Type { default: r }) => both(l, r, |l, r| eq_ty(l, r)),
|
||||
(Const { ty: l, kw_span: _ }, Const { ty: r, kw_span: _ }) => eq_ty(l, r),
|
||||
(
|
||||
Const {
|
||||
ty: lt,
|
||||
kw_span: _,
|
||||
default: ld,
|
||||
},
|
||||
Const {
|
||||
ty: rt,
|
||||
kw_span: _,
|
||||
default: rd,
|
||||
},
|
||||
) => eq_ty(lt, rt) && both(ld, rd, |ld, rd| eq_anon_const(ld, rd)),
|
||||
_ => false,
|
||||
}
|
||||
&& over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r))
|
||||
|
|
@ -527,7 +556,7 @@ pub fn eq_mac_args(l: &MacArgs, r: &MacArgs) -> bool {
|
|||
match (l, r) {
|
||||
(Empty, Empty) => true,
|
||||
(Delimited(_, ld, lts), Delimited(_, rd, rts)) => ld == rd && lts.eq_unspanned(rts),
|
||||
(Eq(_, lts), Eq(_, rts)) => lts.eq_unspanned(rts),
|
||||
(Eq(_, lt), Eq(_, rt)) => lt.kind == rt.kind,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
45
clippy_lints/src/utils/ast_utils/ident_iter.rs
Normal file
45
clippy_lints/src/utils/ast_utils/ident_iter.rs
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
use core::iter::FusedIterator;
|
||||
use rustc_ast::visit::{walk_attribute, walk_expr, Visitor};
|
||||
use rustc_ast::{Attribute, Expr};
|
||||
use rustc_span::symbol::Ident;
|
||||
|
||||
pub struct IdentIter(std::vec::IntoIter<Ident>);
|
||||
|
||||
impl Iterator for IdentIter {
|
||||
type Item = Ident;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.0.next()
|
||||
}
|
||||
}
|
||||
|
||||
impl FusedIterator for IdentIter {}
|
||||
|
||||
impl From<&Expr> for IdentIter {
|
||||
fn from(expr: &Expr) -> Self {
|
||||
let mut visitor = IdentCollector::default();
|
||||
|
||||
walk_expr(&mut visitor, expr);
|
||||
|
||||
IdentIter(visitor.0.into_iter())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Attribute> for IdentIter {
|
||||
fn from(attr: &Attribute) -> Self {
|
||||
let mut visitor = IdentCollector::default();
|
||||
|
||||
walk_attribute(&mut visitor, attr);
|
||||
|
||||
IdentIter(visitor.0.into_iter())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct IdentCollector(Vec<Ident>);
|
||||
|
||||
impl Visitor<'_> for IdentCollector {
|
||||
fn visit_ident(&mut self, ident: Ident) {
|
||||
self.0.push(ident);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
use rustc_ast::ast;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_session::Session;
|
||||
use rustc_span::sym;
|
||||
use std::str::FromStr;
|
||||
|
||||
/// Deprecation status of attributes known by Clippy.
|
||||
|
|
@ -64,11 +65,11 @@ pub fn get_attr<'a>(
|
|||
return false;
|
||||
};
|
||||
let attr_segments = &attr.path.segments;
|
||||
if attr_segments.len() == 2 && attr_segments[0].ident.to_string() == "clippy" {
|
||||
if attr_segments.len() == 2 && attr_segments[0].ident.name == sym::clippy {
|
||||
BUILTIN_ATTRIBUTES
|
||||
.iter()
|
||||
.find_map(|(builtin_name, deprecation_status)| {
|
||||
if *builtin_name == attr_segments[1].ident.to_string() {
|
||||
.find_map(|&(builtin_name, ref deprecation_status)| {
|
||||
if attr_segments[1].ident.name.as_str() == builtin_name {
|
||||
Some(deprecation_status)
|
||||
} else {
|
||||
None
|
||||
|
|
@ -99,7 +100,7 @@ pub fn get_attr<'a>(
|
|||
},
|
||||
DeprecationStatus::None => {
|
||||
diag.cancel();
|
||||
attr_segments[1].ident.to_string() == name
|
||||
attr_segments[1].ident.name.as_str() == name
|
||||
},
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
//! A group of attributes that can be attached to Rust code in order
|
||||
//! to generate a clippy lint detecting said code automatically.
|
||||
|
||||
use crate::utils::{get_attr, higher};
|
||||
use crate::utils::get_attr;
|
||||
use rustc_ast::ast::{Attribute, LitFloatType, LitKind};
|
||||
use rustc_ast::walk_list;
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
|
|
@ -201,32 +201,6 @@ impl<'tcx> Visitor<'tcx> for PrintVisitor {
|
|||
|
||||
#[allow(clippy::too_many_lines)]
|
||||
fn visit_expr(&mut self, expr: &Expr<'_>) {
|
||||
// handle if desugarings
|
||||
// TODO add more desugarings here
|
||||
if let Some((cond, then, opt_else)) = higher::if_block(&expr) {
|
||||
let cond_pat = self.next("cond");
|
||||
let then_pat = self.next("then");
|
||||
if let Some(else_) = opt_else {
|
||||
let else_pat = self.next("else_");
|
||||
println!(
|
||||
" if let Some((ref {}, ref {}, Some({}))) = higher::if_block(&{});",
|
||||
cond_pat, then_pat, else_pat, self.current
|
||||
);
|
||||
self.current = else_pat;
|
||||
self.visit_expr(else_);
|
||||
} else {
|
||||
println!(
|
||||
" if let Some((ref {}, ref {}, None)) = higher::if_block(&{});",
|
||||
cond_pat, then_pat, self.current
|
||||
);
|
||||
}
|
||||
self.current = cond_pat;
|
||||
self.visit_expr(cond);
|
||||
self.current = then_pat;
|
||||
self.visit_expr(then);
|
||||
return;
|
||||
}
|
||||
|
||||
print!(" if let ExprKind::");
|
||||
let current = format!("{}.kind", self.current);
|
||||
match expr.kind {
|
||||
|
|
@ -351,6 +325,25 @@ impl<'tcx> Visitor<'tcx> for PrintVisitor {
|
|||
self.current = body_pat;
|
||||
self.visit_block(body);
|
||||
},
|
||||
ExprKind::If(ref cond, ref then, ref opt_else) => {
|
||||
let cond_pat = self.next("cond");
|
||||
let then_pat = self.next("then");
|
||||
if let Some(ref else_) = *opt_else {
|
||||
let else_pat = self.next("else_");
|
||||
println!(
|
||||
"If(ref {}, ref {}, Some(ref {})) = {};",
|
||||
cond_pat, then_pat, else_pat, current
|
||||
);
|
||||
self.current = else_pat;
|
||||
self.visit_expr(else_);
|
||||
} else {
|
||||
println!("If(ref {}, ref {}, None) = {};", cond_pat, then_pat, current);
|
||||
}
|
||||
self.current = cond_pat;
|
||||
self.visit_expr(cond);
|
||||
self.current = then_pat;
|
||||
self.visit_expr(then);
|
||||
},
|
||||
ExprKind::Match(ref expr, ref arms, desugaring) => {
|
||||
let des = desugaring_name(desugaring);
|
||||
let expr_pat = self.next("expr");
|
||||
|
|
@ -372,6 +365,18 @@ impl<'tcx> Visitor<'tcx> for PrintVisitor {
|
|||
self.current = if_expr_pat;
|
||||
self.visit_expr(if_expr);
|
||||
},
|
||||
hir::Guard::IfLet(ref if_let_pat, ref if_let_expr) => {
|
||||
let if_let_pat_pat = self.next("pat");
|
||||
let if_let_expr_pat = self.next("expr");
|
||||
println!(
|
||||
" if let Guard::IfLet(ref {}, ref {}) = {};",
|
||||
if_let_pat_pat, if_let_expr_pat, guard_pat
|
||||
);
|
||||
self.current = if_let_expr_pat;
|
||||
self.visit_expr(if_let_expr);
|
||||
self.current = if_let_pat_pat;
|
||||
self.visit_pat(if_let_pat);
|
||||
},
|
||||
}
|
||||
}
|
||||
self.current = format!("{}[{}].pat", arms_pat, i);
|
||||
|
|
@ -730,10 +735,7 @@ fn desugaring_name(des: hir::MatchSource) -> String {
|
|||
"MatchSource::IfLetDesugar {{ contains_else_clause: {} }}",
|
||||
contains_else_clause
|
||||
),
|
||||
hir::MatchSource::IfDesugar { contains_else_clause } => format!(
|
||||
"MatchSource::IfDesugar {{ contains_else_clause: {} }}",
|
||||
contains_else_clause
|
||||
),
|
||||
hir::MatchSource::IfLetGuardDesugar => "MatchSource::IfLetGuardDesugar".to_string(),
|
||||
hir::MatchSource::AwaitDesugar => "MatchSource::AwaitDesugar".to_string(),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -106,7 +106,7 @@ macro_rules! define_Conf {
|
|||
|
||||
pub use self::helpers::Conf;
|
||||
define_Conf! {
|
||||
/// Lint: MANUAL_NON_EXHAUSTIVE, MANUAL_STRIP, OPTION_AS_REF_DEREF, MATCH_LIKE_MATCHES_MACRO. The minimum rust version that the project supports
|
||||
/// Lint: REDUNDANT_FIELD_NAMES, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN. The minimum rust version that the project supports
|
||||
(msrv, "msrv": Option<String>, None),
|
||||
/// Lint: BLACKLISTED_NAME. The list of blacklisted names to lint about. NB: `bar` is not here since it has legitimate uses
|
||||
(blacklisted_names, "blacklisted_names": Vec<String>, ["foo", "baz", "quux"].iter().map(ToString::to_string).collect()),
|
||||
|
|
@ -171,6 +171,8 @@ define_Conf! {
|
|||
(warn_on_all_wildcard_imports, "warn_on_all_wildcard_imports": bool, false),
|
||||
/// Lint: DISALLOWED_METHOD. The list of blacklisted methods to lint about. NB: `bar` is not here since it has legitimate uses
|
||||
(disallowed_methods, "disallowed_methods": Vec<String>, Vec::<String>::new()),
|
||||
/// Lint: UNREADABLE_LITERAL. Should the fraction of a decimal be linted to include separators.
|
||||
(unreadable_literal_lint_fractions, "unreadable_literal_lint_fractions": bool, true),
|
||||
}
|
||||
|
||||
impl Default for Conf {
|
||||
|
|
|
|||
|
|
@ -186,7 +186,9 @@ pub fn span_lint_hir_and_then(
|
|||
/// |
|
||||
/// = note: `-D fold-any` implied by `-D warnings`
|
||||
/// ```
|
||||
#[allow(clippy::collapsible_span_lint_calls)]
|
||||
|
||||
#[allow(clippy::unknown_clippy_lints)]
|
||||
#[cfg_attr(feature = "internal-lints", allow(clippy::collapsible_span_lint_calls))]
|
||||
pub fn span_lint_and_sugg<'a, T: LintContext>(
|
||||
cx: &'a T,
|
||||
lint: &'static Lint,
|
||||
|
|
|
|||
|
|
@ -62,6 +62,7 @@ fn identify_some_pure_patterns(expr: &Expr<'_>) -> bool {
|
|||
| ExprKind::Type(..)
|
||||
| ExprKind::DropTemps(..)
|
||||
| ExprKind::Loop(..)
|
||||
| ExprKind::If(..)
|
||||
| ExprKind::Match(..)
|
||||
| ExprKind::Closure(..)
|
||||
| ExprKind::Block(..)
|
||||
|
|
|
|||
|
|
@ -162,8 +162,7 @@ pub fn while_loop<'tcx>(expr: &'tcx hir::Expr<'tcx>) -> Option<(&'tcx hir::Expr<
|
|||
if let hir::Block { expr: Some(expr), .. } = &**block;
|
||||
if let hir::ExprKind::Match(cond, arms, hir::MatchSource::WhileDesugar) = &expr.kind;
|
||||
if let hir::ExprKind::DropTemps(cond) = &cond.kind;
|
||||
if let [arm, ..] = &arms[..];
|
||||
if let hir::Arm { body, .. } = arm;
|
||||
if let [hir::Arm { body, .. }, ..] = &arms[..];
|
||||
then {
|
||||
return Some((cond, body));
|
||||
}
|
||||
|
|
@ -171,33 +170,6 @@ pub fn while_loop<'tcx>(expr: &'tcx hir::Expr<'tcx>) -> Option<(&'tcx hir::Expr<
|
|||
None
|
||||
}
|
||||
|
||||
/// Recover the essential nodes of a desugared if block
|
||||
/// `if cond { then } else { els }` becomes `(cond, then, Some(els))`
|
||||
pub fn if_block<'tcx>(
|
||||
expr: &'tcx hir::Expr<'tcx>,
|
||||
) -> Option<(
|
||||
&'tcx hir::Expr<'tcx>,
|
||||
&'tcx hir::Expr<'tcx>,
|
||||
Option<&'tcx hir::Expr<'tcx>>,
|
||||
)> {
|
||||
if let hir::ExprKind::Match(ref cond, ref arms, hir::MatchSource::IfDesugar { contains_else_clause }) = expr.kind {
|
||||
let cond = if let hir::ExprKind::DropTemps(ref cond) = cond.kind {
|
||||
cond
|
||||
} else {
|
||||
panic!("If block desugar must contain DropTemps");
|
||||
};
|
||||
let then = &arms[0].body;
|
||||
let els = if contains_else_clause {
|
||||
Some(&*arms[1].body)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
Some((cond, then, els))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Represent the pre-expansion arguments of a `vec!` invocation.
|
||||
pub enum VecArgs<'a> {
|
||||
/// `vec![elem; len]`
|
||||
|
|
@ -268,12 +240,11 @@ pub fn extract_assert_macro_args<'tcx>(e: &'tcx Expr<'tcx>) -> Option<Vec<&'tcx
|
|||
|
||||
if let ExprKind::Block(ref block, _) = e.kind {
|
||||
if block.stmts.len() == 1 {
|
||||
if let StmtKind::Semi(ref matchexpr) = block.stmts[0].kind {
|
||||
if let StmtKind::Semi(ref matchexpr) = block.stmts.get(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;
|
||||
if let ExprKind::If(ref clause, _, _) = matchexpr.kind;
|
||||
if let ExprKind::Unary(UnOp::UnNot, condition) = clause.kind;
|
||||
then {
|
||||
return Some(vec![condition]);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -81,12 +81,12 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
match (&left.kind, &right.kind) {
|
||||
match (&reduce_exprkind(&left.kind), &reduce_exprkind(&right.kind)) {
|
||||
(&ExprKind::AddrOf(lb, l_mut, ref le), &ExprKind::AddrOf(rb, r_mut, ref re)) => {
|
||||
lb == rb && l_mut == r_mut && self.eq_expr(le, re)
|
||||
},
|
||||
(&ExprKind::Continue(li), &ExprKind::Continue(ri)) => {
|
||||
both(&li.label, &ri.label, |l, r| l.ident.as_str() == r.ident.as_str())
|
||||
both(&li.label, &ri.label, |l, r| l.ident.name == r.ident.name)
|
||||
},
|
||||
(&ExprKind::Assign(ref ll, ref lr, _), &ExprKind::Assign(ref rl, ref rr, _)) => {
|
||||
self.allow_side_effects && self.eq_expr(ll, rl) && self.eq_expr(lr, rr)
|
||||
|
|
@ -102,7 +102,7 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> {
|
|||
})
|
||||
},
|
||||
(&ExprKind::Break(li, ref le), &ExprKind::Break(ri, ref re)) => {
|
||||
both(&li.label, &ri.label, |l, r| l.ident.as_str() == r.ident.as_str())
|
||||
both(&li.label, &ri.label, |l, r| l.ident.name == r.ident.name)
|
||||
&& both(le, re, |l, r| self.eq_expr(l, r))
|
||||
},
|
||||
(&ExprKind::Box(ref l), &ExprKind::Box(ref r)) => self.eq_expr(l, r),
|
||||
|
|
@ -119,9 +119,12 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> {
|
|||
(&ExprKind::Index(ref la, ref li), &ExprKind::Index(ref ra, ref ri)) => {
|
||||
self.eq_expr(la, ra) && self.eq_expr(li, ri)
|
||||
},
|
||||
(&ExprKind::If(ref lc, ref lt, ref le), &ExprKind::If(ref rc, ref rt, ref re)) => {
|
||||
self.eq_expr(lc, rc) && self.eq_expr(&**lt, &**rt) && both(le, re, |l, r| self.eq_expr(l, r))
|
||||
},
|
||||
(&ExprKind::Lit(ref l), &ExprKind::Lit(ref r)) => l.node == r.node,
|
||||
(&ExprKind::Loop(ref lb, ref ll, ref lls), &ExprKind::Loop(ref rb, ref rl, ref rls)) => {
|
||||
lls == rls && self.eq_block(lb, rb) && both(ll, rl, |l, r| l.ident.as_str() == r.ident.as_str())
|
||||
lls == rls && self.eq_block(lb, rb) && both(ll, rl, |l, r| l.ident.name == r.ident.name)
|
||||
},
|
||||
(&ExprKind::Match(ref le, ref la, ref ls), &ExprKind::Match(ref re, ref ra, ref rs)) => {
|
||||
ls == rs
|
||||
|
|
@ -169,6 +172,8 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> {
|
|||
fn eq_guard(&mut self, left: &Guard<'_>, right: &Guard<'_>) -> bool {
|
||||
match (left, right) {
|
||||
(Guard::If(l), Guard::If(r)) => self.eq_expr(l, r),
|
||||
(Guard::IfLet(lp, le), Guard::IfLet(rp, re)) => self.eq_pat(lp, rp) && self.eq_expr(le, re),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -186,7 +191,7 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> {
|
|||
|
||||
pub fn eq_fieldpat(&mut self, left: &FieldPat<'_>, right: &FieldPat<'_>) -> bool {
|
||||
let (FieldPat { ident: li, pat: lp, .. }, FieldPat { ident: ri, pat: rp, .. }) = (&left, &right);
|
||||
li.name.as_str() == ri.name.as_str() && self.eq_pat(lp, rp)
|
||||
li.name == ri.name && self.eq_pat(lp, rp)
|
||||
}
|
||||
|
||||
/// Checks whether two patterns are the same.
|
||||
|
|
@ -200,7 +205,7 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> {
|
|||
self.eq_qpath(lp, rp) && over(la, ra, |l, r| self.eq_pat(l, r)) && ls == rs
|
||||
},
|
||||
(&PatKind::Binding(ref lb, .., ref li, ref lp), &PatKind::Binding(ref rb, .., ref ri, ref rp)) => {
|
||||
lb == rb && li.name.as_str() == ri.name.as_str() && both(lp, rp, |l, r| self.eq_pat(l, r))
|
||||
lb == rb && li.name == ri.name && both(lp, rp, |l, r| self.eq_pat(l, r))
|
||||
},
|
||||
(&PatKind::Path(ref l), &PatKind::Path(ref r)) => self.eq_qpath(l, r),
|
||||
(&PatKind::Lit(ref l), &PatKind::Lit(ref r)) => self.eq_expr(l, r),
|
||||
|
|
@ -261,8 +266,7 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> {
|
|||
pub fn eq_path_segment(&mut self, left: &PathSegment<'_>, right: &PathSegment<'_>) -> bool {
|
||||
// The == of idents doesn't work with different contexts,
|
||||
// we have to be explicit about hygiene
|
||||
left.ident.as_str() == right.ident.as_str()
|
||||
&& both(&left.args, &right.args, |l, r| self.eq_path_parameters(l, r))
|
||||
left.ident.name == right.ident.name && both(&left.args, &right.args, |l, r| self.eq_path_parameters(l, r))
|
||||
}
|
||||
|
||||
pub fn eq_ty(&mut self, left: &Ty<'_>, right: &Ty<'_>) -> bool {
|
||||
|
|
@ -306,6 +310,32 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Some simple reductions like `{ return }` => `return`
|
||||
fn reduce_exprkind<'hir>(kind: &'hir ExprKind<'hir>) -> &ExprKind<'hir> {
|
||||
if let ExprKind::Block(block, _) = kind {
|
||||
match (block.stmts, block.expr) {
|
||||
// `{}` => `()`
|
||||
([], None) => &ExprKind::Tup(&[]),
|
||||
([], Some(expr)) => match expr.kind {
|
||||
// `{ return .. }` => `return ..`
|
||||
ExprKind::Ret(..) => &expr.kind,
|
||||
_ => kind,
|
||||
},
|
||||
([stmt], None) => match stmt.kind {
|
||||
StmtKind::Expr(expr) | StmtKind::Semi(expr) => match expr.kind {
|
||||
// `{ return ..; }` => `return ..`
|
||||
ExprKind::Ret(..) => &expr.kind,
|
||||
_ => kind,
|
||||
},
|
||||
_ => kind,
|
||||
},
|
||||
_ => kind,
|
||||
}
|
||||
} else {
|
||||
kind
|
||||
}
|
||||
}
|
||||
|
||||
fn swap_binop<'a>(
|
||||
binop: BinOpKind,
|
||||
lhs: &'a Expr<'a>,
|
||||
|
|
@ -491,7 +521,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
asm.options.hash(&mut self.s);
|
||||
for op in asm.operands {
|
||||
for (op, _op_sp) in asm.operands {
|
||||
match op {
|
||||
InlineAsmOperand::In { reg, expr } => {
|
||||
reg.hash(&mut self.s);
|
||||
|
|
@ -536,6 +566,15 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
|
|||
self.hash_name(i.ident.name);
|
||||
}
|
||||
},
|
||||
ExprKind::If(ref cond, ref then, ref else_opt) => {
|
||||
let c: fn(_, _, _) -> _ = ExprKind::If;
|
||||
c.hash(&mut self.s);
|
||||
self.hash_expr(cond);
|
||||
self.hash_expr(&**then);
|
||||
if let Some(ref e) = *else_opt {
|
||||
self.hash_expr(e);
|
||||
}
|
||||
},
|
||||
ExprKind::Match(ref e, arms, ref s) => {
|
||||
self.hash_expr(e);
|
||||
|
||||
|
|
@ -643,7 +682,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
|
|||
|
||||
pub fn hash_guard(&mut self, g: &Guard<'_>) {
|
||||
match g {
|
||||
Guard::If(ref expr) => {
|
||||
Guard::If(ref expr) | Guard::IfLet(_, ref expr) => {
|
||||
self.hash_expr(expr);
|
||||
},
|
||||
}
|
||||
|
|
@ -716,7 +755,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
|
|||
}
|
||||
for segment in path.segments {
|
||||
segment.ident.name.hash(&mut self.s);
|
||||
self.hash_generic_args(segment.generic_args().args);
|
||||
self.hash_generic_args(segment.args().args);
|
||||
}
|
||||
},
|
||||
QPath::TypeRelative(ref ty, ref segment) => {
|
||||
|
|
|
|||
|
|
@ -213,6 +213,15 @@ fn print_expr(cx: &LateContext<'_>, expr: &hir::Expr<'_>, indent: usize) {
|
|||
hir::ExprKind::Loop(..) => {
|
||||
println!("{}Loop", ind);
|
||||
},
|
||||
hir::ExprKind::If(ref cond, _, ref else_opt) => {
|
||||
println!("{}If", ind);
|
||||
println!("{}condition:", ind);
|
||||
print_expr(cx, cond, indent + 1);
|
||||
if let Some(ref els) = *else_opt {
|
||||
println!("{}else:", ind);
|
||||
print_expr(cx, els, indent + 1);
|
||||
}
|
||||
},
|
||||
hir::ExprKind::Match(ref cond, _, ref source) => {
|
||||
println!("{}Match", ind);
|
||||
println!("{}condition:", ind);
|
||||
|
|
@ -293,7 +302,7 @@ fn print_expr(cx: &LateContext<'_>, expr: &hir::Expr<'_>, indent: usize) {
|
|||
println!("{}template: {}", ind, InlineAsmTemplatePiece::to_string(asm.template));
|
||||
println!("{}options: {:?}", ind, asm.options);
|
||||
println!("{}operands:", ind);
|
||||
for op in asm.operands {
|
||||
for (op, _op_sp) in asm.operands {
|
||||
match op {
|
||||
hir::InlineAsmOperand::In { expr, .. }
|
||||
| hir::InlineAsmOperand::InOut { expr, .. }
|
||||
|
|
@ -395,7 +404,7 @@ fn print_item(cx: &LateContext<'_>, item: &hir::Item<'_>) {
|
|||
println!("function of type {:#?}", item_ty);
|
||||
},
|
||||
hir::ItemKind::Mod(..) => println!("module"),
|
||||
hir::ItemKind::ForeignMod(ref fm) => println!("foreign module with abi: {}", fm.abi),
|
||||
hir::ItemKind::ForeignMod { abi, .. } => println!("foreign module with abi: {}", abi),
|
||||
hir::ItemKind::GlobalAsm(ref asm) => println!("global asm: {:?}", asm),
|
||||
hir::ItemKind::TyAlias(..) => {
|
||||
println!("type alias for {:?}", cx.tcx.type_of(did));
|
||||
|
|
@ -423,13 +432,13 @@ fn print_item(cx: &LateContext<'_>, item: &hir::Item<'_>) {
|
|||
hir::ItemKind::TraitAlias(..) => {
|
||||
println!("trait alias");
|
||||
},
|
||||
hir::ItemKind::Impl {
|
||||
hir::ItemKind::Impl(hir::Impl {
|
||||
of_trait: Some(ref _trait_ref),
|
||||
..
|
||||
} => {
|
||||
}) => {
|
||||
println!("trait impl");
|
||||
},
|
||||
hir::ItemKind::Impl { of_trait: None, .. } => {
|
||||
hir::ItemKind::Impl(hir::Impl { of_trait: None, .. }) => {
|
||||
println!("impl");
|
||||
},
|
||||
}
|
||||
|
|
@ -560,5 +569,10 @@ fn print_guard(cx: &LateContext<'_>, guard: &hir::Guard<'_>, indent: usize) {
|
|||
println!("{}If", ind);
|
||||
print_expr(cx, expr, indent + 1);
|
||||
},
|
||||
hir::Guard::IfLet(pat, expr) => {
|
||||
println!("{}IfLet", ind);
|
||||
print_pat(cx, pat, indent + 1);
|
||||
print_expr(cx, expr, indent + 1);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,11 +10,15 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
|||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::hir_id::CRATE_HIR_ID;
|
||||
use rustc_hir::intravisit::{NestedVisitorMap, Visitor};
|
||||
use rustc_hir::{Crate, Expr, ExprKind, HirId, Item, MutTy, Mutability, Node, Path, StmtKind, Ty, TyKind};
|
||||
use rustc_hir::{
|
||||
BinOpKind, Crate, Expr, ExprKind, HirId, Item, MutTy, Mutability, Node, Path, StmtKind, Ty, TyKind, UnOp,
|
||||
};
|
||||
use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass};
|
||||
use rustc_middle::hir::map::Map;
|
||||
use rustc_middle::mir::interpret::ConstValue;
|
||||
use rustc_middle::ty;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::source_map::{Span, Spanned};
|
||||
|
|
@ -247,6 +251,52 @@ declare_clippy_lint! {
|
|||
"invalid path"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:**
|
||||
/// Checks for interning symbols that have already been pre-interned and defined as constants.
|
||||
///
|
||||
/// **Why is this bad?**
|
||||
/// It's faster and easier to use the symbol constant.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// Bad:
|
||||
/// ```rust,ignore
|
||||
/// let _ = sym!(f32);
|
||||
/// ```
|
||||
///
|
||||
/// Good:
|
||||
/// ```rust,ignore
|
||||
/// let _ = sym::f32;
|
||||
/// ```
|
||||
pub INTERNING_DEFINED_SYMBOL,
|
||||
internal,
|
||||
"interning a symbol that is pre-interned and defined as a constant"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for unnecessary conversion from Symbol to a string.
|
||||
///
|
||||
/// **Why is this bad?** It's faster use symbols directly intead of strings.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// Bad:
|
||||
/// ```rust,ignore
|
||||
/// symbol.as_str() == "clippy";
|
||||
/// ```
|
||||
///
|
||||
/// Good:
|
||||
/// ```rust,ignore
|
||||
/// symbol == sym::clippy;
|
||||
/// ```
|
||||
pub UNNECESSARY_SYMBOL_STR,
|
||||
internal,
|
||||
"unnecessary conversion between Symbol and string"
|
||||
}
|
||||
|
||||
declare_lint_pass!(ClippyLintsInternal => [CLIPPY_LINTS_INTERNAL]);
|
||||
|
||||
impl EarlyLintPass for ClippyLintsInternal {
|
||||
|
|
@ -327,11 +377,11 @@ impl<'tcx> LateLintPass<'tcx> for LintWithoutLintPass {
|
|||
} else if is_expn_of(item.span, "impl_lint_pass").is_some()
|
||||
|| is_expn_of(item.span, "declare_lint_pass").is_some()
|
||||
{
|
||||
if let hir::ItemKind::Impl {
|
||||
if let hir::ItemKind::Impl(hir::Impl {
|
||||
of_trait: None,
|
||||
items: ref impl_item_refs,
|
||||
..
|
||||
} = item.kind
|
||||
}) = item.kind
|
||||
{
|
||||
let mut collector = LintCollector {
|
||||
output: &mut self.registered_lints,
|
||||
|
|
@ -840,3 +890,183 @@ impl<'tcx> LateLintPass<'tcx> for InvalidPaths {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct InterningDefinedSymbol {
|
||||
// Maps the symbol value to the constant DefId.
|
||||
symbol_map: FxHashMap<u32, DefId>,
|
||||
}
|
||||
|
||||
impl_lint_pass!(InterningDefinedSymbol => [INTERNING_DEFINED_SYMBOL, UNNECESSARY_SYMBOL_STR]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for InterningDefinedSymbol {
|
||||
fn check_crate(&mut self, cx: &LateContext<'_>, _: &Crate<'_>) {
|
||||
if !self.symbol_map.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
for &module in &[&paths::KW_MODULE, &paths::SYM_MODULE] {
|
||||
if let Some(Res::Def(_, def_id)) = path_to_res(cx, module) {
|
||||
for item in cx.tcx.item_children(def_id).iter() {
|
||||
if_chain! {
|
||||
if let Res::Def(DefKind::Const, item_def_id) = item.res;
|
||||
let ty = cx.tcx.type_of(item_def_id);
|
||||
if match_type(cx, ty, &paths::SYMBOL);
|
||||
if let Ok(ConstValue::Scalar(value)) = cx.tcx.const_eval_poly(item_def_id);
|
||||
if let Ok(value) = value.to_u32();
|
||||
then {
|
||||
self.symbol_map.insert(value, item_def_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
if_chain! {
|
||||
if let ExprKind::Call(func, [arg]) = &expr.kind;
|
||||
if let ty::FnDef(def_id, _) = cx.typeck_results().expr_ty(func).kind();
|
||||
if match_def_path(cx, *def_id, &paths::SYMBOL_INTERN);
|
||||
if let Some(Constant::Str(arg)) = constant_simple(cx, cx.typeck_results(), arg);
|
||||
let value = Symbol::intern(&arg).as_u32();
|
||||
if let Some(&def_id) = self.symbol_map.get(&value);
|
||||
then {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
INTERNING_DEFINED_SYMBOL,
|
||||
is_expn_of(expr.span, "sym").unwrap_or(expr.span),
|
||||
"interning a defined symbol",
|
||||
"try",
|
||||
cx.tcx.def_path_str(def_id),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
if let ExprKind::Binary(op, left, right) = expr.kind {
|
||||
if matches!(op.node, BinOpKind::Eq | BinOpKind::Ne) {
|
||||
let data = [
|
||||
(left, self.symbol_str_expr(left, cx)),
|
||||
(right, self.symbol_str_expr(right, cx)),
|
||||
];
|
||||
match data {
|
||||
// both operands are a symbol string
|
||||
[(_, Some(left)), (_, Some(right))] => {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
UNNECESSARY_SYMBOL_STR,
|
||||
expr.span,
|
||||
"unnecessary `Symbol` to string conversion",
|
||||
"try",
|
||||
format!(
|
||||
"{} {} {}",
|
||||
left.as_symbol_snippet(cx),
|
||||
op.node.as_str(),
|
||||
right.as_symbol_snippet(cx),
|
||||
),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
},
|
||||
// one of the operands is a symbol string
|
||||
[(expr, Some(symbol)), _] | [_, (expr, Some(symbol))] => {
|
||||
// creating an owned string for comparison
|
||||
if matches!(symbol, SymbolStrExpr::Expr { is_to_owned: true, .. }) {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
UNNECESSARY_SYMBOL_STR,
|
||||
expr.span,
|
||||
"unnecessary string allocation",
|
||||
"try",
|
||||
format!("{}.as_str()", symbol.as_symbol_snippet(cx)),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
},
|
||||
// nothing found
|
||||
[(_, None), (_, None)] => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl InterningDefinedSymbol {
|
||||
fn symbol_str_expr<'tcx>(&self, expr: &'tcx Expr<'tcx>, cx: &LateContext<'tcx>) -> Option<SymbolStrExpr<'tcx>> {
|
||||
static IDENT_STR_PATHS: &[&[&str]] = &[&paths::IDENT_AS_STR, &paths::TO_STRING_METHOD];
|
||||
static SYMBOL_STR_PATHS: &[&[&str]] = &[
|
||||
&paths::SYMBOL_AS_STR,
|
||||
&paths::SYMBOL_TO_IDENT_STRING,
|
||||
&paths::TO_STRING_METHOD,
|
||||
];
|
||||
// SymbolStr might be de-referenced: `&*symbol.as_str()`
|
||||
let call = if_chain! {
|
||||
if let ExprKind::AddrOf(_, _, e) = expr.kind;
|
||||
if let ExprKind::Unary(UnOp::UnDeref, e) = e.kind;
|
||||
then { e } else { expr }
|
||||
};
|
||||
if_chain! {
|
||||
// is a method call
|
||||
if let ExprKind::MethodCall(_, _, [item], _) = call.kind;
|
||||
if let Some(did) = cx.typeck_results().type_dependent_def_id(call.hir_id);
|
||||
let ty = cx.typeck_results().expr_ty(item);
|
||||
// ...on either an Ident or a Symbol
|
||||
if let Some(is_ident) = if match_type(cx, ty, &paths::SYMBOL) {
|
||||
Some(false)
|
||||
} else if match_type(cx, ty, &paths::IDENT) {
|
||||
Some(true)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
// ...which converts it to a string
|
||||
let paths = if is_ident { IDENT_STR_PATHS } else { SYMBOL_STR_PATHS };
|
||||
if let Some(path) = paths.iter().find(|path| match_def_path(cx, did, path));
|
||||
then {
|
||||
let is_to_owned = path.last().unwrap().ends_with("string");
|
||||
return Some(SymbolStrExpr::Expr {
|
||||
item,
|
||||
is_ident,
|
||||
is_to_owned,
|
||||
});
|
||||
}
|
||||
}
|
||||
// is a string constant
|
||||
if let Some(Constant::Str(s)) = constant_simple(cx, cx.typeck_results(), expr) {
|
||||
let value = Symbol::intern(&s).as_u32();
|
||||
// ...which matches a symbol constant
|
||||
if let Some(&def_id) = self.symbol_map.get(&value) {
|
||||
return Some(SymbolStrExpr::Const(def_id));
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
enum SymbolStrExpr<'tcx> {
|
||||
/// a string constant with a corresponding symbol constant
|
||||
Const(DefId),
|
||||
/// a "symbol to string" expression like `symbol.as_str()`
|
||||
Expr {
|
||||
/// part that evaluates to `Symbol` or `Ident`
|
||||
item: &'tcx Expr<'tcx>,
|
||||
is_ident: bool,
|
||||
/// whether an owned `String` is created like `to_ident_string()`
|
||||
is_to_owned: bool,
|
||||
},
|
||||
}
|
||||
|
||||
impl<'tcx> SymbolStrExpr<'tcx> {
|
||||
/// Returns a snippet that evaluates to a `Symbol` and is const if possible
|
||||
fn as_symbol_snippet(&self, cx: &LateContext<'_>) -> Cow<'tcx, str> {
|
||||
match *self {
|
||||
Self::Const(def_id) => cx.tcx.def_path_str(def_id).into(),
|
||||
Self::Expr { item, is_ident, .. } => {
|
||||
let mut snip = snippet(cx, item.span.source_callsite(), "..");
|
||||
if is_ident {
|
||||
// get `Ident.name`
|
||||
snip.to_mut().push_str(".name");
|
||||
}
|
||||
snip
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
#[macro_use]
|
||||
pub mod sym;
|
||||
pub mod sym_helper;
|
||||
|
||||
#[allow(clippy::module_name_repetitions)]
|
||||
pub mod ast_utils;
|
||||
|
|
@ -14,6 +14,7 @@ pub mod eager_or_lazy;
|
|||
pub mod higher;
|
||||
mod hir_utils;
|
||||
pub mod inspector;
|
||||
#[cfg(feature = "internal-lints")]
|
||||
pub mod internal_lints;
|
||||
pub mod numeric_literal;
|
||||
pub mod paths;
|
||||
|
|
@ -40,7 +41,7 @@ use rustc_errors::Applicability;
|
|||
use rustc_hir as hir;
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX, LOCAL_CRATE};
|
||||
use rustc_hir::intravisit::{NestedVisitorMap, Visitor};
|
||||
use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
|
||||
use rustc_hir::Node;
|
||||
use rustc_hir::{
|
||||
def, Arm, Block, Body, Constness, Crate, Expr, ExprKind, FnDecl, HirId, ImplItem, ImplItemKind, Item, ItemKind,
|
||||
|
|
@ -51,21 +52,21 @@ use rustc_lint::{LateContext, Level, Lint, LintContext};
|
|||
use rustc_middle::hir::map::Map;
|
||||
use rustc_middle::ty::subst::{GenericArg, GenericArgKind};
|
||||
use rustc_middle::ty::{self, layout::IntegerExt, Ty, TyCtxt, TypeFoldable};
|
||||
use rustc_semver::RustcVersion;
|
||||
use rustc_session::Session;
|
||||
use rustc_span::hygiene::{ExpnKind, MacroKind};
|
||||
use rustc_span::source_map::original_sp;
|
||||
use rustc_span::sym as rustc_sym;
|
||||
use rustc_span::symbol::{self, kw, Symbol};
|
||||
use rustc_span::sym;
|
||||
use rustc_span::symbol::{kw, Symbol};
|
||||
use rustc_span::{BytePos, Pos, Span, DUMMY_SP};
|
||||
use rustc_target::abi::Integer;
|
||||
use rustc_trait_selection::traits::query::normalize::AtExt;
|
||||
use semver::{Version, VersionReq};
|
||||
use smallvec::SmallVec;
|
||||
|
||||
use crate::consts::{constant, Constant};
|
||||
|
||||
pub fn parse_msrv(msrv: &str, sess: Option<&Session>, span: Option<Span>) -> Option<VersionReq> {
|
||||
if let Ok(version) = VersionReq::parse(msrv) {
|
||||
pub fn parse_msrv(msrv: &str, sess: Option<&Session>, span: Option<Span>) -> Option<RustcVersion> {
|
||||
if let Ok(version) = RustcVersion::parse(msrv) {
|
||||
return Some(version);
|
||||
} else if let Some(sess) = sess {
|
||||
if let Some(span) = span {
|
||||
|
|
@ -75,8 +76,8 @@ pub fn parse_msrv(msrv: &str, sess: Option<&Session>, span: Option<Span>) -> Opt
|
|||
None
|
||||
}
|
||||
|
||||
pub fn meets_msrv(msrv: Option<&VersionReq>, lint_msrv: &Version) -> bool {
|
||||
msrv.map_or(true, |msrv| !msrv.matches(lint_msrv))
|
||||
pub fn meets_msrv(msrv: Option<&RustcVersion>, lint_msrv: &RustcVersion) -> bool {
|
||||
msrv.map_or(true, |msrv| msrv.meets(*lint_msrv))
|
||||
}
|
||||
|
||||
macro_rules! extract_msrv_attr {
|
||||
|
|
@ -438,8 +439,8 @@ pub fn trait_ref_of_method<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Optio
|
|||
if_chain! {
|
||||
if parent_impl != hir::CRATE_HIR_ID;
|
||||
if let hir::Node::Item(item) = cx.tcx.hir().get(parent_impl);
|
||||
if let hir::ItemKind::Impl{ of_trait: trait_ref, .. } = &item.kind;
|
||||
then { return trait_ref.as_ref(); }
|
||||
if let hir::ItemKind::Impl(impl_) = &item.kind;
|
||||
then { return impl_.of_trait.as_ref(); }
|
||||
}
|
||||
None
|
||||
}
|
||||
|
|
@ -572,6 +573,67 @@ pub fn contains_name(name: Symbol, expr: &Expr<'_>) -> bool {
|
|||
cn.result
|
||||
}
|
||||
|
||||
/// Returns `true` if `expr` contains a return expression
|
||||
pub fn contains_return(expr: &hir::Expr<'_>) -> bool {
|
||||
struct RetCallFinder {
|
||||
found: bool,
|
||||
}
|
||||
|
||||
impl<'tcx> hir::intravisit::Visitor<'tcx> for RetCallFinder {
|
||||
type Map = Map<'tcx>;
|
||||
|
||||
fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) {
|
||||
if self.found {
|
||||
return;
|
||||
}
|
||||
if let hir::ExprKind::Ret(..) = &expr.kind {
|
||||
self.found = true;
|
||||
} else {
|
||||
hir::intravisit::walk_expr(self, expr);
|
||||
}
|
||||
}
|
||||
|
||||
fn nested_visit_map(&mut self) -> hir::intravisit::NestedVisitorMap<Self::Map> {
|
||||
hir::intravisit::NestedVisitorMap::None
|
||||
}
|
||||
}
|
||||
|
||||
let mut visitor = RetCallFinder { found: false };
|
||||
visitor.visit_expr(expr);
|
||||
visitor.found
|
||||
}
|
||||
|
||||
struct FindMacroCalls<'a, 'b> {
|
||||
names: &'a [&'b str],
|
||||
result: Vec<Span>,
|
||||
}
|
||||
|
||||
impl<'a, 'b, 'tcx> Visitor<'tcx> for FindMacroCalls<'a, 'b> {
|
||||
type Map = Map<'tcx>;
|
||||
|
||||
fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
|
||||
if self.names.iter().any(|fun| is_expn_of(expr.span, fun).is_some()) {
|
||||
self.result.push(expr.span);
|
||||
}
|
||||
// and check sub-expressions
|
||||
intravisit::walk_expr(self, expr);
|
||||
}
|
||||
|
||||
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
|
||||
NestedVisitorMap::None
|
||||
}
|
||||
}
|
||||
|
||||
/// Finds calls of the specified macros in a function body.
|
||||
pub fn find_macro_calls(names: &[&str], body: &Body<'_>) -> Vec<Span> {
|
||||
let mut fmc = FindMacroCalls {
|
||||
names,
|
||||
result: Vec::new(),
|
||||
};
|
||||
fmc.visit_expr(&body.value);
|
||||
fmc.result
|
||||
}
|
||||
|
||||
/// Converts a span to a code snippet if available, otherwise use default.
|
||||
///
|
||||
/// This is useful if you want to provide suggestions for your lint or more generally, if you want
|
||||
|
|
@ -726,8 +788,7 @@ pub fn indent_of<T: LintContext>(cx: &T, span: Span) -> Option<usize> {
|
|||
/// fn into3(self) -> () {}
|
||||
/// ^
|
||||
/// ```
|
||||
#[allow(clippy::needless_pass_by_value)]
|
||||
pub fn position_before_rarrow(s: String) -> Option<usize> {
|
||||
pub fn position_before_rarrow(s: &str) -> Option<usize> {
|
||||
s.rfind("->").map(|rpos| {
|
||||
let mut rpos = rpos;
|
||||
let chars: Vec<char> = s.chars().collect();
|
||||
|
|
@ -1060,7 +1121,7 @@ pub fn is_refutable(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool {
|
|||
/// Checks for the `#[automatically_derived]` attribute all `#[derive]`d
|
||||
/// implementations have.
|
||||
pub fn is_automatically_derived(attrs: &[ast::Attribute]) -> bool {
|
||||
attrs.iter().any(|attr| attr.has_name(rustc_sym::automatically_derived))
|
||||
attrs.iter().any(|attr| attr.has_name(sym::automatically_derived))
|
||||
}
|
||||
|
||||
/// Remove blocks around an expression.
|
||||
|
|
@ -1344,7 +1405,7 @@ pub fn if_sequence<'tcx>(
|
|||
let mut conds = SmallVec::new();
|
||||
let mut blocks: SmallVec<[&Block<'_>; 1]> = SmallVec::new();
|
||||
|
||||
while let Some((ref cond, ref then_expr, ref else_expr)) = higher::if_block(&expr) {
|
||||
while let ExprKind::If(ref cond, ref then_expr, ref else_expr) = expr.kind {
|
||||
conds.push(&**cond);
|
||||
if let ExprKind::Block(ref block, _) = then_expr.kind {
|
||||
blocks.push(block);
|
||||
|
|
@ -1373,12 +1434,13 @@ pub fn parent_node_is_if_expr(expr: &Expr<'_>, cx: &LateContext<'_>) -> bool {
|
|||
let map = cx.tcx.hir();
|
||||
let parent_id = map.get_parent_node(expr.hir_id);
|
||||
let parent_node = map.get(parent_id);
|
||||
|
||||
match parent_node {
|
||||
Node::Expr(e) => higher::if_block(&e).is_some(),
|
||||
Node::Arm(e) => higher::if_block(&e.body).is_some(),
|
||||
_ => false,
|
||||
}
|
||||
matches!(
|
||||
parent_node,
|
||||
Node::Expr(Expr {
|
||||
kind: ExprKind::If(_, _, _),
|
||||
..
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
// Finds the attribute with the given name, if any
|
||||
|
|
@ -1418,8 +1480,8 @@ pub fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
|
|||
false
|
||||
},
|
||||
ty::Dynamic(binder, _) => {
|
||||
for predicate in binder.skip_binder().iter() {
|
||||
if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate {
|
||||
for predicate in binder.iter() {
|
||||
if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate.skip_binder() {
|
||||
if must_use_attr(&cx.tcx.get_attrs(trait_ref.def_id)).is_some() {
|
||||
return true;
|
||||
}
|
||||
|
|
@ -1453,7 +1515,7 @@ pub fn is_must_use_func_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
|||
pub fn is_no_std_crate(krate: &Crate<'_>) -> bool {
|
||||
krate.item.attrs.iter().any(|attr| {
|
||||
if let ast::AttrKind::Normal(ref attr, _) = attr.kind {
|
||||
attr.path == symbol::sym::no_std
|
||||
attr.path == sym::no_std
|
||||
} else {
|
||||
false
|
||||
}
|
||||
|
|
@ -1469,7 +1531,7 @@ pub fn is_no_std_crate(krate: &Crate<'_>) -> bool {
|
|||
/// ```
|
||||
pub fn is_trait_impl_item(cx: &LateContext<'_>, hir_id: HirId) -> bool {
|
||||
if let Some(Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(hir_id)) {
|
||||
matches!(item.kind, ItemKind::Impl{ of_trait: Some(_), .. })
|
||||
matches!(item.kind, ItemKind::Impl(hir::Impl { of_trait: Some(_), .. }))
|
||||
} else {
|
||||
false
|
||||
}
|
||||
|
|
@ -1607,6 +1669,44 @@ where
|
|||
match_expr_list
|
||||
}
|
||||
|
||||
/// Peels off all references on the pattern. Returns the underlying pattern and the number of
|
||||
/// references removed.
|
||||
pub fn peel_hir_pat_refs(pat: &'a Pat<'a>) -> (&'a Pat<'a>, usize) {
|
||||
fn peel(pat: &'a Pat<'a>, count: usize) -> (&'a Pat<'a>, usize) {
|
||||
if let PatKind::Ref(pat, _) = pat.kind {
|
||||
peel(pat, count + 1)
|
||||
} else {
|
||||
(pat, count)
|
||||
}
|
||||
}
|
||||
peel(pat, 0)
|
||||
}
|
||||
|
||||
/// Peels off up to the given number of references on the expression. Returns the underlying
|
||||
/// expression and the number of references removed.
|
||||
pub fn peel_n_hir_expr_refs(expr: &'a Expr<'a>, count: usize) -> (&'a Expr<'a>, usize) {
|
||||
fn f(expr: &'a Expr<'a>, count: usize, target: usize) -> (&'a Expr<'a>, usize) {
|
||||
match expr.kind {
|
||||
ExprKind::AddrOf(_, _, expr) if count != target => f(expr, count + 1, target),
|
||||
_ => (expr, count),
|
||||
}
|
||||
}
|
||||
f(expr, 0, count)
|
||||
}
|
||||
|
||||
/// Peels off all references on the type. Returns the underlying type and the number of references
|
||||
/// removed.
|
||||
pub fn peel_mid_ty_refs(ty: Ty<'_>) -> (Ty<'_>, usize) {
|
||||
fn peel(ty: Ty<'_>, count: usize) -> (Ty<'_>, usize) {
|
||||
if let ty::Ref(_, ty, _) = ty.kind() {
|
||||
peel(ty, count + 1)
|
||||
} else {
|
||||
(ty, count)
|
||||
}
|
||||
}
|
||||
peel(ty, 0)
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! unwrap_cargo_metadata {
|
||||
($cx: ident, $lint: ident, $deps: expr) => {{
|
||||
|
|
@ -1625,6 +1725,18 @@ macro_rules! unwrap_cargo_metadata {
|
|||
}};
|
||||
}
|
||||
|
||||
pub fn is_hir_ty_cfg_dependant(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool {
|
||||
if_chain! {
|
||||
if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind;
|
||||
if let Res::Def(_, def_id) = path.res;
|
||||
then {
|
||||
cx.tcx.has_attr(def_id, sym::cfg) || cx.tcx.has_attr(def_id, sym::cfg_attr)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::{reindent_multiline, without_block_comments};
|
||||
|
|
|
|||
|
|
@ -20,6 +20,8 @@ pub const CLONE_TRAIT: [&str; 3] = ["core", "clone", "Clone"];
|
|||
pub const CLONE_TRAIT_METHOD: [&str; 4] = ["core", "clone", "Clone", "clone"];
|
||||
pub const CMP_MAX: [&str; 3] = ["core", "cmp", "max"];
|
||||
pub const CMP_MIN: [&str; 3] = ["core", "cmp", "min"];
|
||||
pub const COPY: [&str; 3] = ["core", "intrinsics", "copy_nonoverlapping"];
|
||||
pub const COPY_NONOVERLAPPING: [&str; 3] = ["core", "intrinsics", "copy"];
|
||||
pub const COW: [&str; 3] = ["alloc", "borrow", "Cow"];
|
||||
pub const CSTRING_AS_C_STR: [&str; 5] = ["std", "ffi", "c_str", "CString", "as_c_str"];
|
||||
pub const DEFAULT_TRAIT: [&str; 3] = ["core", "default", "Default"];
|
||||
|
|
@ -31,6 +33,7 @@ pub const DISPLAY_TRAIT: [&str; 3] = ["core", "fmt", "Display"];
|
|||
pub const DOUBLE_ENDED_ITERATOR: [&str; 4] = ["core", "iter", "traits", "DoubleEndedIterator"];
|
||||
pub const DROP: [&str; 3] = ["core", "mem", "drop"];
|
||||
pub const DURATION: [&str; 3] = ["core", "time", "Duration"];
|
||||
#[cfg(feature = "internal-lints")]
|
||||
pub const EARLY_CONTEXT: [&str; 2] = ["rustc_lint", "EarlyContext"];
|
||||
pub const EXIT: [&str; 3] = ["std", "process", "exit"];
|
||||
pub const F32_EPSILON: [&str; 4] = ["core", "f32", "<impl f32>", "EPSILON"];
|
||||
|
|
@ -51,6 +54,10 @@ pub const HASH: [&str; 3] = ["core", "hash", "Hash"];
|
|||
pub const HASHMAP: [&str; 5] = ["std", "collections", "hash", "map", "HashMap"];
|
||||
pub const HASHMAP_ENTRY: [&str; 5] = ["std", "collections", "hash", "map", "Entry"];
|
||||
pub const HASHSET: [&str; 5] = ["std", "collections", "hash", "set", "HashSet"];
|
||||
#[cfg(feature = "internal-lints")]
|
||||
pub const IDENT: [&str; 3] = ["rustc_span", "symbol", "Ident"];
|
||||
#[cfg(feature = "internal-lints")]
|
||||
pub const IDENT_AS_STR: [&str; 4] = ["rustc_span", "symbol", "Ident", "as_str"];
|
||||
pub const INDEX: [&str; 3] = ["core", "ops", "Index"];
|
||||
pub const INDEX_MUT: [&str; 3] = ["core", "ops", "IndexMut"];
|
||||
pub const INSERT_STR: [&str; 4] = ["alloc", "string", "String", "insert_str"];
|
||||
|
|
@ -58,9 +65,15 @@ pub const INTO: [&str; 3] = ["core", "convert", "Into"];
|
|||
pub const INTO_ITERATOR: [&str; 5] = ["core", "iter", "traits", "collect", "IntoIterator"];
|
||||
pub const IO_READ: [&str; 3] = ["std", "io", "Read"];
|
||||
pub const IO_WRITE: [&str; 3] = ["std", "io", "Write"];
|
||||
pub const IPADDR_V4: [&str; 4] = ["std", "net", "IpAddr", "V4"];
|
||||
pub const IPADDR_V6: [&str; 4] = ["std", "net", "IpAddr", "V6"];
|
||||
pub const ITERATOR: [&str; 5] = ["core", "iter", "traits", "iterator", "Iterator"];
|
||||
#[cfg(feature = "internal-lints")]
|
||||
pub const KW_MODULE: [&str; 3] = ["rustc_span", "symbol", "kw"];
|
||||
#[cfg(feature = "internal-lints")]
|
||||
pub const LATE_CONTEXT: [&str; 2] = ["rustc_lint", "LateContext"];
|
||||
pub const LINKED_LIST: [&str; 4] = ["alloc", "collections", "linked_list", "LinkedList"];
|
||||
#[cfg(feature = "internal-lints")]
|
||||
pub const LINT: [&str; 2] = ["rustc_lint_defs", "Lint"];
|
||||
pub const MEM_DISCRIMINANT: [&str; 3] = ["core", "mem", "discriminant"];
|
||||
pub const MEM_FORGET: [&str; 3] = ["core", "mem", "forget"];
|
||||
|
|
@ -68,6 +81,8 @@ pub const MEM_MANUALLY_DROP: [&str; 4] = ["core", "mem", "manually_drop", "Manua
|
|||
pub const MEM_MAYBEUNINIT: [&str; 4] = ["core", "mem", "maybe_uninit", "MaybeUninit"];
|
||||
pub const MEM_MAYBEUNINIT_UNINIT: [&str; 5] = ["core", "mem", "maybe_uninit", "MaybeUninit", "uninit"];
|
||||
pub const MEM_REPLACE: [&str; 3] = ["core", "mem", "replace"];
|
||||
pub const MEM_SIZE_OF: [&str; 3] = ["core", "mem", "size_of"];
|
||||
pub const MEM_SIZE_OF_VAL: [&str; 3] = ["core", "mem", "size_of_val"];
|
||||
pub const MUTEX_GUARD: [&str; 4] = ["std", "sync", "mutex", "MutexGuard"];
|
||||
pub const OPEN_OPTIONS: [&str; 3] = ["std", "fs", "OpenOptions"];
|
||||
pub const OPS_MODULE: [&str; 2] = ["core", "ops"];
|
||||
|
|
@ -95,6 +110,9 @@ pub const POLL_READY: [&str; 5] = ["core", "task", "poll", "Poll", "Ready"];
|
|||
pub const PTR_EQ: [&str; 3] = ["core", "ptr", "eq"];
|
||||
pub const PTR_NULL: [&str; 3] = ["core", "ptr", "null"];
|
||||
pub const PTR_NULL_MUT: [&str; 3] = ["core", "ptr", "null_mut"];
|
||||
pub const PTR_SLICE_FROM_RAW_PARTS: [&str; 3] = ["core", "ptr", "slice_from_raw_parts"];
|
||||
pub const PTR_SLICE_FROM_RAW_PARTS_MUT: [&str; 3] = ["core", "ptr", "slice_from_raw_parts_mut"];
|
||||
pub const PTR_SWAP_NONOVERLAPPING: [&str; 3] = ["core", "ptr", "swap_nonoverlapping"];
|
||||
pub const PUSH_STR: [&str; 4] = ["alloc", "string", "String", "push_str"];
|
||||
pub const RANGE_ARGUMENT_TRAIT: [&str; 3] = ["core", "ops", "RangeBounds"];
|
||||
pub const RC: [&str; 3] = ["alloc", "rc", "Rc"];
|
||||
|
|
@ -116,6 +134,8 @@ pub const RWLOCK_READ_GUARD: [&str; 4] = ["std", "sync", "rwlock", "RwLockReadGu
|
|||
pub const RWLOCK_WRITE_GUARD: [&str; 4] = ["std", "sync", "rwlock", "RwLockWriteGuard"];
|
||||
pub const SERDE_DESERIALIZE: [&str; 3] = ["serde", "de", "Deserialize"];
|
||||
pub const SERDE_DE_VISITOR: [&str; 3] = ["serde", "de", "Visitor"];
|
||||
pub const SLICE_FROM_RAW_PARTS: [&str; 4] = ["core", "slice", "raw", "from_raw_parts"];
|
||||
pub const SLICE_FROM_RAW_PARTS_MUT: [&str; 4] = ["core", "slice", "raw", "from_raw_parts_mut"];
|
||||
pub const SLICE_INTO_VEC: [&str; 4] = ["alloc", "slice", "<impl [T]>", "into_vec"];
|
||||
pub const SLICE_ITER: [&str; 4] = ["core", "slice", "iter", "Iter"];
|
||||
pub const STDERR: [&str; 4] = ["std", "io", "stdio", "stderr"];
|
||||
|
|
@ -131,6 +151,17 @@ pub const STR_ENDS_WITH: [&str; 4] = ["core", "str", "<impl str>", "ends_with"];
|
|||
pub const STR_FROM_UTF8: [&str; 4] = ["core", "str", "converts", "from_utf8"];
|
||||
pub const STR_LEN: [&str; 4] = ["core", "str", "<impl str>", "len"];
|
||||
pub const STR_STARTS_WITH: [&str; 4] = ["core", "str", "<impl str>", "starts_with"];
|
||||
#[cfg(feature = "internal-lints")]
|
||||
pub const SYMBOL: [&str; 3] = ["rustc_span", "symbol", "Symbol"];
|
||||
#[cfg(feature = "internal-lints")]
|
||||
pub const SYMBOL_AS_STR: [&str; 4] = ["rustc_span", "symbol", "Symbol", "as_str"];
|
||||
#[cfg(feature = "internal-lints")]
|
||||
pub const SYMBOL_INTERN: [&str; 4] = ["rustc_span", "symbol", "Symbol", "intern"];
|
||||
#[cfg(feature = "internal-lints")]
|
||||
pub const SYMBOL_TO_IDENT_STRING: [&str; 4] = ["rustc_span", "symbol", "Symbol", "to_ident_string"];
|
||||
#[cfg(feature = "internal-lints")]
|
||||
pub const SYM_MODULE: [&str; 3] = ["rustc_span", "symbol", "sym"];
|
||||
#[cfg(feature = "internal-lints")]
|
||||
pub const SYNTAX_CONTEXT: [&str; 3] = ["rustc_span", "hygiene", "SyntaxContext"];
|
||||
pub const TO_OWNED: [&str; 3] = ["alloc", "borrow", "ToOwned"];
|
||||
pub const TO_OWNED_METHOD: [&str; 4] = ["alloc", "borrow", "ToOwned", "to_owned"];
|
||||
|
|
@ -148,3 +179,4 @@ pub const VEC_NEW: [&str; 4] = ["alloc", "vec", "Vec", "new"];
|
|||
pub const VEC_RESIZE: [&str; 4] = ["alloc", "vec", "Vec", "resize"];
|
||||
pub const WEAK_ARC: [&str; 3] = ["alloc", "sync", "Weak"];
|
||||
pub const WEAK_RC: [&str; 3] = ["alloc", "rc", "Weak"];
|
||||
pub const WRITE_BYTES: [&str; 3] = ["core", "intrinsics", "write_bytes"];
|
||||
|
|
|
|||
|
|
@ -72,7 +72,6 @@ impl<'a, 'tcx> Visitor<'tcx> for PtrCloneVisitor<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
walk_expr(self, expr);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -103,6 +103,7 @@ impl<'a> Sugg<'a> {
|
|||
match expr.kind {
|
||||
hir::ExprKind::AddrOf(..)
|
||||
| hir::ExprKind::Box(..)
|
||||
| hir::ExprKind::If(..)
|
||||
| hir::ExprKind::Closure(..)
|
||||
| hir::ExprKind::Unary(..)
|
||||
| hir::ExprKind::Match(..) => Sugg::MaybeParen(snippet),
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
#[macro_export]
|
||||
/// Convenience wrapper around rustc's `Symbol::intern`
|
||||
macro_rules! sym {
|
||||
($tt:tt) => {
|
||||
rustc_span::symbol::Symbol::intern(stringify!($tt))
|
||||
|
|
@ -116,20 +116,27 @@ pub struct ParamBindingIdCollector {
|
|||
}
|
||||
impl<'tcx> ParamBindingIdCollector {
|
||||
fn collect_binding_hir_ids(body: &'tcx hir::Body<'tcx>) -> Vec<hir::HirId> {
|
||||
let mut finder = ParamBindingIdCollector {
|
||||
binding_hir_ids: Vec::new(),
|
||||
};
|
||||
finder.visit_body(body);
|
||||
finder.binding_hir_ids
|
||||
let mut hir_ids: Vec<hir::HirId> = Vec::new();
|
||||
for param in body.params.iter() {
|
||||
let mut finder = ParamBindingIdCollector {
|
||||
binding_hir_ids: Vec::new(),
|
||||
};
|
||||
finder.visit_param(param);
|
||||
for hir_id in &finder.binding_hir_ids {
|
||||
hir_ids.push(*hir_id);
|
||||
}
|
||||
}
|
||||
hir_ids
|
||||
}
|
||||
}
|
||||
impl<'tcx> intravisit::Visitor<'tcx> for ParamBindingIdCollector {
|
||||
type Map = Map<'tcx>;
|
||||
|
||||
fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) {
|
||||
if let hir::PatKind::Binding(_, hir_id, ..) = param.pat.kind {
|
||||
fn visit_pat(&mut self, pat: &'tcx hir::Pat<'tcx>) {
|
||||
if let hir::PatKind::Binding(_, hir_id, ..) = pat.kind {
|
||||
self.binding_hir_ids.push(hir_id);
|
||||
}
|
||||
intravisit::walk_pat(self, pat);
|
||||
}
|
||||
|
||||
fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<Self::Map> {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
use rustc_hir as hir;
|
||||
use rustc_hir::intravisit::{self, Visitor};
|
||||
use rustc_hir::def::Res;
|
||||
use rustc_hir::intravisit::{self, walk_expr, NestedVisitorMap, Visitor};
|
||||
use rustc_hir::{Arm, Expr, ExprKind, HirId, QPath, Stmt};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::hir::map::Map;
|
||||
|
||||
|
|
@ -99,6 +101,13 @@ where
|
|||
}
|
||||
} else {
|
||||
match expr.kind {
|
||||
hir::ExprKind::If(cond, then, else_opt) => {
|
||||
self.inside_stmt(true).visit_expr(cond);
|
||||
self.visit_expr(then);
|
||||
if let Some(el) = else_opt {
|
||||
self.visit_expr(el);
|
||||
}
|
||||
},
|
||||
hir::ExprKind::Match(cond, arms, _) => {
|
||||
self.inside_stmt(true).visit_expr(cond);
|
||||
for arm in arms {
|
||||
|
|
@ -123,3 +132,54 @@ where
|
|||
!ret_finder.failed
|
||||
}
|
||||
}
|
||||
|
||||
pub struct LocalUsedVisitor {
|
||||
pub local_hir_id: HirId,
|
||||
pub used: bool,
|
||||
}
|
||||
|
||||
impl LocalUsedVisitor {
|
||||
pub fn new(local_hir_id: HirId) -> Self {
|
||||
Self {
|
||||
local_hir_id,
|
||||
used: false,
|
||||
}
|
||||
}
|
||||
|
||||
fn check<T>(&mut self, t: T, visit: fn(&mut Self, T)) -> bool {
|
||||
visit(self, t);
|
||||
std::mem::replace(&mut self.used, false)
|
||||
}
|
||||
|
||||
pub fn check_arm(&mut self, arm: &Arm<'_>) -> bool {
|
||||
self.check(arm, Self::visit_arm)
|
||||
}
|
||||
|
||||
pub fn check_expr(&mut self, expr: &Expr<'_>) -> bool {
|
||||
self.check(expr, Self::visit_expr)
|
||||
}
|
||||
|
||||
pub fn check_stmt(&mut self, stmt: &Stmt<'_>) -> bool {
|
||||
self.check(stmt, Self::visit_stmt)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'v> Visitor<'v> for LocalUsedVisitor {
|
||||
type Map = Map<'v>;
|
||||
|
||||
fn visit_expr(&mut self, expr: &'v Expr<'v>) {
|
||||
if let ExprKind::Path(QPath::Resolved(None, path)) = expr.kind {
|
||||
if let Res::Local(id) = path.res {
|
||||
if id == self.local_hir_id {
|
||||
self.used = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
walk_expr(self, expr);
|
||||
}
|
||||
|
||||
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
|
||||
NestedVisitorMap::None
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue