Merge commit 'f712eb5cdc' into clippy-subtree-update

This commit is contained in:
Philipp Krones 2024-11-07 22:31:20 +01:00
parent 4847c40c8b
commit 6ced8c33c0
248 changed files with 5023 additions and 900 deletions

View file

@ -21,7 +21,7 @@ use rustc_hir::{
ImplItem, ImplItemKind, IsAuto, Item, ItemKind, Lit, LoopSource, MatchSource, MutTy, Node, Path, QPath, Safety,
TraitItem, TraitItemKind, Ty, TyKind, UnOp, UnsafeSource, Variant, VariantData, YieldSource,
};
use rustc_lint::{LateContext, LintContext, EarlyContext};
use rustc_lint::{EarlyContext, LateContext, LintContext};
use rustc_middle::ty::TyCtxt;
use rustc_session::Session;
use rustc_span::symbol::{Ident, kw};
@ -63,8 +63,8 @@ fn span_matches_pat(sess: &Session, span: Span, start_pat: Pat, end_pat: Pat) ->
Pat::Num => start_str.as_bytes().first().map_or(false, u8::is_ascii_digit),
} && match end_pat {
Pat::Str(text) => end_str.ends_with(text),
Pat::MultiStr(texts) => texts.iter().any(|s| start_str.ends_with(s)),
Pat::OwnedMultiStr(texts) => texts.iter().any(|s| start_str.starts_with(s)),
Pat::MultiStr(texts) => texts.iter().any(|s| end_str.ends_with(s)),
Pat::OwnedMultiStr(texts) => texts.iter().any(|s| end_str.ends_with(s)),
Pat::Sym(sym) => end_str.ends_with(sym.as_str()),
Pat::Num => end_str.as_bytes().last().map_or(false, u8::is_ascii_hexdigit),
})
@ -333,26 +333,32 @@ fn attr_search_pat(attr: &Attribute) -> (Pat, Pat) {
match attr.kind {
AttrKind::Normal(..) => {
if let Some(ident) = attr.ident() {
// TODO: I feel like it's likely we can use `Cow` instead but this will require quite a bit of
// refactoring
// NOTE: This will likely have false positives, like `allow = 1`
(
Pat::OwnedMultiStr(vec![ident.to_string(), "#".to_owned()]),
Pat::Str(""),
)
let ident_string = ident.to_string();
if attr.style == AttrStyle::Outer {
(
Pat::OwnedMultiStr(vec!["#[".to_owned() + &ident_string, ident_string]),
Pat::Str(""),
)
} else {
(
Pat::OwnedMultiStr(vec!["#![".to_owned() + &ident_string, ident_string]),
Pat::Str(""),
)
}
} else {
(Pat::Str("#"), Pat::Str("]"))
}
},
AttrKind::DocComment(_kind @ CommentKind::Line, ..) => {
if matches!(attr.style, AttrStyle::Outer) {
if attr.style == AttrStyle::Outer {
(Pat::Str("///"), Pat::Str(""))
} else {
(Pat::Str("//!"), Pat::Str(""))
}
},
AttrKind::DocComment(_kind @ CommentKind::Block, ..) => {
if matches!(attr.style, AttrStyle::Outer) {
if attr.style == AttrStyle::Outer {
(Pat::Str("/**"), Pat::Str("*/"))
} else {
(Pat::Str("/*!"), Pat::Str("*/"))

View file

@ -1,3 +1,7 @@
//! A simple const eval API, for use on arbitrary HIR expressions.
//!
//! This cannot use rustc's const eval, aka miri, as arbitrary HIR expressions cannot be lowered to
//! executable MIR bodies, so we have to do this instead.
#![allow(clippy::float_cmp)]
use crate::macros::HirNode;
@ -379,6 +383,8 @@ impl Ord for FullInt {
/// The context required to evaluate a constant expression.
///
/// This is currently limited to constant folding and reading the value of named constants.
///
/// See the module level documentation for some context.
pub struct ConstEvalCtxt<'tcx> {
tcx: TyCtxt<'tcx>,
param_env: ParamEnv<'tcx>,

View file

@ -8,7 +8,9 @@
//! Thank you!
//! ~The `INTERNAL_METADATA_COLLECTOR` lint
use rustc_errors::{Applicability, Diag, DiagMessage, MultiSpan, SubdiagMessage};
use rustc_errors::{
Applicability, Diag, DiagMessage, EmissionGuarantee, MultiSpan, SubdiagMessage, SubstitutionPart, Suggestions,
};
use rustc_hir::HirId;
use rustc_lint::{LateContext, Lint, LintContext};
use rustc_span::Span;
@ -28,6 +30,42 @@ fn docs_link(diag: &mut Diag<'_, ()>, lint: &'static Lint) {
}
}
/// Makes sure that a diagnostic is well formed.
///
/// rustc debug asserts a few properties about spans,
/// but the clippy repo uses a distributed rustc build with debug assertions disabled,
/// so this has historically led to problems during subtree syncs where those debug assertions
/// only started triggered there.
///
/// This function makes sure we also validate them in debug clippy builds.
fn validate_diag(diag: &Diag<'_, impl EmissionGuarantee>) {
let suggestions = match &diag.suggestions {
Suggestions::Enabled(suggs) => &**suggs,
Suggestions::Sealed(suggs) => &**suggs,
Suggestions::Disabled => return,
};
for substitution in suggestions.iter().flat_map(|s| &s.substitutions) {
assert_eq!(
substitution
.parts
.iter()
.find(|SubstitutionPart { snippet, span }| snippet.is_empty() && span.is_empty()),
None,
"span must not be empty and have no suggestion"
);
assert_eq!(
substitution
.parts
.array_windows()
.find(|[a, b]| a.span.overlaps(b.span)),
None,
"suggestion must not have overlapping parts"
);
}
}
/// Emit a basic lint message with a `msg` and a `span`.
///
/// This is the most primitive of our lint emission methods and can
@ -64,6 +102,9 @@ pub fn span_lint<T: LintContext>(cx: &T, lint: &'static Lint, sp: impl Into<Mult
cx.span_lint(lint, sp, |diag| {
diag.primary_message(msg);
docs_link(diag, lint);
#[cfg(debug_assertions)]
validate_diag(diag);
});
}
@ -118,6 +159,9 @@ pub fn span_lint_and_help<T: LintContext>(
diag.help(help.into());
}
docs_link(diag, lint);
#[cfg(debug_assertions)]
validate_diag(diag);
});
}
@ -175,6 +219,9 @@ pub fn span_lint_and_note<T: LintContext>(
diag.note(note.into());
}
docs_link(diag, lint);
#[cfg(debug_assertions)]
validate_diag(diag);
});
}
@ -208,6 +255,9 @@ where
diag.primary_message(msg);
f(diag);
docs_link(diag, lint);
#[cfg(debug_assertions)]
validate_diag(diag);
});
}
@ -240,6 +290,9 @@ pub fn span_lint_hir(cx: &LateContext<'_>, lint: &'static Lint, hir_id: HirId, s
cx.tcx.node_span_lint(lint, hir_id, sp, |diag| {
diag.primary_message(msg);
docs_link(diag, lint);
#[cfg(debug_assertions)]
validate_diag(diag);
});
}
@ -280,6 +333,9 @@ pub fn span_lint_hir_and_then(
diag.primary_message(msg);
f(diag);
docs_link(diag, lint);
#[cfg(debug_assertions)]
validate_diag(diag);
});
}
@ -316,7 +372,7 @@ pub fn span_lint_hir_and_then(
/// |
/// = note: `-D fold-any` implied by `-D warnings`
/// ```
#[expect(clippy::collapsible_span_lint_calls)]
#[cfg_attr(not(debug_assertions), expect(clippy::collapsible_span_lint_calls))]
pub fn span_lint_and_sugg<T: LintContext>(
cx: &T,
lint: &'static Lint,
@ -328,5 +384,8 @@ pub fn span_lint_and_sugg<T: LintContext>(
) {
span_lint_and_then(cx, lint, sp, msg.into(), |diag| {
diag.span_suggestion(sp, help.into(), sugg, applicability);
#[cfg(debug_assertions)]
validate_diag(diag);
});
}

View file

@ -103,7 +103,7 @@ impl<'hir> IfLet<'hir> {
/// Parses an `if let` expression
pub fn hir(cx: &LateContext<'_>, expr: &Expr<'hir>) -> Option<Self> {
if let ExprKind::If(
Expr {
&Expr {
kind:
ExprKind::Let(&hir::LetExpr {
pat: let_pat,
@ -381,12 +381,12 @@ impl<'hir> WhileLet<'hir> {
/// Parses a desugared `while let` loop
pub const fn hir(expr: &Expr<'hir>) -> Option<Self> {
if let ExprKind::Loop(
Block {
&Block {
expr:
Some(Expr {
Some(&Expr {
kind:
ExprKind::If(
Expr {
&Expr {
kind:
ExprKind::Let(&hir::LetExpr {
pat: let_pat,

View file

@ -128,7 +128,7 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> {
self.inter_expr().eq_path_segments(left, right)
}
pub fn eq_modifiers(&mut self, left: TraitBoundModifiers, right: TraitBoundModifiers) -> bool {
pub fn eq_modifiers(left: TraitBoundModifiers, right: TraitBoundModifiers) -> bool {
std::mem::discriminant(&left.constness) == std::mem::discriminant(&right.constness)
&& std::mem::discriminant(&left.polarity) == std::mem::discriminant(&right.polarity)
}
@ -1201,11 +1201,11 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
self.hash_ty(ty);
self.hash_pat(pat);
},
TyKind::Ptr(ref mut_ty) => {
TyKind::Ptr(mut_ty) => {
self.hash_ty(mut_ty.ty);
mut_ty.mutbl.hash(&mut self.s);
},
TyKind::Ref(lifetime, ref mut_ty) => {
TyKind::Ref(lifetime, mut_ty) => {
self.hash_lifetime(lifetime);
self.hash_ty(mut_ty.ty);
mut_ty.mutbl.hash(&mut self.s);
@ -1230,14 +1230,19 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
self.hash_ty(ty);
}
},
TyKind::Path(ref qpath) => self.hash_qpath(qpath),
TyKind::Path(qpath) => self.hash_qpath(qpath),
TyKind::TraitObject(_, lifetime, _) => {
self.hash_lifetime(lifetime);
},
TyKind::Typeof(anon_const) => {
self.hash_body(anon_const.body);
},
TyKind::Err(_) | TyKind::Infer | TyKind::Never | TyKind::InferDelegation(..) | TyKind::OpaqueDef(_) | TyKind::AnonAdt(_) => {},
TyKind::Err(_)
| TyKind::Infer
| TyKind::Never
| TyKind::InferDelegation(..)
| TyKind::OpaqueDef(_)
| TyKind::AnonAdt(_) => {},
}
}

View file

@ -9,6 +9,7 @@
#![feature(rustc_private)]
#![feature(assert_matches)]
#![feature(unwrap_infallible)]
#![feature(array_windows)]
#![recursion_limit = "512"]
#![allow(
clippy::missing_errors_doc,
@ -688,11 +689,11 @@ pub fn find_crates(tcx: TyCtxt<'_>, name: Symbol) -> Vec<Res> {
///
/// This function is expensive and should be used sparingly.
pub fn def_path_res(tcx: TyCtxt<'_>, path: &[&str]) -> Vec<Res> {
let (base, path) = match *path {
let (base, path) = match path {
[primitive] => {
return vec![PrimTy::from_name(Symbol::intern(primitive)).map_or(Res::Err, Res::PrimTy)];
},
[base, ref path @ ..] => (base, path),
[base, path @ ..] => (base, path),
_ => return Vec::new(),
};
@ -744,7 +745,7 @@ pub fn def_path_res_with_base(tcx: TyCtxt<'_>, mut base: Vec<Res>, mut path: &[&
}
/// Resolves a def path like `std::vec::Vec` to its [`DefId`]s, see [`def_path_res`].
pub fn def_path_def_ids(tcx: TyCtxt<'_>, path: &[&str]) -> impl Iterator<Item = DefId> {
pub fn def_path_def_ids(tcx: TyCtxt<'_>, path: &[&str]) -> impl Iterator<Item = DefId> + use<> {
def_path_res(tcx, path).into_iter().filter_map(|res| res.opt_def_id())
}
@ -934,7 +935,7 @@ pub fn is_default_equivalent(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
}
},
ExprKind::Call(repl_func, []) => is_default_equivalent_call(cx, repl_func),
ExprKind::Call(from_func, [ref arg]) => is_default_equivalent_from(cx, from_func, arg),
ExprKind::Call(from_func, [arg]) => is_default_equivalent_from(cx, from_func, arg),
ExprKind::Path(qpath) => is_res_lang_ctor(cx, cx.qpath_res(qpath, e.hir_id), OptionNone),
ExprKind::AddrOf(rustc_hir::BorrowKind::Ref, _, expr) => matches!(expr.kind, ExprKind::Array([])),
_ => false,
@ -947,7 +948,7 @@ fn is_default_equivalent_from(cx: &LateContext<'_>, from_func: &Expr<'_>, arg: &
{
match arg.kind {
ExprKind::Lit(hir::Lit {
node: LitKind::Str(ref sym, _),
node: LitKind::Str(sym, _),
..
}) => return sym.is_empty() && is_path_lang_item(cx, ty, LangItem::String),
ExprKind::Array([]) => return is_path_diagnostic_item(cx, ty, sym::Vec),
@ -1337,15 +1338,17 @@ pub fn get_item_name(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<Symbol> {
pub struct ContainsName<'a, 'tcx> {
pub cx: &'a LateContext<'tcx>,
pub name: Symbol,
pub result: bool,
}
impl<'tcx> Visitor<'tcx> for ContainsName<'_, 'tcx> {
type Result = ControlFlow<()>;
type NestedFilter = nested_filter::OnlyBodies;
fn visit_name(&mut self, name: Symbol) {
fn visit_name(&mut self, name: Symbol) -> Self::Result {
if self.name == name {
self.result = true;
ControlFlow::Break(())
} else {
ControlFlow::Continue(())
}
}
@ -1356,13 +1359,8 @@ impl<'tcx> Visitor<'tcx> for ContainsName<'_, 'tcx> {
/// Checks if an `Expr` contains a certain name.
pub fn contains_name<'tcx>(name: Symbol, expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) -> bool {
let mut cn = ContainsName {
name,
result: false,
cx,
};
cn.visit_expr(expr);
cn.result
let mut cn = ContainsName { cx, name };
cn.visit_expr(expr).is_break()
}
/// Returns `true` if `expr` contains a return expression
@ -3459,7 +3457,7 @@ fn maybe_get_relative_path(from: &DefPath, to: &DefPath, max_super: usize) -> St
pub fn is_parent_stmt(cx: &LateContext<'_>, id: HirId) -> bool {
matches!(
cx.tcx.parent_hir_node(id),
Node::Stmt(..) | Node::Block(Block { stmts: &[], .. })
Node::Stmt(..) | Node::Block(Block { stmts: [], .. })
)
}

View file

@ -19,7 +19,7 @@ use rustc_middle::ty::layout::ValidityRequirement;
use rustc_middle::ty::{
self, AdtDef, AliasTy, AssocItem, AssocKind, Binder, BoundRegion, FnSig, GenericArg, GenericArgKind,
GenericArgsRef, GenericParamDefKind, IntTy, ParamEnv, Region, RegionKind, TraitRef, Ty, TyCtxt, TypeSuperVisitable,
TypeVisitable, TypeVisitableExt, TypeVisitor, UintTy, Upcast, VariantDef, VariantDiscr, TypingMode,
TypeVisitable, TypeVisitableExt, TypeVisitor, TypingMode, UintTy, Upcast, VariantDef, VariantDiscr,
};
use rustc_span::symbol::Ident;
use rustc_span::{DUMMY_SP, Span, Symbol, sym};
@ -274,11 +274,7 @@ pub fn implements_trait_with_env_from_iter<'tcx>(
.map(|arg| arg.into().unwrap_or_else(|| infcx.next_ty_var(DUMMY_SP).into()))
.collect::<Vec<_>>();
let trait_ref = TraitRef::new(
tcx,
trait_id,
[GenericArg::from(ty)].into_iter().chain(args),
);
let trait_ref = TraitRef::new(tcx, trait_id, [GenericArg::from(ty)].into_iter().chain(args));
debug_assert_matches!(
tcx.def_kind(trait_id),
@ -975,9 +971,7 @@ pub fn approx_ty_size<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> u64 {
match (cx.layout_of(ty).map(|layout| layout.size.bytes()), ty.kind()) {
(Ok(size), _) => size,
(Err(_), ty::Tuple(list)) => list.iter().map(|t| approx_ty_size(cx, t)).sum(),
(Err(_), ty::Array(t, n)) => {
n.try_to_target_usize(cx.tcx).unwrap_or_default() * approx_ty_size(cx, *t)
},
(Err(_), ty::Array(t, n)) => n.try_to_target_usize(cx.tcx).unwrap_or_default() * approx_ty_size(cx, *t),
(Err(_), ty::Adt(def, subst)) if def.is_struct() => def
.variants()
.iter()
@ -1284,7 +1278,12 @@ pub fn make_normalized_projection_with_regions<'tcx>(
pub fn normalize_with_regions<'tcx>(tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> {
let cause = ObligationCause::dummy();
match tcx.infer_ctxt().build(TypingMode::from_param_env(param_env)).at(&cause, param_env).query_normalize(ty) {
match tcx
.infer_ctxt()
.build(TypingMode::from_param_env(param_env))
.at(&cause, param_env)
.query_normalize(ty)
{
Ok(ty) => ty.value,
Err(_) => ty,
}

View file

@ -302,9 +302,9 @@ fn type_is_inferable_from_arguments(cx: &LateContext<'_>, expr: &Expr<'_>) -> bo
// Check that all type parameters appear in the functions input types.
(0..(generics.parent_count + generics.own_params.len()) as u32).all(|index| {
fn_sig
.inputs()
.iter()
.any(|input_ty| contains_param(*input_ty.skip_binder(), index))
.inputs()
.iter()
.any(|input_ty| contains_param(*input_ty.skip_binder(), index))
})
}

View file

@ -109,34 +109,28 @@ impl<'tcx> Visitor<'tcx> for ParamBindingIdCollector {
pub struct BindingUsageFinder<'a, 'tcx> {
cx: &'a LateContext<'tcx>,
binding_ids: Vec<HirId>,
usage_found: bool,
}
impl<'a, 'tcx> BindingUsageFinder<'a, 'tcx> {
pub fn are_params_used(cx: &'a LateContext<'tcx>, body: &'tcx hir::Body<'tcx>) -> bool {
let mut finder = BindingUsageFinder {
cx,
binding_ids: ParamBindingIdCollector::collect_binding_hir_ids(body),
usage_found: false,
};
finder.visit_body(body);
finder.usage_found
finder.visit_body(body).is_break()
}
}
impl<'tcx> Visitor<'tcx> for BindingUsageFinder<'_, 'tcx> {
type Result = ControlFlow<()>;
type NestedFilter = nested_filter::OnlyBodies;
fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
if !self.usage_found {
intravisit::walk_expr(self, expr);
}
}
fn visit_path(&mut self, path: &hir::Path<'tcx>, _: HirId) {
fn visit_path(&mut self, path: &hir::Path<'tcx>, _: HirId) -> Self::Result {
if let Res::Local(id) = path.res {
if self.binding_ids.contains(&id) {
self.usage_found = true;
return ControlFlow::Break(());
}
}
ControlFlow::Continue(())
}
fn nested_visit_map(&mut self) -> Self::Map {

View file

@ -324,17 +324,15 @@ pub fn is_local_used<'tcx>(cx: &LateContext<'tcx>, visitable: impl Visitable<'tc
pub fn is_const_evaluatable<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> bool {
struct V<'a, 'tcx> {
cx: &'a LateContext<'tcx>,
is_const: bool,
}
impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
type Result = ControlFlow<()>;
type NestedFilter = intravisit::nested_filter::None;
fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
if !self.is_const {
return;
}
fn visit_expr(&mut self, e: &'tcx Expr<'_>) -> Self::Result {
match e.kind {
ExprKind::ConstBlock(_) => return,
ExprKind::ConstBlock(_) => return ControlFlow::Continue(()),
ExprKind::Call(
&Expr {
kind: ExprKind::Path(ref p),
@ -394,37 +392,34 @@ pub fn is_const_evaluatable<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) ->
| ExprKind::Type(..) => (),
_ => {
self.is_const = false;
return;
return ControlFlow::Break(());
},
}
walk_expr(self, e);
walk_expr(self, e)
}
}
let mut v = V { cx, is_const: true };
v.visit_expr(e);
v.is_const
let mut v = V { cx };
v.visit_expr(e).is_continue()
}
/// Checks if the given expression performs an unsafe operation outside of an unsafe block.
pub fn is_expr_unsafe<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> bool {
struct V<'a, 'tcx> {
cx: &'a LateContext<'tcx>,
is_unsafe: bool,
}
impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
type NestedFilter = nested_filter::OnlyBodies;
type Result = ControlFlow<()>;
fn nested_visit_map(&mut self) -> Self::Map {
self.cx.tcx.hir()
}
fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
if self.is_unsafe {
return;
}
fn visit_expr(&mut self, e: &'tcx Expr<'_>) -> Self::Result {
match e.kind {
ExprKind::Unary(UnOp::Deref, e) if self.cx.typeck_results().expr_ty(e).is_unsafe_ptr() => {
self.is_unsafe = true;
ControlFlow::Break(())
},
ExprKind::MethodCall(..)
if self
@ -435,13 +430,13 @@ pub fn is_expr_unsafe<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> bool {
self.cx.tcx.fn_sig(id).skip_binder().safety() == Safety::Unsafe
}) =>
{
self.is_unsafe = true;
ControlFlow::Break(())
},
ExprKind::Call(func, _) => match *self.cx.typeck_results().expr_ty(func).peel_refs().kind() {
ty::FnDef(id, _) if self.cx.tcx.fn_sig(id).skip_binder().safety() == Safety::Unsafe => {
self.is_unsafe = true;
ControlFlow::Break(())
},
ty::FnPtr(_, hdr) if hdr.safety == Safety::Unsafe => self.is_unsafe = true,
ty::FnPtr(_, hdr) if hdr.safety == Safety::Unsafe => ControlFlow::Break(()),
_ => walk_expr(self, e),
},
ExprKind::Path(ref p)
@ -451,56 +446,54 @@ pub fn is_expr_unsafe<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> bool {
.opt_def_id()
.map_or(false, |id| self.cx.tcx.is_mutable_static(id)) =>
{
self.is_unsafe = true;
ControlFlow::Break(())
},
_ => walk_expr(self, e),
}
}
fn visit_block(&mut self, b: &'tcx Block<'_>) {
if !matches!(b.rules, BlockCheckMode::UnsafeBlock(_)) {
walk_block(self, b);
fn visit_block(&mut self, b: &'tcx Block<'_>) -> Self::Result {
if matches!(b.rules, BlockCheckMode::UnsafeBlock(_)) {
ControlFlow::Continue(())
} else {
walk_block(self, b)
}
}
fn visit_nested_item(&mut self, id: ItemId) {
if let ItemKind::Impl(i) = &self.cx.tcx.hir().item(id).kind {
self.is_unsafe = i.safety == Safety::Unsafe;
fn visit_nested_item(&mut self, id: ItemId) -> Self::Result {
if let ItemKind::Impl(i) = &self.cx.tcx.hir().item(id).kind
&& i.safety == Safety::Unsafe
{
ControlFlow::Break(())
} else {
ControlFlow::Continue(())
}
}
}
let mut v = V { cx, is_unsafe: false };
v.visit_expr(e);
v.is_unsafe
let mut v = V { cx };
v.visit_expr(e).is_break()
}
/// 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 Result = ControlFlow<()>;
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;
}
fn visit_block(&mut self, b: &'tcx Block<'_>) -> Self::Result {
if b.rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) {
self.found_unsafe = true;
return;
ControlFlow::Break(())
} else {
walk_block(self, b)
}
walk_block(self, b);
}
}
let mut v = V {
cx,
found_unsafe: false,
};
v.visit_expr(e);
v.found_unsafe
let mut v = V { cx };
v.visit_expr(e).is_break()
}
/// Runs the given function for each sub-expression producing the final value consumed by the parent