Auto merge of #8305 - camsteffen:util-cleanup, r=flip1995

Factor out several utils, add `path_def_id`

changelog: none

This is generally an effort to reduce the total number of utils. `path_def_id` is added which I believe is more "cross-cutting" and also complements `path_to_local`. Best reviewed one commit at a time.

Added:
* `path_def_id`
* `path_res`

Removed:
 * `is_qpath_def_path`
 * `match_any_diagnostic_items`
 * `expr_path_res`
 * `single_segment_path`
 * `differing_macro_contexts`
 * `is_ty_param_lang_item`
 * `is_ty_param_diagnostic_item`
 * `get_qpath_generics`

Renamed:
* `path_to_res` to `def_path_res`
* `get_qpath_generic_tys` to `qpath_generic_tys`

CC `@Jarcho` since this relates to some of your work and you may have input.
This commit is contained in:
bors 2022-02-07 15:23:23 +00:00
commit 3d43826e32
29 changed files with 249 additions and 309 deletions

View File

@ -1,8 +1,8 @@
use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
use clippy_utils::get_parent_expr;
use clippy_utils::higher; use clippy_utils::higher;
use clippy_utils::source::snippet_block_with_applicability; use clippy_utils::source::snippet_block_with_applicability;
use clippy_utils::ty::implements_trait; use clippy_utils::ty::implements_trait;
use clippy_utils::{differing_macro_contexts, get_parent_expr};
use if_chain::if_chain; use if_chain::if_chain;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::intravisit::{walk_expr, Visitor}; use rustc_hir::intravisit::{walk_expr, Visitor};
@ -97,7 +97,7 @@ impl<'tcx> LateLintPass<'tcx> for BlocksInIfConditions {
if let Some(ex) = &block.expr { if let Some(ex) = &block.expr {
// don't dig into the expression here, just suggest that they remove // don't dig into the expression here, just suggest that they remove
// the block // the block
if expr.span.from_expansion() || differing_macro_contexts(expr.span, ex.span) { if expr.span.from_expansion() || ex.span.from_expansion() {
return; return;
} }
let mut applicability = Applicability::MachineApplicable; let mut applicability = Applicability::MachineApplicable;
@ -122,7 +122,7 @@ impl<'tcx> LateLintPass<'tcx> for BlocksInIfConditions {
} }
} else { } else {
let span = block.expr.as_ref().map_or_else(|| block.stmts[0].span, |e| e.span); let span = block.expr.as_ref().map_or_else(|| block.stmts[0].span, |e| e.span);
if span.from_expansion() || differing_macro_contexts(expr.span, span) { if span.from_expansion() || expr.span.from_expansion() {
return; return;
} }
// move block higher // move block higher

View File

@ -77,7 +77,7 @@ impl<'tcx> LateLintPass<'tcx> for DisallowedMethods {
fn check_crate(&mut self, cx: &LateContext<'_>) { fn check_crate(&mut self, cx: &LateContext<'_>) {
for (index, conf) in self.conf_disallowed.iter().enumerate() { for (index, conf) in self.conf_disallowed.iter().enumerate() {
let segs: Vec<_> = conf.path().split("::").collect(); let segs: Vec<_> = conf.path().split("::").collect();
if let Res::Def(_, id) = clippy_utils::path_to_res(cx, &segs) { if let Res::Def(_, id) = clippy_utils::def_path_res(cx, &segs) {
self.disallowed.insert(id, index); self.disallowed.insert(id, index);
} }
} }

View File

@ -96,7 +96,7 @@ impl<'tcx> LateLintPass<'tcx> for DisallowedTypes {
), ),
}; };
let segs: Vec<_> = path.split("::").collect(); let segs: Vec<_> = path.split("::").collect();
match clippy_utils::path_to_res(cx, &segs) { match clippy_utils::def_path_res(cx, &segs) {
Res::Def(_, id) => { Res::Def(_, id) => {
self.def_ids.insert(id, reason); self.def_ids.insert(id, reason);
}, },

View File

@ -1,5 +1,4 @@
use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_note}; use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_note};
use clippy_utils::differing_macro_contexts;
use clippy_utils::source::snippet_opt; use clippy_utils::source::snippet_opt;
use if_chain::if_chain; use if_chain::if_chain;
use rustc_ast::ast::{BinOpKind, Block, Expr, ExprKind, StmtKind, UnOp}; use rustc_ast::ast::{BinOpKind, Block, Expr, ExprKind, StmtKind, UnOp};
@ -135,7 +134,7 @@ impl EarlyLintPass for Formatting {
/// Implementation of the `SUSPICIOUS_ASSIGNMENT_FORMATTING` lint. /// Implementation of the `SUSPICIOUS_ASSIGNMENT_FORMATTING` lint.
fn check_assign(cx: &EarlyContext<'_>, expr: &Expr) { fn check_assign(cx: &EarlyContext<'_>, expr: &Expr) {
if let ExprKind::Assign(ref lhs, ref rhs, _) = expr.kind { if let ExprKind::Assign(ref lhs, ref rhs, _) = expr.kind {
if !differing_macro_contexts(lhs.span, rhs.span) && !lhs.span.from_expansion() { if !lhs.span.from_expansion() && !rhs.span.from_expansion() {
let eq_span = lhs.span.between(rhs.span); let eq_span = lhs.span.between(rhs.span);
if let ExprKind::Unary(op, ref sub_rhs) = rhs.kind { if let ExprKind::Unary(op, ref sub_rhs) = rhs.kind {
if let Some(eq_snippet) = snippet_opt(cx, eq_span) { if let Some(eq_snippet) = snippet_opt(cx, eq_span) {
@ -165,7 +164,7 @@ fn check_assign(cx: &EarlyContext<'_>, expr: &Expr) {
fn check_unop(cx: &EarlyContext<'_>, expr: &Expr) { fn check_unop(cx: &EarlyContext<'_>, expr: &Expr) {
if_chain! { if_chain! {
if let ExprKind::Binary(ref binop, ref lhs, ref rhs) = expr.kind; if let ExprKind::Binary(ref binop, ref lhs, ref rhs) = expr.kind;
if !differing_macro_contexts(lhs.span, rhs.span) && !lhs.span.from_expansion(); if !lhs.span.from_expansion() && !rhs.span.from_expansion();
// span between BinOp LHS and RHS // span between BinOp LHS and RHS
let binop_span = lhs.span.between(rhs.span); let binop_span = lhs.span.between(rhs.span);
// if RHS is an UnOp // if RHS is an UnOp
@ -206,8 +205,8 @@ fn check_else(cx: &EarlyContext<'_>, expr: &Expr) {
if_chain! { if_chain! {
if let ExprKind::If(_, then, Some(else_)) = &expr.kind; if let ExprKind::If(_, then, Some(else_)) = &expr.kind;
if is_block(else_) || is_if(else_); if is_block(else_) || is_if(else_);
if !differing_macro_contexts(then.span, else_.span); if !then.span.from_expansion() && !else_.span.from_expansion();
if !then.span.from_expansion() && !in_external_macro(cx.sess(), expr.span); if !in_external_macro(cx.sess(), expr.span);
// workaround for rust-lang/rust#43081 // workaround for rust-lang/rust#43081
if expr.span.lo().0 != 0 && expr.span.hi().0 != 0; if expr.span.lo().0 != 0 && expr.span.hi().0 != 0;
@ -268,7 +267,7 @@ fn check_array(cx: &EarlyContext<'_>, expr: &Expr) {
for element in array { for element in array {
if_chain! { if_chain! {
if let ExprKind::Binary(ref op, ref lhs, _) = element.kind; if let ExprKind::Binary(ref op, ref lhs, _) = element.kind;
if has_unary_equivalent(op.node) && !differing_macro_contexts(lhs.span, op.span); if has_unary_equivalent(op.node) && lhs.span.ctxt() == op.span.ctxt();
let space_span = lhs.span.between(op.span); let space_span = lhs.span.between(op.span);
if let Some(space_snippet) = snippet_opt(cx, space_span); if let Some(space_snippet) = snippet_opt(cx, space_span);
let lint_span = lhs.span.with_lo(lhs.span.hi()); let lint_span = lhs.span.with_lo(lhs.span.hi());
@ -291,8 +290,7 @@ fn check_array(cx: &EarlyContext<'_>, expr: &Expr) {
fn check_missing_else(cx: &EarlyContext<'_>, first: &Expr, second: &Expr) { fn check_missing_else(cx: &EarlyContext<'_>, first: &Expr, second: &Expr) {
if_chain! { if_chain! {
if !differing_macro_contexts(first.span, second.span); if !first.span.from_expansion() && !second.span.from_expansion();
if !first.span.from_expansion();
if let ExprKind::If(cond_expr, ..) = &first.kind; if let ExprKind::If(cond_expr, ..) = &first.kind;
if is_block(second) || is_if(second); if is_block(second) || is_if(second);

View File

@ -17,7 +17,6 @@ use rustc_typeck::hir_ty_to_ty;
use if_chain::if_chain; use if_chain::if_chain;
use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then}; use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then};
use clippy_utils::differing_macro_contexts;
use clippy_utils::source::{snippet, snippet_opt}; use clippy_utils::source::{snippet, snippet_opt};
use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::ty::is_type_diagnostic_item;
@ -123,7 +122,7 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitHasher {
vis.visit_ty(impl_.self_ty); vis.visit_ty(impl_.self_ty);
for target in &vis.found { for target in &vis.found {
if differing_macro_contexts(item.span, target.span()) { if item.span.ctxt() != target.span().ctxt() {
return; return;
} }

View File

@ -1,6 +1,6 @@
use clippy_utils::diagnostics::span_lint; use clippy_utils::diagnostics::span_lint;
use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
use clippy_utils::{get_trait_def_id, higher, is_qpath_def_path, paths}; use clippy_utils::{get_trait_def_id, higher, match_def_path, path_def_id, paths};
use rustc_hir::{BorrowKind, Expr, ExprKind}; use rustc_hir::{BorrowKind, Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_session::{declare_lint_pass, declare_tool_lint};
@ -167,13 +167,9 @@ fn is_infinite(cx: &LateContext<'_>, expr: &Expr<'_>) -> Finiteness {
}, },
ExprKind::Block(block, _) => block.expr.as_ref().map_or(Finite, |e| is_infinite(cx, e)), ExprKind::Block(block, _) => block.expr.as_ref().map_or(Finite, |e| is_infinite(cx, e)),
ExprKind::Box(e) | ExprKind::AddrOf(BorrowKind::Ref, _, e) => is_infinite(cx, e), ExprKind::Box(e) | ExprKind::AddrOf(BorrowKind::Ref, _, e) => is_infinite(cx, e),
ExprKind::Call(path, _) => { ExprKind::Call(path, _) => path_def_id(cx, path)
if let ExprKind::Path(ref qpath) = path.kind { .map_or(false, |id| match_def_path(cx, id, &paths::ITER_REPEAT))
is_qpath_def_path(cx, qpath, path.hir_id, &paths::ITER_REPEAT).into() .into(),
} else {
Finite
}
},
ExprKind::Struct(..) => higher::Range::hir(expr).map_or(false, |r| r.end.is_none()).into(), ExprKind::Struct(..) => higher::Range::hir(expr).map_or(false, |r| r.end.is_none()).into(),
_ => Finite, _ => Finite,
} }

View File

@ -1,10 +1,9 @@
use super::SINGLE_ELEMENT_LOOP; use super::SINGLE_ELEMENT_LOOP;
use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::single_segment_path; use clippy_utils::source::{indent_of, snippet_with_applicability};
use clippy_utils::source::{indent_of, snippet};
use if_chain::if_chain; use if_chain::if_chain;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::{BorrowKind, Expr, ExprKind, Pat, PatKind}; use rustc_hir::{BorrowKind, Expr, ExprKind, Pat};
use rustc_lint::LateContext; use rustc_lint::LateContext;
pub(super) fn check<'tcx>( pub(super) fn check<'tcx>(
@ -16,24 +15,21 @@ pub(super) fn check<'tcx>(
) { ) {
let arg_expr = match arg.kind { let arg_expr = match arg.kind {
ExprKind::AddrOf(BorrowKind::Ref, _, ref_arg) => ref_arg, ExprKind::AddrOf(BorrowKind::Ref, _, ref_arg) => ref_arg,
ExprKind::MethodCall(method, args, _) if args.len() == 1 && method.ident.name == rustc_span::sym::iter => { ExprKind::MethodCall(method, [arg], _) if method.ident.name == rustc_span::sym::iter => arg,
&args[0]
},
_ => return, _ => return,
}; };
if_chain! { if_chain! {
if let PatKind::Binding(.., target, _) = pat.kind;
if let ExprKind::Array([arg_expression]) = arg_expr.kind; if let ExprKind::Array([arg_expression]) = arg_expr.kind;
if let ExprKind::Path(ref list_item) = arg_expression.kind;
if let Some(list_item_name) = single_segment_path(list_item).map(|ps| ps.ident.name);
if let ExprKind::Block(block, _) = body.kind; if let ExprKind::Block(block, _) = body.kind;
if !block.stmts.is_empty(); if !block.stmts.is_empty();
then { then {
let mut block_str = snippet(cx, block.span, "..").into_owned(); let mut applicability = Applicability::MachineApplicable;
let pat_snip = snippet_with_applicability(cx, pat.span, "..", &mut applicability);
let arg_snip = snippet_with_applicability(cx, arg_expression.span, "..", &mut applicability);
let mut block_str = snippet_with_applicability(cx, block.span, "..", &mut applicability).into_owned();
block_str.remove(0); block_str.remove(0);
block_str.pop(); block_str.pop();
let indent = " ".repeat(indent_of(cx, block.stmts[0].span).unwrap_or(0));
span_lint_and_sugg( span_lint_and_sugg(
cx, cx,
@ -41,8 +37,8 @@ pub(super) fn check<'tcx>(
expr.span, expr.span,
"for loop over a single element", "for loop over a single element",
"try", "try",
format!("{{\n{}let {} = &{};{}}}", " ".repeat(indent_of(cx, block.stmts[0].span).unwrap_or(0)), target.name, list_item_name, block_str), format!("{{\n{}let {} = &{};{}}}", indent, pat_snip, arg_snip, block_str),
Applicability::MachineApplicable applicability,
) )
} }
} }

View File

@ -1860,22 +1860,22 @@ where
mod redundant_pattern_match { mod redundant_pattern_match {
use super::REDUNDANT_PATTERN_MATCHING; use super::REDUNDANT_PATTERN_MATCHING;
use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::higher;
use clippy_utils::source::snippet; use clippy_utils::source::snippet;
use clippy_utils::sugg::Sugg; use clippy_utils::sugg::Sugg;
use clippy_utils::ty::{implements_trait, is_type_diagnostic_item, is_type_lang_item, match_type}; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item, is_type_lang_item, match_type};
use clippy_utils::{is_lang_ctor, is_qpath_def_path, is_trait_method, paths}; use clippy_utils::{higher, match_def_path};
use clippy_utils::{is_lang_ctor, is_trait_method, paths};
use if_chain::if_chain; use if_chain::if_chain;
use rustc_ast::ast::LitKind; use rustc_ast::ast::LitKind;
use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::fx::FxHashSet;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::LangItem::{OptionNone, OptionSome, PollPending, PollReady, ResultErr, ResultOk}; use rustc_hir::LangItem::{OptionNone, PollPending};
use rustc_hir::{ use rustc_hir::{
intravisit::{walk_expr, Visitor}, intravisit::{walk_expr, Visitor},
Arm, Block, Expr, ExprKind, LangItem, MatchSource, Node, Pat, PatKind, QPath, UnOp, Arm, Block, Expr, ExprKind, LangItem, MatchSource, Node, Pat, PatKind, QPath, UnOp,
}; };
use rustc_lint::LateContext; use rustc_lint::LateContext;
use rustc_middle::ty::{self, subst::GenericArgKind, Ty}; use rustc_middle::ty::{self, subst::GenericArgKind, DefIdTree, Ty};
use rustc_span::sym; use rustc_span::sym;
pub fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { pub fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
@ -2051,28 +2051,31 @@ mod redundant_pattern_match {
has_else: bool, has_else: bool,
) { ) {
// also look inside refs // also look inside refs
let mut kind = &let_pat.kind;
// if we have &None for example, peel it so we can detect "if let None = x" // if we have &None for example, peel it so we can detect "if let None = x"
if let PatKind::Ref(inner, _mutability) = kind { let check_pat = match let_pat.kind {
kind = &inner.kind; PatKind::Ref(inner, _mutability) => inner,
} _ => let_pat,
};
let op_ty = cx.typeck_results().expr_ty(let_expr); let op_ty = cx.typeck_results().expr_ty(let_expr);
// Determine which function should be used, and the type contained by the corresponding // Determine which function should be used, and the type contained by the corresponding
// variant. // variant.
let (good_method, inner_ty) = match kind { let (good_method, inner_ty) = match check_pat.kind {
PatKind::TupleStruct(ref path, [sub_pat], _) => { PatKind::TupleStruct(ref qpath, [sub_pat], _) => {
if let PatKind::Wild = sub_pat.kind { if let PatKind::Wild = sub_pat.kind {
if is_lang_ctor(cx, path, ResultOk) { let res = cx.typeck_results().qpath_res(qpath, check_pat.hir_id);
let Some(id) = res.opt_def_id().and_then(|ctor_id| cx.tcx.parent(ctor_id)) else { return };
let lang_items = cx.tcx.lang_items();
if Some(id) == lang_items.result_ok_variant() {
("is_ok()", try_get_generic_ty(op_ty, 0).unwrap_or(op_ty)) ("is_ok()", try_get_generic_ty(op_ty, 0).unwrap_or(op_ty))
} else if is_lang_ctor(cx, path, ResultErr) { } else if Some(id) == lang_items.result_err_variant() {
("is_err()", try_get_generic_ty(op_ty, 1).unwrap_or(op_ty)) ("is_err()", try_get_generic_ty(op_ty, 1).unwrap_or(op_ty))
} else if is_lang_ctor(cx, path, OptionSome) { } else if Some(id) == lang_items.option_some_variant() {
("is_some()", op_ty) ("is_some()", op_ty)
} else if is_lang_ctor(cx, path, PollReady) { } else if Some(id) == lang_items.poll_ready_variant() {
("is_ready()", op_ty) ("is_ready()", op_ty)
} else if is_qpath_def_path(cx, path, sub_pat.hir_id, &paths::IPADDR_V4) { } else if match_def_path(cx, id, &paths::IPADDR_V4) {
("is_ipv4()", op_ty) ("is_ipv4()", op_ty)
} else if is_qpath_def_path(cx, path, sub_pat.hir_id, &paths::IPADDR_V6) { } else if match_def_path(cx, id, &paths::IPADDR_V6) {
("is_ipv6()", op_ty) ("is_ipv6()", op_ty)
} else { } else {
return; return;
@ -2272,17 +2275,22 @@ mod redundant_pattern_match {
should_be_left: &'a str, should_be_left: &'a str,
should_be_right: &'a str, should_be_right: &'a str,
) -> Option<&'a str> { ) -> Option<&'a str> {
let body_node_pair = if is_qpath_def_path(cx, path_left, arms[0].pat.hir_id, expected_left) let left_id = cx
&& is_qpath_def_path(cx, path_right, arms[1].pat.hir_id, expected_right) .typeck_results()
{ .qpath_res(path_left, arms[0].pat.hir_id)
(&(*arms[0].body).kind, &(*arms[1].body).kind) .opt_def_id()?;
} else if is_qpath_def_path(cx, path_right, arms[1].pat.hir_id, expected_left) let right_id = cx
&& is_qpath_def_path(cx, path_left, arms[0].pat.hir_id, expected_right) .typeck_results()
{ .qpath_res(path_right, arms[1].pat.hir_id)
(&(*arms[1].body).kind, &(*arms[0].body).kind) .opt_def_id()?;
} else { let body_node_pair =
return None; if match_def_path(cx, left_id, expected_left) && match_def_path(cx, right_id, expected_right) {
}; (&(*arms[0].body).kind, &(*arms[1].body).kind)
} else if match_def_path(cx, right_id, expected_left) && match_def_path(cx, right_id, expected_right) {
(&(*arms[1].body).kind, &(*arms[0].body).kind)
} else {
return None;
};
match body_node_pair { match body_node_pair {
(ExprKind::Lit(ref lit_left), ExprKind::Lit(ref lit_right)) => match (&lit_left.node, &lit_right.node) { (ExprKind::Lit(ref lit_left), ExprKind::Lit(ref lit_right)) => match (&lit_left.node, &lit_right.node) {

View File

@ -1,13 +1,12 @@
use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_with_applicability; use clippy_utils::source::snippet_with_applicability;
use clippy_utils::{method_chain_args, single_segment_path}; use clippy_utils::{method_chain_args, path_def_id};
use if_chain::if_chain; use if_chain::if_chain;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir as hir; use rustc_hir as hir;
use rustc_lint::LateContext; use rustc_lint::LateContext;
use rustc_lint::Lint; use rustc_lint::Lint;
use rustc_middle::ty; use rustc_middle::ty::{self, DefIdTree};
use rustc_span::sym;
/// Wrapper fn for `CHARS_NEXT_CMP` and `CHARS_LAST_CMP` lints. /// Wrapper fn for `CHARS_NEXT_CMP` and `CHARS_LAST_CMP` lints.
pub(super) fn check( pub(super) fn check(
@ -19,11 +18,9 @@ pub(super) fn check(
) -> bool { ) -> bool {
if_chain! { if_chain! {
if let Some(args) = method_chain_args(info.chain, chain_methods); if let Some(args) = method_chain_args(info.chain, chain_methods);
if let hir::ExprKind::Call(fun, arg_char) = info.other.kind; if let hir::ExprKind::Call(fun, [arg_char]) = info.other.kind;
if arg_char.len() == 1; if let Some(id) = path_def_id(cx, fun).and_then(|ctor_id| cx.tcx.parent(ctor_id));
if let hir::ExprKind::Path(ref qpath) = fun.kind; if Some(id) == cx.tcx.lang_items().option_some_variant();
if let Some(segment) = single_segment_path(qpath);
if segment.ident.name == sym::Some;
then { then {
let mut applicability = Applicability::MachineApplicable; let mut applicability = Applicability::MachineApplicable;
let self_ty = cx.typeck_results().expr_ty_adjusted(&args[0][0]).peel_refs(); let self_ty = cx.typeck_results().expr_ty_adjusted(&args[0][0]).peel_refs();
@ -42,7 +39,7 @@ pub(super) fn check(
if info.eq { "" } else { "!" }, if info.eq { "" } else { "!" },
snippet_with_applicability(cx, args[0][0].span, "..", &mut applicability), snippet_with_applicability(cx, args[0][0].span, "..", &mut applicability),
suggest, suggest,
snippet_with_applicability(cx, arg_char[0].span, "..", &mut applicability)), snippet_with_applicability(cx, arg_char.span, "..", &mut applicability)),
applicability, applicability,
); );

View File

@ -1,6 +1,6 @@
use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::is_qpath_def_path;
use clippy_utils::source::snippet_with_applicability; use clippy_utils::source::snippet_with_applicability;
use clippy_utils::{match_def_path, path_def_id};
use if_chain::if_chain; use if_chain::if_chain;
use rustc_ast::ast; use rustc_ast::ast;
use rustc_errors::Applicability; use rustc_errors::Applicability;
@ -93,12 +93,12 @@ fn is_min_or_max<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>) -> Option<M
let ty_str = ty.to_string(); let ty_str = ty.to_string();
// `std::T::MAX` `std::T::MIN` constants // `std::T::MAX` `std::T::MIN` constants
if let hir::ExprKind::Path(path) = &expr.kind { if let Some(id) = path_def_id(cx, expr) {
if is_qpath_def_path(cx, path, expr.hir_id, &["core", &ty_str, "MAX"][..]) { if match_def_path(cx, id, &["core", &ty_str, "MAX"]) {
return Some(MinMax::Max); return Some(MinMax::Max);
} }
if is_qpath_def_path(cx, path, expr.hir_id, &["core", &ty_str, "MIN"][..]) { if match_def_path(cx, id, &["core", &ty_str, "MIN"]) {
return Some(MinMax::Min); return Some(MinMax::Min);
} }
} }

View File

@ -1,11 +1,12 @@
use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet; use clippy_utils::source::snippet;
use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::ty::is_type_diagnostic_item;
use clippy_utils::{is_lang_ctor, single_segment_path}; use clippy_utils::{is_lang_ctor, path_def_id};
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir as hir; use rustc_hir as hir;
use rustc_hir::LangItem::{OptionNone, OptionSome}; use rustc_hir::LangItem::{OptionNone, OptionSome};
use rustc_lint::LateContext; use rustc_lint::LateContext;
use rustc_middle::ty::DefIdTree;
use rustc_span::symbol::sym; use rustc_span::symbol::sym;
use super::OPTION_MAP_OR_NONE; use super::OPTION_MAP_OR_NONE;
@ -76,13 +77,11 @@ pub(super) fn check<'tcx>(
if let hir::ExprKind::Closure(_, _, id, span, _) = map_arg.kind; if let hir::ExprKind::Closure(_, _, id, span, _) = map_arg.kind;
let arg_snippet = snippet(cx, span, ".."); let arg_snippet = snippet(cx, span, "..");
let body = cx.tcx.hir().body(id); let body = cx.tcx.hir().body(id);
if let Some((func, arg_char)) = reduce_unit_expression(cx, &body.value); if let Some((func, [arg_char])) = reduce_unit_expression(cx, &body.value);
if arg_char.len() == 1; if let Some(id) = path_def_id(cx, func).and_then(|ctor_id| cx.tcx.parent(ctor_id));
if let hir::ExprKind::Path(ref qpath) = func.kind; if Some(id) == cx.tcx.lang_items().option_some_variant();
if let Some(segment) = single_segment_path(qpath);
if segment.ident.name == sym::Some;
then { then {
let func_snippet = snippet(cx, arg_char[0].span, ".."); let func_snippet = snippet(cx, arg_char.span, "..");
let msg = "called `map_or(None, ..)` on an `Option` value. This can be done more directly by calling \ let msg = "called `map_or(None, ..)` on an `Option` value. This can be done more directly by calling \
`map(..)` instead"; `map(..)` instead";
return span_lint_and_sugg( return span_lint_and_sugg(

View File

@ -1,5 +1,4 @@
use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::differing_macro_contexts;
use clippy_utils::source::snippet_with_applicability; use clippy_utils::source::snippet_with_applicability;
use clippy_utils::ty::is_copy; use clippy_utils::ty::is_copy;
use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::ty::is_type_diagnostic_item;
@ -48,7 +47,7 @@ pub(super) fn check<'tcx>(
} }
} }
if differing_macro_contexts(unwrap_arg.span, map_span) { if unwrap_arg.span.ctxt() != map_span.ctxt() {
return; return;
} }

View File

@ -20,8 +20,8 @@ use rustc_span::symbol::sym;
use clippy_utils::consts::{constant, Constant}; use clippy_utils::consts::{constant, Constant};
use clippy_utils::sugg::Sugg; use clippy_utils::sugg::Sugg;
use clippy_utils::{ use clippy_utils::{
expr_path_res, get_item_name, get_parent_expr, in_constant, is_diag_trait_item, is_integer_const, iter_input_pats, get_item_name, get_parent_expr, in_constant, is_diag_trait_item, is_integer_const, iter_input_pats,
last_path_segment, match_any_def_paths, paths, unsext, SpanlessEq, last_path_segment, match_any_def_paths, path_def_id, paths, unsext, SpanlessEq,
}; };
declare_clippy_lint! { declare_clippy_lint! {
@ -583,8 +583,7 @@ fn check_to_owned(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>, left:
) )
}, },
ExprKind::Call(path, [arg]) => { ExprKind::Call(path, [arg]) => {
if expr_path_res(cx, path) if path_def_id(cx, path)
.opt_def_id()
.and_then(|id| match_any_def_paths(cx, id, &[&paths::FROM_STR_METHOD, &paths::FROM_FROM])) .and_then(|id| match_any_def_paths(cx, id, &[&paths::FROM_STR_METHOD, &paths::FROM_FROM]))
.is_some() .is_some()
{ {

View File

@ -58,7 +58,7 @@ impl_lint_pass!(ImportRename => [MISSING_ENFORCED_IMPORT_RENAMES]);
impl LateLintPass<'_> for ImportRename { impl LateLintPass<'_> for ImportRename {
fn check_crate(&mut self, cx: &LateContext<'_>) { fn check_crate(&mut self, cx: &LateContext<'_>) {
for Rename { path, rename } in &self.conf_renames { for Rename { path, rename } in &self.conf_renames {
if let Res::Def(_, id) = clippy_utils::path_to_res(cx, &path.split("::").collect::<Vec<_>>()) { if let Res::Def(_, id) = clippy_utils::def_path_res(cx, &path.split("::").collect::<Vec<_>>()) {
self.renames.insert(id, Symbol::intern(rename)); self.renames.insert(id, Symbol::intern(rename));
} }
} }

View File

@ -3,9 +3,7 @@
use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then};
use clippy_utils::source::snippet_opt; use clippy_utils::source::snippet_opt;
use clippy_utils::ty::expr_sig; use clippy_utils::ty::expr_sig;
use clippy_utils::{ use clippy_utils::{get_expr_use_or_unification_node, is_lint_allowed, path_def_id, path_to_local, paths};
expr_path_res, get_expr_use_or_unification_node, is_lint_allowed, match_any_diagnostic_items, path_to_local, paths,
};
use if_chain::if_chain; use if_chain::if_chain;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::def_id::DefId; use rustc_hir::def_id::DefId;
@ -673,8 +671,8 @@ fn get_rptr_lm<'tcx>(ty: &'tcx hir::Ty<'tcx>) -> Option<(&'tcx Lifetime, Mutabil
fn is_null_path(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { fn is_null_path(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
if let ExprKind::Call(pathexp, []) = expr.kind { if let ExprKind::Call(pathexp, []) = expr.kind {
expr_path_res(cx, pathexp).opt_def_id().map_or(false, |id| { path_def_id(cx, pathexp).map_or(false, |id| {
match_any_diagnostic_items(cx, id, &[sym::ptr_null, sym::ptr_null_mut]).is_some() matches!(cx.tcx.get_diagnostic_name(id), Some(sym::ptr_null | sym::ptr_null_mut))
}) })
} else { } else {
false false

View File

@ -2,19 +2,18 @@ use clippy_utils::consts::{constant, Constant};
use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then};
use clippy_utils::source::{snippet, snippet_opt, snippet_with_applicability}; use clippy_utils::source::{snippet, snippet_opt, snippet_with_applicability};
use clippy_utils::sugg::Sugg; use clippy_utils::sugg::Sugg;
use clippy_utils::{get_parent_expr, in_constant, is_integer_const, meets_msrv, msrvs, single_segment_path}; use clippy_utils::{get_parent_expr, in_constant, is_integer_const, meets_msrv, msrvs, path_to_local};
use clippy_utils::{higher, SpanlessEq}; use clippy_utils::{higher, SpanlessEq};
use if_chain::if_chain; use if_chain::if_chain;
use rustc_ast::ast::RangeLimits; use rustc_ast::ast::RangeLimits;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::{BinOpKind, Expr, ExprKind, PathSegment, QPath}; use rustc_hir::{BinOpKind, Expr, ExprKind, HirId, PathSegment, QPath};
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty; use rustc_middle::ty;
use rustc_semver::RustcVersion; use rustc_semver::RustcVersion;
use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::source_map::{Span, Spanned}; use rustc_span::source_map::{Span, Spanned};
use rustc_span::sym; use rustc_span::sym;
use rustc_span::symbol::Ident;
use std::cmp::Ordering; use std::cmp::Ordering;
declare_clippy_lint! { declare_clippy_lint! {
@ -220,12 +219,12 @@ fn check_possible_range_contains(cx: &LateContext<'_>, op: BinOpKind, l: &Expr<'
_ => return, _ => return,
}; };
// value, name, order (higher/lower), inclusiveness // value, name, order (higher/lower), inclusiveness
if let (Some((lval, lname, name_span, lval_span, lord, linc)), Some((rval, rname, _, rval_span, rord, rinc))) = if let (Some((lval, lid, name_span, lval_span, lord, linc)), Some((rval, rid, _, rval_span, rord, rinc))) =
(check_range_bounds(cx, l), check_range_bounds(cx, r)) (check_range_bounds(cx, l), check_range_bounds(cx, r))
{ {
// we only lint comparisons on the same name and with different // we only lint comparisons on the same name and with different
// direction // direction
if lname != rname || lord == rord { if lid != rid || lord == rord {
return; return;
} }
let ord = Constant::partial_cmp(cx.tcx, cx.typeck_results().expr_ty(l), &lval, &rval); let ord = Constant::partial_cmp(cx.tcx, cx.typeck_results().expr_ty(l), &lval, &rval);
@ -293,7 +292,7 @@ fn check_possible_range_contains(cx: &LateContext<'_>, op: BinOpKind, l: &Expr<'
} }
} }
fn check_range_bounds(cx: &LateContext<'_>, ex: &Expr<'_>) -> Option<(Constant, Ident, Span, Span, Ordering, bool)> { fn check_range_bounds(cx: &LateContext<'_>, ex: &Expr<'_>) -> Option<(Constant, HirId, Span, Span, Ordering, bool)> {
if let ExprKind::Binary(ref op, l, r) = ex.kind { if let ExprKind::Binary(ref op, l, r) = ex.kind {
let (inclusive, ordering) = match op.node { let (inclusive, ordering) = match op.node {
BinOpKind::Gt => (false, Ordering::Greater), BinOpKind::Gt => (false, Ordering::Greater),
@ -302,11 +301,11 @@ fn check_range_bounds(cx: &LateContext<'_>, ex: &Expr<'_>) -> Option<(Constant,
BinOpKind::Le => (true, Ordering::Less), BinOpKind::Le => (true, Ordering::Less),
_ => return None, _ => return None,
}; };
if let Some(id) = match_ident(l) { if let Some(id) = path_to_local(l) {
if let Some((c, _)) = constant(cx, cx.typeck_results(), r) { if let Some((c, _)) = constant(cx, cx.typeck_results(), r) {
return Some((c, id, l.span, r.span, ordering, inclusive)); return Some((c, id, l.span, r.span, ordering, inclusive));
} }
} else if let Some(id) = match_ident(r) { } else if let Some(id) = path_to_local(r) {
if let Some((c, _)) = constant(cx, cx.typeck_results(), l) { if let Some((c, _)) = constant(cx, cx.typeck_results(), l) {
return Some((c, id, r.span, l.span, ordering.reverse(), inclusive)); return Some((c, id, r.span, l.span, ordering.reverse(), inclusive));
} }
@ -315,17 +314,6 @@ fn check_range_bounds(cx: &LateContext<'_>, ex: &Expr<'_>) -> Option<(Constant,
None None
} }
fn match_ident(e: &Expr<'_>) -> Option<Ident> {
if let ExprKind::Path(ref qpath) = e.kind {
if let Some(seg) = single_segment_path(qpath) {
if seg.args.is_none() {
return Some(seg.ident);
}
}
}
None
}
fn check_range_zip_with_len(cx: &LateContext<'_>, path: &PathSegment<'_>, args: &[Expr<'_>], span: Span) { fn check_range_zip_with_len(cx: &LateContext<'_>, path: &PathSegment<'_>, args: &[Expr<'_>], span: Span) {
if_chain! { if_chain! {
if path.ident.as_str() == "zip"; if path.ident.as_str() == "zip";

View File

@ -2,7 +2,7 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
use clippy_utils::source::snippet_with_applicability; use clippy_utils::source::snippet_with_applicability;
use clippy_utils::sugg::Sugg; use clippy_utils::sugg::Sugg;
use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::ty::is_type_diagnostic_item;
use clippy_utils::{can_mut_borrow_both, differing_macro_contexts, eq_expr_value, std_or_core}; use clippy_utils::{can_mut_borrow_both, eq_expr_value, std_or_core};
use if_chain::if_chain; use if_chain::if_chain;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::{BinOpKind, Block, Expr, ExprKind, PatKind, QPath, Stmt, StmtKind}; use rustc_hir::{BinOpKind, Block, Expr, ExprKind, PatKind, QPath, Stmt, StmtKind};
@ -172,7 +172,7 @@ fn check_suspicious_swap(cx: &LateContext<'_>, block: &Block<'_>) {
if_chain! { if_chain! {
if let StmtKind::Semi(first) = w[0].kind; if let StmtKind::Semi(first) = w[0].kind;
if let StmtKind::Semi(second) = w[1].kind; if let StmtKind::Semi(second) = w[1].kind;
if !differing_macro_contexts(first.span, second.span); if first.span.ctxt() == second.span.ctxt();
if let ExprKind::Assign(lhs0, rhs0, _) = first.kind; if let ExprKind::Assign(lhs0, rhs0, _) = first.kind;
if let ExprKind::Assign(lhs1, rhs1, _) = second.kind; if let ExprKind::Assign(lhs1, rhs1, _) = second.kind;
if eq_expr_value(cx, lhs0, rhs1); if eq_expr_value(cx, lhs0, rhs1);

View File

@ -1,29 +1,31 @@
use super::utils::is_layout_incompatible; use super::utils::is_layout_incompatible;
use super::UNSOUND_COLLECTION_TRANSMUTE; use super::UNSOUND_COLLECTION_TRANSMUTE;
use clippy_utils::diagnostics::span_lint; use clippy_utils::diagnostics::span_lint;
use clippy_utils::match_any_diagnostic_items;
use rustc_hir::Expr; use rustc_hir::Expr;
use rustc_lint::LateContext; use rustc_lint::LateContext;
use rustc_middle::ty::{self, Ty}; use rustc_middle::ty::{self, Ty};
use rustc_span::symbol::{sym, Symbol}; use rustc_span::symbol::sym;
// used to check for UNSOUND_COLLECTION_TRANSMUTE
static COLLECTIONS: &[Symbol] = &[
sym::Vec,
sym::VecDeque,
sym::BinaryHeap,
sym::BTreeSet,
sym::BTreeMap,
sym::HashSet,
sym::HashMap,
];
/// Checks for `unsound_collection_transmute` lint. /// Checks for `unsound_collection_transmute` lint.
/// Returns `true` if it's triggered, otherwise returns `false`. /// Returns `true` if it's triggered, otherwise returns `false`.
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, from_ty: Ty<'tcx>, to_ty: Ty<'tcx>) -> bool { pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, from_ty: Ty<'tcx>, to_ty: Ty<'tcx>) -> bool {
match (&from_ty.kind(), &to_ty.kind()) { match (&from_ty.kind(), &to_ty.kind()) {
(ty::Adt(from_adt, from_substs), ty::Adt(to_adt, to_substs)) => { (ty::Adt(from_adt, from_substs), ty::Adt(to_adt, to_substs)) => {
if from_adt.did != to_adt.did || match_any_diagnostic_items(cx, to_adt.did, COLLECTIONS).is_none() { if from_adt.did != to_adt.did {
return false;
}
if !matches!(
cx.tcx.get_diagnostic_name(to_adt.did),
Some(
sym::BTreeMap
| sym::BTreeSet
| sym::BinaryHeap
| sym::HashMap
| sym::HashSet
| sym::Vec
| sym::VecDeque
)
) {
return false; return false;
} }
if from_substs if from_substs

View File

@ -1,8 +1,8 @@
use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::is_ty_param_diagnostic_item; use clippy_utils::{path_def_id, qpath_generic_tys};
use rustc_hir::{self as hir, def_id::DefId, QPath}; use rustc_hir::{self as hir, def_id::DefId, QPath};
use rustc_lint::LateContext; use rustc_lint::LateContext;
use rustc_span::symbol::sym; use rustc_span::{sym, Symbol};
use super::BOX_COLLECTION; use super::BOX_COLLECTION;
@ -11,10 +11,9 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_
if Some(def_id) == cx.tcx.lang_items().owned_box(); if Some(def_id) == cx.tcx.lang_items().owned_box();
if let Some(item_type) = get_std_collection(cx, qpath); if let Some(item_type) = get_std_collection(cx, qpath);
then { then {
let generic = if item_type == "String" { let generic = match item_type {
"" sym::String => "",
} else { _ => "<..>",
"<..>"
}; };
span_lint_and_help( span_lint_and_help(
cx, cx,
@ -37,14 +36,10 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_
} }
} }
fn get_std_collection(cx: &LateContext<'_>, qpath: &QPath<'_>) -> Option<&'static str> { fn get_std_collection(cx: &LateContext<'_>, qpath: &QPath<'_>) -> Option<Symbol> {
if is_ty_param_diagnostic_item(cx, qpath, sym::Vec).is_some() { let param = qpath_generic_tys(qpath).next()?;
Some("Vec") let id = path_def_id(cx, param)?;
} else if is_ty_param_diagnostic_item(cx, qpath, sym::String).is_some() { cx.tcx
Some("String") .get_diagnostic_name(id)
} else if is_ty_param_diagnostic_item(cx, qpath, sym::HashMap).is_some() { .filter(|&name| matches!(name, sym::HashMap | sym::String | sym::Vec))
Some("HashMap")
} else {
None
}
} }

View File

@ -1,5 +1,6 @@
use clippy_utils::diagnostics::span_lint; use clippy_utils::diagnostics::span_lint;
use clippy_utils::is_ty_param_diagnostic_item; use clippy_utils::{path_def_id, qpath_generic_tys};
use if_chain::if_chain;
use rustc_hir::{self as hir, def_id::DefId, QPath}; use rustc_hir::{self as hir, def_id::DefId, QPath};
use rustc_lint::LateContext; use rustc_lint::LateContext;
use rustc_span::symbol::sym; use rustc_span::symbol::sym;
@ -7,16 +8,21 @@ use rustc_span::symbol::sym;
use super::OPTION_OPTION; use super::OPTION_OPTION;
pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_>, def_id: DefId) -> bool { pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_>, def_id: DefId) -> bool {
if cx.tcx.is_diagnostic_item(sym::Option, def_id) && is_ty_param_diagnostic_item(cx, qpath, sym::Option).is_some() { if_chain! {
span_lint( if cx.tcx.is_diagnostic_item(sym::Option, def_id);
cx, if let Some(arg) = qpath_generic_tys(qpath).next();
OPTION_OPTION, if path_def_id(cx, arg) == Some(def_id);
hir_ty.span, then {
"consider using `Option<T>` instead of `Option<Option<T>>` or a custom \ span_lint(
enum if you need to distinguish all 3 cases", cx,
); OPTION_OPTION,
true hir_ty.span,
} else { "consider using `Option<T>` instead of `Option<Option<T>>` or a custom \
false enum if you need to distinguish all 3 cases",
);
true
} else {
false
}
} }
} }

View File

@ -1,6 +1,6 @@
use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_with_applicability; use clippy_utils::source::snippet_with_applicability;
use clippy_utils::{get_qpath_generic_tys, is_ty_param_diagnostic_item}; use clippy_utils::{path_def_id, qpath_generic_tys};
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::{self as hir, def_id::DefId, QPath, TyKind}; use rustc_hir::{self as hir, def_id::DefId, QPath, TyKind};
use rustc_lint::LateContext; use rustc_lint::LateContext;
@ -20,12 +20,17 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_
format!("Rc<{}>", alternate), format!("Rc<{}>", alternate),
Applicability::MachineApplicable, Applicability::MachineApplicable,
); );
} else if let Some(ty) = is_ty_param_diagnostic_item(cx, qpath, sym::Vec) { } else {
let Some(ty) = qpath_generic_tys(qpath).next() else { return false };
let Some(id) = path_def_id(cx, ty) else { return false };
if !cx.tcx.is_diagnostic_item(sym::Vec, id) {
return false;
}
let qpath = match &ty.kind { let qpath = match &ty.kind {
TyKind::Path(qpath) => qpath, TyKind::Path(qpath) => qpath,
_ => return false, _ => return false,
}; };
let inner_span = match get_qpath_generic_tys(qpath).next() { let inner_span = match qpath_generic_tys(qpath).next() {
Some(ty) => ty.span, Some(ty) => ty.span,
None => return false, None => return false,
}; };
@ -55,12 +60,16 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_
format!("Arc<{}>", alternate), format!("Arc<{}>", alternate),
Applicability::MachineApplicable, Applicability::MachineApplicable,
); );
} else if let Some(ty) = is_ty_param_diagnostic_item(cx, qpath, sym::Vec) { } else if let Some(ty) = qpath_generic_tys(qpath).next() {
let Some(id) = path_def_id(cx, ty) else { return false };
if !cx.tcx.is_diagnostic_item(sym::Vec, id) {
return false;
}
let qpath = match &ty.kind { let qpath = match &ty.kind {
TyKind::Path(qpath) => qpath, TyKind::Path(qpath) => qpath,
_ => return false, _ => return false,
}; };
let inner_span = match get_qpath_generic_tys(qpath).next() { let inner_span = match qpath_generic_tys(qpath).next() {
Some(ty) => ty.span, Some(ty) => ty.span,
None => return false, None => return false,
}; };
@ -85,13 +94,13 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_
} }
fn match_buffer_type(cx: &LateContext<'_>, qpath: &QPath<'_>) -> Option<&'static str> { fn match_buffer_type(cx: &LateContext<'_>, qpath: &QPath<'_>) -> Option<&'static str> {
if is_ty_param_diagnostic_item(cx, qpath, sym::String).is_some() { let ty = qpath_generic_tys(qpath).next()?;
Some("str") let id = path_def_id(cx, ty)?;
} else if is_ty_param_diagnostic_item(cx, qpath, sym::OsString).is_some() { let path = match cx.tcx.get_diagnostic_name(id)? {
Some("std::ffi::OsStr") sym::String => "str",
} else if is_ty_param_diagnostic_item(cx, qpath, sym::PathBuf).is_some() { sym::OsString => "std::ffi::OsStr",
Some("std::path::Path") sym::PathBuf => "std::path::Path",
} else { _ => return None,
None };
} Some(path)
} }

View File

@ -1,5 +1,5 @@
use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::is_ty_param_diagnostic_item; use clippy_utils::{path_def_id, qpath_generic_tys};
use if_chain::if_chain; use if_chain::if_chain;
use rustc_hir::{self as hir, def_id::DefId, QPath}; use rustc_hir::{self as hir, def_id::DefId, QPath};
use rustc_lint::LateContext; use rustc_lint::LateContext;
@ -10,7 +10,9 @@ use super::RC_MUTEX;
pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_>, def_id: DefId) -> bool { pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_>, def_id: DefId) -> bool {
if_chain! { if_chain! {
if cx.tcx.is_diagnostic_item(sym::Rc, def_id) ; if cx.tcx.is_diagnostic_item(sym::Rc, def_id) ;
if let Some(_) = is_ty_param_diagnostic_item(cx, qpath, sym::Mutex) ; if let Some(arg) = qpath_generic_tys(qpath).next();
if let Some(id) = path_def_id(cx, arg);
if cx.tcx.is_diagnostic_item(sym::Mutex, id);
then { then {
span_lint_and_help( span_lint_and_help(
cx, cx,

View File

@ -1,8 +1,8 @@
use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::source::{snippet, snippet_with_applicability};
use clippy_utils::{get_qpath_generic_tys, is_ty_param_diagnostic_item, is_ty_param_lang_item}; use clippy_utils::{path_def_id, qpath_generic_tys};
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::{self as hir, def_id::DefId, LangItem, QPath, TyKind}; use rustc_hir::{self as hir, def_id::DefId, QPath, TyKind};
use rustc_lint::LateContext; use rustc_lint::LateContext;
use rustc_span::symbol::sym; use rustc_span::symbol::sym;
@ -39,21 +39,20 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_
return true; return true;
} }
let (inner_sym, ty) = if let Some(ty) = is_ty_param_lang_item(cx, qpath, LangItem::OwnedBox) { let Some(ty) = qpath_generic_tys(qpath).next() else { return false };
("Box", ty) let Some(id) = path_def_id(cx, ty) else { return false };
} else if let Some(ty) = is_ty_param_diagnostic_item(cx, qpath, sym::Rc) { let (inner_sym, ty) = match cx.tcx.get_diagnostic_name(id) {
("Rc", ty) Some(sym::Arc) => ("Arc", ty),
} else if let Some(ty) = is_ty_param_diagnostic_item(cx, qpath, sym::Arc) { Some(sym::Rc) => ("Rc", ty),
("Arc", ty) _ if Some(id) == cx.tcx.lang_items().owned_box() => ("Box", ty),
} else { _ => return false,
return false;
}; };
let inner_qpath = match &ty.kind { let inner_qpath = match &ty.kind {
TyKind::Path(inner_qpath) => inner_qpath, TyKind::Path(inner_qpath) => inner_qpath,
_ => return false, _ => return false,
}; };
let inner_span = match get_qpath_generic_tys(inner_qpath).next() { let inner_span = match qpath_generic_tys(inner_qpath).next() {
Some(ty) => { Some(ty) => {
// Box<Box<dyn T>> is smaller than Box<dyn T> because of wide pointers // Box<Box<dyn T>> is smaller than Box<dyn T> because of wide pointers
if matches!(ty.kind, TyKind::TraitObject(..)) { if matches!(ty.kind, TyKind::TraitObject(..)) {

View File

@ -1,7 +1,7 @@
use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::higher; use clippy_utils::higher;
use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::ty::is_type_diagnostic_item;
use clippy_utils::{differing_macro_contexts, path_to_local, usage::is_potentially_mutated}; use clippy_utils::{path_to_local, usage::is_potentially_mutated};
use if_chain::if_chain; use if_chain::if_chain;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::intravisit::{walk_expr, walk_fn, FnKind, Visitor}; use rustc_hir::intravisit::{walk_expr, walk_fn, FnKind, Visitor};
@ -238,8 +238,9 @@ impl<'a, 'tcx> Visitor<'tcx> for UnwrappableVariablesVisitor<'a, 'tcx> {
if let Some(unwrappable) = self.unwrappables.iter() if let Some(unwrappable) = self.unwrappables.iter()
.find(|u| u.local_id == id); .find(|u| u.local_id == id);
// Span contexts should not differ with the conditional branch // Span contexts should not differ with the conditional branch
if !differing_macro_contexts(unwrappable.branch.span, expr.span); let span_ctxt = expr.span.ctxt();
if !differing_macro_contexts(unwrappable.branch.span, unwrappable.check.span); if unwrappable.branch.span.ctxt() == span_ctxt;
if unwrappable.check.span.ctxt() == span_ctxt;
then { then {
if call_to_unwrap == unwrappable.safe_to_unwrap { if call_to_unwrap == unwrappable.safe_to_unwrap {
let is_entire_condition = unwrappable.is_entire_condition; let is_entire_condition = unwrappable.is_entire_condition;

View File

@ -4,8 +4,8 @@ use clippy_utils::macros::root_macro_call_first_node;
use clippy_utils::source::snippet; use clippy_utils::source::snippet;
use clippy_utils::ty::match_type; use clippy_utils::ty::match_type;
use clippy_utils::{ use clippy_utils::{
higher, is_else_clause, is_expn_of, is_expr_path_def_path, is_lint_allowed, match_def_path, method_calls, def_path_res, higher, is_else_clause, is_expn_of, is_expr_path_def_path, is_lint_allowed, match_def_path,
path_to_res, paths, peel_blocks_with_stmt, SpanlessEq, method_calls, paths, peel_blocks_with_stmt, SpanlessEq,
}; };
use if_chain::if_chain; use if_chain::if_chain;
use rustc_ast as ast; use rustc_ast as ast;
@ -844,7 +844,7 @@ impl<'tcx> LateLintPass<'tcx> for MatchTypeOnDiagItem {
// Extract the path to the matched type // Extract the path to the matched type
if let Some(segments) = path_to_matched_type(cx, ty_path); if let Some(segments) = path_to_matched_type(cx, ty_path);
let segments: Vec<&str> = segments.iter().map(Symbol::as_str).collect(); let segments: Vec<&str> = segments.iter().map(Symbol::as_str).collect();
if let Some(ty_did) = path_to_res(cx, &segments[..]).opt_def_id(); if let Some(ty_did) = def_path_res(cx, &segments[..]).opt_def_id();
// Check if the matched type is a diagnostic item // Check if the matched type is a diagnostic item
if let Some(item_name) = cx.tcx.get_diagnostic_name(ty_did); if let Some(item_name) = cx.tcx.get_diagnostic_name(ty_did);
then { then {
@ -917,7 +917,7 @@ fn path_to_matched_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option<Ve
// This is not a complete resolver for paths. It works on all the paths currently used in the paths // This is not a complete resolver for paths. It works on all the paths currently used in the paths
// module. That's all it does and all it needs to do. // module. That's all it does and all it needs to do.
pub fn check_path(cx: &LateContext<'_>, path: &[&str]) -> bool { pub fn check_path(cx: &LateContext<'_>, path: &[&str]) -> bool {
if path_to_res(cx, path) != Res::Err { if def_path_res(cx, path) != Res::Err {
return true; return true;
} }
@ -999,7 +999,7 @@ impl<'tcx> LateLintPass<'tcx> for InterningDefinedSymbol {
} }
for &module in &[&paths::KW_MODULE, &paths::SYM_MODULE] { for &module in &[&paths::KW_MODULE, &paths::SYM_MODULE] {
if let Some(def_id) = path_to_res(cx, module).opt_def_id() { if let Some(def_id) = def_path_res(cx, module).opt_def_id() {
for item in cx.tcx.module_children(def_id).iter() { for item in cx.tcx.module_children(def_id).iter() {
if_chain! { if_chain! {
if let Res::Def(DefKind::Const, item_def_id) = item.res; if let Res::Def(DefKind::Const, item_def_id) = item.res;

View File

@ -1,5 +1,4 @@
use crate::consts::{constant_context, constant_simple}; use crate::consts::{constant_context, constant_simple};
use crate::differing_macro_contexts;
use crate::source::snippet_opt; use crate::source::snippet_opt;
use rustc_ast::ast::InlineAsmTemplatePiece; use rustc_ast::ast::InlineAsmTemplatePiece;
use rustc_data_structures::fx::FxHasher; use rustc_data_structures::fx::FxHasher;
@ -186,7 +185,7 @@ impl HirEqInterExpr<'_, '_, '_> {
#[allow(clippy::similar_names)] #[allow(clippy::similar_names)]
pub fn eq_expr(&mut self, left: &Expr<'_>, right: &Expr<'_>) -> bool { pub fn eq_expr(&mut self, left: &Expr<'_>, right: &Expr<'_>) -> bool {
if !self.inner.allow_side_effects && differing_macro_contexts(left.span, right.span) { if !self.inner.allow_side_effects && left.span.ctxt() != right.span.ctxt() {
return false; return false;
} }

View File

@ -77,9 +77,9 @@ use rustc_hir::itemlikevisit::ItemLikeVisitor;
use rustc_hir::LangItem::{OptionNone, ResultErr, ResultOk}; use rustc_hir::LangItem::{OptionNone, ResultErr, ResultOk};
use rustc_hir::{ use rustc_hir::{
def, lang_items, Arm, ArrayLen, BindingAnnotation, Block, BlockCheckMode, Body, Constness, Destination, Expr, def, lang_items, Arm, ArrayLen, BindingAnnotation, Block, BlockCheckMode, Body, Constness, Destination, Expr,
ExprKind, FnDecl, ForeignItem, GenericArgs, HirId, Impl, ImplItem, ImplItemKind, IsAsync, Item, ItemKind, LangItem, ExprKind, FnDecl, ForeignItem, HirId, Impl, ImplItem, ImplItemKind, IsAsync, Item, ItemKind, LangItem, Local,
Local, MatchSource, Mutability, Node, Param, Pat, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, MatchSource, Mutability, Node, Param, Pat, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, Target,
Target, TraitItem, TraitItemKind, TraitRef, TyKind, UnOp, TraitItem, TraitItemKind, TraitRef, TyKind, UnOp,
}; };
use rustc_lint::{LateContext, Level, Lint, LintContext}; use rustc_lint::{LateContext, Level, Lint, LintContext};
use rustc_middle::hir::place::PlaceBase; use rustc_middle::hir::place::PlaceBase;
@ -134,13 +134,6 @@ macro_rules! extract_msrv_attr {
}; };
} }
/// Returns `true` if the two spans come from differing expansions (i.e., one is
/// from a macro and one isn't).
#[must_use]
pub fn differing_macro_contexts(lhs: Span, rhs: Span) -> bool {
rhs.ctxt() != lhs.ctxt()
}
/// If the given expression is a local binding, find the initializer expression. /// If the given expression is a local binding, find the initializer expression.
/// If that initializer expression is another local binding, find its initializer again. /// If that initializer expression is another local binding, find its initializer again.
/// This process repeats as long as possible (but usually no more than once). Initializer /// This process repeats as long as possible (but usually no more than once). Initializer
@ -262,44 +255,6 @@ pub fn is_wild(pat: &Pat<'_>) -> bool {
matches!(pat.kind, PatKind::Wild) matches!(pat.kind, PatKind::Wild)
} }
/// Checks if the first type parameter is a lang item.
pub fn is_ty_param_lang_item<'tcx>(
cx: &LateContext<'_>,
qpath: &QPath<'tcx>,
item: LangItem,
) -> Option<&'tcx hir::Ty<'tcx>> {
let ty = get_qpath_generic_tys(qpath).next()?;
if let TyKind::Path(qpath) = &ty.kind {
cx.qpath_res(qpath, ty.hir_id)
.opt_def_id()
.map_or(false, |id| {
cx.tcx.lang_items().require(item).map_or(false, |lang_id| id == lang_id)
})
.then(|| ty)
} else {
None
}
}
/// Checks if the first type parameter is a diagnostic item.
pub fn is_ty_param_diagnostic_item<'tcx>(
cx: &LateContext<'_>,
qpath: &QPath<'tcx>,
item: Symbol,
) -> Option<&'tcx hir::Ty<'tcx>> {
let ty = get_qpath_generic_tys(qpath).next()?;
if let TyKind::Path(qpath) = &ty.kind {
cx.qpath_res(qpath, ty.hir_id)
.opt_def_id()
.map_or(false, |id| cx.tcx.is_diagnostic_item(item, id))
.then(|| ty)
} else {
None
}
}
/// Checks if the method call given in `expr` belongs to the given trait. /// Checks if the method call given in `expr` belongs to the given trait.
/// This is a deprecated function, consider using [`is_trait_method`]. /// This is a deprecated function, consider using [`is_trait_method`].
pub fn match_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, path: &[&str]) -> bool { pub fn match_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, path: &[&str]) -> bool {
@ -360,35 +315,17 @@ pub fn last_path_segment<'tcx>(path: &QPath<'tcx>) -> &'tcx PathSegment<'tcx> {
} }
} }
pub fn get_qpath_generics<'tcx>(path: &QPath<'tcx>) -> Option<&'tcx GenericArgs<'tcx>> { pub fn qpath_generic_tys<'tcx>(qpath: &QPath<'tcx>) -> impl Iterator<Item = &'tcx hir::Ty<'tcx>> {
match path { last_path_segment(qpath)
QPath::Resolved(_, p) => p.segments.last().and_then(|s| s.args), .args
QPath::TypeRelative(_, s) => s.args, .map_or(&[][..], |a| a.args)
QPath::LangItem(..) => None,
}
}
pub fn get_qpath_generic_tys<'tcx>(path: &QPath<'tcx>) -> impl Iterator<Item = &'tcx hir::Ty<'tcx>> {
get_qpath_generics(path)
.map_or([].as_ref(), |a| a.args)
.iter() .iter()
.filter_map(|a| { .filter_map(|a| match a {
if let hir::GenericArg::Type(ty) = a { hir::GenericArg::Type(ty) => Some(ty),
Some(ty) _ => None,
} else {
None
}
}) })
} }
pub fn single_segment_path<'tcx>(path: &QPath<'tcx>) -> Option<&'tcx PathSegment<'tcx>> {
match *path {
QPath::Resolved(_, path) => path.segments.get(0),
QPath::TypeRelative(_, seg) => Some(seg),
QPath::LangItem(..) => None,
}
}
/// THIS METHOD IS DEPRECATED and will eventually be removed since it does not match against the /// THIS METHOD IS DEPRECATED and will eventually be removed since it does not match against the
/// entire path or resolved `DefId`. Prefer using `match_def_path`. Consider getting a `DefId` from /// entire path or resolved `DefId`. Prefer using `match_def_path`. Consider getting a `DefId` from
/// `QPath::Resolved.1.res.opt_def_id()`. /// `QPath::Resolved.1.res.opt_def_id()`.
@ -420,37 +357,17 @@ pub fn match_qpath(path: &QPath<'_>, segments: &[&str]) -> bool {
} }
} }
/// If the expression is a path, resolve it. Otherwise, return `Res::Err`.
pub fn expr_path_res(cx: &LateContext<'_>, expr: &Expr<'_>) -> Res {
if let ExprKind::Path(p) = &expr.kind {
cx.qpath_res(p, expr.hir_id)
} else {
Res::Err
}
}
/// Resolves the path to a `DefId` and checks if it matches the given path.
pub fn is_qpath_def_path(cx: &LateContext<'_>, path: &QPath<'_>, hir_id: HirId, segments: &[&str]) -> bool {
cx.qpath_res(path, hir_id)
.opt_def_id()
.map_or(false, |id| match_def_path(cx, id, segments))
}
/// If the expression is a path, resolves it to a `DefId` and checks if it matches the given path. /// If the expression is a path, resolves it to a `DefId` and checks if it matches the given path.
/// ///
/// Please use `is_expr_diagnostic_item` if the target is a diagnostic item. /// Please use `is_expr_diagnostic_item` if the target is a diagnostic item.
pub fn is_expr_path_def_path(cx: &LateContext<'_>, expr: &Expr<'_>, segments: &[&str]) -> bool { pub fn is_expr_path_def_path(cx: &LateContext<'_>, expr: &Expr<'_>, segments: &[&str]) -> bool {
expr_path_res(cx, expr) path_def_id(cx, expr).map_or(false, |id| match_def_path(cx, id, segments))
.opt_def_id()
.map_or(false, |id| match_def_path(cx, id, segments))
} }
/// If the expression is a path, resolves it to a `DefId` and checks if it matches the given /// If the expression is a path, resolves it to a `DefId` and checks if it matches the given
/// diagnostic item. /// diagnostic item.
pub fn is_expr_diagnostic_item(cx: &LateContext<'_>, expr: &Expr<'_>, diag_item: Symbol) -> bool { pub fn is_expr_diagnostic_item(cx: &LateContext<'_>, expr: &Expr<'_>, diag_item: Symbol) -> bool {
expr_path_res(cx, expr) path_def_id(cx, expr).map_or(false, |id| cx.tcx.is_diagnostic_item(diag_item, id))
.opt_def_id()
.map_or(false, |id| cx.tcx.is_diagnostic_item(diag_item, id))
} }
/// THIS METHOD IS DEPRECATED and will eventually be removed since it does not match against the /// THIS METHOD IS DEPRECATED and will eventually be removed since it does not match against the
@ -497,8 +414,46 @@ pub fn path_to_local_id(expr: &Expr<'_>, id: HirId) -> bool {
path_to_local(expr) == Some(id) path_to_local(expr) == Some(id)
} }
/// Gets the definition associated to a path. pub trait MaybePath<'hir> {
pub fn path_to_res(cx: &LateContext<'_>, path: &[&str]) -> Res { fn hir_id(&self) -> HirId;
fn qpath_opt(&self) -> Option<&QPath<'hir>>;
}
macro_rules! maybe_path {
($ty:ident, $kind:ident) => {
impl<'hir> MaybePath<'hir> for hir::$ty<'hir> {
fn hir_id(&self) -> HirId {
self.hir_id
}
fn qpath_opt(&self) -> Option<&QPath<'hir>> {
match &self.kind {
hir::$kind::Path(qpath) => Some(qpath),
_ => None,
}
}
}
};
}
maybe_path!(Expr, ExprKind);
maybe_path!(Pat, PatKind);
maybe_path!(Ty, TyKind);
/// If `maybe_path` is a path node, resolves it, otherwise returns `Res::Err`
pub fn path_res<'tcx>(cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx>) -> Res {
match maybe_path.qpath_opt() {
None => Res::Err,
Some(qpath) => cx.qpath_res(qpath, maybe_path.hir_id()),
}
}
/// If `maybe_path` is a path node which resolves to an item, retrieves the item ID
pub fn path_def_id<'tcx>(cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx>) -> Option<DefId> {
path_res(cx, maybe_path).opt_def_id()
}
/// Resolves a def path like `std::vec::Vec`.
/// This function is expensive and should be used sparingly.
pub fn def_path_res(cx: &LateContext<'_>, path: &[&str]) -> Res {
macro_rules! try_res { macro_rules! try_res {
($e:expr) => { ($e:expr) => {
match $e { match $e {
@ -574,7 +529,7 @@ pub fn path_to_res(cx: &LateContext<'_>, path: &[&str]) -> Res {
/// Convenience function to get the `DefId` of a trait by path. /// Convenience function to get the `DefId` of a trait by path.
/// It could be a trait or trait alias. /// It could be a trait or trait alias.
pub fn get_trait_def_id(cx: &LateContext<'_>, path: &[&str]) -> Option<DefId> { pub fn get_trait_def_id(cx: &LateContext<'_>, path: &[&str]) -> Option<DefId> {
match path_to_res(cx, path) { match def_path_res(cx, path) {
Res::Def(DefKind::Trait | DefKind::TraitAlias, trait_id) => Some(trait_id), Res::Def(DefKind::Trait | DefKind::TraitAlias, trait_id) => Some(trait_id),
_ => None, _ => None,
} }
@ -1649,7 +1604,7 @@ pub fn match_function_call<'tcx>(
/// Checks if the given `DefId` matches any of the paths. Returns the index of matching path, if /// Checks if the given `DefId` matches any of the paths. Returns the index of matching path, if
/// any. /// any.
/// ///
/// Please use `match_any_diagnostic_items` if the targets are all diagnostic items. /// Please use `tcx.get_diagnostic_name` if the targets are all diagnostic items.
pub fn match_any_def_paths(cx: &LateContext<'_>, did: DefId, paths: &[&[&str]]) -> Option<usize> { pub fn match_any_def_paths(cx: &LateContext<'_>, did: DefId, paths: &[&[&str]]) -> Option<usize> {
let search_path = cx.get_def_path(did); let search_path = cx.get_def_path(did);
paths paths
@ -1657,14 +1612,6 @@ pub fn match_any_def_paths(cx: &LateContext<'_>, did: DefId, paths: &[&[&str]])
.position(|p| p.iter().map(|x| Symbol::intern(x)).eq(search_path.iter().copied())) .position(|p| p.iter().map(|x| Symbol::intern(x)).eq(search_path.iter().copied()))
} }
/// Checks if the given `DefId` matches any of provided diagnostic items. Returns the index of
/// matching path, if any.
pub fn match_any_diagnostic_items(cx: &LateContext<'_>, def_id: DefId, diag_items: &[Symbol]) -> Option<usize> {
diag_items
.iter()
.position(|item| cx.tcx.is_diagnostic_item(*item, def_id))
}
/// Checks if the given `DefId` matches the path. /// Checks if the given `DefId` matches the path.
pub fn match_def_path<'tcx>(cx: &LateContext<'tcx>, did: DefId, syms: &[&str]) -> bool { pub fn match_def_path<'tcx>(cx: &LateContext<'tcx>, did: DefId, syms: &[&str]) -> bool {
// We should probably move to Symbols in Clippy as well rather than interning every time. // We should probably move to Symbols in Clippy as well rather than interning every time.
@ -1817,8 +1764,7 @@ pub fn is_expr_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool
match expr.kind { match expr.kind {
ExprKind::Closure(_, _, body_id, _, _) => is_body_identity_function(cx, cx.tcx.hir().body(body_id)), ExprKind::Closure(_, _, body_id, _, _) => is_body_identity_function(cx, cx.tcx.hir().body(body_id)),
ExprKind::Path(ref path) => is_qpath_def_path(cx, path, expr.hir_id, &paths::CONVERT_IDENTITY), _ => path_def_id(cx, expr).map_or(false, |id| match_def_path(cx, id, &paths::CONVERT_IDENTITY)),
_ => false,
} }
} }

View File

@ -20,7 +20,7 @@ use rustc_trait_selection::infer::InferCtxtExt;
use rustc_trait_selection::traits::query::normalize::AtExt; use rustc_trait_selection::traits::query::normalize::AtExt;
use std::iter; use std::iter;
use crate::{expr_path_res, match_def_path, must_use_attr}; use crate::{match_def_path, must_use_attr, path_res};
// Checks if the given type implements copy. // Checks if the given type implements copy.
pub fn is_copy<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { pub fn is_copy<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
@ -443,7 +443,7 @@ impl<'tcx> ExprFnSig<'tcx> {
/// If the expression is function like, get the signature for it. /// If the expression is function like, get the signature for it.
pub fn expr_sig<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) -> Option<ExprFnSig<'tcx>> { pub fn expr_sig<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) -> Option<ExprFnSig<'tcx>> {
if let Res::Def(DefKind::Fn | DefKind::Ctor(_, CtorKind::Fn) | DefKind::AssocFn, id) = expr_path_res(cx, expr) { if let Res::Def(DefKind::Fn | DefKind::Ctor(_, CtorKind::Fn) | DefKind::AssocFn, id) = path_res(cx, expr) {
Some(ExprFnSig::Sig(cx.tcx.fn_sig(id))) Some(ExprFnSig::Sig(cx.tcx.fn_sig(id)))
} else { } else {
let ty = cx.typeck_results().expr_ty_adjusted(expr).peel_refs(); let ty = cx.typeck_results().expr_ty_adjusted(expr).peel_refs();

View File

@ -235,7 +235,11 @@ Use the following functions to deal with macros:
assert_eq!(in_external_macro(cx.sess(), match_span), true); assert_eq!(in_external_macro(cx.sess(), match_span), true);
``` ```
- `differing_macro_contexts()`: returns true if the two given spans are not from the same context - `span.ctxt()`: the span's context represents whether it is from expansion, and if so, what expanded it
One thing `SpanContext` is useful for is to check if two spans are in the same context. For example,
in `a == b`, `a` and `b` have the same context. In a `macro_rules!` with `a == $b`, `$b` is expanded to some
expression with a different context from `a`.
```rust ```rust
macro_rules! m { macro_rules! m {
@ -252,7 +256,7 @@ Use the following functions to deal with macros:
// These spans are not from the same context // These spans are not from the same context
// x.is_some() is from inside the macro // x.is_some() is from inside the macro
// x.unwrap() is from outside the macro // x.unwrap() is from outside the macro
assert_eq!(differing_macro_contexts(x_is_some_span, x_unwrap_span), true); assert_eq!(x_is_some_span.ctxt(), x_unwrap_span.ctxt());
``` ```
[TyS]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TyS.html [TyS]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TyS.html