Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ debugging to find the actual problem behind the issue.

[`T-middle`] issues can be more involved and require verifying types. The [`ty`] module contains a
lot of methods that are useful, though one of the most useful would be `expr_ty` (gives the type of
an AST expression). `match_def_path()` in Clippy's `utils` module can also be useful.
an AST expression).

[`good-first-issue`]: https://github.com/rust-lang/rust-clippy/labels/good-first-issue
[`S-inactive-closed`]: https://github.com/rust-lang/rust-clippy/pulls?q=is%3Aclosed+label%3AS-inactive-closed
Expand Down
4 changes: 2 additions & 2 deletions book/src/development/common_tools_writing_lints.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ arguments have to be checked separately.

```rust
use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item};
use clippy_utils::{paths, match_def_path};
use clippy_utils::paths;
use rustc_span::symbol::sym;
use rustc_hir::LangItem;

Expand All @@ -108,7 +108,7 @@ impl LateLintPass<'_> for MyStructLint {

// 3. Using the type path
// This method should be avoided if possible
if match_def_path(cx, def_id, &paths::RESULT) {
if paths::RESULT.matches_ty(cx, ty) {
// The type is a `core::result::Result`
}
}
Expand Down
16 changes: 9 additions & 7 deletions book/src/development/trait_checking.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,22 +73,24 @@ impl LateLintPass<'_> for CheckDropTraitLint {
## Using Type Path

If neither diagnostic item nor a language item is available, we can use
[`clippy_utils::paths`][paths] with the `match_trait_method` to determine trait
implementation.
[`clippy_utils::paths`][paths] to determine get a trait's `DefId`.

> **Note**: This approach should be avoided if possible, the best thing to do would be to make a PR to [`rust-lang/rust`][rust] adding a diagnostic item.

Below, we check if the given `expr` implements the `Iterator`'s trait method `cloned` :
Below, we check if the given `expr` implements [`core::iter::Step`](https://doc.rust-lang.org/std/iter/trait.Step.html):

```rust
use clippy_utils::{match_trait_method, paths};
use clippy_utils::{implements_trait, paths};
use rustc_hir::Expr;
use rustc_lint::{LateContext, LateLintPass};

impl LateLintPass<'_> for CheckTokioAsyncReadExtTrait {
impl LateLintPass<'_> for CheckIterStep {
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
if match_trait_method(cx, expr, &paths::CORE_ITER_CLONED) {
println!("`expr` implements `CORE_ITER_CLONED` trait!");
let ty = cx.typeck_results().expr_ty(expr);
if let Some(trait_def_id) = paths::ITER_STEP.first(cx)
&& implements_trait(cx, ty, trait_def_id, &[])
{
println!("`expr` implements the `core::iter::Step` trait!");
}
}
}
Expand Down
3 changes: 0 additions & 3 deletions clippy.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,11 @@ lint-commented-code = true
[[disallowed-methods]]
path = "rustc_lint::context::LintContext::lint"
reason = "this function does not add a link to our documentation, please use the `clippy_utils::diagnostics::span_lint*` functions instead"
allow-invalid = true

[[disallowed-methods]]
path = "rustc_lint::context::LintContext::span_lint"
reason = "this function does not add a link to our documentation, please use the `clippy_utils::diagnostics::span_lint*` functions instead"
allow-invalid = true

[[disallowed-methods]]
path = "rustc_middle::ty::context::TyCtxt::node_span_lint"
reason = "this function does not add a link to our documentation, please use the `clippy_utils::diagnostics::span_lint_hir*` functions instead"
allow-invalid = true
87 changes: 37 additions & 50 deletions clippy_config/src/types.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
use clippy_utils::PathNS;
use rustc_data_structures::fx::FxHashMap;
use rustc_errors::{Applicability, Diag};
use rustc_hir::PrimTy;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::def::DefKind;
use rustc_hir::def_id::DefIdMap;
use rustc_middle::ty::TyCtxt;
use rustc_span::{Span, Symbol};
Expand Down Expand Up @@ -133,6 +134,7 @@ impl DisallowedPathEnum {
pub fn create_disallowed_map<const REPLACEMENT_ALLOWED: bool>(
tcx: TyCtxt<'_>,
disallowed_paths: &'static [DisallowedPath<REPLACEMENT_ALLOWED>],
ns: PathNS,
def_kind_predicate: impl Fn(DefKind) -> bool,
predicate_description: &str,
allow_prim_tys: bool,
Expand All @@ -145,62 +147,47 @@ pub fn create_disallowed_map<const REPLACEMENT_ALLOWED: bool>(
FxHashMap::default();
for disallowed_path in disallowed_paths {
let path = disallowed_path.path();
let path_split = path.split("::").collect::<Vec<_>>();
let mut resolutions = clippy_utils::def_path_res(tcx, &path_split);

let mut found_def_id = None;
let mut found_prim_ty = false;
resolutions.retain(|res| match res {
Res::Def(def_kind, def_id) => {
found_def_id = Some(*def_id);
def_kind_predicate(*def_kind)
},
Res::PrimTy(_) => {
found_prim_ty = true;
allow_prim_tys
},
_ => false,
});
let sym_path: Vec<Symbol> = path.split("::").map(Symbol::intern).collect();
let mut resolutions = clippy_utils::lookup_path(tcx, ns, &sym_path);
resolutions.retain(|&def_id| def_kind_predicate(tcx.def_kind(def_id)));

let (prim_ty, found_prim_ty) = if let &[name] = sym_path.as_slice()
&& let Some(prim) = PrimTy::from_name(name)
{
(allow_prim_tys.then_some(prim), true)
} else {
(None, false)
};

if resolutions.is_empty()
&& prim_ty.is_none()
&& !disallowed_path.allow_invalid
// Don't warn about unloaded crates:
// https://github.com/rust-lang/rust-clippy/pull/14397#issuecomment-2848328221
&& (path_split.len() < 2
|| !clippy_utils::find_crates(tcx, Symbol::intern(path_split[0])).is_empty())
&& (sym_path.len() < 2 || !clippy_utils::find_crates(tcx, sym_path[0]).is_empty())
{
let span = disallowed_path.span();

if let Some(def_id) = found_def_id {
tcx.sess.dcx().span_warn(
span,
format!(
"expected a {predicate_description}, found {} {}",
tcx.def_descr_article(def_id),
tcx.def_descr(def_id)
),
);
// Relookup the path in an arbitrary namespace to get a good `expected, found` message
let found_def_ids = clippy_utils::lookup_path(tcx, PathNS::Arbitrary, &sym_path);
let message = if let Some(&def_id) = found_def_ids.first() {
let (article, description) = tcx.article_and_description(def_id);
format!("expected a {predicate_description}, found {article} {description}")
} else if found_prim_ty {
tcx.sess.dcx().span_warn(
span,
format!("expected a {predicate_description}, found a primitive type",),
);
} else if !disallowed_path.allow_invalid {
tcx.sess.dcx().span_warn(
span,
format!("`{path}` does not refer to an existing {predicate_description}"),
);
}
format!("expected a {predicate_description}, found a primitive type")
} else {
format!("`{path}` does not refer to a reachable {predicate_description}")
};
tcx.sess
.dcx()
.struct_span_warn(disallowed_path.span(), message)
.with_help("add `allow-invalid = true` to the entry to suppress this warning")
.emit();
}

for res in resolutions {
match res {
Res::Def(_, def_id) => {
def_ids.insert(def_id, (path, disallowed_path));
},
Res::PrimTy(ty) => {
prim_tys.insert(ty, (path, disallowed_path));
},
_ => unreachable!(),
}
for def_id in resolutions {
def_ids.insert(def_id, (path, disallowed_path));
}
if let Some(ty) = prim_ty {
prim_tys.insert(ty, (path, disallowed_path));
}
}

Expand Down
13 changes: 6 additions & 7 deletions clippy_lints/src/await_holding_invalid.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use clippy_config::Conf;
use clippy_config::types::{DisallowedPathWithoutReplacement, create_disallowed_map};
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::{match_def_path, paths};
use clippy_utils::{PathNS, paths};
use rustc_hir as hir;
use rustc_hir::def_id::{DefId, DefIdMap};
use rustc_lint::{LateContext, LateLintPass};
Expand Down Expand Up @@ -182,6 +182,7 @@ impl AwaitHolding {
let (def_ids, _) = create_disallowed_map(
tcx,
&conf.await_holding_invalid_types,
PathNS::Type,
crate::disallowed_types::def_kind_predicate,
"type",
false,
Expand Down Expand Up @@ -275,12 +276,10 @@ fn emit_invalid_type(
}

fn is_mutex_guard(cx: &LateContext<'_>, def_id: DefId) -> bool {
cx.tcx.is_diagnostic_item(sym::MutexGuard, def_id)
|| cx.tcx.is_diagnostic_item(sym::RwLockReadGuard, def_id)
|| cx.tcx.is_diagnostic_item(sym::RwLockWriteGuard, def_id)
|| match_def_path(cx, def_id, &paths::PARKING_LOT_MUTEX_GUARD)
|| match_def_path(cx, def_id, &paths::PARKING_LOT_RWLOCK_READ_GUARD)
|| match_def_path(cx, def_id, &paths::PARKING_LOT_RWLOCK_WRITE_GUARD)
match cx.tcx.get_diagnostic_name(def_id) {
Some(name) => matches!(name, sym::MutexGuard | sym::RwLockReadGuard | sym::RwLockWriteGuard),
None => paths::PARKING_LOT_GUARDS.iter().any(|guard| guard.matches(cx, def_id)),
}
}

fn is_refcell_ref(cx: &LateContext<'_>, def_id: DefId) -> bool {
Expand Down
4 changes: 2 additions & 2 deletions clippy_lints/src/casts/manual_dangling_ptr.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::SpanRangeExt;
use clippy_utils::{expr_or_init, match_def_path, path_def_id, paths, std_or_core};
use clippy_utils::{expr_or_init, path_def_id, paths, std_or_core};
use rustc_ast::LitKind;
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind, GenericArg, Mutability, QPath, Ty, TyKind};
Expand Down Expand Up @@ -54,7 +54,7 @@ fn is_expr_const_aligned(cx: &LateContext<'_>, expr: &Expr<'_>, to: &Ty<'_>) ->
fn is_align_of_call(cx: &LateContext<'_>, fun: &Expr<'_>, to: &Ty<'_>) -> bool {
if let ExprKind::Path(QPath::Resolved(_, path)) = fun.kind
&& let Some(fun_id) = path_def_id(cx, fun)
&& match_def_path(cx, fun_id, &paths::ALIGN_OF)
&& paths::ALIGN_OF.matches(cx, fun_id)
&& let Some(args) = path.segments.last().and_then(|seg| seg.args)
&& let [GenericArg::Type(generic_ty)] = args.args
{
Expand Down
4 changes: 2 additions & 2 deletions clippy_lints/src/derive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use std::ops::ControlFlow;

use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_then, span_lint_hir_and_then};
use clippy_utils::ty::{implements_trait, implements_trait_with_env, is_copy};
use clippy_utils::{has_non_exhaustive_attr, is_lint_allowed, match_def_path, paths};
use clippy_utils::{has_non_exhaustive_attr, is_lint_allowed, paths};
use rustc_errors::Applicability;
use rustc_hir::def_id::DefId;
use rustc_hir::intravisit::{FnKind, Visitor, walk_expr, walk_fn, walk_item};
Expand Down Expand Up @@ -377,7 +377,7 @@ fn check_unsafe_derive_deserialize<'tcx>(
}

if let Some(trait_def_id) = trait_ref.trait_def_id()
&& match_def_path(cx, trait_def_id, &paths::SERDE_DESERIALIZE)
&& paths::SERDE_DESERIALIZE.matches(cx, trait_def_id)
&& let ty::Adt(def, _) = ty.kind()
&& let Some(local_def_id) = def.did().as_local()
&& let adt_hir_id = cx.tcx.local_def_id_to_hir_id(local_def_id)
Expand Down
2 changes: 2 additions & 0 deletions clippy_lints/src/disallowed_macros.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use clippy_config::Conf;
use clippy_config::types::{DisallowedPath, create_disallowed_map};
use clippy_utils::PathNS;
use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then};
use clippy_utils::macros::macro_backtrace;
use rustc_data_structures::fx::FxHashSet;
Expand Down Expand Up @@ -75,6 +76,7 @@ impl DisallowedMacros {
let (disallowed, _) = create_disallowed_map(
tcx,
&conf.disallowed_macros,
PathNS::Macro,
|def_kind| matches!(def_kind, DefKind::Macro(_)),
"macro",
false,
Expand Down
2 changes: 2 additions & 0 deletions clippy_lints/src/disallowed_methods.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use clippy_config::Conf;
use clippy_config::types::{DisallowedPath, create_disallowed_map};
use clippy_utils::PathNS;
use clippy_utils::diagnostics::span_lint_and_then;
use rustc_hir::def::{CtorKind, DefKind, Res};
use rustc_hir::def_id::DefIdMap;
Expand Down Expand Up @@ -66,6 +67,7 @@ impl DisallowedMethods {
let (disallowed, _) = create_disallowed_map(
tcx,
&conf.disallowed_methods,
PathNS::Value,
|def_kind| {
matches!(
def_kind,
Expand Down
10 changes: 9 additions & 1 deletion clippy_lints/src/disallowed_types.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use clippy_config::Conf;
use clippy_config::types::{DisallowedPath, create_disallowed_map};
use clippy_utils::PathNS;
use clippy_utils::diagnostics::span_lint_and_then;
use rustc_data_structures::fx::FxHashMap;
use rustc_hir::def::{DefKind, Res};
Expand Down Expand Up @@ -60,7 +61,14 @@ pub struct DisallowedTypes {

impl DisallowedTypes {
pub fn new(tcx: TyCtxt<'_>, conf: &'static Conf) -> Self {
let (def_ids, prim_tys) = create_disallowed_map(tcx, &conf.disallowed_types, def_kind_predicate, "type", true);
let (def_ids, prim_tys) = create_disallowed_map(
tcx,
&conf.disallowed_types,
PathNS::Type,
def_kind_predicate,
"type",
true,
);
Self { def_ids, prim_tys }
}

Expand Down
4 changes: 2 additions & 2 deletions clippy_lints/src/functions/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ mod too_many_arguments;
mod too_many_lines;

use clippy_config::Conf;
use clippy_utils::def_path_def_ids;
use clippy_utils::msrvs::Msrv;
use clippy_utils::{PathNS, lookup_path_str};
use rustc_hir as hir;
use rustc_hir::intravisit;
use rustc_lint::{LateContext, LateLintPass};
Expand Down Expand Up @@ -469,7 +469,7 @@ impl Functions {
trait_ids: conf
.allow_renamed_params_for
.iter()
.flat_map(|p| def_path_def_ids(tcx, &p.split("::").collect::<Vec<_>>()))
.flat_map(|p| lookup_path_str(tcx, PathNS::Type, p))
.collect(),
msrv: conf.msrv,
}
Expand Down
12 changes: 4 additions & 8 deletions clippy_lints/src/let_underscore.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::ty::{implements_trait, is_must_use_ty, match_type};
use clippy_utils::ty::{implements_trait, is_must_use_ty};
use clippy_utils::{is_from_proc_macro, is_must_use_func_call, paths};
use rustc_hir::{LetStmt, LocalSource, PatKind};
use rustc_lint::{LateContext, LateLintPass};
Expand Down Expand Up @@ -129,12 +129,6 @@ declare_clippy_lint! {

declare_lint_pass!(LetUnderscore => [LET_UNDERSCORE_MUST_USE, LET_UNDERSCORE_LOCK, LET_UNDERSCORE_FUTURE, LET_UNDERSCORE_UNTYPED]);

const SYNC_GUARD_PATHS: [&[&str]; 3] = [
&paths::PARKING_LOT_MUTEX_GUARD,
&paths::PARKING_LOT_RWLOCK_READ_GUARD,
&paths::PARKING_LOT_RWLOCK_WRITE_GUARD,
];

impl<'tcx> LateLintPass<'tcx> for LetUnderscore {
fn check_local(&mut self, cx: &LateContext<'tcx>, local: &LetStmt<'tcx>) {
if matches!(local.source, LocalSource::Normal)
Expand All @@ -144,7 +138,9 @@ impl<'tcx> LateLintPass<'tcx> for LetUnderscore {
{
let init_ty = cx.typeck_results().expr_ty(init);
let contains_sync_guard = init_ty.walk().any(|inner| match inner.unpack() {
GenericArgKind::Type(inner_ty) => SYNC_GUARD_PATHS.iter().any(|path| match_type(cx, inner_ty, path)),
GenericArgKind::Type(inner_ty) => inner_ty
.ty_adt_def()
.is_some_and(|adt| paths::PARKING_LOT_GUARDS.iter().any(|path| path.matches(cx, adt.did()))),
GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false,
});
if contains_sync_guard {
Expand Down
4 changes: 2 additions & 2 deletions clippy_lints/src/manual_option_as_slice.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use clippy_config::Conf;
use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
use clippy_utils::msrvs::Msrv;
use clippy_utils::{is_none_arm, msrvs, peel_hir_expr_refs, sym};
use clippy_utils::{is_none_arm, msrvs, paths, peel_hir_expr_refs, sym};
use rustc_errors::Applicability;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::{Arm, Expr, ExprKind, LangItem, Pat, PatKind, QPath, is_range_literal};
Expand Down Expand Up @@ -220,5 +220,5 @@ fn is_empty_slice(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
}

fn is_slice_from_ref(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
clippy_utils::is_expr_path_def_path(cx, expr, &["core", "slice", "raw", "from_ref"])
paths::SLICE_FROM_REF.matches_path(cx, expr)
}
9 changes: 3 additions & 6 deletions clippy_lints/src/methods/io_other_error.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::{expr_or_init, paths};
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind, QPath};
use rustc_lint::LateContext;
Expand All @@ -8,13 +9,9 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, path: &Expr<'_>, args
if let [error_kind, error] = args
&& !expr.span.from_expansion()
&& !error_kind.span.from_expansion()
&& clippy_utils::is_expr_path_def_path(cx, path, &clippy_utils::paths::IO_ERROR_NEW)
&& clippy_utils::is_expr_path_def_path(
cx,
clippy_utils::expr_or_init(cx, error_kind),
&clippy_utils::paths::IO_ERRORKIND_OTHER,
)
&& let ExprKind::Path(QPath::TypeRelative(_, new_segment)) = path.kind
&& paths::IO_ERROR_NEW.matches_path(cx, path)
&& paths::IO_ERRORKIND_OTHER_CTOR.matches_path(cx, expr_or_init(cx, error_kind))
&& msrv.meets(cx, msrvs::IO_ERROR_OTHER)
{
span_lint_and_then(
Expand Down
Loading