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:
commit
c1586e141f
70 changed files with 883 additions and 1477 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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`
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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 }
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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(¯o_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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
17
clippy_lints_internal/src/internal_paths.rs
Normal file
17
clippy_lints_internal/src/internal_paths.rs
Normal 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);
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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()));
|
||||
|
|
|
|||
|
|
@ -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<'_>) {
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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> {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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)]
|
||||
|
|
|
|||
|
|
@ -1,4 +0,0 @@
|
|||
#![allow(clippy::unnecessary_def_path)]
|
||||
|
||||
pub static OPTION: [&str; 3] = ["core", "option", "Option"];
|
||||
pub const RESULT: &[&str] = &["core", "result", "Result"];
|
||||
|
|
@ -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() {}
|
||||
|
|
@ -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
|
||||
|
||||
|
|
@ -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() {}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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"];
|
||||
}
|
||||
|
|
@ -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
|
||||
|
||||
|
|
@ -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" },
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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]]
|
||||
|
|
|
|||
|
|
@ -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() {}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
| _____________^
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue