Merge commit '7c21f91b15' into clippyup
This commit is contained in:
parent
82f469f81b
commit
7cd86aa1be
293 changed files with 6318 additions and 2753 deletions
|
|
@ -5,7 +5,7 @@ use if_chain::if_chain;
|
|||
use rustc_ast::ast::{self, LitFloatType, LitKind};
|
||||
use rustc_data_structures::sync::Lrc;
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_hir::{BinOp, BinOpKind, Block, Expr, ExprKind, HirId, QPath, UnOp};
|
||||
use rustc_hir::{BinOp, BinOpKind, Block, Expr, ExprKind, HirId, Item, ItemKind, Node, QPath, UnOp};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::mir::interpret::Scalar;
|
||||
use rustc_middle::ty::subst::{Subst, SubstsRef};
|
||||
|
|
@ -400,6 +400,22 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
|
|||
let res = self.typeck_results.qpath_res(qpath, id);
|
||||
match res {
|
||||
Res::Def(DefKind::Const | DefKind::AssocConst, def_id) => {
|
||||
// Check if this constant is based on `cfg!(..)`,
|
||||
// which is NOT constant for our purposes.
|
||||
if let Some(node) = self.lcx.tcx.hir().get_if_local(def_id) &&
|
||||
let Node::Item(&Item {
|
||||
kind: ItemKind::Const(_, body_id),
|
||||
..
|
||||
}) = node &&
|
||||
let Node::Expr(&Expr {
|
||||
kind: ExprKind::Lit(_),
|
||||
span,
|
||||
..
|
||||
}) = self.lcx.tcx.hir().get(body_id.hir_id) &&
|
||||
is_direct_expn_of(span, "cfg").is_some() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let substs = self.typeck_results.node_substs(id);
|
||||
let substs = if self.substs.is_empty() {
|
||||
substs
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use crate::consts::{constant_context, constant_simple};
|
||||
use crate::consts::constant_simple;
|
||||
use crate::source::snippet_opt;
|
||||
use rustc_ast::ast::InlineAsmTemplatePiece;
|
||||
use rustc_data_structures::fx::FxHasher;
|
||||
|
|
@ -16,15 +16,14 @@ use rustc_span::Symbol;
|
|||
use std::hash::{Hash, Hasher};
|
||||
|
||||
/// Type used to check whether two ast are the same. This is different from the
|
||||
/// operator
|
||||
/// `==` on ast types as this operator would compare true equality with ID and
|
||||
/// span.
|
||||
/// operator `==` on ast types as this operator would compare true equality with
|
||||
/// ID and span.
|
||||
///
|
||||
/// Note that some expressions kinds are not considered but could be added.
|
||||
pub struct SpanlessEq<'a, 'tcx> {
|
||||
/// Context used to evaluate constant expressions.
|
||||
cx: &'a LateContext<'tcx>,
|
||||
maybe_typeck_results: Option<&'tcx TypeckResults<'tcx>>,
|
||||
maybe_typeck_results: Option<(&'tcx TypeckResults<'tcx>, &'tcx TypeckResults<'tcx>)>,
|
||||
allow_side_effects: bool,
|
||||
expr_fallback: Option<Box<dyn FnMut(&Expr<'_>, &Expr<'_>) -> bool + 'a>>,
|
||||
}
|
||||
|
|
@ -33,7 +32,7 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> {
|
|||
pub fn new(cx: &'a LateContext<'tcx>) -> Self {
|
||||
Self {
|
||||
cx,
|
||||
maybe_typeck_results: cx.maybe_typeck_results(),
|
||||
maybe_typeck_results: cx.maybe_typeck_results().map(|x| (x, x)),
|
||||
allow_side_effects: true,
|
||||
expr_fallback: None,
|
||||
}
|
||||
|
|
@ -102,9 +101,9 @@ impl HirEqInterExpr<'_, '_, '_> {
|
|||
(&StmtKind::Local(l), &StmtKind::Local(r)) => {
|
||||
// This additional check ensures that the type of the locals are equivalent even if the init
|
||||
// expression or type have some inferred parts.
|
||||
if let Some(typeck) = self.inner.maybe_typeck_results {
|
||||
let l_ty = typeck.pat_ty(l.pat);
|
||||
let r_ty = typeck.pat_ty(r.pat);
|
||||
if let Some((typeck_lhs, typeck_rhs)) = self.inner.maybe_typeck_results {
|
||||
let l_ty = typeck_lhs.pat_ty(l.pat);
|
||||
let r_ty = typeck_rhs.pat_ty(r.pat);
|
||||
if l_ty != r_ty {
|
||||
return false;
|
||||
}
|
||||
|
|
@ -182,9 +181,17 @@ impl HirEqInterExpr<'_, '_, '_> {
|
|||
}
|
||||
|
||||
pub fn eq_body(&mut self, left: BodyId, right: BodyId) -> bool {
|
||||
let cx = self.inner.cx;
|
||||
let eval_const = |body| constant_context(cx, cx.tcx.typeck_body(body)).expr(&cx.tcx.hir().body(body).value);
|
||||
eval_const(left) == eval_const(right)
|
||||
// swap out TypeckResults when hashing a body
|
||||
let old_maybe_typeck_results = self.inner.maybe_typeck_results.replace((
|
||||
self.inner.cx.tcx.typeck_body(left),
|
||||
self.inner.cx.tcx.typeck_body(right),
|
||||
));
|
||||
let res = self.eq_expr(
|
||||
&self.inner.cx.tcx.hir().body(left).value,
|
||||
&self.inner.cx.tcx.hir().body(right).value,
|
||||
);
|
||||
self.inner.maybe_typeck_results = old_maybe_typeck_results;
|
||||
res
|
||||
}
|
||||
|
||||
#[allow(clippy::similar_names)]
|
||||
|
|
@ -193,10 +200,10 @@ impl HirEqInterExpr<'_, '_, '_> {
|
|||
return false;
|
||||
}
|
||||
|
||||
if let Some(typeck_results) = self.inner.maybe_typeck_results {
|
||||
if let Some((typeck_lhs, typeck_rhs)) = self.inner.maybe_typeck_results {
|
||||
if let (Some(l), Some(r)) = (
|
||||
constant_simple(self.inner.cx, typeck_results, left),
|
||||
constant_simple(self.inner.cx, typeck_results, right),
|
||||
constant_simple(self.inner.cx, typeck_lhs, left),
|
||||
constant_simple(self.inner.cx, typeck_rhs, right),
|
||||
) {
|
||||
if l == r {
|
||||
return true;
|
||||
|
|
@ -674,8 +681,9 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
|
|||
self.hash_expr(out_expr);
|
||||
}
|
||||
},
|
||||
InlineAsmOperand::Const { anon_const } => self.hash_body(anon_const.body),
|
||||
InlineAsmOperand::SymFn { anon_const } => self.hash_body(anon_const.body),
|
||||
InlineAsmOperand::Const { anon_const } | InlineAsmOperand::SymFn { anon_const } => {
|
||||
self.hash_body(anon_const.body);
|
||||
},
|
||||
InlineAsmOperand::SymStatic { path, def_id: _ } => self.hash_qpath(path),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -367,7 +367,7 @@ impl<'tcx> FormatArgsExpn<'tcx> {
|
|||
expr_visitor_no_bodies(|e| {
|
||||
// if we're still inside of the macro definition...
|
||||
if e.span.ctxt() == expr.span.ctxt() {
|
||||
// ArgumnetV1::new_<format_trait>(<value>)
|
||||
// ArgumentV1::new_<format_trait>(<value>)
|
||||
if_chain! {
|
||||
if let ExprKind::Call(callee, [val]) = e.kind;
|
||||
if let ExprKind::Path(QPath::TypeRelative(ty, seg)) = callee.kind;
|
||||
|
|
|
|||
|
|
@ -32,4 +32,5 @@ msrv_aliases! {
|
|||
1,28,0 { FROM_BOOL }
|
||||
1,17,0 { FIELD_INIT_SHORTHAND, STATIC_IN_CONST, EXPECT_ERR }
|
||||
1,16,0 { STR_REPEAT }
|
||||
1,24,0 { IS_ASCII_DIGIT }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ impl<'a> NumericLiteral<'a> {
|
|||
.trim_start()
|
||||
.chars()
|
||||
.next()
|
||||
.map_or(false, |c| c.is_digit(10))
|
||||
.map_or(false, |c| c.is_ascii_digit())
|
||||
{
|
||||
let (unsuffixed, suffix) = split_suffix(src, lit_kind);
|
||||
let float = matches!(lit_kind, LitKind::Float(..));
|
||||
|
|
|
|||
|
|
@ -61,6 +61,7 @@ pub const IO_READ: [&str; 3] = ["std", "io", "Read"];
|
|||
pub const IO_WRITE: [&str; 3] = ["std", "io", "Write"];
|
||||
pub const IPADDR_V4: [&str; 5] = ["std", "net", "ip", "IpAddr", "V4"];
|
||||
pub const IPADDR_V6: [&str; 5] = ["std", "net", "ip", "IpAddr", "V6"];
|
||||
pub const ITER_COUNT: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "count"];
|
||||
pub const ITER_REPEAT: [&str; 5] = ["core", "iter", "sources", "repeat", "repeat"];
|
||||
#[allow(clippy::invalid_paths)] // internal lints do not know about all external crates
|
||||
pub const ITERTOOLS_NEXT_TUPLE: [&str; 3] = ["itertools", "Itertools", "next_tuple"];
|
||||
|
|
@ -148,6 +149,8 @@ pub const CONVERT_IDENTITY: [&str; 3] = ["core", "convert", "identity"];
|
|||
pub const STD_FS_CREATE_DIR: [&str; 3] = ["std", "fs", "create_dir"];
|
||||
pub const STRING_AS_MUT_STR: [&str; 4] = ["alloc", "string", "String", "as_mut_str"];
|
||||
pub const STRING_AS_STR: [&str; 4] = ["alloc", "string", "String", "as_str"];
|
||||
pub const STRING_NEW: [&str; 4] = ["alloc", "string", "String", "new"];
|
||||
pub const STR_BYTES: [&str; 4] = ["core", "str", "<impl str>", "bytes"];
|
||||
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"];
|
||||
|
|
|
|||
|
|
@ -211,8 +211,9 @@ fn check_statement<'tcx>(
|
|||
|
||||
StatementKind::FakeRead(box (_, place)) => check_place(tcx, *place, span, body),
|
||||
// just an assignment
|
||||
StatementKind::SetDiscriminant { place, .. } | StatementKind::Deinit(place) =>
|
||||
check_place(tcx, **place, span, body),
|
||||
StatementKind::SetDiscriminant { place, .. } | StatementKind::Deinit(place) => {
|
||||
check_place(tcx, **place, span, body)
|
||||
},
|
||||
|
||||
StatementKind::CopyNonOverlapping(box rustc_middle::mir::CopyNonOverlapping { dst, src, count }) => {
|
||||
check_operand(tcx, dst, span, body)?;
|
||||
|
|
|
|||
|
|
@ -7,9 +7,28 @@ use rustc_errors::Applicability;
|
|||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LintContext};
|
||||
use rustc_span::hygiene;
|
||||
use rustc_span::source_map::SourceMap;
|
||||
use rustc_span::{BytePos, Pos, Span, SyntaxContext};
|
||||
use std::borrow::Cow;
|
||||
|
||||
/// Checks if the span starts with the given text. This will return false if the span crosses
|
||||
/// multiple files or if source is not available.
|
||||
///
|
||||
/// This is used to check for proc macros giving unhelpful spans to things.
|
||||
pub fn span_starts_with<T: LintContext>(cx: &T, span: Span, text: &str) -> bool {
|
||||
fn helper(sm: &SourceMap, span: Span, text: &str) -> bool {
|
||||
let pos = sm.lookup_byte_offset(span.lo());
|
||||
let Some(ref src) = pos.sf.src else {
|
||||
return false;
|
||||
};
|
||||
let end = span.hi() - pos.sf.start_pos;
|
||||
src.get(pos.pos.0 as usize..end.0 as usize)
|
||||
// Expression spans can include wrapping parenthesis. Remove them first.
|
||||
.map_or(false, |s| s.trim_start_matches('(').starts_with(text))
|
||||
}
|
||||
helper(cx.sess().source_map(), span, text)
|
||||
}
|
||||
|
||||
/// Like `snippet_block`, but add braces if the expr is not an `ExprKind::Block`.
|
||||
/// Also takes an `Option<String>` which can be put inside the braces.
|
||||
pub fn expr_block<'a, T: LintContext>(
|
||||
|
|
@ -89,7 +108,7 @@ pub fn is_present_in_source<T: LintContext>(cx: &T, span: Span) -> bool {
|
|||
true
|
||||
}
|
||||
|
||||
/// Returns the positon just before rarrow
|
||||
/// Returns the position just before rarrow
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// fn into(self) -> () {}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ use rustc_span::source_map::{BytePos, CharPos, Pos, Span, SyntaxContext};
|
|||
use rustc_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId};
|
||||
use std::borrow::Cow;
|
||||
use std::convert::TryInto;
|
||||
use std::fmt::Display;
|
||||
use std::fmt::{Display, Write as _};
|
||||
use std::iter;
|
||||
use std::ops::{Add, Neg, Not, Sub};
|
||||
|
||||
|
|
@ -902,7 +902,7 @@ impl<'tcx> Delegate<'tcx> for DerefDelegate<'_, 'tcx> {
|
|||
if cmt.place.projections.is_empty() {
|
||||
// handle item without any projection, that needs an explicit borrowing
|
||||
// i.e.: suggest `&x` instead of `x`
|
||||
self.suggestion_start.push_str(&format!("{}&{}", start_snip, ident_str));
|
||||
let _ = write!(self.suggestion_start, "{}&{}", start_snip, ident_str);
|
||||
} else {
|
||||
// cases where a parent `Call` or `MethodCall` is using the item
|
||||
// i.e.: suggest `.contains(&x)` for `.find(|x| [1, 2, 3].contains(x)).is_none()`
|
||||
|
|
@ -917,8 +917,7 @@ impl<'tcx> Delegate<'tcx> for DerefDelegate<'_, 'tcx> {
|
|||
// given expression is the self argument and will be handled completely by the compiler
|
||||
// i.e.: `|x| x.is_something()`
|
||||
ExprKind::MethodCall(_, [self_expr, ..], _) if self_expr.hir_id == cmt.hir_id => {
|
||||
self.suggestion_start
|
||||
.push_str(&format!("{}{}", start_snip, ident_str_with_proj));
|
||||
let _ = write!(self.suggestion_start, "{}{}", start_snip, ident_str_with_proj);
|
||||
self.next_pos = span.hi();
|
||||
return;
|
||||
},
|
||||
|
|
@ -1026,8 +1025,7 @@ impl<'tcx> Delegate<'tcx> for DerefDelegate<'_, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
self.suggestion_start
|
||||
.push_str(&format!("{}{}", start_snip, replacement_str));
|
||||
let _ = write!(self.suggestion_start, "{}{}", start_snip, replacement_str);
|
||||
}
|
||||
self.next_pos = span.hi();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,11 +3,11 @@
|
|||
#![allow(clippy::module_name_repetitions)]
|
||||
|
||||
use rustc_ast::ast::Mutability;
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def::{CtorKind, DefKind, Res};
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::{Expr, TyKind, Unsafety};
|
||||
use rustc_hir::{Expr, LangItem, TyKind, Unsafety};
|
||||
use rustc_infer::infer::TyCtxtInferExt;
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::mir::interpret::{ConstValue, Scalar};
|
||||
|
|
@ -22,7 +22,7 @@ use rustc_trait_selection::infer::InferCtxtExt;
|
|||
use rustc_trait_selection::traits::query::normalize::AtExt;
|
||||
use std::iter;
|
||||
|
||||
use crate::{match_def_path, must_use_attr, path_res};
|
||||
use crate::{match_def_path, must_use_attr, path_res, paths};
|
||||
|
||||
// Checks if the given type implements copy.
|
||||
pub fn is_copy<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
|
||||
|
|
@ -83,6 +83,20 @@ pub fn get_associated_type<'tcx>(
|
|||
})
|
||||
}
|
||||
|
||||
/// Get the diagnostic name of a type, e.g. `sym::HashMap`. To check if a type
|
||||
/// implements a trait marked with a diagnostic item use [`implements_trait`].
|
||||
///
|
||||
/// For a further exploitation what diagnostic items are see [diagnostic items] in
|
||||
/// rustc-dev-guide.
|
||||
///
|
||||
/// [Diagnostic Items]: https://rustc-dev-guide.rust-lang.org/diagnostics/diagnostic-items.html
|
||||
pub fn get_type_diagnostic_name(cx: &LateContext<'_>, ty: Ty<'_>) -> Option<Symbol> {
|
||||
match ty.kind() {
|
||||
ty::Adt(adt, _) => cx.tcx.get_diagnostic_name(adt.did()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if ty has `iter` or `iter_mut` methods
|
||||
pub fn has_iter_method(cx: &LateContext<'_>, probably_ref_ty: Ty<'_>) -> Option<Symbol> {
|
||||
// FIXME: instead of this hard-coded list, we should check if `<adt>::iter`
|
||||
|
|
@ -319,6 +333,57 @@ pub fn match_type(cx: &LateContext<'_>, ty: Ty<'_>, path: &[&str]) -> bool {
|
|||
}
|
||||
}
|
||||
|
||||
/// Checks if the drop order for a type matters. Some std types implement drop solely to
|
||||
/// deallocate memory. For these types, and composites containing them, changing the drop order
|
||||
/// won't result in any observable side effects.
|
||||
pub fn needs_ordered_drop<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
|
||||
fn needs_ordered_drop_inner<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, seen: &mut FxHashSet<Ty<'tcx>>) -> bool {
|
||||
if !seen.insert(ty) {
|
||||
return false;
|
||||
}
|
||||
if !ty.has_significant_drop(cx.tcx, cx.param_env) {
|
||||
false
|
||||
}
|
||||
// Check for std types which implement drop, but only for memory allocation.
|
||||
else if is_type_lang_item(cx, ty, LangItem::OwnedBox)
|
||||
|| matches!(
|
||||
get_type_diagnostic_name(cx, ty),
|
||||
Some(sym::HashSet | sym::Rc | sym::Arc | sym::cstring_type)
|
||||
)
|
||||
|| match_type(cx, ty, &paths::WEAK_RC)
|
||||
|| match_type(cx, ty, &paths::WEAK_ARC)
|
||||
{
|
||||
// Check all of the generic arguments.
|
||||
if let ty::Adt(_, subs) = ty.kind() {
|
||||
subs.types().any(|ty| needs_ordered_drop_inner(cx, ty, seen))
|
||||
} else {
|
||||
true
|
||||
}
|
||||
} else if !cx
|
||||
.tcx
|
||||
.lang_items()
|
||||
.drop_trait()
|
||||
.map_or(false, |id| implements_trait(cx, ty, id, &[]))
|
||||
{
|
||||
// This type doesn't implement drop, so no side effects here.
|
||||
// Check if any component type has any.
|
||||
match ty.kind() {
|
||||
ty::Tuple(fields) => fields.iter().any(|ty| needs_ordered_drop_inner(cx, ty, seen)),
|
||||
ty::Array(ty, _) => needs_ordered_drop_inner(cx, *ty, seen),
|
||||
ty::Adt(adt, subs) => adt
|
||||
.all_fields()
|
||||
.map(|f| f.ty(cx.tcx, subs))
|
||||
.any(|ty| needs_ordered_drop_inner(cx, ty, seen)),
|
||||
_ => true,
|
||||
}
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
needs_ordered_drop_inner(cx, ty, &mut FxHashSet::default())
|
||||
}
|
||||
|
||||
/// 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) {
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ use crate::visitors::{expr_visitor, expr_visitor_no_bodies};
|
|||
use rustc_hir as hir;
|
||||
use rustc_hir::intravisit::{self, Visitor};
|
||||
use rustc_hir::HirIdSet;
|
||||
use rustc_hir::{Expr, ExprKind, HirId};
|
||||
use rustc_hir::{Expr, ExprKind, HirId, Node};
|
||||
use rustc_infer::infer::TyCtxtInferExt;
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::hir::nested_filter;
|
||||
|
|
@ -169,6 +169,32 @@ pub fn contains_return_break_continue_macro(expression: &Expr<'_>) -> bool {
|
|||
|
||||
pub fn local_used_after_expr(cx: &LateContext<'_>, local_id: HirId, after: &Expr<'_>) -> bool {
|
||||
let Some(block) = utils::get_enclosing_block(cx, local_id) else { return false };
|
||||
|
||||
// for _ in 1..3 {
|
||||
// local
|
||||
// }
|
||||
//
|
||||
// let closure = || local;
|
||||
// closure();
|
||||
// closure();
|
||||
let in_loop_or_closure = cx
|
||||
.tcx
|
||||
.hir()
|
||||
.parent_iter(after.hir_id)
|
||||
.take_while(|&(id, _)| id != block.hir_id)
|
||||
.any(|(_, node)| {
|
||||
matches!(
|
||||
node,
|
||||
Node::Expr(Expr {
|
||||
kind: ExprKind::Loop(..) | ExprKind::Closure(..),
|
||||
..
|
||||
})
|
||||
)
|
||||
});
|
||||
if in_loop_or_closure {
|
||||
return true;
|
||||
}
|
||||
|
||||
let mut used_after_expr = false;
|
||||
let mut past_expr = false;
|
||||
expr_visitor(cx, |expr| {
|
||||
|
|
@ -178,7 +204,10 @@ pub fn local_used_after_expr(cx: &LateContext<'_>, local_id: HirId, after: &Expr
|
|||
|
||||
if expr.hir_id == after.hir_id {
|
||||
past_expr = true;
|
||||
} else if past_expr && utils::path_to_local_id(expr, local_id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if past_expr && utils::path_to_local_id(expr, local_id) {
|
||||
used_after_expr = true;
|
||||
}
|
||||
!used_after_expr
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
use crate::path_to_local_id;
|
||||
use core::ops::ControlFlow;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_hir::intravisit::{self, walk_block, walk_expr, Visitor};
|
||||
use rustc_hir::{
|
||||
Arm, Block, BlockCheckMode, Body, BodyId, Expr, ExprKind, HirId, ItemId, ItemKind, Stmt, UnOp, Unsafety,
|
||||
Arm, Block, BlockCheckMode, Body, BodyId, Expr, ExprKind, HirId, ItemId, ItemKind, Stmt, UnOp, UnsafeSource,
|
||||
Unsafety,
|
||||
};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::hir::map::Map;
|
||||
|
|
@ -370,3 +372,67 @@ pub fn is_expr_unsafe<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> bool {
|
|||
v.visit_expr(e);
|
||||
v.is_unsafe
|
||||
}
|
||||
|
||||
/// Checks if the given expression contains an unsafe block
|
||||
pub fn contains_unsafe_block<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> bool {
|
||||
struct V<'cx, 'tcx> {
|
||||
cx: &'cx LateContext<'tcx>,
|
||||
found_unsafe: bool,
|
||||
}
|
||||
impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
|
||||
type NestedFilter = nested_filter::OnlyBodies;
|
||||
fn nested_visit_map(&mut self) -> Self::Map {
|
||||
self.cx.tcx.hir()
|
||||
}
|
||||
|
||||
fn visit_block(&mut self, b: &'tcx Block<'_>) {
|
||||
if self.found_unsafe {
|
||||
return;
|
||||
}
|
||||
if b.rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) {
|
||||
self.found_unsafe = true;
|
||||
return;
|
||||
}
|
||||
walk_block(self, b);
|
||||
}
|
||||
}
|
||||
let mut v = V {
|
||||
cx,
|
||||
found_unsafe: false,
|
||||
};
|
||||
v.visit_expr(e);
|
||||
v.found_unsafe
|
||||
}
|
||||
|
||||
/// Runs the given function for each sub-expression producing the final value consumed by the parent
|
||||
/// of the give expression.
|
||||
///
|
||||
/// e.g. for the following expression
|
||||
/// ```rust,ignore
|
||||
/// if foo {
|
||||
/// f(0)
|
||||
/// } else {
|
||||
/// 1 + 1
|
||||
/// }
|
||||
/// ```
|
||||
/// this will pass both `f(0)` and `1+1` to the given function.
|
||||
pub fn for_each_value_source<'tcx, B>(
|
||||
e: &'tcx Expr<'tcx>,
|
||||
f: &mut impl FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B>,
|
||||
) -> ControlFlow<B> {
|
||||
match e.kind {
|
||||
ExprKind::Block(Block { expr: Some(e), .. }, _) => for_each_value_source(e, f),
|
||||
ExprKind::Match(_, arms, _) => {
|
||||
for arm in arms {
|
||||
for_each_value_source(arm.body, f)?;
|
||||
}
|
||||
ControlFlow::Continue(())
|
||||
},
|
||||
ExprKind::If(_, if_expr, Some(else_expr)) => {
|
||||
for_each_value_source(if_expr, f)?;
|
||||
for_each_value_source(else_expr, f)
|
||||
},
|
||||
ExprKind::DropTemps(e) => for_each_value_source(e, f),
|
||||
_ => f(e),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue