| 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 { |
| false |
| } |
| } |
| |
| 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_else( |
| || { |
| 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<Symbol> { |
| 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<Symbol>, Option<Symbol>) { |
| 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), |
| } |
| } |