Replace str path utils with new PathLookup type (#14705)

The `&[&str]` path based `clippy_utils` have been removed and replace
with a new type `PathLookup`:

-
[`match_trait_method`](https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/fn.match_trait_method.html)
-
[`match_qpath`](https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/fn.match_qpath.html)
-
[`match_path`](https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/fn.match_path.html)
-
[`match_any_def_paths`](https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/fn.match_any_def_paths.html)
-
[`match_def_path`](https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/fn.match_def_path.html)
-
[`match_type`](https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/ty/fn.match_type.html)
-
[`get_trait_def_id`](https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/fn.get_trait_def_id.html)

Internally `PathLookup` is a lazy call to `lookup_path` (the new name
for
[`def_path_res`](https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/fn.def_path_res.html)
to distinguish it from
[`path_res`](https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/fn.path_res.html))

The `invalid_paths` internal lint is removed, it could be reimplemented
but it feels redundant since every path should be covered by a test
anyway

### User facing changes

- `manual_saturating_arithmetic` now checks for `u32::MAX/MIN` instead
of only detecting the legacy numeric consts (`std::u32::MAX/MIN`),
`clippy::legacy_numeric_constants` will redirect usages of the legacy
versions to the new one

- `allow-invalid = true` now suppresses all invalid path warnings,
currently you can run into a warning that can't be ignored in some
situations, e.g. with `serde` without the `derive` feature

  ```
  warning: expected a macro, found a trait
   --> /home/gh-Alexendoo/temp/clippy.toml:2:5
    |
  2 |     { path = "serde::Serialize", allow-invalid = true },
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  ```

- Re-exports of primitives types like `std::primitive::*` no longer work
in `disallowed-types`, this seems acceptable since it would be unusual
to deny a primitive this way rather than writing e.g. `usize`. Type
aliases such as `c_char` are unaffected

- A similar slight performance improvement to
https://github.com/rust-lang/rust-clippy/pull/14650
  ```bash
$ hyperfine -w 2 -p 'touch src/cargo/lib.rs' 'cargo +master clippy'
'cargo +lazy-paths clippy'
  ```
  ```
  Benchmark 1: cargo +master clippy
Time (mean ± σ): 6.829 s ± 0.064 s [User: 6.079 s, System: 0.673 s]
    Range (min … max):    6.705 s …  6.907 s    10 runs

  Benchmark 2: cargo +lazy-paths clippy
Time (mean ± σ): 6.765 s ± 0.064 s [User: 5.984 s, System: 0.698 s]
    Range (min … max):    6.636 s …  6.834 s    10 runs

  Summary
    cargo +lazy-paths clippy ran
      1.01 ± 0.01 times faster than cargo +master clippy
  ```

changelog: none
This commit is contained in:
Jason Newcomb 2025-05-05 21:46:40 +00:00 committed by GitHub
commit c1586e141f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
70 changed files with 883 additions and 1477 deletions

View file

@ -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

View file

@ -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;
@ -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`
}
}

View file

@ -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!");
}
}
}

View file

@ -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

View file

@ -1,7 +1,8 @@
use clippy_utils::paths::{PathNS, find_crates, lookup_path};
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};
@ -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,
@ -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 sym_path: Vec<Symbol> = path.split("::").map(Symbol::intern).collect();
let mut resolutions = 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)
};
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,
});
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 || !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 = 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));
}
}

View file

@ -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::paths::{self, PathNS};
use rustc_hir as hir;
use rustc_hir::def_id::{DefId, DefIdMap};
use rustc_lint::{LateContext, LateLintPass};
@ -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,
@ -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 {

View file

@ -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};
@ -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
{

View file

@ -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};
@ -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)

View file

@ -2,6 +2,7 @@ use clippy_config::Conf;
use clippy_config::types::{DisallowedPath, create_disallowed_map};
use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then};
use clippy_utils::macros::macro_backtrace;
use clippy_utils::paths::PathNS;
use rustc_data_structures::fx::FxHashSet;
use rustc_hir::def::DefKind;
use rustc_hir::def_id::DefIdMap;
@ -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,

View file

@ -1,6 +1,7 @@
use clippy_config::Conf;
use clippy_config::types::{DisallowedPath, create_disallowed_map};
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::paths::PathNS;
use rustc_hir::def::{CtorKind, DefKind, Res};
use rustc_hir::def_id::DefIdMap;
use rustc_hir::{Expr, ExprKind};
@ -66,6 +67,7 @@ impl DisallowedMethods {
let (disallowed, _) = create_disallowed_map(
tcx,
&conf.disallowed_methods,
PathNS::Value,
|def_kind| {
matches!(
def_kind,

View file

@ -1,6 +1,7 @@
use clippy_config::Conf;
use clippy_config::types::{DisallowedPath, create_disallowed_map};
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::paths::PathNS;
use rustc_data_structures::fx::FxHashMap;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::DefIdMap;
@ -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 }
}

View file

@ -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::paths::{PathNS, lookup_path_str};
use rustc_hir as hir;
use rustc_hir::intravisit;
use rustc_lint::{LateContext, LateLintPass};
@ -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,
}

View file

@ -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};
@ -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)
@ -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 {

View file

@ -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};
@ -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)
}

View file

@ -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;
@ -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(

View file

@ -1,9 +1,10 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::{match_def_path, path_def_id};
use clippy_utils::{path_res, sym};
use rustc_ast::ast;
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_hir::def::Res;
use rustc_lint::LateContext;
use rustc_middle::ty::layout::LayoutOf;
@ -79,16 +80,15 @@ fn is_min_or_max(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option<MinMax> {
}
let ty = cx.typeck_results().expr_ty(expr);
let ty_str = ty.to_string();
// `std::T::MAX` `std::T::MIN` constants
if let Some(id) = path_def_id(cx, expr) {
if match_def_path(cx, id, &["core", &ty_str, "MAX"]) {
return Some(MinMax::Max);
}
if match_def_path(cx, id, &["core", &ty_str, "MIN"]) {
return Some(MinMax::Min);
// `T::MAX` and `T::MIN` constants
if let hir::ExprKind::Path(hir::QPath::TypeRelative(base, seg)) = expr.kind
&& let Res::PrimTy(_) = path_res(cx, base)
{
match seg.ident.name {
sym::MAX => return Some(MinMax::Max),
sym::MIN => return Some(MinMax::Min),
_ => {},
}
}

View file

@ -7,9 +7,8 @@ use rustc_span::Span;
use super::NEEDLESS_CHARACTER_ITERATION;
use super::utils::get_last_chain_binding_hir_id;
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::paths::CHAR_IS_ASCII;
use clippy_utils::source::SpanRangeExt;
use clippy_utils::{match_def_path, path_to_local_id, peel_blocks, sym};
use clippy_utils::{is_path_diagnostic_item, path_to_local_id, peel_blocks, sym};
fn peels_expr_ref<'a, 'tcx>(mut expr: &'a Expr<'tcx>) -> &'a Expr<'tcx> {
while let ExprKind::AddrOf(_, _, e) = expr.kind {
@ -76,9 +75,7 @@ fn handle_expr(
// If we have `!is_ascii`, then only `.any()` should warn. And if the condition is
// `is_ascii`, then only `.all()` should warn.
if revert != is_all
&& let ExprKind::Path(path) = fn_path.kind
&& let Some(fn_def_id) = cx.qpath_res(&path, fn_path.hir_id).opt_def_id()
&& match_def_path(cx, fn_def_id, &CHAR_IS_ASCII)
&& is_path_diagnostic_item(cx, fn_path, sym::char_is_ascii)
&& path_to_local_id(peels_expr_ref(arg), first_param)
&& let Some(snippet) = before_chars.get_source_text(cx)
{

View file

@ -1,8 +1,8 @@
use rustc_data_structures::fx::FxHashMap;
use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
use clippy_utils::ty::{is_type_diagnostic_item, match_type};
use clippy_utils::{match_any_def_paths, paths};
use clippy_utils::paths;
use clippy_utils::ty::is_type_diagnostic_item;
use rustc_ast::ast::LitKind;
use rustc_hir::{Expr, ExprKind};
use rustc_lint::LateContext;
@ -13,7 +13,7 @@ use rustc_span::{Span, sym};
use super::{NONSENSICAL_OPEN_OPTIONS, SUSPICIOUS_OPEN_OPTIONS};
fn is_open_options(cx: &LateContext<'_>, ty: Ty<'_>) -> bool {
is_type_diagnostic_item(cx, ty, sym::FsOpenOptions) || match_type(cx, ty, &paths::TOKIO_IO_OPEN_OPTIONS)
is_type_diagnostic_item(cx, ty, sym::FsOpenOptions) || paths::TOKIO_IO_OPEN_OPTIONS.matches_ty(cx, ty)
}
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, recv: &'tcx Expr<'_>) {
@ -126,14 +126,14 @@ fn get_open_options(
&& let ExprKind::Path(path) = callee.kind
&& let Some(did) = cx.qpath_res(&path, callee.hir_id).opt_def_id()
{
let std_file_options = [sym::file_options, sym::open_options_new];
let is_std_options = matches!(
cx.tcx.get_diagnostic_name(did),
Some(sym::file_options | sym::open_options_new)
);
let tokio_file_options: &[&[&str]] = &[&paths::TOKIO_IO_OPEN_OPTIONS_NEW, &paths::TOKIO_FILE_OPTIONS];
let is_std_options = std_file_options
.into_iter()
.any(|sym| cx.tcx.is_diagnostic_item(sym, did));
is_std_options || match_any_def_paths(cx, did, tokio_file_options).is_some()
is_std_options
|| paths::TOKIO_IO_OPEN_OPTIONS_NEW.matches(cx, did)
|| paths::TOKIO_FILE_OPTIONS.matches(cx, did)
} else {
false
}

View file

@ -4,7 +4,7 @@ use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::source::snippet_with_context;
use clippy_utils::usage::local_used_after_expr;
use clippy_utils::visitors::{Descend, for_each_expr};
use clippy_utils::{is_diag_item_method, match_def_path, path_to_local_id, paths};
use clippy_utils::{is_diag_item_method, path_to_local_id, paths};
use core::ops::ControlFlow;
use rustc_errors::Applicability;
use rustc_hir::{
@ -288,7 +288,7 @@ fn parse_iter_usage<'tcx>(
match (name.ident.as_str(), args) {
("next", []) if cx.tcx.trait_of_item(did) == Some(iter_id) => (IterUsageKind::Nth(0), e.span),
("next_tuple", []) => {
return if match_def_path(cx, did, &paths::ITERTOOLS_NEXT_TUPLE)
return if paths::ITERTOOLS_NEXT_TUPLE.matches(cx, did)
&& let ty::Adt(adt_def, subs) = cx.typeck_results().expr_ty(e).kind()
&& cx.tcx.is_diagnostic_item(sym::Option, adt_def.did())
&& let ty::Tuple(subs) = subs.type_at(0).kind()

View file

@ -1,9 +1,7 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::ty::{implements_trait, should_call_clone_as_function, walk_ptrs_ty_depth};
use clippy_utils::{
get_parent_expr, is_diag_trait_item, match_def_path, path_to_local_id, peel_blocks, strip_pat_refs,
};
use clippy_utils::{get_parent_expr, is_diag_trait_item, path_to_local_id, peel_blocks, strip_pat_refs};
use rustc_errors::Applicability;
use rustc_hir::{self as hir, LangItem};
use rustc_lint::LateContext;
@ -81,8 +79,9 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, call_name: &str,
applicability,
);
}
} else if match_def_path(cx, def_id, &["core", "option", "Option", call_name])
|| match_def_path(cx, def_id, &["core", "result", "Result", call_name])
} else if let Some(impl_id) = cx.tcx.opt_parent(def_id)
&& let Some(adt) = cx.tcx.type_of(impl_id).instantiate_identity().ty_adt_def()
&& (cx.tcx.lang_items().option_type() == Some(adt.did()) || cx.tcx.is_diagnostic_item(sym::Result, adt.did()))
{
let rcv_ty = cx.typeck_results().expr_ty(recvr).peel_refs();
let res_ty = cx.typeck_results().expr_ty(expr).peel_refs();

View file

@ -1,6 +1,6 @@
use clippy_config::Conf;
use clippy_utils::def_path_def_ids;
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::paths::{PathNS, lookup_path_str};
use clippy_utils::source::SpanRangeExt;
use rustc_errors::Applicability;
use rustc_hir::def::Res;
@ -56,8 +56,12 @@ impl ImportRename {
renames: conf
.enforced_import_renames
.iter()
.map(|x| (x.path.split("::").collect::<Vec<_>>(), Symbol::intern(&x.rename)))
.flat_map(|(path, rename)| def_path_def_ids(tcx, &path).map(move |id| (id, rename)))
.map(|x| (&x.path, Symbol::intern(&x.rename)))
.flat_map(|(path, rename)| {
lookup_path_str(tcx, PathNS::Arbitrary, path)
.into_iter()
.map(move |id| (id, rename))
})
.collect(),
}
}

View file

@ -1,8 +1,9 @@
use clippy_config::Conf;
use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::paths::{self, PathNS, find_crates, lookup_path_str};
use clippy_utils::visitors::for_each_expr;
use clippy_utils::{def_path_def_ids, fn_def_id, is_no_std_crate, path_def_id};
use clippy_utils::{fn_def_id, is_no_std_crate, path_def_id, sym};
use rustc_data_structures::fx::FxIndexMap;
use rustc_errors::Applicability;
use rustc_hir::def::{DefKind, Res};
@ -62,10 +63,7 @@ static FUNCTION_REPLACEMENTS: &[(&str, Option<&str>)] = &[
pub struct NonStdLazyStatic {
msrv: Msrv,
lazy_static_lazy_static: Vec<DefId>,
once_cell_crate: Vec<CrateNum>,
once_cell_sync_lazy: Vec<DefId>,
once_cell_sync_lazy_new: Vec<DefId>,
once_cell_crates: Vec<CrateNum>,
sugg_map: FxIndexMap<DefId, Option<String>>,
lazy_type_defs: FxIndexMap<DefId, LazyInfo>,
uses_other_once_cell_types: bool,
@ -76,10 +74,7 @@ impl NonStdLazyStatic {
pub fn new(conf: &'static Conf) -> Self {
Self {
msrv: conf.msrv,
lazy_static_lazy_static: Vec::new(),
once_cell_crate: Vec::new(),
once_cell_sync_lazy: Vec::new(),
once_cell_sync_lazy_new: Vec::new(),
once_cell_crates: Vec::new(),
sugg_map: FxIndexMap::default(),
lazy_type_defs: FxIndexMap::default(),
uses_other_once_cell_types: false,
@ -95,17 +90,15 @@ fn can_use_lazy_cell(cx: &LateContext<'_>, msrv: Msrv) -> bool {
impl<'hir> LateLintPass<'hir> for NonStdLazyStatic {
fn check_crate(&mut self, cx: &LateContext<'hir>) {
// Fetch def_ids for external paths
self.lazy_static_lazy_static = def_path_def_ids(cx.tcx, &["lazy_static", "lazy_static"]).collect();
self.once_cell_sync_lazy = def_path_def_ids(cx.tcx, &["once_cell", "sync", "Lazy"]).collect();
self.once_cell_sync_lazy_new = def_path_def_ids(cx.tcx, &["once_cell", "sync", "Lazy", "new"]).collect();
// And CrateNums for `once_cell` crate
self.once_cell_crate = self.once_cell_sync_lazy.iter().map(|d| d.krate).collect();
// Add CrateNums for `once_cell` crate
self.once_cell_crates = find_crates(cx.tcx, sym::once_cell)
.iter()
.map(|def_id| def_id.krate)
.collect();
// Convert hardcoded fn replacement list into a map with def_id
for (path, sugg) in FUNCTION_REPLACEMENTS {
let path_vec: Vec<&str> = path.split("::").collect();
for did in def_path_def_ids(cx.tcx, &path_vec) {
for did in lookup_path_str(cx.tcx, PathNS::Value, path) {
self.sugg_map.insert(did, sugg.map(ToOwned::to_owned));
}
}
@ -114,7 +107,7 @@ impl<'hir> LateLintPass<'hir> for NonStdLazyStatic {
fn check_item(&mut self, cx: &LateContext<'hir>, item: &Item<'hir>) {
if let ItemKind::Static(..) = item.kind
&& let Some(macro_call) = clippy_utils::macros::root_macro_call(item.span)
&& self.lazy_static_lazy_static.contains(&macro_call.def_id)
&& paths::LAZY_STATIC.matches(cx, macro_call.def_id)
&& can_use_lazy_cell(cx, self.msrv)
{
span_lint(
@ -130,7 +123,7 @@ impl<'hir> LateLintPass<'hir> for NonStdLazyStatic {
return;
}
if let Some(lazy_info) = LazyInfo::from_item(self, cx, item)
if let Some(lazy_info) = LazyInfo::from_item(cx, item)
&& can_use_lazy_cell(cx, self.msrv)
{
self.lazy_type_defs.insert(item.owner_id.to_def_id(), lazy_info);
@ -155,9 +148,9 @@ impl<'hir> LateLintPass<'hir> for NonStdLazyStatic {
if let rustc_hir::TyKind::Path(qpath) = ty.peel_refs().kind
&& let Some(ty_def_id) = cx.qpath_res(&qpath, ty.hir_id).opt_def_id()
// Is from `once_cell` crate
&& self.once_cell_crate.contains(&ty_def_id.krate)
&& self.once_cell_crates.contains(&ty_def_id.krate)
// And is NOT `once_cell::sync::Lazy`
&& !self.once_cell_sync_lazy.contains(&ty_def_id)
&& !paths::ONCE_CELL_SYNC_LAZY.matches(cx, ty_def_id)
{
self.uses_other_once_cell_types = true;
}
@ -190,12 +183,12 @@ struct LazyInfo {
}
impl LazyInfo {
fn from_item(state: &NonStdLazyStatic, cx: &LateContext<'_>, item: &Item<'_>) -> Option<Self> {
fn from_item(cx: &LateContext<'_>, item: &Item<'_>) -> Option<Self> {
// Check if item is a `once_cell:sync::Lazy` static.
if let ItemKind::Static(_, ty, _, body_id) = item.kind
&& let Some(path_def_id) = path_def_id(cx, ty)
&& let hir::TyKind::Path(hir::QPath::Resolved(_, path)) = ty.kind
&& state.once_cell_sync_lazy.contains(&path_def_id)
&& paths::ONCE_CELL_SYNC_LAZY.matches(cx, path_def_id)
{
let ty_span_no_args = path_span_without_args(path);
let body = cx.tcx.hir_body(body_id);
@ -204,7 +197,7 @@ impl LazyInfo {
let mut new_fn_calls = FxIndexMap::default();
for_each_expr::<(), ()>(cx, body, |ex| {
if let Some((fn_did, call_span)) = fn_def_id_and_span_from_body(cx, ex, body_id)
&& state.once_cell_sync_lazy_new.contains(&fn_did)
&& paths::ONCE_CELL_SYNC_LAZY_NEW.matches(cx, fn_did)
{
new_fn_calls.insert(call_span, fn_did);
}

View file

@ -2,8 +2,9 @@ use std::fmt::Display;
use clippy_utils::consts::{ConstEvalCtxt, Constant};
use clippy_utils::diagnostics::{span_lint, span_lint_and_help};
use clippy_utils::paths::PathLookup;
use clippy_utils::source::SpanRangeExt;
use clippy_utils::{def_path_res_with_base, find_crates, path_def_id, paths, sym};
use clippy_utils::{path_def_id, paths};
use rustc_ast::ast::{LitKind, StrStyle};
use rustc_hir::def_id::DefIdMap;
use rustc_hir::{BorrowKind, Expr, ExprKind, OwnerId};
@ -121,17 +122,9 @@ impl_lint_pass!(Regex => [INVALID_REGEX, TRIVIAL_REGEX, REGEX_CREATION_IN_LOOPS]
impl<'tcx> LateLintPass<'tcx> for Regex {
fn check_crate(&mut self, cx: &LateContext<'tcx>) {
// We don't use `match_def_path` here because that relies on matching the exact path, which changed
// between regex 1.8 and 1.9
//
// `def_path_res_with_base` will resolve through re-exports but is relatively heavy, so we only
// perform the operation once and store the results
let regex_crates = find_crates(cx.tcx, sym::regex);
let mut resolve = |path: &[&str], kind: RegexKind| {
for res in def_path_res_with_base(cx.tcx, regex_crates.clone(), &path[1..]) {
if let Some(id) = res.opt_def_id() {
self.definitions.insert(id, kind);
}
let mut resolve = |path: &PathLookup, kind: RegexKind| {
for &id in path.get(cx) {
self.definitions.insert(id, kind);
}
};

View file

@ -1,5 +1,5 @@
use clippy_utils::diagnostics::span_lint;
use clippy_utils::{get_trait_def_id, paths};
use clippy_utils::paths;
use rustc_hir::{Impl, Item, ItemKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::declare_lint_pass;
@ -32,9 +32,7 @@ impl<'tcx> LateLintPass<'tcx> for SerdeApi {
}) = item.kind
{
let did = trait_ref.path.res.def_id();
if let Some(visit_did) = get_trait_def_id(cx.tcx, &paths::SERDE_DE_VISITOR)
&& did == visit_did
{
if paths::SERDE_DE_VISITOR.matches(cx, did) {
let mut seen_str = None;
let mut seen_string = None;
for item in *items {

View file

@ -3,7 +3,7 @@ use clippy_utils::higher::VecArgs;
use clippy_utils::macros::root_macro_call_first_node;
use clippy_utils::source::SpanRangeExt;
use clippy_utils::ty::implements_trait;
use clippy_utils::{get_trait_def_id, is_no_std_crate};
use clippy_utils::{is_no_std_crate, paths};
use rustc_ast::{LitIntType, LitKind, UintTy};
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind, LangItem, QPath, StructTailExpr};
@ -100,7 +100,7 @@ impl LateLintPass<'_> for SingleRangeInVecInit {
&& let Some(start_snippet) = start.span.get_source_text(cx)
&& let Some(end_snippet) = end.span.get_source_text(cx)
{
let should_emit_every_value = if let Some(step_def_id) = get_trait_def_id(cx.tcx, &["core", "iter", "Step"])
let should_emit_every_value = if let Some(step_def_id) = paths::ITER_STEP.only(cx)
&& implements_trait(cx, ty, step_def_id, &[])
{
true

View file

@ -1,10 +1,9 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::{match_def_path, sym};
use clippy_utils::{paths, sym};
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty;
use rustc_session::declare_lint_pass;
declare_clippy_lint! {
@ -40,27 +39,18 @@ impl<'tcx> LateLintPass<'tcx> for ToDigitIsSome {
if let hir::ExprKind::MethodCall(is_some_path, to_digit_expr, [], _) = &expr.kind
&& is_some_path.ident.name == sym::is_some
{
let match_result = match &to_digit_expr.kind {
let match_result = match to_digit_expr.kind {
hir::ExprKind::MethodCall(to_digits_path, char_arg, [radix_arg], _) => {
if to_digits_path.ident.name == sym::to_digit
&& let char_arg_ty = cx.typeck_results().expr_ty_adjusted(char_arg)
&& *char_arg_ty.kind() == ty::Char
&& cx.typeck_results().expr_ty_adjusted(char_arg).is_char()
{
Some((true, *char_arg, radix_arg))
Some((true, char_arg, radix_arg))
} else {
None
}
},
hir::ExprKind::Call(to_digits_call, [char_arg, radix_arg]) => {
if let hir::ExprKind::Path(to_digits_path) = &to_digits_call.kind
&& let to_digits_call_res = cx.qpath_res(to_digits_path, to_digits_call.hir_id)
&& let Some(to_digits_def_id) = to_digits_call_res.opt_def_id()
&& match_def_path(
cx,
to_digits_def_id,
&["core", "char", "methods", "<impl char>", "to_digit"],
)
{
if paths::CHAR_TO_DIGIT.matches_path(cx, to_digits_call) {
Some((false, char_arg, radix_arg))
} else {
None

View file

@ -1,6 +1,6 @@
use clippy_utils::diagnostics::span_lint_hir_and_then;
use clippy_utils::macros::{is_panic, root_macro_call_first_node};
use clippy_utils::{is_res_lang_ctor, is_trait_method, match_def_path, match_trait_method, paths, peel_blocks};
use clippy_utils::{is_res_lang_ctor, paths, peel_blocks};
use hir::{ExprKind, HirId, PatKind};
use rustc_hir as hir;
use rustc_lint::{LateContext, LateLintPass};
@ -93,14 +93,14 @@ impl<'tcx> LateLintPass<'tcx> for UnusedIoAmount {
return;
}
let async_paths: [&[&str]; 4] = [
let async_paths = [
&paths::TOKIO_IO_ASYNCREADEXT,
&paths::TOKIO_IO_ASYNCWRITEEXT,
&paths::FUTURES_IO_ASYNCREADEXT,
&paths::FUTURES_IO_ASYNCWRITEEXT,
];
if async_paths.into_iter().any(|path| match_def_path(cx, trait_id, path)) {
if async_paths.into_iter().any(|path| path.matches(cx, trait_id)) {
return;
}
}
@ -291,19 +291,28 @@ fn check_io_mode(cx: &LateContext<'_>, call: &hir::Expr<'_>) -> Option<IoOp> {
},
};
match (
is_trait_method(cx, call, sym::IoRead),
is_trait_method(cx, call, sym::IoWrite),
match_trait_method(cx, call, &paths::FUTURES_IO_ASYNCREADEXT)
|| match_trait_method(cx, call, &paths::TOKIO_IO_ASYNCREADEXT),
match_trait_method(cx, call, &paths::TOKIO_IO_ASYNCWRITEEXT)
|| match_trait_method(cx, call, &paths::FUTURES_IO_ASYNCWRITEEXT),
) {
(true, _, _, _) => Some(IoOp::SyncRead(vectorized)),
(_, true, _, _) => Some(IoOp::SyncWrite(vectorized)),
(_, _, true, _) => Some(IoOp::AsyncRead(vectorized)),
(_, _, _, true) => Some(IoOp::AsyncWrite(vectorized)),
_ => None,
if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(call.hir_id)
&& let Some(trait_def_id) = cx.tcx.trait_of_item(method_def_id)
{
if let Some(diag_name) = cx.tcx.get_diagnostic_name(trait_def_id) {
match diag_name {
sym::IoRead => Some(IoOp::SyncRead(vectorized)),
sym::IoWrite => Some(IoOp::SyncWrite(vectorized)),
_ => None,
}
} else if paths::FUTURES_IO_ASYNCREADEXT.matches(cx, trait_def_id)
|| paths::TOKIO_IO_ASYNCREADEXT.matches(cx, trait_def_id)
{
Some(IoOp::AsyncRead(vectorized))
} else if paths::TOKIO_IO_ASYNCWRITEEXT.matches(cx, trait_def_id)
|| paths::FUTURES_IO_ASYNCWRITEEXT.matches(cx, trait_def_id)
{
Some(IoOp::AsyncWrite(vectorized))
} else {
None
}
} else {
None
}
}

View file

@ -1,16 +1,18 @@
use clippy_utils::{get_attr, higher};
use clippy_utils::{MaybePath, get_attr, higher, path_def_id};
use itertools::Itertools;
use rustc_ast::LitIntType;
use rustc_ast::ast::{LitFloatType, LitKind};
use rustc_data_structures::fx::FxHashMap;
use rustc_hir::def_id::DefId;
use rustc_hir::{
self as hir, BindingMode, CaptureBy, Closure, ClosureKind, ConstArg, ConstArgKind, CoroutineKind, ExprKind,
FnRetTy, HirId, Lit, PatExprKind, PatKind, QPath, StmtKind, StructTailExpr, TyKind,
FnRetTy, HirId, Lit, PatExprKind, PatKind, QPath, StmtKind, StructTailExpr,
};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_session::declare_lint_pass;
use rustc_span::symbol::{Ident, Symbol};
use std::cell::Cell;
use std::fmt::{Display, Formatter, Write as _};
use std::fmt::{Display, Formatter};
declare_lint_pass!(
/// ### What it does
@ -148,6 +150,15 @@ fn check_node(cx: &LateContext<'_>, hir_id: HirId, f: impl Fn(&PrintVisitor<'_,
}
}
fn paths_static_name(cx: &LateContext<'_>, id: DefId) -> String {
cx.get_def_path(id)
.iter()
.map(Symbol::as_str)
.filter(|s| !s.starts_with('<'))
.join("_")
.to_uppercase()
}
struct Binding<T> {
name: String,
value: T,
@ -257,11 +268,44 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> {
chain!(self, "{symbol}.as_str() == {:?}", symbol.value.as_str());
}
fn qpath(&self, qpath: &Binding<&QPath<'_>>) {
fn qpath<'p>(&self, qpath: &Binding<&QPath<'_>>, has_hir_id: &Binding<&impl MaybePath<'p>>) {
if let QPath::LangItem(lang_item, ..) = *qpath.value {
chain!(self, "matches!({qpath}, QPath::LangItem(LangItem::{lang_item:?}, _))");
} else if let Ok(path) = path_to_string(qpath.value) {
chain!(self, "match_qpath({qpath}, &[{}])", path);
} else if let Some(def_id) = self.cx.qpath_res(qpath.value, has_hir_id.value.hir_id()).opt_def_id()
&& !def_id.is_local()
{
bind!(self, def_id);
chain!(
self,
"let Some({def_id}) = cx.qpath_res({qpath}, {has_hir_id}.hir_id).opt_def_id()"
);
if let Some(name) = self.cx.tcx.get_diagnostic_name(def_id.value) {
chain!(self, "cx.tcx.is_diagnostic_item(sym::{name}, {def_id})");
} else {
chain!(
self,
"paths::{}.matches(cx, {def_id}) // Add the path to `clippy_utils::paths` if needed",
paths_static_name(self.cx, def_id.value)
);
}
}
}
fn maybe_path<'p>(&self, path: &Binding<&impl MaybePath<'p>>) {
if let Some(id) = path_def_id(self.cx, path.value)
&& !id.is_local()
{
if let Some(lang) = self.cx.tcx.lang_items().from_def_id(id) {
chain!(self, "is_path_lang_item(cx, {path}, LangItem::{}", lang.name());
} else if let Some(name) = self.cx.tcx.get_diagnostic_name(id) {
chain!(self, "is_path_diagnostic_item(cx, {path}, sym::{name})");
} else {
chain!(
self,
"paths::{}.matches_path(cx, {path}) // Add the path to `clippy_utils::paths` if needed",
paths_static_name(self.cx, id)
);
}
}
}
@ -270,7 +314,6 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> {
ConstArgKind::Path(ref qpath) => {
bind!(self, qpath);
chain!(self, "let ConstArgKind::Path(ref {qpath}) = {const_arg}.kind");
self.qpath(qpath);
},
ConstArgKind::Anon(anon_const) => {
bind!(self, anon_const);
@ -394,12 +437,10 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> {
bind!(self, let_expr);
kind!("Let({let_expr})");
self.pat(field!(let_expr.pat));
// Does what ExprKind::Cast does, only adds a clause for the type
// if it's a path
if let Some(TyKind::Path(qpath)) = let_expr.value.ty.as_ref().map(|ty| &ty.kind) {
bind!(self, qpath);
chain!(self, "let TyKind::Path(ref {qpath}) = {let_expr}.ty.kind");
self.qpath(qpath);
if let Some(ty) = let_expr.value.ty {
bind!(self, ty);
chain!(self, "let Some({ty}) = {let_expr}.ty");
self.maybe_path(ty);
}
self.expr(field!(let_expr.init));
},
@ -451,11 +492,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> {
ExprKind::Cast(expr, cast_ty) => {
bind!(self, expr, cast_ty);
kind!("Cast({expr}, {cast_ty})");
if let TyKind::Path(ref qpath) = cast_ty.value.kind {
bind!(self, qpath);
chain!(self, "let TyKind::Path(ref {qpath}) = {cast_ty}.kind");
self.qpath(qpath);
}
self.maybe_path(cast_ty);
self.expr(expr);
},
ExprKind::Type(expr, _ty) => {
@ -561,10 +598,8 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> {
self.expr(object);
self.expr(index);
},
ExprKind::Path(ref qpath) => {
bind!(self, qpath);
kind!("Path(ref {qpath})");
self.qpath(qpath);
ExprKind::Path(_) => {
self.maybe_path(expr);
},
ExprKind::AddrOf(kind, mutability, inner) => {
bind!(self, inner);
@ -608,7 +643,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> {
StructTailExpr::None | StructTailExpr::DefaultFields(_) => None,
});
kind!("Struct({qpath}, {fields}, {base})");
self.qpath(qpath);
self.qpath(qpath, expr);
self.slice(fields, |field| {
self.ident(field!(field.ident));
self.expr(field!(field.expr));
@ -648,7 +683,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> {
self.expr(expr);
}
fn pat_expr(&self, lit: &Binding<&hir::PatExpr<'_>>) {
fn pat_expr(&self, lit: &Binding<&hir::PatExpr<'_>>, pat: &Binding<&hir::Pat<'_>>) {
let kind = |kind| chain!(self, "let PatExprKind::{kind} = {lit}.kind");
macro_rules! kind {
($($t:tt)*) => (kind(format_args!($($t)*)));
@ -657,15 +692,11 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> {
PatExprKind::Lit { lit, negated } => {
bind!(self, lit);
bind!(self, negated);
kind!("Lit{{ref {lit}, {negated} }}");
kind!("Lit {{ ref {lit}, {negated} }}");
self.lit(lit);
},
PatExprKind::ConstBlock(_) => kind!("ConstBlock(_)"),
PatExprKind::Path(ref qpath) => {
bind!(self, qpath);
kind!("Path(ref {qpath})");
self.qpath(qpath);
},
PatExprKind::Path(_) => self.maybe_path(pat),
}
}
@ -697,7 +728,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> {
PatKind::Struct(ref qpath, fields, ignore) => {
bind!(self, qpath, fields);
kind!("Struct(ref {qpath}, {fields}, {ignore})");
self.qpath(qpath);
self.qpath(qpath, pat);
self.slice(fields, |field| {
self.ident(field!(field.ident));
self.pat(field!(field.pat));
@ -711,7 +742,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> {
PatKind::TupleStruct(ref qpath, fields, skip_pos) => {
bind!(self, qpath, fields);
kind!("TupleStruct(ref {qpath}, {fields}, {skip_pos:?})");
self.qpath(qpath);
self.qpath(qpath, pat);
self.slice(fields, |pat| self.pat(pat));
},
PatKind::Tuple(fields, skip_pos) => {
@ -743,13 +774,13 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> {
PatKind::Expr(lit_expr) => {
bind!(self, lit_expr);
kind!("Expr({lit_expr})");
self.pat_expr(lit_expr);
self.pat_expr(lit_expr, pat);
},
PatKind::Range(start, end, end_kind) => {
opt_bind!(self, start, end);
kind!("Range({start}, {end}, RangeEnd::{end_kind:?})");
start.if_some(|e| self.pat_expr(e));
end.if_some(|e| self.pat_expr(e));
start.if_some(|e| self.pat_expr(e, pat));
end.if_some(|e| self.pat_expr(e, pat));
},
PatKind::Slice(start, middle, end) => {
bind!(self, start, end);
@ -797,32 +828,3 @@ fn has_attr(cx: &LateContext<'_>, hir_id: HirId) -> bool {
let attrs = cx.tcx.hir_attrs(hir_id);
get_attr(cx.sess(), attrs, "author").count() > 0
}
fn path_to_string(path: &QPath<'_>) -> Result<String, ()> {
fn inner(s: &mut String, path: &QPath<'_>) -> Result<(), ()> {
match *path {
QPath::Resolved(_, path) => {
for (i, segment) in path.segments.iter().enumerate() {
if i > 0 {
*s += ", ";
}
write!(s, "{:?}", segment.ident.as_str()).unwrap();
}
},
QPath::TypeRelative(ty, segment) => match &ty.kind {
TyKind::Path(inner_path) => {
inner(s, inner_path)?;
*s += ", ";
write!(s, "{:?}", segment.ident.as_str()).unwrap();
},
other => write!(s, "/* unimplemented: {other:?}*/").unwrap(),
},
QPath::LangItem(..) => return Err(()),
}
Ok(())
}
let mut s = String::new();
inner(&mut s, path)?;
Ok(s)
}

View file

@ -1,6 +1,6 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet;
use clippy_utils::{SpanlessEq, is_expr_path_def_path, is_lint_allowed, peel_blocks_with_stmt};
use clippy_utils::{SpanlessEq, is_lint_allowed, peel_blocks_with_stmt};
use rustc_errors::Applicability;
use rustc_hir::{Closure, Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
@ -10,6 +10,8 @@ use rustc_span::Span;
use std::borrow::{Borrow, Cow};
use crate::internal_paths;
declare_tool_lint! {
/// ### What it does
/// Lints `span_lint_and_then` function calls, where the
@ -80,7 +82,7 @@ impl<'tcx> LateLintPass<'tcx> for CollapsibleCalls {
}
if let ExprKind::Call(func, [call_cx, call_lint, call_sp, call_msg, call_f]) = expr.kind
&& is_expr_path_def_path(cx, func, &["clippy_utils", "diagnostics", "span_lint_and_then"])
&& internal_paths::SPAN_LINT_AND_THEN.matches_path(cx, func)
&& let ExprKind::Closure(&Closure { body, .. }) = call_f.kind
&& let body = cx.tcx.hir_body(body)
&& let only_expr = peel_blocks_with_stmt(body.value)

View file

@ -0,0 +1,17 @@
use clippy_utils::paths::{PathLookup, PathNS};
use clippy_utils::{sym, type_path, value_path};
// Paths inside rustc
pub static EARLY_LINT_PASS: PathLookup = type_path!(rustc_lint::passes::EarlyLintPass);
pub static KW_MODULE: PathLookup = type_path!(rustc_span::symbol::kw);
pub static LINT: PathLookup = type_path!(rustc_lint_defs::Lint);
pub static SYMBOL: PathLookup = type_path!(rustc_span::symbol::Symbol);
pub static SYMBOL_AS_STR: PathLookup = value_path!(rustc_span::symbol::Symbol::as_str);
pub static SYM_MODULE: PathLookup = type_path!(rustc_span::symbol::sym);
pub static SYNTAX_CONTEXT: PathLookup = type_path!(rustc_span::hygiene::SyntaxContext);
// Paths in clippy itself
pub static CLIPPY_SYM_MODULE: PathLookup = type_path!(clippy_utils::sym);
pub static MSRV_STACK: PathLookup = type_path!(clippy_utils::msrvs::MsrvStack);
pub static PATH_LOOKUP_NEW: PathLookup = value_path!(clippy_utils::paths::PathLookup::new);
pub static SPAN_LINT_AND_THEN: PathLookup = value_path!(clippy_utils::diagnostics::span_lint_and_then);

View file

@ -1,108 +0,0 @@
use clippy_utils::consts::{ConstEvalCtxt, Constant};
use clippy_utils::def_path_res;
use clippy_utils::diagnostics::span_lint;
use rustc_hir as hir;
use rustc_hir::Item;
use rustc_hir::def::DefKind;
use rustc_lint::{LateContext, LateLintPass};
use rustc_lint_defs::declare_tool_lint;
use rustc_middle::ty::fast_reject::SimplifiedType;
use rustc_middle::ty::{self, FloatTy};
use rustc_session::declare_lint_pass;
use rustc_span::symbol::Symbol;
declare_tool_lint! {
/// ### What it does
/// Checks the paths module for invalid paths.
///
/// ### Why is this bad?
/// It indicates a bug in the code.
///
/// ### Example
/// None.
pub clippy::INVALID_PATHS,
Warn,
"invalid path",
report_in_external_macro: true
}
declare_lint_pass!(InvalidPaths => [INVALID_PATHS]);
impl<'tcx> LateLintPass<'tcx> for InvalidPaths {
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
let local_def_id = &cx.tcx.parent_module(item.hir_id());
let mod_name = &cx.tcx.item_name(local_def_id.to_def_id());
if mod_name.as_str() == "paths"
&& let hir::ItemKind::Const(.., body_id) = item.kind
&& let Some(Constant::Vec(path)) = ConstEvalCtxt::with_env(
cx.tcx,
ty::TypingEnv::post_analysis(cx.tcx, item.owner_id),
cx.tcx.typeck(item.owner_id),
)
.eval_simple(cx.tcx.hir_body(body_id).value)
&& let Some(path) = path
.iter()
.map(|x| {
if let Constant::Str(s) = x {
Some(s.as_str())
} else {
None
}
})
.collect::<Option<Vec<&str>>>()
&& !check_path(cx, &path[..])
{
span_lint(cx, INVALID_PATHS, item.span, "invalid path");
}
}
}
// 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.
pub fn check_path(cx: &LateContext<'_>, path: &[&str]) -> bool {
if !def_path_res(cx.tcx, path).is_empty() {
return true;
}
// Some implementations can't be found by `path_to_res`, particularly inherent
// implementations of native types. Check lang items.
let path_syms: Vec<_> = path.iter().map(|p| Symbol::intern(p)).collect();
let lang_items = cx.tcx.lang_items();
// This list isn't complete, but good enough for our current list of paths.
let incoherent_impls = [
SimplifiedType::Float(FloatTy::F32),
SimplifiedType::Float(FloatTy::F64),
SimplifiedType::Slice,
SimplifiedType::Str,
SimplifiedType::Bool,
SimplifiedType::Char,
]
.iter()
.flat_map(|&ty| cx.tcx.incoherent_impls(ty).iter())
.copied();
for item_def_id in lang_items.iter().map(|(_, def_id)| def_id).chain(incoherent_impls) {
let lang_item_path = cx.get_def_path(item_def_id);
if path_syms.starts_with(&lang_item_path)
&& let [item] = &path_syms[lang_item_path.len()..]
{
if matches!(
cx.tcx.def_kind(item_def_id),
DefKind::Mod | DefKind::Enum | DefKind::Trait
) {
for child in cx.tcx.module_children(item_def_id) {
if child.ident.name == *item {
return true;
}
}
} else {
for child in cx.tcx.associated_item_def_ids(item_def_id) {
if cx.tcx.item_name(*child) == *item {
return true;
}
}
}
}
}
false
}

View file

@ -32,7 +32,7 @@ extern crate rustc_span;
mod almost_standard_lint_formulation;
mod collapsible_calls;
mod invalid_paths;
mod internal_paths;
mod lint_without_lint_pass;
mod msrv_attr_impl;
mod outer_expn_data_pass;
@ -46,7 +46,6 @@ use rustc_lint::{Lint, LintStore};
static LINTS: &[&Lint] = &[
almost_standard_lint_formulation::ALMOST_STANDARD_LINT_FORMULATION,
collapsible_calls::COLLAPSIBLE_SPAN_LINT_CALLS,
invalid_paths::INVALID_PATHS,
lint_without_lint_pass::DEFAULT_LINT,
lint_without_lint_pass::INVALID_CLIPPY_VERSION_ATTRIBUTE,
lint_without_lint_pass::LINT_WITHOUT_LINT_PASS,
@ -66,10 +65,9 @@ pub fn register_lints(store: &mut LintStore) {
store.register_early_pass(|| Box::new(unsorted_clippy_utils_paths::UnsortedClippyUtilsPaths));
store.register_early_pass(|| Box::new(produce_ice::ProduceIce));
store.register_late_pass(|_| Box::new(collapsible_calls::CollapsibleCalls));
store.register_late_pass(|_| Box::new(invalid_paths::InvalidPaths));
store.register_late_pass(|_| Box::<symbols::Symbols>::default());
store.register_late_pass(|_| Box::<lint_without_lint_pass::LintWithoutLintPass>::default());
store.register_late_pass(|_| Box::<unnecessary_def_path::UnnecessaryDefPath>::default());
store.register_late_pass(|_| Box::new(unnecessary_def_path::UnnecessaryDefPath));
store.register_late_pass(|_| Box::new(outer_expn_data_pass::OuterExpnDataPass));
store.register_late_pass(|_| Box::new(msrv_attr_impl::MsrvAttrImpl));
store.register_late_pass(|_| Box::new(almost_standard_lint_formulation::AlmostStandardFormulation::new()));

View file

@ -1,6 +1,7 @@
use crate::internal_paths;
use clippy_utils::diagnostics::{span_lint, span_lint_and_help};
use clippy_utils::is_lint_allowed;
use clippy_utils::macros::root_macro_call_first_node;
use clippy_utils::{is_lint_allowed, match_def_path, paths};
use rustc_ast::ast::LitKind;
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
use rustc_hir as hir;
@ -209,10 +210,10 @@ pub(super) fn is_lint_ref_type(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool {
&& let TyKind::Path(ref path) = inner.kind
&& let Res::Def(DefKind::Struct, def_id) = cx.qpath_res(path, inner.hir_id)
{
return match_def_path(cx, def_id, &paths::LINT);
internal_paths::LINT.matches(cx, def_id)
} else {
false
}
false
}
fn check_invalid_clippy_version_attribute(cx: &LateContext<'_>, item: &'_ Item<'_>) {

View file

@ -1,7 +1,6 @@
use crate::internal_paths;
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet;
use clippy_utils::ty::match_type;
use clippy_utils::{match_def_path, paths};
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_lint::{LateContext, LateLintPass, LintContext};
@ -31,7 +30,7 @@ impl LateLintPass<'_> for MsrvAttrImpl {
.tcx
.impl_trait_ref(item.owner_id)
.map(EarlyBinder::instantiate_identity)
&& match_def_path(cx, trait_ref.def_id, &paths::EARLY_LINT_PASS)
&& internal_paths::EARLY_LINT_PASS.matches(cx, trait_ref.def_id)
&& let ty::Adt(self_ty_def, _) = trait_ref.self_ty().kind()
&& self_ty_def.is_struct()
&& self_ty_def.all_fields().any(|f| {
@ -40,7 +39,7 @@ impl LateLintPass<'_> for MsrvAttrImpl {
.instantiate_identity()
.walk()
.filter(|t| matches!(t.unpack(), GenericArgKind::Type(_)))
.any(|t| match_type(cx, t.expect_ty(), &paths::MSRV_STACK))
.any(|t| internal_paths::MSRV_STACK.matches_ty(cx, t.expect_ty()))
})
&& !items.iter().any(|item| item.ident.name.as_str() == "check_attributes")
{

View file

@ -1,6 +1,6 @@
use crate::internal_paths;
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::ty::match_type;
use clippy_utils::{is_lint_allowed, method_calls, paths};
use clippy_utils::{is_lint_allowed, method_calls};
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_lint::{LateContext, LateLintPass};
@ -45,7 +45,7 @@ impl<'tcx> LateLintPass<'tcx> for OuterExpnDataPass {
&& let (self_arg, args) = arg_lists[1]
&& args.is_empty()
&& let self_ty = cx.typeck_results().expr_ty(self_arg).peel_refs()
&& match_type(cx, self_ty, &paths::SYNTAX_CONTEXT)
&& internal_paths::SYNTAX_CONTEXT.matches_ty(cx, self_ty)
{
span_lint_and_sugg(
cx,

View file

@ -1,6 +1,5 @@
use crate::internal_paths;
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::ty::match_type;
use clippy_utils::{def_path_def_ids, match_def_path, paths};
use rustc_ast::LitKind;
use rustc_data_structures::fx::FxHashMap;
use rustc_errors::Applicability;
@ -69,12 +68,12 @@ impl_lint_pass!(Symbols => [INTERNING_LITERALS, SYMBOL_AS_STR]);
impl<'tcx> LateLintPass<'tcx> for Symbols {
fn check_crate(&mut self, cx: &LateContext<'_>) {
let modules = [
("kw", &paths::KW_MODULE[..]),
("sym", &paths::SYM_MODULE),
("sym", &paths::CLIPPY_SYM_MODULE),
("kw", &internal_paths::KW_MODULE),
("sym", &internal_paths::SYM_MODULE),
("sym", &internal_paths::CLIPPY_SYM_MODULE),
];
for (prefix, module) in modules {
for def_id in def_path_def_ids(cx.tcx, module) {
for def_id in module.get(cx) {
// When linting `clippy_utils` itself we can't use `module_children` as it's a local def id. It will
// still lint but the suggestion will say to add it to `sym.rs` even if it's already there
if def_id.is_local() {
@ -84,7 +83,7 @@ impl<'tcx> LateLintPass<'tcx> for Symbols {
for item in cx.tcx.module_children(def_id) {
if let Res::Def(DefKind::Const, item_def_id) = item.res
&& let ty = cx.tcx.type_of(item_def_id).instantiate_identity()
&& match_type(cx, ty, &paths::SYMBOL)
&& internal_paths::SYMBOL.matches_ty(cx, ty)
&& let Ok(ConstValue::Scalar(value)) = cx.tcx.const_eval_poly(item_def_id)
&& let Some(value) = value.to_u32().discard_err()
{
@ -160,7 +159,7 @@ fn suggestion(symbols: &mut FxHashMap<u32, (&'static str, Symbol)>, name: Symbol
fn as_str_span(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<Span> {
if let ExprKind::MethodCall(_, recv, [], _) = expr.kind
&& let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id)
&& match_def_path(cx, method_def_id, &paths::SYMBOL_AS_STR)
&& internal_paths::SYMBOL_AS_STR.matches(cx, method_def_id)
{
Some(recv.span.shrink_to_hi().to(expr.span.shrink_to_hi()))
} else {

View file

@ -1,23 +1,14 @@
use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then};
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::{def_path_def_ids, is_lint_allowed, match_any_def_paths, peel_hir_expr_refs};
use rustc_ast::ast::LitKind;
use rustc_data_structures::fx::{FxHashSet, FxIndexSet};
use rustc_errors::Applicability;
use rustc_hir::def::{DefKind, Res};
use crate::internal_paths;
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::paths::{PathNS, lookup_path};
use clippy_utils::{path_def_id, peel_ref_operators};
use rustc_hir::def_id::DefId;
use rustc_hir::{Expr, ExprKind, LetStmt, Mutability, Node};
use rustc_hir::{Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_lint_defs::declare_tool_lint;
use rustc_lint_defs::{declare_lint_pass, declare_tool_lint};
use rustc_middle::mir::ConstValue;
use rustc_middle::mir::interpret::{Allocation, GlobalAlloc};
use rustc_middle::ty::{self, Ty};
use rustc_session::impl_lint_pass;
use rustc_span::Span;
use rustc_span::symbol::Symbol;
use std::str;
declare_tool_lint! {
/// ### What it does
/// Checks for usage of def paths when a diagnostic item or a `LangItem` could be used.
@ -28,12 +19,14 @@ declare_tool_lint! {
///
/// ### Example
/// ```rust,ignore
/// utils::match_type(cx, ty, &paths::VEC)
/// pub static VEC: PathLookup = path!(alloc::vec::Vec);
///
/// VEC.contains_ty(cx, ty)
/// ```
///
/// Use instead:
/// ```rust,ignore
/// utils::is_type_diagnostic_item(cx, ty, sym::Vec)
/// is_type_diagnostic_item(cx, ty, sym::Vec)
/// ```
pub clippy::UNNECESSARY_DEF_PATH,
Warn,
@ -41,257 +34,65 @@ declare_tool_lint! {
report_in_external_macro: true
}
impl_lint_pass!(UnnecessaryDefPath => [UNNECESSARY_DEF_PATH]);
#[derive(Default)]
pub struct UnnecessaryDefPath {
array_def_ids: FxIndexSet<(DefId, Span)>,
linted_def_ids: FxHashSet<DefId>,
}
declare_lint_pass!(UnnecessaryDefPath => [UNNECESSARY_DEF_PATH]);
impl<'tcx> LateLintPass<'tcx> for UnnecessaryDefPath {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
if is_lint_allowed(cx, UNNECESSARY_DEF_PATH, expr.hir_id) {
return;
}
match expr.kind {
ExprKind::Call(func, args) => self.check_call(cx, func, args, expr.span),
ExprKind::Array(elements) => self.check_array(cx, elements, expr.span),
_ => {},
}
}
fn check_crate_post(&mut self, cx: &LateContext<'tcx>) {
for &(def_id, span) in &self.array_def_ids {
if self.linted_def_ids.contains(&def_id) {
continue;
}
let (msg, sugg) = if let Some(sym) = cx.tcx.get_diagnostic_name(def_id) {
("diagnostic item", format!("sym::{sym}"))
} else if let Some(sym) = get_lang_item_name(cx, def_id) {
("language item", format!("LangItem::{sym}"))
} else {
continue;
};
span_lint_and_help(
cx,
UNNECESSARY_DEF_PATH,
span,
format!("hardcoded path to a {msg}"),
None,
format!("convert all references to use `{sugg}`"),
);
}
}
}
impl UnnecessaryDefPath {
#[allow(clippy::too_many_lines)]
fn check_call(&mut self, cx: &LateContext<'_>, func: &Expr<'_>, args: &[Expr<'_>], span: Span) {
enum Item {
LangItem(&'static str),
DiagnosticItem(Symbol),
}
static PATHS: &[&[&str]] = &[
&["clippy_utils", "match_def_path"],
&["clippy_utils", "match_trait_method"],
&["clippy_utils", "ty", "match_type"],
&["clippy_utils", "is_expr_path_def_path"],
];
if let [cx_arg, def_arg, args @ ..] = args
&& let ExprKind::Path(path) = &func.kind
&& let Some(id) = cx.qpath_res(path, func.hir_id).opt_def_id()
&& let Some(which_path) = match_any_def_paths(cx, id, PATHS)
&& let item_arg = if which_path == 4 { &args[1] } else { &args[0] }
// Extract the path to the matched type
&& let Some(segments) = path_to_matched_type(cx, item_arg)
&& let segments = segments.iter().map(|sym| &**sym).collect::<Vec<_>>()
&& let Some(def_id) = def_path_def_ids(cx.tcx, &segments[..]).next()
if let ExprKind::Call(ctor, [_, path]) = expr.kind
&& internal_paths::PATH_LOOKUP_NEW.matches_path(cx, ctor)
&& let ExprKind::Array(segments) = peel_ref_operators(cx, path).kind
&& let Some(macro_id) = expr.span.ctxt().outer_expn_data().macro_def_id
{
// Check if the target item is a diagnostic item or LangItem.
#[rustfmt::skip]
let (msg, item) = if let Some(item_name)
= cx.tcx.diagnostic_items(def_id.krate).id_to_name.get(&def_id)
{
(
"use of a def path to a diagnostic item",
Item::DiagnosticItem(*item_name),
)
} else if let Some(item_name) = get_lang_item_name(cx, def_id) {
(
"use of a def path to a `LangItem`",
Item::LangItem(item_name),
)
} else {
return;
let ns = match cx.tcx.item_name(macro_id).as_str() {
"type_path" => PathNS::Type,
"value_path" => PathNS::Value,
"macro_path" => PathNS::Macro,
_ => unreachable!(),
};
let has_ctor = match cx.tcx.def_kind(def_id) {
DefKind::Struct => {
let variant = cx.tcx.adt_def(def_id).non_enum_variant();
variant.ctor.is_some() && variant.fields.iter().all(|f| f.vis.is_public())
},
DefKind::Variant => {
let variant = cx.tcx.adt_def(cx.tcx.parent(def_id)).variant_with_id(def_id);
variant.ctor.is_some() && variant.fields.iter().all(|f| f.vis.is_public())
},
_ => false,
};
let path: Vec<Symbol> = segments
.iter()
.map(|segment| {
if let Some(const_def_id) = path_def_id(cx, segment)
&& let Ok(ConstValue::Scalar(value)) = cx.tcx.const_eval_poly(const_def_id)
&& let Some(value) = value.to_u32().discard_err()
{
Symbol::new(value)
} else {
panic!("failed to resolve path {:?}", expr.span);
}
})
.collect();
let mut app = Applicability::MachineApplicable;
let cx_snip = snippet_with_applicability(cx, cx_arg.span, "..", &mut app);
let def_snip = snippet_with_applicability(cx, def_arg.span, "..", &mut app);
let (sugg, with_note) = match (which_path, item) {
// match_def_path
(0, Item::DiagnosticItem(item)) => (
format!("{cx_snip}.tcx.is_diagnostic_item(sym::{item}, {def_snip})"),
has_ctor,
),
(0, Item::LangItem(item)) => (
format!("{cx_snip}.tcx.lang_items().get(LangItem::{item}) == Some({def_snip})"),
has_ctor,
),
// match_trait_method
(1, Item::DiagnosticItem(item)) => {
(format!("is_trait_method({cx_snip}, {def_snip}, sym::{item})"), false)
},
// match_type
(2, Item::DiagnosticItem(item)) => (
format!("is_type_diagnostic_item({cx_snip}, {def_snip}, sym::{item})"),
false,
),
(2, Item::LangItem(item)) => (
format!("is_type_lang_item({cx_snip}, {def_snip}, LangItem::{item})"),
false,
),
// is_expr_path_def_path
(3, Item::DiagnosticItem(item)) if has_ctor => (
format!("is_res_diag_ctor({cx_snip}, path_res({cx_snip}, {def_snip}), sym::{item})",),
false,
),
(3, Item::LangItem(item)) if has_ctor => (
format!("is_res_lang_ctor({cx_snip}, path_res({cx_snip}, {def_snip}), LangItem::{item})",),
false,
),
(3, Item::DiagnosticItem(item)) => (
format!("is_path_diagnostic_item({cx_snip}, {def_snip}, sym::{item})"),
false,
),
(3, Item::LangItem(item)) => (
format!(
"path_res({cx_snip}, {def_snip}).opt_def_id()\
.map_or(false, |id| {cx_snip}.tcx.lang_items().get(LangItem::{item}) == Some(id))",
),
false,
),
_ => return,
};
span_lint_and_then(cx, UNNECESSARY_DEF_PATH, span, msg, |diag| {
diag.span_suggestion(span, "try", sugg, app);
if with_note {
diag.help(
"if this `DefId` came from a constructor expression or pattern then the \
parent `DefId` should be used instead",
for def_id in lookup_path(cx.tcx, ns, &path) {
if let Some(name) = cx.tcx.get_diagnostic_name(def_id) {
span_lint_and_then(
cx,
UNNECESSARY_DEF_PATH,
expr.span.source_callsite(),
format!("a diagnostic name exists for this path: sym::{name}"),
|diag| {
diag.help(
"remove the `PathLookup` and use utilities such as `cx.tcx.is_diagnostic_item` instead",
);
diag.help("see also https://doc.rust-lang.org/nightly/nightly-rustc/?search=diag&filter-crate=clippy_utils");
},
);
} else if let Some(item_name) = get_lang_item_name(cx, def_id) {
span_lint_and_then(
cx,
UNNECESSARY_DEF_PATH,
expr.span.source_callsite(),
format!("a language item exists for this path: LangItem::{item_name}"),
|diag| {
diag.help("remove the `PathLookup` and use utilities such as `cx.tcx.lang_items` instead");
diag.help("see also https://doc.rust-lang.org/nightly/nightly-rustc/?search=lang&filter-crate=clippy_utils");
},
);
}
});
self.linted_def_ids.insert(def_id);
}
}
fn check_array(&mut self, cx: &LateContext<'_>, elements: &[Expr<'_>], span: Span) {
let Some(path) = path_from_array(elements) else { return };
for def_id in def_path_def_ids(cx.tcx, &path.iter().map(AsRef::as_ref).collect::<Vec<_>>()) {
self.array_def_ids.insert((def_id, span));
}
}
}
fn path_to_matched_type(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<Vec<String>> {
match peel_hir_expr_refs(expr).0.kind {
ExprKind::Path(ref qpath) => match cx.qpath_res(qpath, expr.hir_id) {
Res::Local(hir_id) => {
if let Node::LetStmt(LetStmt { init: Some(init), .. }) = cx.tcx.parent_hir_node(hir_id) {
path_to_matched_type(cx, init)
} else {
None
}
},
Res::Def(DefKind::Static { .. }, def_id) => read_mir_alloc_def_path(
cx,
cx.tcx.eval_static_initializer(def_id).ok()?.inner(),
cx.tcx.type_of(def_id).instantiate_identity(),
),
Res::Def(DefKind::Const, def_id) => match cx.tcx.const_eval_poly(def_id).ok()? {
ConstValue::Indirect { alloc_id, offset } if offset.bytes() == 0 => {
let alloc = cx.tcx.global_alloc(alloc_id).unwrap_memory();
read_mir_alloc_def_path(cx, alloc.inner(), cx.tcx.type_of(def_id).instantiate_identity())
},
_ => None,
},
_ => None,
},
ExprKind::Array(exprs) => path_from_array(exprs),
_ => None,
}
}
fn read_mir_alloc_def_path<'tcx>(cx: &LateContext<'tcx>, alloc: &'tcx Allocation, ty: Ty<'_>) -> Option<Vec<String>> {
let (alloc, ty) = if let ty::Ref(_, ty, Mutability::Not) = *ty.kind() {
let &alloc = alloc.provenance().ptrs().values().next()?;
if let GlobalAlloc::Memory(alloc) = cx.tcx.global_alloc(alloc.alloc_id()) {
(alloc.inner(), ty)
} else {
return None;
}
} else {
(alloc, ty)
};
if let ty::Array(ty, _) | ty::Slice(ty) = *ty.kind()
&& let ty::Ref(_, ty, Mutability::Not) = *ty.kind()
&& ty.is_str()
{
alloc
.provenance()
.ptrs()
.values()
.map(|&alloc| {
if let GlobalAlloc::Memory(alloc) = cx.tcx.global_alloc(alloc.alloc_id()) {
let alloc = alloc.inner();
str::from_utf8(alloc.inspect_with_uninit_and_ptr_outside_interpreter(0..alloc.len()))
.ok()
.map(ToOwned::to_owned)
} else {
None
}
})
.collect()
} else {
None
}
}
fn path_from_array(exprs: &[Expr<'_>]) -> Option<Vec<String>> {
exprs
.iter()
.map(|expr| {
if let ExprKind::Lit(lit) = &expr.kind
&& let LitKind::Str(sym, _) = lit.node
{
return Some((*sym.as_str()).to_owned());
}
None
})
.collect()
}
}
}
fn get_lang_item_name(cx: &LateContext<'_>, def_id: DefId) -> Option<&'static str> {

View file

@ -97,28 +97,28 @@ use rustc_data_structures::packed::Pu128;
use rustc_data_structures::unhash::UnhashMap;
use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk};
use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE, LocalDefId, LocalModDefId};
use rustc_hir::def_id::{DefId, LocalDefId, LocalModDefId};
use rustc_hir::definitions::{DefPath, DefPathData};
use rustc_hir::hir_id::{HirIdMap, HirIdSet};
use rustc_hir::intravisit::{FnKind, Visitor, walk_expr};
use rustc_hir::{
self as hir, Arm, BindingMode, Block, BlockCheckMode, Body, ByRef, Closure, ConstArgKind, ConstContext,
CoroutineDesugaring, CoroutineKind, Destination, Expr, ExprField, ExprKind, FnDecl, FnRetTy, GenericArg,
GenericArgs, HirId, Impl, ImplItem, ImplItemKind, ImplItemRef, Item, ItemKind, LangItem, LetStmt, MatchSource,
Mutability, Node, OwnerId, OwnerNode, Param, Pat, PatExpr, PatExprKind, PatKind, Path, PathSegment, PrimTy, QPath,
Stmt, StmtKind, TraitFn, TraitItem, TraitItemKind, TraitItemRef, TraitRef, TyKind, UnOp, def,
self as hir, Arm, BindingMode, Block, BlockCheckMode, Body, ByRef, Closure, ConstArgKind, CoroutineDesugaring,
CoroutineKind, Destination, Expr, ExprField, ExprKind, FnDecl, FnRetTy, GenericArg, GenericArgs, HirId, Impl,
ImplItem, ImplItemKind, Item, ItemKind, LangItem, LetStmt, MatchSource, Mutability, Node, OwnerId, OwnerNode,
Param, Pat, PatExpr, PatExprKind, PatKind, Path, PathSegment, QPath, Stmt, StmtKind, TraitFn, TraitItem,
TraitItemKind, TraitRef, TyKind, UnOp, def,
};
use rustc_lexer::{TokenKind, tokenize};
use rustc_lint::{LateContext, Level, Lint, LintContext};
use rustc_middle::hir::nested_filter;
use rustc_middle::hir::place::PlaceBase;
use rustc_middle::lint::LevelAndSource;
use rustc_middle::mir::{AggregateKind, Operand, RETURN_PLACE, Rvalue, StatementKind, TerminatorKind};
use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow};
use rustc_middle::ty::fast_reject::SimplifiedType;
use rustc_middle::ty::layout::IntegerExt;
use rustc_middle::ty::{
self as rustc_ty, Binder, BorrowKind, ClosureKind, EarlyBinder, FloatTy, GenericArgKind, GenericArgsRef, IntTy, Ty,
TyCtxt, TypeFlags, TypeVisitableExt, UintTy, UpvarCapture,
self as rustc_ty, Binder, BorrowKind, ClosureKind, EarlyBinder, GenericArgKind, GenericArgsRef, IntTy, Ty, TyCtxt,
TypeFlags, TypeVisitableExt, UintTy, UpvarCapture,
};
use rustc_span::hygiene::{ExpnKind, MacroKind};
use rustc_span::source_map::SourceMap;
@ -131,7 +131,6 @@ use crate::consts::{ConstEvalCtxt, Constant, mir_to_const};
use crate::higher::Range;
use crate::ty::{adt_and_variant_of_res, can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type};
use crate::visitors::for_each_expr_without_closures;
use rustc_middle::hir::nested_filter;
#[macro_export]
macro_rules! extract_msrv_attr {
@ -239,7 +238,7 @@ pub fn is_in_const_context(cx: &LateContext<'_>) -> bool {
/// * const blocks (or inline consts)
/// * associated constants
pub fn is_inside_always_const_context(tcx: TyCtxt<'_>, hir_id: HirId) -> bool {
use ConstContext::{Const, ConstFn, Static};
use rustc_hir::ConstContext::{Const, ConstFn, Static};
let Some(ctx) = tcx.hir_body_const_context(tcx.hir_enclosing_body_owner(hir_id)) else {
return false;
};
@ -347,14 +346,6 @@ pub fn is_ty_alias(qpath: &QPath<'_>) -> bool {
}
}
/// Checks if the method call given in `expr` belongs to the given trait.
/// This is a deprecated function, consider using [`is_trait_method`].
pub fn match_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, path: &[&str]) -> bool {
let def_id = cx.typeck_results().type_dependent_def_id(expr.hir_id).unwrap();
let trt_id = cx.tcx.trait_of_item(def_id);
trt_id.is_some_and(|trt_id| match_def_path(cx, trt_id, path))
}
/// Checks if the given method call expression calls an inherent method.
pub fn is_inherent_method_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) {
@ -438,44 +429,6 @@ pub fn qpath_generic_tys<'tcx>(qpath: &QPath<'tcx>) -> impl Iterator<Item = &'tc
})
}
/// THIS METHOD IS DEPRECATED. Matches a `QPath` against a slice of segment string literals.
///
/// 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
/// `QPath::Resolved.1.res.opt_def_id()`.
///
/// There is also `match_path` if you are dealing with a `rustc_hir::Path` instead of a
/// `rustc_hir::QPath`.
///
/// # Examples
/// ```rust,ignore
/// match_qpath(path, &["std", "rt", "begin_unwind"])
/// ```
pub fn match_qpath(path: &QPath<'_>, segments: &[&str]) -> bool {
match *path {
QPath::Resolved(_, path) => match_path(path, segments),
QPath::TypeRelative(ty, segment) => match ty.kind {
TyKind::Path(ref inner_path) => {
if let [prefix @ .., end] = segments
&& match_qpath(inner_path, prefix)
{
return segment.ident.name.as_str() == *end;
}
false
},
_ => false,
},
QPath::LangItem(..) => false,
}
}
/// If the expression is a path, resolves it to a `DefId` and checks if it matches the given path.
///
/// Please use `is_path_diagnostic_item` if the target is a diagnostic item.
pub fn is_expr_path_def_path(cx: &LateContext<'_>, expr: &Expr<'_>, segments: &[&str]) -> bool {
path_def_id(cx, expr).is_some_and(|id| match_def_path(cx, id, segments))
}
/// If `maybe_path` is a path node which resolves to an item, resolves it to a `DefId` and checks if
/// it matches the given lang item.
pub fn is_path_lang_item<'tcx>(cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx>, lang_item: LangItem) -> bool {
@ -492,34 +445,6 @@ pub fn is_path_diagnostic_item<'tcx>(
path_def_id(cx, maybe_path).is_some_and(|id| cx.tcx.is_diagnostic_item(diag_item, id))
}
/// THIS METHOD IS DEPRECATED. Matches a `Path` against a slice of segment string literals.
///
/// 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
/// `QPath::Resolved.1.res.opt_def_id()`.
///
/// There is also `match_qpath` if you are dealing with a `rustc_hir::QPath` instead of a
/// `rustc_hir::Path`.
///
/// # Examples
///
/// ```rust,ignore
/// if match_path(&trait_ref.path, &paths::HASH) {
/// // This is the `std::hash::Hash` trait.
/// }
///
/// if match_path(ty_path, &["rustc", "lint", "Lint"]) {
/// // This is a `rustc_middle::lint::Lint`.
/// }
/// ```
pub fn match_path(path: &Path<'_>, segments: &[&str]) -> bool {
path.segments
.iter()
.rev()
.zip(segments.iter().rev())
.all(|(a, b)| a.ident.name.as_str() == *b)
}
/// If the expression is a path to a local, returns the canonical `HirId` of the local.
pub fn path_to_local(expr: &Expr<'_>) -> Option<HirId> {
if let ExprKind::Path(QPath::Resolved(None, path)) = expr.kind
@ -586,201 +511,6 @@ pub fn path_def_id<'tcx>(cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx>
path_res(cx, maybe_path).opt_def_id()
}
fn find_primitive_impls<'tcx>(tcx: TyCtxt<'tcx>, name: &str) -> impl Iterator<Item = DefId> + 'tcx {
let ty = match name {
"bool" => SimplifiedType::Bool,
"char" => SimplifiedType::Char,
"str" => SimplifiedType::Str,
"array" => SimplifiedType::Array,
"slice" => SimplifiedType::Slice,
// FIXME: rustdoc documents these two using just `pointer`.
//
// Maybe this is something we should do here too.
"const_ptr" => SimplifiedType::Ptr(Mutability::Not),
"mut_ptr" => SimplifiedType::Ptr(Mutability::Mut),
"isize" => SimplifiedType::Int(IntTy::Isize),
"i8" => SimplifiedType::Int(IntTy::I8),
"i16" => SimplifiedType::Int(IntTy::I16),
"i32" => SimplifiedType::Int(IntTy::I32),
"i64" => SimplifiedType::Int(IntTy::I64),
"i128" => SimplifiedType::Int(IntTy::I128),
"usize" => SimplifiedType::Uint(UintTy::Usize),
"u8" => SimplifiedType::Uint(UintTy::U8),
"u16" => SimplifiedType::Uint(UintTy::U16),
"u32" => SimplifiedType::Uint(UintTy::U32),
"u64" => SimplifiedType::Uint(UintTy::U64),
"u128" => SimplifiedType::Uint(UintTy::U128),
"f32" => SimplifiedType::Float(FloatTy::F32),
"f64" => SimplifiedType::Float(FloatTy::F64),
_ => {
return [].iter().copied();
},
};
tcx.incoherent_impls(ty).iter().copied()
}
fn non_local_item_children_by_name(tcx: TyCtxt<'_>, def_id: DefId, name: Symbol) -> Vec<Res> {
match tcx.def_kind(def_id) {
DefKind::Mod | DefKind::Enum | DefKind::Trait => tcx
.module_children(def_id)
.iter()
.filter(|item| item.ident.name == name)
.map(|child| child.res.expect_non_local())
.collect(),
DefKind::Impl { .. } => tcx
.associated_item_def_ids(def_id)
.iter()
.copied()
.filter(|assoc_def_id| tcx.item_name(*assoc_def_id) == name)
.map(|assoc_def_id| Res::Def(tcx.def_kind(assoc_def_id), assoc_def_id))
.collect(),
_ => Vec::new(),
}
}
fn local_item_children_by_name(tcx: TyCtxt<'_>, local_id: LocalDefId, name: Symbol) -> Vec<Res> {
let root_mod;
let item_kind = match tcx.hir_node_by_def_id(local_id) {
Node::Crate(r#mod) => {
root_mod = ItemKind::Mod(Ident::dummy(), r#mod);
&root_mod
},
Node::Item(item) => &item.kind,
_ => return Vec::new(),
};
let res = |ident: Ident, owner_id: OwnerId| {
if ident.name == name {
let def_id = owner_id.to_def_id();
Some(Res::Def(tcx.def_kind(def_id), def_id))
} else {
None
}
};
match item_kind {
ItemKind::Mod(_, r#mod) => r#mod
.item_ids
.iter()
.filter_map(|&item_id| {
let ident = tcx.hir_item(item_id).kind.ident()?;
res(ident, item_id.owner_id)
})
.collect(),
ItemKind::Impl(r#impl) => r#impl
.items
.iter()
.filter_map(|&ImplItemRef { ident, id, .. }| res(ident, id.owner_id))
.collect(),
ItemKind::Trait(.., trait_item_refs) => trait_item_refs
.iter()
.filter_map(|&TraitItemRef { ident, id, .. }| res(ident, id.owner_id))
.collect(),
_ => Vec::new(),
}
}
fn item_children_by_name(tcx: TyCtxt<'_>, def_id: DefId, name: Symbol) -> Vec<Res> {
if let Some(local_id) = def_id.as_local() {
local_item_children_by_name(tcx, local_id, name)
} else {
non_local_item_children_by_name(tcx, def_id, name)
}
}
/// Finds the crates called `name`, may be multiple due to multiple major versions.
pub fn find_crates(tcx: TyCtxt<'_>, name: Symbol) -> Vec<Res> {
tcx.crates(())
.iter()
.copied()
.filter(move |&num| tcx.crate_name(num) == name)
.map(CrateNum::as_def_id)
.map(|id| Res::Def(tcx.def_kind(id), id))
.collect()
}
/// Resolves a def path like `std::vec::Vec`.
///
/// Can return multiple resolutions when there are multiple versions of the same crate, e.g.
/// `memchr::memchr` could return the functions from both memchr 1.0 and memchr 2.0.
///
/// Also returns multiple results when there are multiple paths under the same name e.g. `std::vec`
/// would have both a [`DefKind::Mod`] and [`DefKind::Macro`].
///
/// 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 {
[primitive] => {
return vec![PrimTy::from_name(Symbol::intern(primitive)).map_or(Res::Err, Res::PrimTy)];
},
[base, path @ ..] => (base, path),
_ => return Vec::new(),
};
let base_sym = Symbol::intern(base);
let local_crate = if tcx.crate_name(LOCAL_CRATE) == base_sym {
Some(LOCAL_CRATE.as_def_id())
} else {
None
};
let crates = find_primitive_impls(tcx, base)
.chain(local_crate)
.map(|id| Res::Def(tcx.def_kind(id), id))
.chain(find_crates(tcx, base_sym))
.collect();
def_path_res_with_base(tcx, crates, path)
}
/// Resolves a def path like `vec::Vec` with the base `std`.
///
/// This is lighter than [`def_path_res`], and should be called with [`find_crates`] looking up
/// items from the same crate repeatedly, although should still be used sparingly.
pub fn def_path_res_with_base(tcx: TyCtxt<'_>, mut base: Vec<Res>, mut path: &[&str]) -> Vec<Res> {
while let [segment, rest @ ..] = path {
path = rest;
let segment = Symbol::intern(segment);
base = base
.into_iter()
.filter_map(|res| res.opt_def_id())
.flat_map(|def_id| {
// When the current def_id is e.g. `struct S`, check the impl items in
// `impl S { ... }`
let inherent_impl_children = tcx
.inherent_impls(def_id)
.iter()
.flat_map(|&impl_def_id| item_children_by_name(tcx, impl_def_id, segment));
let direct_children = item_children_by_name(tcx, def_id, segment);
inherent_impl_children.chain(direct_children)
})
.collect();
}
base
}
/// 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> + use<> {
def_path_res(tcx, path).into_iter().filter_map(|res| res.opt_def_id())
}
/// Convenience function to get the `DefId` of a trait by path.
/// It could be a trait or trait alias.
///
/// This function is expensive and should be used sparingly.
pub fn get_trait_def_id(tcx: TyCtxt<'_>, path: &[&str]) -> Option<DefId> {
def_path_res(tcx, path).into_iter().find_map(|res| match res {
Res::Def(DefKind::Trait | DefKind::TraitAlias, trait_id) => Some(trait_id),
_ => None,
})
}
/// Gets the `hir::TraitRef` of the trait the given method is implemented for.
///
/// Use this if you want to find the `TraitRef` of the `Add` trait in this example:
@ -2065,24 +1795,6 @@ pub fn in_automatically_derived(tcx: TyCtxt<'_>, id: HirId) -> bool {
})
}
/// Checks if the given `DefId` matches any of the paths. Returns the index of matching path, if
/// any.
///
/// 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> {
let search_path = cx.get_def_path(did);
paths
.iter()
.position(|p| p.iter().map(|x| Symbol::intern(x)).eq(search_path.iter().copied()))
}
/// Checks if the given `DefId` matches the path.
pub fn match_def_path(cx: &LateContext<'_>, did: DefId, syms: &[&str]) -> bool {
// We should probably move to Symbols in Clippy as well rather than interning every time.
let path = cx.get_def_path(did);
syms.iter().map(|x| Symbol::intern(x)).eq(path.iter().copied())
}
/// Checks if the given `DefId` matches the `libc` item.
pub fn match_libc_symbol(cx: &LateContext<'_>, did: DefId, name: &str) -> bool {
let path = cx.get_def_path(did);

View file

@ -4,62 +4,332 @@
//! Whenever possible, please consider diagnostic items over hardcoded paths.
//! See <https://github.com/rust-lang/rust-clippy/issues/5393> for more information.
// Paths inside rustc
pub const APPLICABILITY: [&str; 2] = ["rustc_lint_defs", "Applicability"];
pub const APPLICABILITY_VALUES: [[&str; 3]; 4] = [
["rustc_lint_defs", "Applicability", "Unspecified"],
["rustc_lint_defs", "Applicability", "HasPlaceholders"],
["rustc_lint_defs", "Applicability", "MaybeIncorrect"],
["rustc_lint_defs", "Applicability", "MachineApplicable"],
];
pub const DIAG: [&str; 2] = ["rustc_errors", "Diag"];
pub const EARLY_CONTEXT: [&str; 2] = ["rustc_lint", "EarlyContext"];
pub const EARLY_LINT_PASS: [&str; 3] = ["rustc_lint", "passes", "EarlyLintPass"];
pub const IDENT: [&str; 3] = ["rustc_span", "symbol", "Ident"];
pub const IDENT_AS_STR: [&str; 4] = ["rustc_span", "symbol", "Ident", "as_str"];
pub const KW_MODULE: [&str; 3] = ["rustc_span", "symbol", "kw"];
pub const LATE_CONTEXT: [&str; 2] = ["rustc_lint", "LateContext"];
pub const LINT: [&str; 2] = ["rustc_lint_defs", "Lint"];
pub const SYMBOL: [&str; 3] = ["rustc_span", "symbol", "Symbol"];
pub const SYMBOL_AS_STR: [&str; 4] = ["rustc_span", "symbol", "Symbol", "as_str"];
pub const SYMBOL_TO_IDENT_STRING: [&str; 4] = ["rustc_span", "symbol", "Symbol", "to_ident_string"];
pub const SYM_MODULE: [&str; 3] = ["rustc_span", "symbol", "sym"];
pub const SYNTAX_CONTEXT: [&str; 3] = ["rustc_span", "hygiene", "SyntaxContext"];
use crate::{MaybePath, path_def_id, sym};
use rustc_ast::Mutability;
use rustc_data_structures::fx::FxHashMap;
use rustc_hir::def::Namespace::{MacroNS, TypeNS, ValueNS};
use rustc_hir::def::{DefKind, Namespace};
use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId};
use rustc_hir::{ImplItemRef, ItemKind, Node, OwnerId, TraitItemRef};
use rustc_lint::LateContext;
use rustc_middle::ty::fast_reject::SimplifiedType;
use rustc_middle::ty::{FloatTy, IntTy, Ty, TyCtxt, UintTy};
use rustc_span::{Ident, STDLIB_STABLE_CRATES, Symbol};
use std::sync::OnceLock;
/// Specifies whether to resolve a path in the [`TypeNS`], [`ValueNS`], [`MacroNS`] or in an
/// arbitrary namespace
#[derive(Clone, Copy, PartialEq, Debug)]
pub enum PathNS {
Type,
Value,
Macro,
/// Resolves to the name in the first available namespace, e.g. for `std::vec` this would return
/// either the macro or the module but **not** both
///
/// Must only be used when the specific resolution is unimportant such as in
/// `missing_enforced_import_renames`
Arbitrary,
}
impl PathNS {
fn matches(self, ns: Option<Namespace>) -> bool {
let required = match self {
PathNS::Type => TypeNS,
PathNS::Value => ValueNS,
PathNS::Macro => MacroNS,
PathNS::Arbitrary => return true,
};
ns == Some(required)
}
}
/// Lazily resolves a path into a list of [`DefId`]s using [`lookup_path`].
///
/// Typically it will contain one [`DefId`] or none, but in some situations there can be multiple:
/// - `memchr::memchr` could return the functions from both memchr 1.0 and memchr 2.0
/// - `alloc::boxed::Box::downcast` would return a function for each of the different inherent impls
/// ([1], [2], [3])
///
/// [1]: https://doc.rust-lang.org/std/boxed/struct.Box.html#method.downcast
/// [2]: https://doc.rust-lang.org/std/boxed/struct.Box.html#method.downcast-1
/// [3]: https://doc.rust-lang.org/std/boxed/struct.Box.html#method.downcast-2
pub struct PathLookup {
ns: PathNS,
path: &'static [Symbol],
once: OnceLock<Vec<DefId>>,
}
impl PathLookup {
/// Only exported for tests and `clippy_lints_internal`
#[doc(hidden)]
pub const fn new(ns: PathNS, path: &'static [Symbol]) -> Self {
Self {
ns,
path,
once: OnceLock::new(),
}
}
/// Returns the list of [`DefId`]s that the path resolves to
pub fn get(&self, cx: &LateContext<'_>) -> &[DefId] {
self.once.get_or_init(|| lookup_path(cx.tcx, self.ns, self.path))
}
/// Returns the single [`DefId`] that the path resolves to, this can only be used for paths into
/// stdlib crates to avoid the issue of multiple [`DefId`]s being returned
///
/// May return [`None`] in `no_std`/`no_core` environments
pub fn only(&self, cx: &LateContext<'_>) -> Option<DefId> {
let ids = self.get(cx);
debug_assert!(STDLIB_STABLE_CRATES.contains(&self.path[0]));
debug_assert!(ids.len() <= 1, "{ids:?}");
ids.first().copied()
}
/// Checks if the path resolves to the given `def_id`
pub fn matches(&self, cx: &LateContext<'_>, def_id: DefId) -> bool {
self.get(cx).contains(&def_id)
}
/// Resolves `maybe_path` to a [`DefId`] and checks if the [`PathLookup`] matches it
pub fn matches_path<'tcx>(&self, cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx>) -> bool {
path_def_id(cx, maybe_path).is_some_and(|def_id| self.matches(cx, def_id))
}
/// Checks if the path resolves to `ty`'s definition, must be an `Adt`
pub fn matches_ty(&self, cx: &LateContext<'_>, ty: Ty<'_>) -> bool {
ty.ty_adt_def().is_some_and(|adt| self.matches(cx, adt.did()))
}
}
macro_rules! path_macros {
($($name:ident: $ns:expr,)*) => {
$(
/// Only exported for tests and `clippy_lints_internal`
#[doc(hidden)]
#[macro_export]
macro_rules! $name {
($$($$seg:ident $$(::)?)*) => {
PathLookup::new($ns, &[$$(sym::$$seg,)*])
};
}
)*
};
}
path_macros! {
type_path: PathNS::Type,
value_path: PathNS::Value,
macro_path: PathNS::Macro,
}
// Paths in `core`/`alloc`/`std`. This should be avoided and cleaned up by adding diagnostic items.
pub const CHAR_IS_ASCII: [&str; 5] = ["core", "char", "methods", "<impl char>", "is_ascii"];
pub const IO_ERROR_NEW: [&str; 5] = ["std", "io", "error", "Error", "new"];
pub const IO_ERRORKIND_OTHER: [&str; 5] = ["std", "io", "error", "ErrorKind", "Other"];
pub const ALIGN_OF: [&str; 3] = ["core", "mem", "align_of"];
// Paths in clippy itself
pub const MSRV_STACK: [&str; 3] = ["clippy_utils", "msrvs", "MsrvStack"];
pub const CLIPPY_SYM_MODULE: [&str; 2] = ["clippy_utils", "sym"];
pub static ALIGN_OF: PathLookup = value_path!(core::mem::align_of);
pub static CHAR_TO_DIGIT: PathLookup = value_path!(char::to_digit);
pub static IO_ERROR_NEW: PathLookup = value_path!(std::io::Error::new);
pub static IO_ERRORKIND_OTHER_CTOR: PathLookup = value_path!(std::io::ErrorKind::Other);
pub static ITER_STEP: PathLookup = type_path!(core::iter::Step);
pub static SLICE_FROM_REF: PathLookup = value_path!(core::slice::from_ref);
// Paths in external crates
#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates
pub const FUTURES_IO_ASYNCREADEXT: [&str; 3] = ["futures_util", "io", "AsyncReadExt"];
#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates
pub const FUTURES_IO_ASYNCWRITEEXT: [&str; 3] = ["futures_util", "io", "AsyncWriteExt"];
pub const ITERTOOLS_NEXT_TUPLE: [&str; 3] = ["itertools", "Itertools", "next_tuple"];
pub const PARKING_LOT_MUTEX_GUARD: [&str; 3] = ["lock_api", "mutex", "MutexGuard"];
pub const PARKING_LOT_RWLOCK_READ_GUARD: [&str; 3] = ["lock_api", "rwlock", "RwLockReadGuard"];
pub const PARKING_LOT_RWLOCK_WRITE_GUARD: [&str; 3] = ["lock_api", "rwlock", "RwLockWriteGuard"];
pub const REGEX_BUILDER_NEW: [&str; 3] = ["regex", "RegexBuilder", "new"];
pub const REGEX_BYTES_BUILDER_NEW: [&str; 4] = ["regex", "bytes", "RegexBuilder", "new"];
pub const REGEX_BYTES_NEW: [&str; 4] = ["regex", "bytes", "Regex", "new"];
pub const REGEX_BYTES_SET_NEW: [&str; 4] = ["regex", "bytes", "RegexSet", "new"];
pub const REGEX_NEW: [&str; 3] = ["regex", "Regex", "new"];
pub const REGEX_SET_NEW: [&str; 3] = ["regex", "RegexSet", "new"];
pub const SERDE_DESERIALIZE: [&str; 3] = ["serde", "de", "Deserialize"];
pub const SERDE_DE_VISITOR: [&str; 3] = ["serde", "de", "Visitor"];
#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates
pub const TOKIO_FILE_OPTIONS: [&str; 5] = ["tokio", "fs", "file", "File", "options"];
#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates
pub const TOKIO_IO_ASYNCREADEXT: [&str; 5] = ["tokio", "io", "util", "async_read_ext", "AsyncReadExt"];
#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates
pub const TOKIO_IO_ASYNCWRITEEXT: [&str; 5] = ["tokio", "io", "util", "async_write_ext", "AsyncWriteExt"];
#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates
pub const TOKIO_IO_OPEN_OPTIONS: [&str; 4] = ["tokio", "fs", "open_options", "OpenOptions"];
#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates
pub const TOKIO_IO_OPEN_OPTIONS_NEW: [&str; 5] = ["tokio", "fs", "open_options", "OpenOptions", "new"];
pub static FUTURES_IO_ASYNCREADEXT: PathLookup = type_path!(futures_util::AsyncReadExt);
pub static FUTURES_IO_ASYNCWRITEEXT: PathLookup = type_path!(futures_util::AsyncWriteExt);
pub static ITERTOOLS_NEXT_TUPLE: PathLookup = value_path!(itertools::Itertools::next_tuple);
pub static PARKING_LOT_GUARDS: [PathLookup; 3] = [
type_path!(lock_api::mutex::MutexGuard),
type_path!(lock_api::rwlock::RwLockReadGuard),
type_path!(lock_api::rwlock::RwLockWriteGuard),
];
pub static REGEX_BUILDER_NEW: PathLookup = value_path!(regex::RegexBuilder::new);
pub static REGEX_BYTES_BUILDER_NEW: PathLookup = value_path!(regex::bytes::RegexBuilder::new);
pub static REGEX_BYTES_NEW: PathLookup = value_path!(regex::bytes::Regex::new);
pub static REGEX_BYTES_SET_NEW: PathLookup = value_path!(regex::bytes::RegexSet::new);
pub static REGEX_NEW: PathLookup = value_path!(regex::Regex::new);
pub static REGEX_SET_NEW: PathLookup = value_path!(regex::RegexSet::new);
pub static SERDE_DESERIALIZE: PathLookup = type_path!(serde::de::Deserialize);
pub static SERDE_DE_VISITOR: PathLookup = type_path!(serde::de::Visitor);
pub static TOKIO_FILE_OPTIONS: PathLookup = value_path!(tokio::fs::File::options);
pub static TOKIO_IO_ASYNCREADEXT: PathLookup = type_path!(tokio::io::AsyncReadExt);
pub static TOKIO_IO_ASYNCWRITEEXT: PathLookup = type_path!(tokio::io::AsyncWriteExt);
pub static TOKIO_IO_OPEN_OPTIONS: PathLookup = type_path!(tokio::fs::OpenOptions);
pub static TOKIO_IO_OPEN_OPTIONS_NEW: PathLookup = value_path!(tokio::fs::OpenOptions::new);
pub static LAZY_STATIC: PathLookup = macro_path!(lazy_static::lazy_static);
pub static ONCE_CELL_SYNC_LAZY: PathLookup = type_path!(once_cell::sync::Lazy);
pub static ONCE_CELL_SYNC_LAZY_NEW: PathLookup = value_path!(once_cell::sync::Lazy::new);
// Paths for internal lints go in `clippy_lints_internal/src/internal_paths.rs`
/// Equivalent to a [`lookup_path`] after splitting the input string on `::`
///
/// This function is expensive and should be used sparingly.
pub fn lookup_path_str(tcx: TyCtxt<'_>, ns: PathNS, path: &str) -> Vec<DefId> {
let path: Vec<Symbol> = path.split("::").map(Symbol::intern).collect();
lookup_path(tcx, ns, &path)
}
/// Resolves a def path like `std::vec::Vec`.
///
/// Typically it will return one [`DefId`] or none, but in some situations there can be multiple:
/// - `memchr::memchr` could return the functions from both memchr 1.0 and memchr 2.0
/// - `alloc::boxed::Box::downcast` would return a function for each of the different inherent impls
/// ([1], [2], [3])
///
/// This function is expensive and should be used sparingly.
///
/// [1]: https://doc.rust-lang.org/std/boxed/struct.Box.html#method.downcast
/// [2]: https://doc.rust-lang.org/std/boxed/struct.Box.html#method.downcast-1
/// [3]: https://doc.rust-lang.org/std/boxed/struct.Box.html#method.downcast-2
pub fn lookup_path(tcx: TyCtxt<'_>, ns: PathNS, path: &[Symbol]) -> Vec<DefId> {
let (root, rest) = match *path {
[] | [_] => return Vec::new(),
[root, ref rest @ ..] => (root, rest),
};
let mut out = Vec::new();
for &base in find_crates(tcx, root).iter().chain(find_primitive_impls(tcx, root)) {
lookup_with_base(tcx, base, ns, rest, &mut out);
}
out
}
/// Finds the crates called `name`, may be multiple due to multiple major versions.
pub fn find_crates(tcx: TyCtxt<'_>, name: Symbol) -> &'static [DefId] {
static BY_NAME: OnceLock<FxHashMap<Symbol, Vec<DefId>>> = OnceLock::new();
let map = BY_NAME.get_or_init(|| {
let mut map = FxHashMap::default();
map.insert(tcx.crate_name(LOCAL_CRATE), vec![LOCAL_CRATE.as_def_id()]);
for &num in tcx.crates(()) {
map.entry(tcx.crate_name(num)).or_default().push(num.as_def_id());
}
map
});
match map.get(&name) {
Some(def_ids) => def_ids,
None => &[],
}
}
fn find_primitive_impls(tcx: TyCtxt<'_>, name: Symbol) -> &[DefId] {
let ty = match name {
sym::bool => SimplifiedType::Bool,
sym::char => SimplifiedType::Char,
sym::str => SimplifiedType::Str,
sym::array => SimplifiedType::Array,
sym::slice => SimplifiedType::Slice,
// FIXME: rustdoc documents these two using just `pointer`.
//
// Maybe this is something we should do here too.
sym::const_ptr => SimplifiedType::Ptr(Mutability::Not),
sym::mut_ptr => SimplifiedType::Ptr(Mutability::Mut),
sym::isize => SimplifiedType::Int(IntTy::Isize),
sym::i8 => SimplifiedType::Int(IntTy::I8),
sym::i16 => SimplifiedType::Int(IntTy::I16),
sym::i32 => SimplifiedType::Int(IntTy::I32),
sym::i64 => SimplifiedType::Int(IntTy::I64),
sym::i128 => SimplifiedType::Int(IntTy::I128),
sym::usize => SimplifiedType::Uint(UintTy::Usize),
sym::u8 => SimplifiedType::Uint(UintTy::U8),
sym::u16 => SimplifiedType::Uint(UintTy::U16),
sym::u32 => SimplifiedType::Uint(UintTy::U32),
sym::u64 => SimplifiedType::Uint(UintTy::U64),
sym::u128 => SimplifiedType::Uint(UintTy::U128),
sym::f32 => SimplifiedType::Float(FloatTy::F32),
sym::f64 => SimplifiedType::Float(FloatTy::F64),
_ => return &[],
};
tcx.incoherent_impls(ty)
}
/// Resolves a def path like `vec::Vec` with the base `std`.
fn lookup_with_base(tcx: TyCtxt<'_>, mut base: DefId, ns: PathNS, mut path: &[Symbol], out: &mut Vec<DefId>) {
loop {
match *path {
[segment] => {
out.extend(item_child_by_name(tcx, base, ns, segment));
// When the current def_id is e.g. `struct S`, check the impl items in
// `impl S { ... }`
let inherent_impl_children = tcx
.inherent_impls(base)
.iter()
.filter_map(|&impl_def_id| item_child_by_name(tcx, impl_def_id, ns, segment));
out.extend(inherent_impl_children);
return;
},
[segment, ref rest @ ..] => {
path = rest;
let Some(child) = item_child_by_name(tcx, base, PathNS::Type, segment) else {
return;
};
base = child;
},
[] => unreachable!(),
}
}
}
fn item_child_by_name(tcx: TyCtxt<'_>, def_id: DefId, ns: PathNS, name: Symbol) -> Option<DefId> {
if let Some(local_id) = def_id.as_local() {
local_item_child_by_name(tcx, local_id, ns, name)
} else {
non_local_item_child_by_name(tcx, def_id, ns, name)
}
}
fn local_item_child_by_name(tcx: TyCtxt<'_>, local_id: LocalDefId, ns: PathNS, name: Symbol) -> Option<DefId> {
let root_mod;
let item_kind = match tcx.hir_node_by_def_id(local_id) {
Node::Crate(r#mod) => {
root_mod = ItemKind::Mod(Ident::dummy(), r#mod);
&root_mod
},
Node::Item(item) => &item.kind,
_ => return None,
};
let res = |ident: Ident, owner_id: OwnerId| {
if ident.name == name && ns.matches(tcx.def_kind(owner_id).ns()) {
Some(owner_id.to_def_id())
} else {
None
}
};
match item_kind {
ItemKind::Mod(_, r#mod) => r#mod.item_ids.iter().find_map(|&item_id| {
let ident = tcx.hir_item(item_id).kind.ident()?;
res(ident, item_id.owner_id)
}),
ItemKind::Impl(r#impl) => r#impl
.items
.iter()
.find_map(|&ImplItemRef { ident, id, .. }| res(ident, id.owner_id)),
ItemKind::Trait(.., trait_item_refs) => trait_item_refs
.iter()
.find_map(|&TraitItemRef { ident, id, .. }| res(ident, id.owner_id)),
_ => None,
}
}
fn non_local_item_child_by_name(tcx: TyCtxt<'_>, def_id: DefId, ns: PathNS, name: Symbol) -> Option<DefId> {
match tcx.def_kind(def_id) {
DefKind::Mod | DefKind::Enum | DefKind::Trait => tcx.module_children(def_id).iter().find_map(|child| {
if child.ident.name == name && ns.matches(child.res.ns()) {
child.res.opt_def_id()
} else {
None
}
}),
DefKind::Impl { .. } => tcx
.associated_item_def_ids(def_id)
.iter()
.copied()
.find(|assoc_def_id| tcx.item_name(*assoc_def_id) == name && ns.matches(tcx.def_kind(assoc_def_id).ns())),
_ => None,
}
}

View file

@ -1,6 +1,6 @@
#![allow(non_upper_case_globals)]
use rustc_span::symbol::{PREDEFINED_SYMBOLS_COUNT, Symbol};
use rustc_span::symbol::PREDEFINED_SYMBOLS_COUNT;
#[doc(no_inline)]
pub use rustc_span::sym::*;
@ -24,33 +24,45 @@ macro_rules! generate {
];
$(
pub const $name: Symbol = Symbol::new(PREDEFINED_SYMBOLS_COUNT + ${index()});
pub const $name: rustc_span::Symbol = rustc_span::Symbol::new(PREDEFINED_SYMBOLS_COUNT + ${index()});
)*
};
}
generate! {
abs,
align_of,
as_bytes,
as_deref_mut,
as_deref,
as_mut,
AsyncReadExt,
AsyncWriteExt,
Binary,
build_hasher,
bytes,
cargo_clippy: "cargo-clippy",
Cargo_toml: "Cargo.toml",
cast,
chars,
CLIPPY_ARGS,
CLIPPY_CONF_DIR,
clippy_utils,
clone_into,
cloned,
collect,
const_ptr,
contains,
copied,
CRLF: "\r\n",
Current,
de,
Deserialize,
diagnostics,
EarlyLintPass,
ends_with,
error,
ErrorKind,
exp,
extend,
finish_non_exhaustive,
@ -58,48 +70,87 @@ generate! {
flat_map,
for_each,
from_raw,
from_ref,
from_str_radix,
fs,
futures_util,
get,
hygiene,
insert,
int_roundings,
into_bytes,
into_owned,
IntoIter,
io,
is_ascii,
is_empty,
is_err,
is_none,
is_ok,
is_some,
itertools,
Itertools,
kw,
last,
lazy_static,
Lazy,
LF: "\n",
Lint,
lock_api,
LowerExp,
LowerHex,
max,
MAX,
mem,
min,
MIN,
mode,
msrv,
msrvs,
MsrvStack,
mut_ptr,
mutex,
next_tuple,
Octal,
once_cell,
OpenOptions,
or_default,
Other,
parse,
PathLookup,
paths,
push,
regex,
Regex,
RegexBuilder,
RegexSet,
reserve,
resize,
restriction,
rustc_lint_defs,
rustc_lint,
rustc_span,
rustfmt_skip,
rwlock,
serde,
set_len,
set_mode,
set_readonly,
signum,
span_lint_and_then,
split_whitespace,
split,
Start,
Step,
symbol,
Symbol,
SyntaxContext,
take,
TBD,
then_some,
to_digit,
to_owned,
tokio,
unused_extern_crates,
unwrap_err,
unwrap_or_default,
@ -107,6 +158,7 @@ generate! {
UpperHex,
V4,
V6,
Visitor,
Weak,
with_capacity,
wrapping_offset,

View file

@ -32,7 +32,8 @@ use std::assert_matches::debug_assert_matches;
use std::collections::hash_map::Entry;
use std::iter;
use crate::{def_path_def_ids, match_def_path, path_res};
use crate::path_res;
use crate::paths::{PathNS, lookup_path_str};
mod type_certainty;
pub use type_certainty::expr_type_is_certain;
@ -229,9 +230,7 @@ pub fn has_iter_method(cx: &LateContext<'_>, probably_ref_ty: Ty<'_>) -> Option<
/// Checks whether a type implements a trait.
/// The function returns false in case the type contains an inference variable.
///
/// See:
/// * [`get_trait_def_id`](super::get_trait_def_id) to get a trait [`DefId`].
/// * [Common tools for writing lints] for an example how to use this function and other options.
/// See [Common tools for writing lints] for an example how to use this function and other options.
///
/// [Common tools for writing lints]: https://github.com/rust-lang/rust-clippy/blob/master/book/src/development/common_tools_writing_lints.md#checking-if-a-type-implements-a-specific-trait
pub fn implements_trait<'tcx>(
@ -424,17 +423,6 @@ pub fn is_isize_or_usize(typ: Ty<'_>) -> bool {
matches!(typ.kind(), ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize))
}
/// Checks if type is struct, enum or union type with the given def path.
///
/// If the type is a diagnostic item, use `is_type_diagnostic_item` instead.
/// If you change the signature, remember to update the internal lint `MatchTypeOnDiagItem`
pub fn match_type(cx: &LateContext<'_>, ty: Ty<'_>, path: &[&str]) -> bool {
match ty.kind() {
ty::Adt(adt, _) => match_def_path(cx, adt.did(), path),
_ => false,
}
}
/// Checks if the drop order for a type matters.
///
/// Some std types implement drop solely to deallocate memory. For these types, and composites
@ -1131,10 +1119,7 @@ impl<'tcx> InteriorMut<'tcx> {
pub fn new(tcx: TyCtxt<'tcx>, ignore_interior_mutability: &[String]) -> Self {
let ignored_def_ids = ignore_interior_mutability
.iter()
.flat_map(|ignored_ty| {
let path: Vec<&str> = ignored_ty.split("::").collect();
def_path_def_ids(tcx, path.as_slice())
})
.flat_map(|ignored_ty| lookup_path_str(tcx, PathNS::Type, ignored_ty))
.collect();
Self {

View file

@ -11,14 +11,14 @@
//! As a heuristic, `expr_type_is_certain` may produce false negatives, but a false positive should
//! be considered a bug.
use crate::def_path_res;
use crate::paths::{PathNS, lookup_path};
use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::DefId;
use rustc_hir::intravisit::{InferKind, Visitor, VisitorExt, walk_qpath, walk_ty};
use rustc_hir::{self as hir, AmbigArg, Expr, ExprKind, GenericArgs, HirId, Node, PathSegment, QPath, TyKind};
use rustc_lint::LateContext;
use rustc_middle::ty::{self, AdtDef, GenericArgKind, Ty};
use rustc_span::{Span, Symbol};
use rustc_span::Span;
mod certainty;
use certainty::{Certainty, Meet, join, meet};
@ -194,7 +194,7 @@ fn path_segment_certainty(
path_segment: &PathSegment<'_>,
resolves_to_type: bool,
) -> Certainty {
let certainty = match update_res(cx, parent_certainty, path_segment).unwrap_or(path_segment.res) {
let certainty = match update_res(cx, parent_certainty, path_segment, resolves_to_type).unwrap_or(path_segment.res) {
// A definition's type is certain if it refers to something without generics (e.g., a crate or module, or
// an unparameterized type), or the generics are instantiated with arguments that are certain.
//
@ -267,17 +267,24 @@ fn path_segment_certainty(
/// For at least some `QPath::TypeRelative`, the path segment's `res` can be `Res::Err`.
/// `update_res` tries to fix the resolution when `parent_certainty` is `Certain(Some(..))`.
fn update_res(cx: &LateContext<'_>, parent_certainty: Certainty, path_segment: &PathSegment<'_>) -> Option<Res> {
fn update_res(
cx: &LateContext<'_>,
parent_certainty: Certainty,
path_segment: &PathSegment<'_>,
resolves_to_type: bool,
) -> Option<Res> {
if path_segment.res == Res::Err
&& let Some(def_id) = parent_certainty.to_def_id()
{
let mut def_path = cx.get_def_path(def_id);
def_path.push(path_segment.ident.name);
let reses = def_path_res(cx.tcx, &def_path.iter().map(Symbol::as_str).collect::<Vec<_>>());
if let [res] = reses.as_slice() { Some(*res) } else { None }
} else {
None
let ns = if resolves_to_type { PathNS::Type } else { PathNS::Value };
if let &[id] = lookup_path(cx.tcx, ns, &def_path).as_slice() {
return Some(Res::Def(cx.tcx.def_kind(id), id));
}
}
None
}
#[allow(clippy::cast_possible_truncation)]

View file

@ -1,4 +0,0 @@
#![allow(clippy::unnecessary_def_path)]
pub static OPTION: [&str; 3] = ["core", "option", "Option"];
pub const RESULT: &[&str] = &["core", "result", "Result"];

View file

@ -1,30 +0,0 @@
#![deny(clippy::invalid_paths)]
#![allow(clippy::missing_clippy_version_attribute, clippy::unnecessary_def_path)]
mod paths {
// Good path
pub const ANY_TRAIT: [&str; 3] = ["std", "any", "Any"];
// Path to method on inherent impl of a primitive type
pub const F32_EPSILON: [&str; 4] = ["core", "f32", "<impl f32>", "EPSILON"];
// Path to method on inherent impl
pub const ARC_PTR_EQ: [&str; 4] = ["alloc", "sync", "Arc", "ptr_eq"];
// Path with empty segment
pub const TRANSMUTE: [&str; 4] = ["core", "intrinsics", "", "transmute"];
//~^ invalid_paths
// Path with bad crate
pub const BAD_CRATE_PATH: [&str; 2] = ["bad", "path"];
//~^ invalid_paths
// Path with bad module
pub const BAD_MOD_PATH: [&str; 2] = ["std", "xxx"];
//~^ invalid_paths
// Path to method on an enum inherent impl
pub const OPTION_IS_SOME: [&str; 4] = ["core", "option", "Option", "is_some"];
}
fn main() {}

View file

@ -1,26 +0,0 @@
error: invalid path
--> tests/ui-internal/invalid_paths.rs:15:5
|
LL | pub const TRANSMUTE: [&str; 4] = ["core", "intrinsics", "", "transmute"];
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: the lint level is defined here
--> tests/ui-internal/invalid_paths.rs:1:9
|
LL | #![deny(clippy::invalid_paths)]
| ^^^^^^^^^^^^^^^^^^^^^
error: invalid path
--> tests/ui-internal/invalid_paths.rs:19:5
|
LL | pub const BAD_CRATE_PATH: [&str; 2] = ["bad", "path"];
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: invalid path
--> tests/ui-internal/invalid_paths.rs:23:5
|
LL | pub const BAD_MOD_PATH: [&str; 2] = ["std", "xxx"];
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to 3 previous errors

View file

@ -1,77 +0,0 @@
//@aux-build:paths.rs
#![deny(clippy::unnecessary_def_path)]
#![feature(rustc_private)]
#![allow(clippy::unnecessary_map_or)]
extern crate clippy_utils;
extern crate paths;
extern crate rustc_hir;
extern crate rustc_lint;
extern crate rustc_middle;
extern crate rustc_span;
#[allow(unused)]
use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item, match_type};
#[allow(unused)]
use clippy_utils::{
is_enum_variant_ctor, is_expr_path_def_path, is_path_diagnostic_item, is_res_lang_ctor, is_trait_method,
match_def_path, match_trait_method, path_res,
};
#[allow(unused)]
use rustc_hir::LangItem;
#[allow(unused)]
use rustc_span::sym;
use rustc_hir::Expr;
use rustc_hir::def_id::DefId;
use rustc_lint::LateContext;
use rustc_middle::ty::Ty;
#[allow(unused, clippy::unnecessary_def_path)]
static OPTION: [&str; 3] = ["core", "option", "Option"];
#[allow(unused, clippy::unnecessary_def_path)]
const RESULT: &[&str] = &["core", "result", "Result"];
fn _f<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, did: DefId, expr: &Expr<'_>) {
let _ = is_type_diagnostic_item(cx, ty, sym::Option);
//~^ unnecessary_def_path
let _ = is_type_diagnostic_item(cx, ty, sym::Result);
//~^ unnecessary_def_path
let _ = is_type_diagnostic_item(cx, ty, sym::Result);
//~^ unnecessary_def_path
#[allow(unused, clippy::unnecessary_def_path)]
let rc_path = &["alloc", "rc", "Rc"];
let _ = is_type_diagnostic_item(cx, ty, sym::Rc);
//~^ unnecessary_def_path
let _ = is_type_diagnostic_item(cx, ty, sym::Option);
//~^ unnecessary_def_path
let _ = is_type_diagnostic_item(cx, ty, sym::Result);
//~^ unnecessary_def_path
let _ = is_type_lang_item(cx, ty, LangItem::OwnedBox);
//~^ unnecessary_def_path
let _ = is_type_diagnostic_item(cx, ty, sym::maybe_uninit_uninit);
//~^ unnecessary_def_path
let _ = cx.tcx.lang_items().get(LangItem::OwnedBox) == Some(did);
//~^ unnecessary_def_path
let _ = cx.tcx.is_diagnostic_item(sym::Option, did);
//~^ unnecessary_def_path
let _ = cx.tcx.lang_items().get(LangItem::OptionSome) == Some(did);
//~^ unnecessary_def_path
let _ = is_trait_method(cx, expr, sym::AsRef);
//~^ unnecessary_def_path
let _ = is_path_diagnostic_item(cx, expr, sym::Option);
//~^ unnecessary_def_path
let _ = path_res(cx, expr).opt_def_id().map_or(false, |id| cx.tcx.lang_items().get(LangItem::IteratorNext) == Some(id));
//~^ unnecessary_def_path
let _ = is_res_lang_ctor(cx, path_res(cx, expr), LangItem::OptionSome);
//~^ unnecessary_def_path
}
fn main() {}

View file

@ -1,77 +1,20 @@
//@aux-build:paths.rs
#![deny(clippy::unnecessary_def_path)]
#![feature(rustc_private)]
#![allow(clippy::unnecessary_map_or)]
extern crate clippy_utils;
extern crate paths;
extern crate rustc_hir;
extern crate rustc_lint;
extern crate rustc_middle;
extern crate rustc_span;
use clippy_utils::paths::{PathLookup, PathNS};
use clippy_utils::{macro_path, sym, type_path, value_path};
#[allow(unused)]
use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item, match_type};
#[allow(unused)]
use clippy_utils::{
is_enum_variant_ctor, is_expr_path_def_path, is_path_diagnostic_item, is_res_lang_ctor, is_trait_method,
match_def_path, match_trait_method, path_res,
};
static OPTION: PathLookup = type_path!(core::option::Option);
//~^ unnecessary_def_path
static SOME: PathLookup = type_path!(core::option::Option::Some);
//~^ unnecessary_def_path
#[allow(unused)]
use rustc_hir::LangItem;
#[allow(unused)]
use rustc_span::sym;
static RESULT: PathLookup = type_path!(core::result::Result);
//~^ unnecessary_def_path
static RESULT_VIA_STD: PathLookup = type_path!(std::result::Result);
//~^ unnecessary_def_path
use rustc_hir::Expr;
use rustc_hir::def_id::DefId;
use rustc_lint::LateContext;
use rustc_middle::ty::Ty;
static VEC_NEW: PathLookup = value_path!(alloc::vec::Vec::new);
//~^ unnecessary_def_path
#[allow(unused, clippy::unnecessary_def_path)]
static OPTION: [&str; 3] = ["core", "option", "Option"];
#[allow(unused, clippy::unnecessary_def_path)]
const RESULT: &[&str] = &["core", "result", "Result"];
fn _f<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, did: DefId, expr: &Expr<'_>) {
let _ = match_type(cx, ty, &OPTION);
//~^ unnecessary_def_path
let _ = match_type(cx, ty, RESULT);
//~^ unnecessary_def_path
let _ = match_type(cx, ty, &["core", "result", "Result"]);
//~^ unnecessary_def_path
#[allow(unused, clippy::unnecessary_def_path)]
let rc_path = &["alloc", "rc", "Rc"];
let _ = clippy_utils::ty::match_type(cx, ty, rc_path);
//~^ unnecessary_def_path
let _ = match_type(cx, ty, &paths::OPTION);
//~^ unnecessary_def_path
let _ = match_type(cx, ty, paths::RESULT);
//~^ unnecessary_def_path
let _ = match_type(cx, ty, &["alloc", "boxed", "Box"]);
//~^ unnecessary_def_path
let _ = match_type(cx, ty, &["core", "mem", "maybe_uninit", "MaybeUninit", "uninit"]);
//~^ unnecessary_def_path
let _ = match_def_path(cx, did, &["alloc", "boxed", "Box"]);
//~^ unnecessary_def_path
let _ = match_def_path(cx, did, &["core", "option", "Option"]);
//~^ unnecessary_def_path
let _ = match_def_path(cx, did, &["core", "option", "Option", "Some"]);
//~^ unnecessary_def_path
let _ = match_trait_method(cx, expr, &["core", "convert", "AsRef"]);
//~^ unnecessary_def_path
let _ = is_expr_path_def_path(cx, expr, &["core", "option", "Option"]);
//~^ unnecessary_def_path
let _ = is_expr_path_def_path(cx, expr, &["core", "iter", "traits", "Iterator", "next"]);
//~^ unnecessary_def_path
let _ = is_expr_path_def_path(cx, expr, &["core", "option", "Option", "Some"]);
//~^ unnecessary_def_path
}
fn main() {}
static VEC_MACRO: PathLookup = macro_path!(std::vec);
//~^ unnecessary_def_path

View file

@ -1,100 +1,58 @@
error: use of a def path to a diagnostic item
--> tests/ui-internal/unnecessary_def_path.rs:37:13
error: a diagnostic name exists for this path: sym::Option
--> tests/ui-internal/unnecessary_def_path.rs:6:29
|
LL | let _ = match_type(cx, ty, &OPTION);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::Option)`
LL | static OPTION: PathLookup = type_path!(core::option::Option);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: the lint level is defined here
--> tests/ui-internal/unnecessary_def_path.rs:2:9
|
LL | #![deny(clippy::unnecessary_def_path)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= help: remove the `PathLookup` and use utilities such as `cx.tcx.is_diagnostic_item` instead
= help: see also https://doc.rust-lang.org/nightly/nightly-rustc/?search=diag&filter-crate=clippy_utils
= note: `-D clippy::unnecessary-def-path` implied by `-D warnings`
= help: to override `-D warnings` add `#[allow(clippy::unnecessary_def_path)]`
error: use of a def path to a diagnostic item
--> tests/ui-internal/unnecessary_def_path.rs:39:13
error: a language item exists for this path: LangItem::OptionSome
--> tests/ui-internal/unnecessary_def_path.rs:8:27
|
LL | let _ = match_type(cx, ty, RESULT);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::Result)`
error: use of a def path to a diagnostic item
--> tests/ui-internal/unnecessary_def_path.rs:41:13
LL | static SOME: PathLookup = type_path!(core::option::Option::Some);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
LL | let _ = match_type(cx, ty, &["core", "result", "Result"]);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::Result)`
= help: remove the `PathLookup` and use utilities such as `cx.tcx.lang_items` instead
= help: see also https://doc.rust-lang.org/nightly/nightly-rustc/?search=lang&filter-crate=clippy_utils
error: use of a def path to a diagnostic item
--> tests/ui-internal/unnecessary_def_path.rs:46:13
error: a diagnostic name exists for this path: sym::Result
--> tests/ui-internal/unnecessary_def_path.rs:11:29
|
LL | let _ = clippy_utils::ty::match_type(cx, ty, rc_path);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::Rc)`
error: use of a def path to a diagnostic item
--> tests/ui-internal/unnecessary_def_path.rs:49:13
LL | static RESULT: PathLookup = type_path!(core::result::Result);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
LL | let _ = match_type(cx, ty, &paths::OPTION);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::Option)`
= help: remove the `PathLookup` and use utilities such as `cx.tcx.is_diagnostic_item` instead
= help: see also https://doc.rust-lang.org/nightly/nightly-rustc/?search=diag&filter-crate=clippy_utils
error: use of a def path to a diagnostic item
--> tests/ui-internal/unnecessary_def_path.rs:51:13
error: a diagnostic name exists for this path: sym::Result
--> tests/ui-internal/unnecessary_def_path.rs:13:37
|
LL | let _ = match_type(cx, ty, paths::RESULT);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::Result)`
error: use of a def path to a `LangItem`
--> tests/ui-internal/unnecessary_def_path.rs:54:13
LL | static RESULT_VIA_STD: PathLookup = type_path!(std::result::Result);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
LL | let _ = match_type(cx, ty, &["alloc", "boxed", "Box"]);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_lang_item(cx, ty, LangItem::OwnedBox)`
= help: remove the `PathLookup` and use utilities such as `cx.tcx.is_diagnostic_item` instead
= help: see also https://doc.rust-lang.org/nightly/nightly-rustc/?search=diag&filter-crate=clippy_utils
error: use of a def path to a diagnostic item
--> tests/ui-internal/unnecessary_def_path.rs:56:13
error: a diagnostic name exists for this path: sym::vec_new
--> tests/ui-internal/unnecessary_def_path.rs:16:30
|
LL | let _ = match_type(cx, ty, &["core", "mem", "maybe_uninit", "MaybeUninit", "uninit"]);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::maybe_uninit_uninit)`
error: use of a def path to a `LangItem`
--> tests/ui-internal/unnecessary_def_path.rs:59:13
LL | static VEC_NEW: PathLookup = value_path!(alloc::vec::Vec::new);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
LL | let _ = match_def_path(cx, did, &["alloc", "boxed", "Box"]);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `cx.tcx.lang_items().get(LangItem::OwnedBox) == Some(did)`
= help: remove the `PathLookup` and use utilities such as `cx.tcx.is_diagnostic_item` instead
= help: see also https://doc.rust-lang.org/nightly/nightly-rustc/?search=diag&filter-crate=clippy_utils
error: use of a def path to a diagnostic item
--> tests/ui-internal/unnecessary_def_path.rs:61:13
error: a diagnostic name exists for this path: sym::vec_macro
--> tests/ui-internal/unnecessary_def_path.rs:19:32
|
LL | let _ = match_def_path(cx, did, &["core", "option", "Option"]);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `cx.tcx.is_diagnostic_item(sym::Option, did)`
error: use of a def path to a `LangItem`
--> tests/ui-internal/unnecessary_def_path.rs:63:13
LL | static VEC_MACRO: PathLookup = macro_path!(std::vec);
| ^^^^^^^^^^^^^^^^^^^^^
|
LL | let _ = match_def_path(cx, did, &["core", "option", "Option", "Some"]);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `cx.tcx.lang_items().get(LangItem::OptionSome) == Some(did)`
|
= help: if this `DefId` came from a constructor expression or pattern then the parent `DefId` should be used instead
= help: remove the `PathLookup` and use utilities such as `cx.tcx.is_diagnostic_item` instead
= help: see also https://doc.rust-lang.org/nightly/nightly-rustc/?search=diag&filter-crate=clippy_utils
error: use of a def path to a diagnostic item
--> tests/ui-internal/unnecessary_def_path.rs:66:13
|
LL | let _ = match_trait_method(cx, expr, &["core", "convert", "AsRef"]);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_trait_method(cx, expr, sym::AsRef)`
error: use of a def path to a diagnostic item
--> tests/ui-internal/unnecessary_def_path.rs:69:13
|
LL | let _ = is_expr_path_def_path(cx, expr, &["core", "option", "Option"]);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_path_diagnostic_item(cx, expr, sym::Option)`
error: use of a def path to a `LangItem`
--> tests/ui-internal/unnecessary_def_path.rs:71:13
|
LL | let _ = is_expr_path_def_path(cx, expr, &["core", "iter", "traits", "Iterator", "next"]);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `path_res(cx, expr).opt_def_id().map_or(false, |id| cx.tcx.lang_items().get(LangItem::IteratorNext) == Some(id))`
error: use of a def path to a `LangItem`
--> tests/ui-internal/unnecessary_def_path.rs:73:13
|
LL | let _ = is_expr_path_def_path(cx, expr, &["core", "option", "Option", "Some"]);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_res_lang_ctor(cx, path_res(cx, expr), LangItem::OptionSome)`
error: aborting due to 15 previous errors
error: aborting due to 6 previous errors

View file

@ -1,19 +0,0 @@
#![feature(rustc_private)]
#![allow(unused)]
#![deny(clippy::unnecessary_def_path)]
extern crate rustc_hir;
use rustc_hir::LangItem;
fn main() {
const DEREF_TRAIT: [&str; 4] = ["core", "ops", "deref", "Deref"];
//~^ unnecessary_def_path
const DEREF_MUT_TRAIT: [&str; 4] = ["core", "ops", "deref", "DerefMut"];
//~^ unnecessary_def_path
const DEREF_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "Deref", "deref"];
//~^ unnecessary_def_path
// Don't lint, not a diagnostic or language item
const OPS_MOD: [&str; 2] = ["core", "ops"];
}

View file

@ -1,31 +0,0 @@
error: hardcoded path to a diagnostic item
--> tests/ui-internal/unnecessary_def_path_hardcoded_path.rs:10:36
|
LL | const DEREF_TRAIT: [&str; 4] = ["core", "ops", "deref", "Deref"];
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: convert all references to use `sym::Deref`
note: the lint level is defined here
--> tests/ui-internal/unnecessary_def_path_hardcoded_path.rs:3:9
|
LL | #![deny(clippy::unnecessary_def_path)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: hardcoded path to a language item
--> tests/ui-internal/unnecessary_def_path_hardcoded_path.rs:12:40
|
LL | const DEREF_MUT_TRAIT: [&str; 4] = ["core", "ops", "deref", "DerefMut"];
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: convert all references to use `LangItem::DerefMut`
error: hardcoded path to a diagnostic item
--> tests/ui-internal/unnecessary_def_path_hardcoded_path.rs:14:43
|
LL | const DEREF_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "Deref", "deref"];
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: convert all references to use `sym::deref_method`
error: aborting due to 3 previous errors

View file

@ -6,7 +6,7 @@ disallowed-types = [
"std::thread::Thread",
"std::time::Instant",
"std::io::Read",
"std::primitive::usize",
"usize",
"bool",
# can give path and reason with an inline table
{ path = "std::net::Ipv4Addr", reason = "no IPv4 allowed" },

View file

@ -37,7 +37,7 @@ error: use of a disallowed type `std::io::Read`
LL | fn trait_obj(_: &dyn std::io::Read) {}
| ^^^^^^^^^^^^^
error: use of a disallowed type `std::primitive::usize`
error: use of a disallowed type `usize`
--> tests/ui-toml/toml_disallowed_types/conf_disallowed_types.rs:26:33
|
LL | fn full_and_single_path_prim(_: usize, _: bool) {}
@ -49,13 +49,13 @@ error: use of a disallowed type `bool`
LL | fn full_and_single_path_prim(_: usize, _: bool) {}
| ^^^^
error: use of a disallowed type `std::primitive::usize`
error: use of a disallowed type `usize`
--> tests/ui-toml/toml_disallowed_types/conf_disallowed_types.rs:30:28
|
LL | fn const_generics<const C: usize>() {}
| ^^^^^
error: use of a disallowed type `std::primitive::usize`
error: use of a disallowed type `usize`
--> tests/ui-toml/toml_disallowed_types/conf_disallowed_types.rs:33:24
|
LL | struct GenArg<const U: usize>([u8; U]);
@ -123,7 +123,7 @@ error: use of a disallowed type `proc_macro2::Ident`
LL | let _ = syn::Ident::new("", todo!());
| ^^^^^^^^^^
error: use of a disallowed type `std::primitive::usize`
error: use of a disallowed type `usize`
--> tests/ui-toml/toml_disallowed_types/conf_disallowed_types.rs:61:12
|
LL | let _: usize = 64_usize;

View file

@ -1,12 +1,15 @@
[[disallowed-types]]
path = "std::result::Result::Err"
[[disallowed-macros]]
path = "bool"
[[disallowed-methods]]
path = "std::process::current_exe"
[[disallowed-methods]]
path = ""
[[disallowed-types]]
path = "std::result::Result::Err"
# negative test
[[disallowed-methods]]

View file

@ -1,5 +1,6 @@
//@error-in-other-file: expected a macro, found a primitive type
//@error-in-other-file: `std::process::current_exe` does not refer to an existing function
//@error-in-other-file: expected a type, found a tuple variant
//@error-in-other-file: `std::process::current_exe` does not refer to a reachable function
//@error-in-other-file: `` does not refer to a reachable function
//@error-in-other-file: expected a type, found a variant
fn main() {}

View file

@ -1,23 +1,38 @@
warning: expected a macro, found a primitive type
--> $DIR/tests/ui-toml/toml_invalid_path/clippy.toml:4:1
--> $DIR/tests/ui-toml/toml_invalid_path/clippy.toml:1:1
|
LL | / [[disallowed-macros]]
LL | | path = "bool"
| |_____________^
|
= help: add `allow-invalid = true` to the entry to suppress this warning
warning: `std::process::current_exe` does not refer to an existing function
--> $DIR/tests/ui-toml/toml_invalid_path/clippy.toml:7:1
warning: `std::process::current_exe` does not refer to a reachable function
--> $DIR/tests/ui-toml/toml_invalid_path/clippy.toml:4:1
|
LL | / [[disallowed-methods]]
LL | | path = "std::process::current_exe"
| |__________________________________^
|
= help: add `allow-invalid = true` to the entry to suppress this warning
warning: expected a type, found a tuple variant
--> $DIR/tests/ui-toml/toml_invalid_path/clippy.toml:1:1
warning: `` does not refer to a reachable function
--> $DIR/tests/ui-toml/toml_invalid_path/clippy.toml:7:1
|
LL | / [[disallowed-methods]]
LL | | path = ""
| |_________^
|
= help: add `allow-invalid = true` to the entry to suppress this warning
warning: expected a type, found a variant
--> $DIR/tests/ui-toml/toml_invalid_path/clippy.toml:10:1
|
LL | / [[disallowed-types]]
LL | | path = "std::result::Result::Err"
| |_________________________________^
|
= help: add `allow-invalid = true` to the entry to suppress this warning
warning: 3 warnings emitted
warning: 4 warnings emitted

View file

@ -1,5 +1,5 @@
//@error-in-other-file: `regex::Regex::new_` does not refer to an existing function
//@error-in-other-file: `regex::Regex_::new` does not refer to an existing function
//@error-in-other-file: `regex::Regex::new_` does not refer to a reachable function
//@error-in-other-file: `regex::Regex_::new` does not refer to a reachable function
extern crate regex;

View file

@ -1,16 +1,20 @@
warning: `regex::Regex::new_` does not refer to an existing function
warning: `regex::Regex::new_` does not refer to a reachable function
--> $DIR/tests/ui-toml/toml_unloaded_crate/clippy.toml:3:1
|
LL | / [[disallowed-methods]]
LL | | path = "regex::Regex::new_"
| |___________________________^
|
= help: add `allow-invalid = true` to the entry to suppress this warning
warning: `regex::Regex_::new` does not refer to an existing function
warning: `regex::Regex_::new` does not refer to a reachable function
--> $DIR/tests/ui-toml/toml_unloaded_crate/clippy.toml:6:1
|
LL | / [[disallowed-methods]]
LL | | path = "regex::Regex_::new"
| |___________________________^
|
= help: add `allow-invalid = true` to the entry to suppress this warning
warning: 2 warnings emitted

View file

@ -1,8 +1,6 @@
if let StmtKind::Let(local) = stmt.kind
&& let Some(init) = local.init
&& let ExprKind::Cast(expr, cast_ty) = init.kind
&& let TyKind::Path(ref qpath) = cast_ty.kind
&& match_qpath(qpath, &["char"])
&& let ExprKind::Lit(ref lit) = expr.kind
&& let LitKind::Int(69, LitIntType::Unsuffixed) = lit.node
&& let PatKind::Binding(BindingMode::NONE, _, name, None) = local.pat.kind

View file

@ -14,8 +14,6 @@ if let ExprKind::Block(block, None) = expr.kind
&& name1.as_str() == "_t"
&& let StmtKind::Semi(e) = block.stmts[2].kind
&& let ExprKind::Unary(UnOp::Neg, inner) = e.kind
&& let ExprKind::Path(ref qpath) = inner.kind
&& match_qpath(qpath, &["x"])
&& block.expr.is_none()
{
// report your lint here
@ -25,18 +23,14 @@ if let ExprKind::Block(block, None) = expr.kind
&& let StmtKind::Let(local) = block.stmts[0].kind
&& let Some(init) = local.init
&& let ExprKind::Call(func, args) = init.kind
&& let ExprKind::Path(ref qpath) = func.kind
&& match_qpath(qpath, &["String", "new"])
&& is_path_diagnostic_item(cx, func, sym::string_new)
&& args.is_empty()
&& let PatKind::Binding(BindingMode::NONE, _, name, None) = local.pat.kind
&& name.as_str() == "expr"
&& let Some(trailing_expr) = block.expr
&& let ExprKind::Call(func1, args1) = trailing_expr.kind
&& let ExprKind::Path(ref qpath1) = func1.kind
&& match_qpath(qpath1, &["drop"])
&& is_path_diagnostic_item(cx, func1, sym::mem_drop)
&& args1.len() == 1
&& let ExprKind::Path(ref qpath2) = args1[0].kind
&& match_qpath(qpath2, &["expr"])
{
// report your lint here
}

View file

@ -1,8 +1,7 @@
if let StmtKind::Let(local) = stmt.kind
&& let Some(init) = local.init
&& let ExprKind::Call(func, args) = init.kind
&& let ExprKind::Path(ref qpath) = func.kind
&& match_qpath(qpath, &["{{root}}", "std", "cmp", "min"])
&& is_path_diagnostic_item(cx, func, sym::cmp_min)
&& args.len() == 2
&& let ExprKind::Lit(ref lit) = args[0].kind
&& let LitKind::Int(3, LitIntType::Unsuffixed) = lit.node

View file

@ -31,10 +31,8 @@ if let StmtKind::Let(local) = stmt.kind
if let ExprKind::If(cond, then, Some(else_expr)) = expr.kind
&& let ExprKind::Let(let_expr) = cond.kind
&& let PatKind::Expr(lit_expr) = let_expr.pat.kind
&& let PatExprKind::Lit{ref lit, negated } = lit_expr.kind
&& let PatExprKind::Lit { ref lit, negated } = lit_expr.kind
&& let LitKind::Bool(true) = lit.node
&& let ExprKind::Path(ref qpath) = let_expr.init.kind
&& match_qpath(qpath, &["a"])
&& let ExprKind::Block(block, None) = then.kind
&& block.stmts.is_empty()
&& block.expr.is_none()

View file

@ -1,11 +1,8 @@
if let StmtKind::Let(local) = stmt.kind
&& let Some(init) = local.init
&& let ExprKind::Call(func, args) = init.kind
&& let ExprKind::Path(ref qpath) = func.kind
&& match_qpath(qpath, &["std", "mem", "transmute"])
&& is_path_diagnostic_item(cx, func, sym::transmute)
&& args.len() == 1
&& let ExprKind::Path(ref qpath1) = args[0].kind
&& match_qpath(qpath1, &["ZPTR"])
&& let PatKind::Wild = local.pat.kind
{
// report your lint here

View file

@ -14,8 +14,6 @@ if let Some(higher::ForLoop { pat: pat, arg: arg, body: body, .. }) = higher::Fo
&& block.stmts.len() == 1
&& let StmtKind::Let(local) = block.stmts[0].kind
&& let Some(init) = local.init
&& let ExprKind::Path(ref qpath1) = init.kind
&& match_qpath(qpath1, &["y"])
&& let PatKind::Binding(BindingMode::NONE, _, name1, None) = local.pat.kind
&& name1.as_str() == "z"
&& block.expr.is_none()
@ -64,8 +62,6 @@ if let Some(higher::ForLoop { pat: pat, arg: arg, body: body, .. }) = higher::Fo
// report your lint here
}
if let Some(higher::While { condition: condition, body: body }) = higher::While::hir(expr)
&& let ExprKind::Path(ref qpath) = condition.kind
&& match_qpath(qpath, &["a"])
&& let ExprKind::Block(block, None) = body.kind
&& block.stmts.len() == 1
&& let StmtKind::Semi(e) = block.stmts[0].kind
@ -77,10 +73,8 @@ if let Some(higher::While { condition: condition, body: body }) = higher::While:
}
if let Some(higher::WhileLet { let_pat: let_pat, let_expr: let_expr, if_then: if_then }) = higher::WhileLet::hir(expr)
&& let PatKind::Expr(lit_expr) = let_pat.kind
&& let PatExprKind::Lit{ref lit, negated } = lit_expr.kind
&& let PatExprKind::Lit { ref lit, negated } = lit_expr.kind
&& let LitKind::Bool(true) = lit.node
&& let ExprKind::Path(ref qpath) = let_expr.kind
&& match_qpath(qpath, &["a"])
&& let ExprKind::Block(block, None) = if_then.kind
&& block.stmts.len() == 1
&& let StmtKind::Semi(e) = block.stmts[0].kind

View file

@ -7,12 +7,10 @@ if let StmtKind::Let(local) = stmt.kind
&& block.stmts.len() == 1
&& let StmtKind::Semi(e) = block.stmts[0].kind
&& let ExprKind::Call(func, args) = e.kind
&& let ExprKind::Path(ref qpath) = func.kind
&& match_qpath(qpath, &["$crate", "io", "_print"])
&& paths::STD_IO_STDIO__PRINT.matches_path(cx, func) // Add the path to `clippy_utils::paths` if needed
&& args.len() == 1
&& let ExprKind::Call(func1, args1) = args[0].kind
&& let ExprKind::Path(ref qpath1) = func1.kind
&& match_qpath(qpath1, &["format_arguments", "new_v1"])
&& paths::CORE_FMT_ARGUMENTS_NEW_V1.matches_path(cx, func1) // Add the path to `clippy_utils::paths` if needed
&& args1.len() == 2
&& let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner) = args1[0].kind
&& let ExprKind::Array(elements) = inner.kind
@ -27,12 +25,9 @@ if let StmtKind::Let(local) = stmt.kind
&& let ExprKind::Array(elements1) = inner1.kind
&& elements1.len() == 1
&& let ExprKind::Call(func2, args2) = elements1[0].kind
&& let ExprKind::Path(ref qpath2) = func2.kind
&& match_qpath(qpath2, &["format_argument", "new_display"])
&& paths::CORE_FMT_RT_ARGUMENT_NEW_DISPLAY.matches_path(cx, func2) // Add the path to `clippy_utils::paths` if needed
&& args2.len() == 1
&& let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner2) = args2[0].kind
&& let ExprKind::Path(ref qpath3) = inner2.kind
&& match_qpath(qpath3, &["x"])
&& block.expr.is_none()
&& let PatKind::Binding(BindingMode::NONE, _, name, None) = local.pat.kind
&& name.as_str() == "print_text"

View file

@ -17,12 +17,10 @@ if let Some(higher::ForLoop { pat: pat, arg: arg, body: body, .. }) = higher::Fo
&& block1.stmts.len() == 1
&& let StmtKind::Semi(e1) = block1.stmts[0].kind
&& let ExprKind::Call(func, args) = e1.kind
&& let ExprKind::Path(ref qpath1) = func.kind
&& match_qpath(qpath1, &["$crate", "io", "_print"])
&& paths::STD_IO_STDIO__PRINT.matches_path(cx, func) // Add the path to `clippy_utils::paths` if needed
&& args.len() == 1
&& let ExprKind::Call(func1, args1) = args[0].kind
&& let ExprKind::Path(ref qpath2) = func1.kind
&& match_qpath(qpath2, &["format_arguments", "new_v1"])
&& paths::CORE_FMT_ARGUMENTS_NEW_V1.matches_path(cx, func1) // Add the path to `clippy_utils::paths` if needed
&& args1.len() == 2
&& let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner) = args1[0].kind
&& let ExprKind::Array(elements) = inner.kind
@ -37,12 +35,9 @@ if let Some(higher::ForLoop { pat: pat, arg: arg, body: body, .. }) = higher::Fo
&& let ExprKind::Array(elements1) = inner1.kind
&& elements1.len() == 1
&& let ExprKind::Call(func2, args2) = elements1[0].kind
&& let ExprKind::Path(ref qpath3) = func2.kind
&& match_qpath(qpath3, &["format_argument", "new_display"])
&& paths::CORE_FMT_RT_ARGUMENT_NEW_DISPLAY.matches_path(cx, func2) // Add the path to `clippy_utils::paths` if needed
&& args2.len() == 1
&& let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner2) = args2[0].kind
&& let ExprKind::Path(ref qpath4) = inner2.kind
&& match_qpath(qpath4, &["i"])
&& block1.expr.is_none()
&& block.expr.is_none()
{

View file

@ -5,13 +5,13 @@ if let StmtKind::Let(local) = stmt.kind
&& let LitKind::Int(42, LitIntType::Unsuffixed) = lit.node
&& arms.len() == 3
&& let PatKind::Expr(lit_expr) = arms[0].pat.kind
&& let PatExprKind::Lit{ref lit1, negated } = lit_expr.kind
&& let PatExprKind::Lit { ref lit1, negated } = lit_expr.kind
&& let LitKind::Int(16, LitIntType::Unsuffixed) = lit1.node
&& arms[0].guard.is_none()
&& let ExprKind::Lit(ref lit2) = arms[0].body.kind
&& let LitKind::Int(5, LitIntType::Unsuffixed) = lit2.node
&& let PatKind::Expr(lit_expr1) = arms[1].pat.kind
&& let PatExprKind::Lit{ref lit3, negated1 } = lit_expr1.kind
&& let PatExprKind::Lit { ref lit3, negated1 } = lit_expr1.kind
&& let LitKind::Int(17, LitIntType::Unsuffixed) = lit3.node
&& arms[1].guard.is_none()
&& let ExprKind::Block(block, None) = arms[1].body.kind
@ -23,8 +23,6 @@ if let StmtKind::Let(local) = stmt.kind
&& let PatKind::Binding(BindingMode::NONE, _, name, None) = local1.pat.kind
&& name.as_str() == "x"
&& let Some(trailing_expr) = block.expr
&& let ExprKind::Path(ref qpath) = trailing_expr.kind
&& match_qpath(qpath, &["x"])
&& let PatKind::Wild = arms[2].pat.kind
&& arms[2].guard.is_none()
&& let ExprKind::Lit(ref lit5) = arms[2].body.kind

View file

@ -1,5 +1,4 @@
if let ExprKind::Struct(qpath, fields, None) = expr.kind
&& match_qpath(qpath, &["Test"])
&& fields.len() == 1
&& fields[0].ident.as_str() == "field"
&& let ExprKind::If(cond, then, Some(else_expr)) = fields[0].expr.kind
@ -20,11 +19,10 @@ if let ExprKind::Struct(qpath, fields, None) = expr.kind
// report your lint here
}
if let PatKind::Struct(ref qpath, fields, false) = arm.pat.kind
&& match_qpath(qpath, &["Test"])
&& fields.len() == 1
&& fields[0].ident.as_str() == "field"
&& let PatKind::Expr(lit_expr) = fields[0].pat.kind
&& let PatExprKind::Lit{ref lit, negated } = lit_expr.kind
&& let PatExprKind::Lit { ref lit, negated } = lit_expr.kind
&& let LitKind::Int(1, LitIntType::Unsuffixed) = lit.node
&& arm.guard.is_none()
&& let ExprKind::Block(block, None) = arm.body.kind
@ -34,10 +32,9 @@ if let PatKind::Struct(ref qpath, fields, false) = arm.pat.kind
// report your lint here
}
if let PatKind::TupleStruct(ref qpath, fields, None) = arm.pat.kind
&& match_qpath(qpath, &["TestTuple"])
&& fields.len() == 1
&& let PatKind::Expr(lit_expr) = fields[0].kind
&& let PatExprKind::Lit{ref lit, negated } = lit_expr.kind
&& let PatExprKind::Lit { ref lit, negated } = lit_expr.kind
&& let LitKind::Int(1, LitIntType::Unsuffixed) = lit.node
&& arm.guard.is_none()
&& let ExprKind::Block(block, None) = arm.body.kind
@ -48,8 +45,6 @@ if let PatKind::TupleStruct(ref qpath, fields, None) = arm.pat.kind
}
if let ExprKind::MethodCall(method_name, receiver, args, _) = expr.kind
&& method_name.ident.as_str() == "test"
&& let ExprKind::Path(ref qpath) = receiver.kind
&& match_qpath(qpath, &["test_method_call"])
&& args.is_empty()
{
// report your lint here

View file

@ -1,7 +1,5 @@
#![allow(clippy::legacy_numeric_constants, unused_imports)]
use std::{i32, i128, u32, u128};
fn main() {
let _ = 1u32.saturating_add(1);
//~^ manual_saturating_arithmetic

View file

@ -1,7 +1,5 @@
#![allow(clippy::legacy_numeric_constants, unused_imports)]
use std::{i32, i128, u32, u128};
fn main() {
let _ = 1u32.checked_add(1).unwrap_or(u32::max_value());
//~^ manual_saturating_arithmetic

View file

@ -1,5 +1,5 @@
error: manual saturating arithmetic
--> tests/ui/manual_saturating_arithmetic.rs:6:13
--> tests/ui/manual_saturating_arithmetic.rs:4:13
|
LL | let _ = 1u32.checked_add(1).unwrap_or(u32::max_value());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_add`: `1u32.saturating_add(1)`
@ -8,19 +8,19 @@ LL | let _ = 1u32.checked_add(1).unwrap_or(u32::max_value());
= help: to override `-D warnings` add `#[allow(clippy::manual_saturating_arithmetic)]`
error: manual saturating arithmetic
--> tests/ui/manual_saturating_arithmetic.rs:8:13
--> tests/ui/manual_saturating_arithmetic.rs:6:13
|
LL | let _ = 1u32.checked_add(1).unwrap_or(u32::MAX);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_add`: `1u32.saturating_add(1)`
error: manual saturating arithmetic
--> tests/ui/manual_saturating_arithmetic.rs:10:13
--> tests/ui/manual_saturating_arithmetic.rs:8:13
|
LL | let _ = 1u8.checked_add(1).unwrap_or(255);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_add`: `1u8.saturating_add(1)`
error: manual saturating arithmetic
--> tests/ui/manual_saturating_arithmetic.rs:12:13
--> tests/ui/manual_saturating_arithmetic.rs:10:13
|
LL | let _ = 1u128
| _____________^
@ -30,49 +30,49 @@ LL | | .unwrap_or(340_282_366_920_938_463_463_374_607_431_768_211_455);
| |_______________________________________________________________________^ help: consider using `saturating_add`: `1u128.saturating_add(1)`
error: manual saturating arithmetic
--> tests/ui/manual_saturating_arithmetic.rs:18:13
--> tests/ui/manual_saturating_arithmetic.rs:16:13
|
LL | let _ = 1u32.checked_mul(1).unwrap_or(u32::MAX);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_mul`: `1u32.saturating_mul(1)`
error: manual saturating arithmetic
--> tests/ui/manual_saturating_arithmetic.rs:21:13
--> tests/ui/manual_saturating_arithmetic.rs:19:13
|
LL | let _ = 1u32.checked_sub(1).unwrap_or(u32::min_value());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_sub`: `1u32.saturating_sub(1)`
error: manual saturating arithmetic
--> tests/ui/manual_saturating_arithmetic.rs:23:13
--> tests/ui/manual_saturating_arithmetic.rs:21:13
|
LL | let _ = 1u32.checked_sub(1).unwrap_or(u32::MIN);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_sub`: `1u32.saturating_sub(1)`
error: manual saturating arithmetic
--> tests/ui/manual_saturating_arithmetic.rs:25:13
--> tests/ui/manual_saturating_arithmetic.rs:23:13
|
LL | let _ = 1u8.checked_sub(1).unwrap_or(0);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_sub`: `1u8.saturating_sub(1)`
error: manual saturating arithmetic
--> tests/ui/manual_saturating_arithmetic.rs:30:13
--> tests/ui/manual_saturating_arithmetic.rs:28:13
|
LL | let _ = 1i32.checked_add(1).unwrap_or(i32::max_value());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_add`: `1i32.saturating_add(1)`
error: manual saturating arithmetic
--> tests/ui/manual_saturating_arithmetic.rs:32:13
--> tests/ui/manual_saturating_arithmetic.rs:30:13
|
LL | let _ = 1i32.checked_add(1).unwrap_or(i32::MAX);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_add`: `1i32.saturating_add(1)`
error: manual saturating arithmetic
--> tests/ui/manual_saturating_arithmetic.rs:34:13
--> tests/ui/manual_saturating_arithmetic.rs:32:13
|
LL | let _ = 1i8.checked_add(1).unwrap_or(127);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_add`: `1i8.saturating_add(1)`
error: manual saturating arithmetic
--> tests/ui/manual_saturating_arithmetic.rs:36:13
--> tests/ui/manual_saturating_arithmetic.rs:34:13
|
LL | let _ = 1i128
| _____________^
@ -82,25 +82,25 @@ LL | | .unwrap_or(170_141_183_460_469_231_731_687_303_715_884_105_727);
| |_______________________________________________________________________^ help: consider using `saturating_add`: `1i128.saturating_add(1)`
error: manual saturating arithmetic
--> tests/ui/manual_saturating_arithmetic.rs:40:13
--> tests/ui/manual_saturating_arithmetic.rs:38:13
|
LL | let _ = 1i32.checked_add(-1).unwrap_or(i32::min_value());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_add`: `1i32.saturating_add(-1)`
error: manual saturating arithmetic
--> tests/ui/manual_saturating_arithmetic.rs:42:13
--> tests/ui/manual_saturating_arithmetic.rs:40:13
|
LL | let _ = 1i32.checked_add(-1).unwrap_or(i32::MIN);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_add`: `1i32.saturating_add(-1)`
error: manual saturating arithmetic
--> tests/ui/manual_saturating_arithmetic.rs:44:13
--> tests/ui/manual_saturating_arithmetic.rs:42:13
|
LL | let _ = 1i8.checked_add(-1).unwrap_or(-128);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_add`: `1i8.saturating_add(-1)`
error: manual saturating arithmetic
--> tests/ui/manual_saturating_arithmetic.rs:46:13
--> tests/ui/manual_saturating_arithmetic.rs:44:13
|
LL | let _ = 1i128
| _____________^
@ -110,25 +110,25 @@ LL | | .unwrap_or(-170_141_183_460_469_231_731_687_303_715_884_105_728);
| |________________________________________________________________________^ help: consider using `saturating_add`: `1i128.saturating_add(-1)`
error: manual saturating arithmetic
--> tests/ui/manual_saturating_arithmetic.rs:54:13
--> tests/ui/manual_saturating_arithmetic.rs:52:13
|
LL | let _ = 1i32.checked_sub(1).unwrap_or(i32::min_value());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_sub`: `1i32.saturating_sub(1)`
error: manual saturating arithmetic
--> tests/ui/manual_saturating_arithmetic.rs:56:13
--> tests/ui/manual_saturating_arithmetic.rs:54:13
|
LL | let _ = 1i32.checked_sub(1).unwrap_or(i32::MIN);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_sub`: `1i32.saturating_sub(1)`
error: manual saturating arithmetic
--> tests/ui/manual_saturating_arithmetic.rs:58:13
--> tests/ui/manual_saturating_arithmetic.rs:56:13
|
LL | let _ = 1i8.checked_sub(1).unwrap_or(-128);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_sub`: `1i8.saturating_sub(1)`
error: manual saturating arithmetic
--> tests/ui/manual_saturating_arithmetic.rs:60:13
--> tests/ui/manual_saturating_arithmetic.rs:58:13
|
LL | let _ = 1i128
| _____________^
@ -138,25 +138,25 @@ LL | | .unwrap_or(-170_141_183_460_469_231_731_687_303_715_884_105_728);
| |________________________________________________________________________^ help: consider using `saturating_sub`: `1i128.saturating_sub(1)`
error: manual saturating arithmetic
--> tests/ui/manual_saturating_arithmetic.rs:64:13
--> tests/ui/manual_saturating_arithmetic.rs:62:13
|
LL | let _ = 1i32.checked_sub(-1).unwrap_or(i32::max_value());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_sub`: `1i32.saturating_sub(-1)`
error: manual saturating arithmetic
--> tests/ui/manual_saturating_arithmetic.rs:66:13
--> tests/ui/manual_saturating_arithmetic.rs:64:13
|
LL | let _ = 1i32.checked_sub(-1).unwrap_or(i32::MAX);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_sub`: `1i32.saturating_sub(-1)`
error: manual saturating arithmetic
--> tests/ui/manual_saturating_arithmetic.rs:68:13
--> tests/ui/manual_saturating_arithmetic.rs:66:13
|
LL | let _ = 1i8.checked_sub(-1).unwrap_or(127);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_sub`: `1i8.saturating_sub(-1)`
error: manual saturating arithmetic
--> tests/ui/manual_saturating_arithmetic.rs:70:13
--> tests/ui/manual_saturating_arithmetic.rs:68:13
|
LL | let _ = 1i128
| _____________^