use clippy_utils::macros::{is_panic, macro_backtrace}; use rustc_ast::{AttrId, MetaItemInner}; use rustc_hir::{ Block, Expr, ExprKind, ImplItem, ImplItemKind, Item, ItemKind, StmtKind, TraitFn, TraitItem, TraitItemKind, }; use rustc_lint::{LateContext, Level}; use rustc_middle::ty; use rustc_span::sym; use rustc_span::symbol::Symbol; pub(super) fn is_word(nmi: &MetaItemInner, expected: Symbol) -> bool { if let MetaItemInner::MetaItem(mi) = &nmi { mi.is_word() && mi.has_name(expected) } else { false } } pub(super) fn is_lint_level(symbol: Symbol, attr_id: AttrId) -> bool { Level::from_symbol(symbol, Some(attr_id)).is_some() } pub(super) fn is_relevant_item(cx: &LateContext<'_>, item: &Item<'_>) -> bool { if let ItemKind::Fn { body: eid, .. } = item.kind { is_relevant_expr(cx, cx.tcx.typeck_body(eid), cx.tcx.hir_body(eid).value) } else { true } } pub(super) fn is_relevant_impl(cx: &LateContext<'_>, item: &ImplItem<'_>) -> bool { match item.kind { ImplItemKind::Fn(_, eid) => is_relevant_expr(cx, cx.tcx.typeck_body(eid), cx.tcx.hir_body(eid).value), _ => false, } } pub(super) fn is_relevant_trait(cx: &LateContext<'_>, item: &TraitItem<'_>) -> bool { match item.kind { TraitItemKind::Fn(_, TraitFn::Required(_)) => true, TraitItemKind::Fn(_, TraitFn::Provided(eid)) => { is_relevant_expr(cx, cx.tcx.typeck_body(eid), cx.tcx.hir_body(eid).value) }, _ => false, } } fn is_relevant_block(cx: &LateContext<'_>, typeck_results: &ty::TypeckResults<'_>, block: &Block<'_>) -> bool { block.stmts.first().map_or( block .expr .as_ref() .is_some_and(|e| is_relevant_expr(cx, typeck_results, e)), |stmt| match &stmt.kind { StmtKind::Let(_) => true, StmtKind::Expr(expr) | StmtKind::Semi(expr) => is_relevant_expr(cx, typeck_results, expr), StmtKind::Item(_) => false, }, ) } fn is_relevant_expr(cx: &LateContext<'_>, typeck_results: &ty::TypeckResults<'_>, expr: &Expr<'_>) -> bool { if macro_backtrace(expr.span).last().is_some_and(|macro_call| { is_panic(cx, macro_call.def_id) || cx.tcx.item_name(macro_call.def_id) == sym::unreachable }) { return false; } match &expr.kind { ExprKind::Block(block, _) => is_relevant_block(cx, typeck_results, block), ExprKind::Ret(Some(e)) => is_relevant_expr(cx, typeck_results, e), ExprKind::Ret(None) | ExprKind::Break(_, None) => false, _ => true, } } /// Returns the lint name if it is clippy lint. pub(super) fn extract_clippy_lint(lint: &MetaItemInner) -> Option { match namespace_and_lint(lint) { (Some(sym::clippy), name) => name, _ => None, } } /// Returns the lint namespace, if any, as well as the lint name. (`None`, `None`) means /// the lint had less than 1 or more than 2 segments. pub(super) fn namespace_and_lint(lint: &MetaItemInner) -> (Option, Option) { match lint.meta_item().map(|m| m.path.segments.as_slice()).unwrap_or_default() { [name] => (None, Some(name.ident.name)), [namespace, name] => (Some(namespace.ident.name), Some(name.ident.name)), _ => (None, None), } }