Replace str path utils with new PathLookup type

This commit is contained in:
Alex Macleod
2025-04-28 17:12:16 +00:00
parent ea13461967
commit b768fbe4bc
70 changed files with 799 additions and 1400 deletions
+1 -1
View File
@@ -77,7 +77,7 @@ debugging to find the actual problem behind the issue.
[`T-middle`] issues can be more involved and require verifying types. The [`ty`] module contains a
lot of methods that are useful, though one of the most useful would be `expr_ty` (gives the type of
an AST expression). `match_def_path()` in Clippy's `utils` module can also be useful.
an AST expression).
[`good-first-issue`]: https://github.com/rust-lang/rust-clippy/labels/good-first-issue
[`S-inactive-closed`]: https://github.com/rust-lang/rust-clippy/pulls?q=is%3Aclosed+label%3AS-inactive-closed
@@ -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`
}
}
+9 -7
View File
@@ -73,22 +73,24 @@ impl LateLintPass<'_> for CheckDropTraitLint {
## Using Type Path
If neither diagnostic item nor a language item is available, we can use
[`clippy_utils::paths`][paths] with the `match_trait_method` to determine trait
implementation.
[`clippy_utils::paths`][paths] to determine get a trait's `DefId`.
> **Note**: This approach should be avoided if possible, the best thing to do would be to make a PR to [`rust-lang/rust`][rust] adding a diagnostic item.
Below, we check if the given `expr` implements the `Iterator`'s trait method `cloned` :
Below, we check if the given `expr` implements [`core::iter::Step`](https://doc.rust-lang.org/std/iter/trait.Step.html):
```rust
use clippy_utils::{match_trait_method, paths};
use clippy_utils::{implements_trait, paths};
use rustc_hir::Expr;
use rustc_lint::{LateContext, LateLintPass};
impl LateLintPass<'_> for CheckTokioAsyncReadExtTrait {
impl LateLintPass<'_> for CheckIterStep {
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
if match_trait_method(cx, expr, &paths::CORE_ITER_CLONED) {
println!("`expr` implements `CORE_ITER_CLONED` trait!");
let ty = cx.typeck_results().expr_ty(expr);
if let Some(trait_def_id) = paths::ITER_STEP.first(cx)
&& implements_trait(cx, ty, trait_def_id, &[])
{
println!("`expr` implements the `core::iter::Step` trait!");
}
}
}
-3
View File
@@ -7,14 +7,11 @@ lint-commented-code = true
[[disallowed-methods]]
path = "rustc_lint::context::LintContext::lint"
reason = "this function does not add a link to our documentation, please use the `clippy_utils::diagnostics::span_lint*` functions instead"
allow-invalid = true
[[disallowed-methods]]
path = "rustc_lint::context::LintContext::span_lint"
reason = "this function does not add a link to our documentation, please use the `clippy_utils::diagnostics::span_lint*` functions instead"
allow-invalid = true
[[disallowed-methods]]
path = "rustc_middle::ty::context::TyCtxt::node_span_lint"
reason = "this function does not add a link to our documentation, please use the `clippy_utils::diagnostics::span_lint_hir*` functions instead"
allow-invalid = true
+36 -49
View File
@@ -1,7 +1,8 @@
use clippy_utils::PathNS;
use rustc_data_structures::fx::FxHashMap;
use rustc_errors::{Applicability, Diag};
use rustc_hir::PrimTy;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::def::DefKind;
use rustc_hir::def_id::DefIdMap;
use rustc_middle::ty::TyCtxt;
use rustc_span::{Span, Symbol};
@@ -133,6 +134,7 @@ fn allow_invalid(&self) -> bool {
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 = clippy_utils::lookup_path(tcx, ns, &sym_path);
resolutions.retain(|&def_id| def_kind_predicate(tcx.def_kind(def_id)));
let (prim_ty, found_prim_ty) = if let &[name] = sym_path.as_slice()
&& let Some(prim) = PrimTy::from_name(name)
{
(allow_prim_tys.then_some(prim), true)
} else {
(None, false)
};
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 || !clippy_utils::find_crates(tcx, sym_path[0]).is_empty())
{
let span = disallowed_path.span();
if let Some(def_id) = found_def_id {
tcx.sess.dcx().span_warn(
span,
format!(
"expected a {predicate_description}, found {} {}",
tcx.def_descr_article(def_id),
tcx.def_descr(def_id)
),
);
// Relookup the path in an arbitrary namespace to get a good `expected, found` message
let found_def_ids = clippy_utils::lookup_path(tcx, PathNS::Arbitrary, &sym_path);
let message = if let Some(&def_id) = found_def_ids.first() {
let (article, description) = tcx.article_and_description(def_id);
format!("expected a {predicate_description}, found {article} {description}")
} else if found_prim_ty {
tcx.sess.dcx().span_warn(
span,
format!("expected a {predicate_description}, found a primitive type",),
);
} else if !disallowed_path.allow_invalid {
tcx.sess.dcx().span_warn(
span,
format!("`{path}` does not refer to an existing {predicate_description}"),
);
}
format!("expected a {predicate_description}, found a primitive type")
} else {
format!("`{path}` does not refer to a reachable {predicate_description}")
};
tcx.sess
.dcx()
.struct_span_warn(disallowed_path.span(), message)
.with_help("add `allow-invalid = true` to the entry to suppress this warning")
.emit();
}
for res in resolutions {
match res {
Res::Def(_, def_id) => {
def_ids.insert(def_id, (path, disallowed_path));
},
Res::PrimTy(ty) => {
prim_tys.insert(ty, (path, disallowed_path));
},
_ => unreachable!(),
}
for def_id in resolutions {
def_ids.insert(def_id, (path, disallowed_path));
}
if let Some(ty) = prim_ty {
prim_tys.insert(ty, (path, disallowed_path));
}
}
+6 -7
View File
@@ -1,7 +1,7 @@
use clippy_config::Conf;
use clippy_config::types::{DisallowedPathWithoutReplacement, create_disallowed_map};
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::{match_def_path, paths};
use clippy_utils::{PathNS, paths};
use rustc_hir as hir;
use rustc_hir::def_id::{DefId, DefIdMap};
use rustc_lint::{LateContext, LateLintPass};
@@ -182,6 +182,7 @@ pub(crate) fn new(tcx: TyCtxt<'_>, conf: &'static Conf) -> Self {
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 -2
View File
@@ -2,7 +2,7 @@
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 has_unsafe<'tcx>(cx: &LateContext<'tcx>, item: &'tcx Item<'_>) -> bool {
}
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
View File
@@ -1,5 +1,6 @@
use clippy_config::Conf;
use clippy_config::types::{DisallowedPath, create_disallowed_map};
use clippy_utils::PathNS;
use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then};
use clippy_utils::macros::macro_backtrace;
use rustc_data_structures::fx::FxHashSet;
@@ -75,6 +76,7 @@ pub fn new(tcx: TyCtxt<'_>, conf: &'static Conf, earlies: AttrStorage) -> Self {
let (disallowed, _) = create_disallowed_map(
tcx,
&conf.disallowed_macros,
PathNS::Macro,
|def_kind| matches!(def_kind, DefKind::Macro(_)),
"macro",
false,
+2
View File
@@ -1,5 +1,6 @@
use clippy_config::Conf;
use clippy_config::types::{DisallowedPath, create_disallowed_map};
use clippy_utils::PathNS;
use clippy_utils::diagnostics::span_lint_and_then;
use rustc_hir::def::{CtorKind, DefKind, Res};
use rustc_hir::def_id::DefIdMap;
@@ -66,6 +67,7 @@ pub fn new(tcx: TyCtxt<'_>, conf: &'static Conf) -> Self {
let (disallowed, _) = create_disallowed_map(
tcx,
&conf.disallowed_methods,
PathNS::Value,
|def_kind| {
matches!(
def_kind,
+9 -1
View File
@@ -1,5 +1,6 @@
use clippy_config::Conf;
use clippy_config::types::{DisallowedPath, create_disallowed_map};
use clippy_utils::PathNS;
use clippy_utils::diagnostics::span_lint_and_then;
use rustc_data_structures::fx::FxHashMap;
use rustc_hir::def::{DefKind, Res};
@@ -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 }
}
+2 -2
View File
@@ -9,8 +9,8 @@
mod too_many_lines;
use clippy_config::Conf;
use clippy_utils::def_path_def_ids;
use clippy_utils::msrvs::Msrv;
use clippy_utils::{PathNS, lookup_path_str};
use rustc_hir as hir;
use rustc_hir::intravisit;
use rustc_lint::{LateContext, LateLintPass};
@@ -469,7 +469,7 @@ pub fn new(tcx: TyCtxt<'_>, conf: &'static Conf) -> Self {
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,
}
+4 -8
View File
@@ -1,5 +1,5 @@
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::ty::{implements_trait, is_must_use_ty, match_type};
use clippy_utils::ty::{implements_trait, is_must_use_ty};
use clippy_utils::{is_from_proc_macro, is_must_use_func_call, paths};
use rustc_hir::{LetStmt, LocalSource, PatKind};
use rustc_lint::{LateContext, LateLintPass};
@@ -129,12 +129,6 @@
declare_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 @@ fn check_local(&mut self, cx: &LateContext<'tcx>, local: &LetStmt<'tcx>) {
{
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 {
+2 -2
View File
@@ -1,7 +1,7 @@
use clippy_config::Conf;
use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
use clippy_utils::msrvs::Msrv;
use clippy_utils::{is_none_arm, msrvs, peel_hir_expr_refs, sym};
use clippy_utils::{is_none_arm, msrvs, paths, peel_hir_expr_refs, sym};
use rustc_errors::Applicability;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::{Arm, Expr, ExprKind, LangItem, Pat, PatKind, QPath, is_range_literal};
@@ -220,5 +220,5 @@ fn is_empty_slice(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
}
fn is_slice_from_ref(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
clippy_utils::is_expr_path_def_path(cx, expr, &["core", "slice", "raw", "from_ref"])
paths::SLICE_FROM_REF.matches_path(cx, expr)
}
+3 -6
View File
@@ -1,5 +1,6 @@
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::{expr_or_init, paths};
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind, QPath};
use rustc_lint::LateContext;
@@ -8,13 +9,9 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, path: &Expr<'_>, args
if let [error_kind, error] = args
&& !expr.span.from_expansion()
&& !error_kind.span.from_expansion()
&& clippy_utils::is_expr_path_def_path(cx, path, &clippy_utils::paths::IO_ERROR_NEW)
&& clippy_utils::is_expr_path_def_path(
cx,
clippy_utils::expr_or_init(cx, error_kind),
&clippy_utils::paths::IO_ERRORKIND_OTHER,
)
&& let ExprKind::Path(QPath::TypeRelative(_, new_segment)) = path.kind
&& paths::IO_ERROR_NEW.matches_path(cx, path)
&& paths::IO_ERRORKIND_OTHER_CTOR.matches_path(cx, expr_or_init(cx, error_kind))
&& msrv.meets(cx, msrvs::IO_ERROR_OTHER)
{
span_lint_and_then(
@@ -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 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)
{
+10 -10
View File
@@ -1,8 +1,8 @@
use rustc_data_structures::fx::FxHashMap;
use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
use clippy_utils::ty::{is_type_diagnostic_item, match_type};
use clippy_utils::{match_any_def_paths, paths};
use clippy_utils::paths;
use clippy_utils::ty::is_type_diagnostic_item;
use rustc_ast::ast::LitKind;
use rustc_hir::{Expr, ExprKind};
use rustc_lint::LateContext;
@@ -13,7 +13,7 @@
use 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
}
+2 -2
View File
@@ -4,7 +4,7 @@
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()
+4 -5
View File
@@ -1,9 +1,7 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::ty::{implements_trait, should_call_clone_as_function, walk_ptrs_ty_depth};
use clippy_utils::{
get_parent_expr, is_diag_trait_item, match_def_path, path_to_local_id, peel_blocks, strip_pat_refs,
};
use clippy_utils::{get_parent_expr, is_diag_trait_item, path_to_local_id, peel_blocks, strip_pat_refs};
use rustc_errors::Applicability;
use rustc_hir::{self as hir, LangItem};
use rustc_lint::LateContext;
@@ -81,8 +79,9 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, call_name: &str,
applicability,
);
}
} else if match_def_path(cx, def_id, &["core", "option", "Option", call_name])
|| match_def_path(cx, def_id, &["core", "result", "Result", call_name])
} else if let Some(impl_id) = cx.tcx.opt_parent(def_id)
&& let Some(adt) = cx.tcx.type_of(impl_id).instantiate_identity().ty_adt_def()
&& (cx.tcx.lang_items().option_type() == Some(adt.did()) || cx.tcx.is_diagnostic_item(sym::Result, adt.did()))
{
let rcv_ty = cx.typeck_results().expr_ty(recvr).peel_refs();
let res_ty = cx.typeck_results().expr_ty(expr).peel_refs();
@@ -1,7 +1,7 @@
use clippy_config::Conf;
use clippy_utils::def_path_def_ids;
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::SpanRangeExt;
use clippy_utils::{PathNS, lookup_path_str};
use rustc_errors::Applicability;
use rustc_hir::def::Res;
use rustc_hir::def_id::DefIdMap;
@@ -56,8 +56,12 @@ pub fn new(tcx: TyCtxt<'_>, conf: &'static Conf) -> Self {
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(),
}
}
+16 -24
View File
@@ -2,7 +2,7 @@
use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
use clippy_utils::msrvs::{self, Msrv};
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::{PathNS, find_crates, fn_def_id, is_no_std_crate, lookup_path_str, path_def_id, paths, sym};
use rustc_data_structures::fx::FxIndexMap;
use rustc_errors::Applicability;
use rustc_hir::def::{DefKind, Res};
@@ -62,10 +62,7 @@
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 +73,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 +89,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 +106,7 @@ fn check_crate(&mut self, cx: &LateContext<'hir>) {
fn check_item(&mut self, cx: &LateContext<'hir>, item: &Item<'hir>) {
if let ItemKind::Static(..) = item.kind
&& let Some(macro_call) = clippy_utils::macros::root_macro_call(item.span)
&& self.lazy_static_lazy_static.contains(&macro_call.def_id)
&& paths::LAZY_STATIC.matches(cx, macro_call.def_id)
&& can_use_lazy_cell(cx, self.msrv)
{
span_lint(
@@ -130,7 +122,7 @@ fn check_item(&mut self, cx: &LateContext<'hir>, item: &Item<'hir>) {
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 +147,9 @@ fn check_ty(&mut self, cx: &LateContext<'hir>, ty: &'hir rustc_hir::Ty<'hir, rus
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 +182,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 +196,7 @@ fn from_item(state: &NonStdLazyStatic, cx: &LateContext<'_>, item: &Item<'_>) ->
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);
}
+5 -12
View File
@@ -2,8 +2,9 @@
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 @@ pub struct Regex {
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);
}
};
+2 -4
View File
@@ -1,5 +1,5 @@
use clippy_utils::diagnostics::span_lint;
use clippy_utils::{get_trait_def_id, paths};
use clippy_utils::paths;
use rustc_hir::{Impl, Item, ItemKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::declare_lint_pass;
@@ -32,9 +32,7 @@ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
}) = 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 {
+2 -2
View File
@@ -3,7 +3,7 @@
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 @@ fn check_expr<'tcx>(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
&& 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
+5 -15
View File
@@ -1,10 +1,9 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::{match_def_path, sym};
use clippy_utils::{paths, sym};
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty;
use rustc_session::declare_lint_pass;
declare_clippy_lint! {
@@ -40,27 +39,18 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
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
+25 -16
View File
@@ -1,6 +1,6 @@
use clippy_utils::diagnostics::span_lint_hir_and_then;
use clippy_utils::macros::{is_panic, root_macro_call_first_node};
use clippy_utils::{is_res_lang_ctor, is_trait_method, match_def_path, match_trait_method, paths, peel_blocks};
use clippy_utils::{is_res_lang_ctor, paths, peel_blocks};
use hir::{ExprKind, HirId, PatKind};
use rustc_hir as hir;
use rustc_lint::{LateContext, LateLintPass};
@@ -93,14 +93,14 @@ fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx hir::Block<'tcx>)
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
}
}
+66 -64
View File
@@ -1,16 +1,18 @@
use clippy_utils::{get_attr, higher};
use clippy_utils::{MaybePath, get_attr, higher, path_def_id};
use itertools::Itertools;
use rustc_ast::LitIntType;
use rustc_ast::ast::{LitFloatType, LitKind};
use rustc_data_structures::fx::FxHashMap;
use rustc_hir::def_id::DefId;
use rustc_hir::{
self as hir, BindingMode, CaptureBy, Closure, ClosureKind, ConstArg, ConstArgKind, CoroutineKind, ExprKind,
FnRetTy, HirId, Lit, PatExprKind, PatKind, QPath, StmtKind, StructTailExpr, TyKind,
FnRetTy, HirId, Lit, PatExprKind, PatKind, QPath, StmtKind, StructTailExpr,
};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_session::declare_lint_pass;
use rustc_span::symbol::{Ident, Symbol};
use std::cell::Cell;
use std::fmt::{Display, Formatter, Write as _};
use std::fmt::{Display, Formatter};
declare_lint_pass!(
/// ### What it does
@@ -148,6 +150,15 @@ fn check_node(cx: &LateContext<'_>, hir_id: HirId, f: impl Fn(&PrintVisitor<'_,
}
}
fn paths_static_name(cx: &LateContext<'_>, id: DefId) -> String {
cx.get_def_path(id)
.iter()
.map(Symbol::as_str)
.filter(|s| !s.starts_with('<'))
.join("_")
.to_uppercase()
}
struct Binding<T> {
name: String,
value: T,
@@ -257,11 +268,44 @@ fn symbol(&self, symbol: &Binding<Symbol>) {
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 @@ fn const_arg(&self, const_arg: &Binding<&ConstArg<'_>>) {
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 @@ macro_rules! kind {
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 @@ macro_rules! kind {
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 @@ macro_rules! kind {
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 @@ macro_rules! kind {
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 @@ fn body(&self, body_id: &Binding<hir::BodyId>) {
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 @@ macro_rules! kind {
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 @@ macro_rules! kind {
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 @@ macro_rules! kind {
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 @@ macro_rules! kind {
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 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 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
}
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)
@@ -0,0 +1,17 @@
use clippy_utils::paths::PathLookup;
use clippy_utils::{PathNS, 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);
-108
View File
@@ -1,108 +0,0 @@
use clippy_utils::consts::{ConstEvalCtxt, Constant};
use clippy_utils::def_path_res;
use clippy_utils::diagnostics::span_lint;
use rustc_hir as hir;
use rustc_hir::Item;
use rustc_hir::def::DefKind;
use rustc_lint::{LateContext, LateLintPass};
use rustc_lint_defs::declare_tool_lint;
use rustc_middle::ty::fast_reject::SimplifiedType;
use rustc_middle::ty::{self, FloatTy};
use rustc_session::declare_lint_pass;
use rustc_span::symbol::Symbol;
declare_tool_lint! {
/// ### What it does
/// Checks the paths module for invalid paths.
///
/// ### Why is this bad?
/// It indicates a bug in the code.
///
/// ### Example
/// None.
pub clippy::INVALID_PATHS,
Warn,
"invalid path",
report_in_external_macro: true
}
declare_lint_pass!(InvalidPaths => [INVALID_PATHS]);
impl<'tcx> LateLintPass<'tcx> for InvalidPaths {
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
let local_def_id = &cx.tcx.parent_module(item.hir_id());
let mod_name = &cx.tcx.item_name(local_def_id.to_def_id());
if mod_name.as_str() == "paths"
&& let hir::ItemKind::Const(.., body_id) = item.kind
&& let Some(Constant::Vec(path)) = ConstEvalCtxt::with_env(
cx.tcx,
ty::TypingEnv::post_analysis(cx.tcx, item.owner_id),
cx.tcx.typeck(item.owner_id),
)
.eval_simple(cx.tcx.hir_body(body_id).value)
&& let Some(path) = path
.iter()
.map(|x| {
if let Constant::Str(s) = x {
Some(s.as_str())
} else {
None
}
})
.collect::<Option<Vec<&str>>>()
&& !check_path(cx, &path[..])
{
span_lint(cx, INVALID_PATHS, item.span, "invalid path");
}
}
}
// This is not a complete resolver for paths. It works on all the paths currently used in the paths
// module. That's all it does and all it needs to do.
pub fn check_path(cx: &LateContext<'_>, path: &[&str]) -> bool {
if !def_path_res(cx.tcx, path).is_empty() {
return true;
}
// Some implementations can't be found by `path_to_res`, particularly inherent
// implementations of native types. Check lang items.
let path_syms: Vec<_> = path.iter().map(|p| Symbol::intern(p)).collect();
let lang_items = cx.tcx.lang_items();
// This list isn't complete, but good enough for our current list of paths.
let incoherent_impls = [
SimplifiedType::Float(FloatTy::F32),
SimplifiedType::Float(FloatTy::F64),
SimplifiedType::Slice,
SimplifiedType::Str,
SimplifiedType::Bool,
SimplifiedType::Char,
]
.iter()
.flat_map(|&ty| cx.tcx.incoherent_impls(ty).iter())
.copied();
for item_def_id in lang_items.iter().map(|(_, def_id)| def_id).chain(incoherent_impls) {
let lang_item_path = cx.get_def_path(item_def_id);
if path_syms.starts_with(&lang_item_path)
&& let [item] = &path_syms[lang_item_path.len()..]
{
if matches!(
cx.tcx.def_kind(item_def_id),
DefKind::Mod | DefKind::Enum | DefKind::Trait
) {
for child in cx.tcx.module_children(item_def_id) {
if child.ident.name == *item {
return true;
}
}
} else {
for child in cx.tcx.associated_item_def_ids(item_def_id) {
if cx.tcx.item_name(*child) == *item {
return true;
}
}
}
}
}
false
}
+2 -4
View File
@@ -32,7 +32,7 @@
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 @@
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<'_>) {
+3 -4
View File
@@ -1,7 +1,6 @@
use crate::internal_paths;
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet;
use clippy_utils::ty::match_type;
use clippy_utils::{match_def_path, paths};
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_lint::{LateContext, LateLintPass, LintContext};
@@ -31,7 +30,7 @@ fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) {
.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 @@ fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) {
.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 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
&& 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,
+7 -8
View File
@@ -1,6 +1,5 @@
use crate::internal_paths;
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::ty::match_type;
use clippy_utils::{def_path_def_ids, match_def_path, paths};
use rustc_ast::LitKind;
use rustc_data_structures::fx::FxHashMap;
use rustc_errors::Applicability;
@@ -69,12 +68,12 @@ pub struct Symbols {
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 @@ fn check_crate(&mut self, cx: &LateContext<'_>) {
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 {
+58 -258
View File
@@ -1,23 +1,13 @@
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::{PathNS, lookup_path, 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 +18,14 @@
///
/// ### 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 +33,65 @@
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> {
+136 -221
View File
@@ -96,8 +96,9 @@
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::Namespace::{MacroNS, TypeNS, ValueNS};
use rustc_hir::def::{DefKind, Namespace, Res};
use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId, LocalModDefId};
use rustc_hir::definitions::{DefPath, DefPathData};
use rustc_hir::hir_id::{HirIdMap, HirIdSet};
use rustc_hir::intravisit::{FnKind, Visitor, walk_expr};
@@ -105,8 +106,8 @@
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,
Mutability, Node, OwnerId, OwnerNode, Param, Pat, PatExpr, PatExprKind, PatKind, Path, PathSegment, QPath, Stmt,
StmtKind, TraitFn, TraitItem, TraitItemKind, TraitItemRef, TraitRef, TyKind, UnOp, def,
};
use rustc_lexer::{TokenKind, tokenize};
use rustc_lint::{LateContext, Level, Lint, LintContext};
@@ -347,14 +348,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 +431,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 +447,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,60 +513,57 @@ 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 {
fn find_primitive_impls(tcx: TyCtxt<'_>, name: Symbol) -> &[DefId] {
let ty = match name {
"bool" => SimplifiedType::Bool,
"char" => SimplifiedType::Char,
"str" => SimplifiedType::Str,
"array" => SimplifiedType::Array,
"slice" => SimplifiedType::Slice,
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.
"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();
},
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).iter().copied()
tcx.incoherent_impls(ty)
}
fn non_local_item_children_by_name(tcx: TyCtxt<'_>, def_id: DefId, name: Symbol) -> Vec<Res> {
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()
.filter(|item| item.ident.name == name)
.map(|child| child.res.expect_non_local())
.collect(),
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()
.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(),
.find(|assoc_def_id| tcx.item_name(*assoc_def_id) == name && ns.matches(tcx.def_kind(assoc_def_id).ns())),
_ => None,
}
}
fn local_item_children_by_name(tcx: TyCtxt<'_>, local_id: LocalDefId, name: Symbol) -> Vec<Res> {
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) => {
@@ -647,138 +571,147 @@ fn local_item_children_by_name(tcx: TyCtxt<'_>, local_id: LocalDefId, name: Symb
&root_mod
},
Node::Item(item) => &item.kind,
_ => return Vec::new(),
_ => return None,
};
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))
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()
.filter_map(|&item_id| {
let ident = tcx.hir_item(item_id).kind.ident()?;
res(ident, item_id.owner_id)
})
.collect(),
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()
.filter_map(|&ImplItemRef { ident, id, .. }| res(ident, id.owner_id))
.collect(),
.find_map(|&ImplItemRef { ident, id, .. }| res(ident, id.owner_id)),
ItemKind::Trait(.., trait_item_refs) => trait_item_refs
.iter()
.filter_map(|&TraitItemRef { ident, id, .. }| res(ident, id.owner_id))
.collect(),
_ => Vec::new(),
.find_map(|&TraitItemRef { ident, id, .. }| res(ident, id.owner_id)),
_ => None,
}
}
fn item_children_by_name(tcx: TyCtxt<'_>, def_id: DefId, name: Symbol) -> Vec<Res> {
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_children_by_name(tcx, local_id, name)
local_item_child_by_name(tcx, local_id, ns, name)
} else {
non_local_item_children_by_name(tcx, def_id, name)
non_local_item_child_by_name(tcx, def_id, ns, 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()
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 => &[],
}
}
/// 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)
}
}
/// 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`].
/// 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.
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(),
///
/// [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 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)
let mut out = Vec::new();
for &base in find_crates(tcx, root).iter().chain(find_primitive_impls(tcx, root)) {
lookup_path_with_base(tcx, base, ns, rest, &mut out);
}
out
}
/// 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);
fn lookup_path_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));
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)
.inherent_impls(base)
.iter()
.flat_map(|&impl_def_id| item_children_by_name(tcx, impl_def_id, segment));
.filter_map(|&impl_def_id| item_child_by_name(tcx, impl_def_id, ns, segment));
out.extend(inherent_impl_children);
let direct_children = item_children_by_name(tcx, def_id, segment);
inherent_impl_children.chain(direct_children)
})
.collect();
return;
},
[segment, ref rest @ ..] => {
path = rest;
let Some(child) = item_child_by_name(tcx, base, PathNS::Type, segment) else {
return;
};
base = child;
},
[] => unreachable!(),
}
}
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.
/// Equivalent to a [`lookup_path`] after splitting the input string on `::`
///
/// 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,
})
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)
}
/// Gets the `hir::TraitRef` of the trait the given method is implemented for.
@@ -2065,24 +1998,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);
+118 -55
View File
@@ -4,62 +4,125 @@
//! 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, PathNS, lookup_path, path_def_id, sym};
use rustc_hir::def_id::DefId;
use rustc_lint::LateContext;
use rustc_middle::ty::Ty;
use rustc_span::{STDLIB_STABLE_CRATES, Symbol};
use std::sync::OnceLock;
/// 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`
+54 -2
View File
@@ -1,6 +1,6 @@
#![allow(non_upper_case_globals)]
use rustc_span::symbol::{PREDEFINED_SYMBOLS_COUNT, Symbol};
use rustc_span::symbol::PREDEFINED_SYMBOLS_COUNT;
#[doc(no_inline)]
pub use rustc_span::sym::*;
@@ -24,33 +24,45 @@ macro_rules! generate {
];
$(
pub const $name: Symbol = Symbol::new(PREDEFINED_SYMBOLS_COUNT + ${index()});
pub const $name: rustc_span::Symbol = rustc_span::Symbol::new(PREDEFINED_SYMBOLS_COUNT + ${index()});
)*
};
}
generate! {
abs,
align_of,
as_bytes,
as_deref_mut,
as_deref,
as_mut,
AsyncReadExt,
AsyncWriteExt,
Binary,
build_hasher,
bytes,
cargo_clippy: "cargo-clippy",
Cargo_toml: "Cargo.toml",
cast,
chars,
CLIPPY_ARGS,
CLIPPY_CONF_DIR,
clippy_utils,
clone_into,
cloned,
collect,
const_ptr,
contains,
copied,
CRLF: "\r\n",
Current,
de,
Deserialize,
diagnostics,
EarlyLintPass,
ends_with,
error,
ErrorKind,
exp,
extend,
finish_non_exhaustive,
@@ -58,48 +70,87 @@ macro_rules! 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 @@ macro_rules! generate {
UpperHex,
V4,
V6,
Visitor,
Weak,
with_capacity,
wrapping_offset,
+3 -19
View File
@@ -32,7 +32,7 @@
use std::collections::hash_map::Entry;
use std::iter;
use crate::{def_path_def_ids, match_def_path, path_res};
use crate::{PathNS, lookup_path_str, path_res};
mod type_certainty;
pub use type_certainty::expr_type_is_certain;
@@ -229,9 +229,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 +422,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 +1118,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 {
+15 -8
View File
@@ -11,14 +11,14 @@
//! As a heuristic, `expr_type_is_certain` may produce false negatives, but a false positive should
//! be considered a bug.
use crate::def_path_res;
use crate::{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)]
-4
View File
@@ -1,4 +0,0 @@
#![allow(clippy::unnecessary_def_path)]
pub static OPTION: [&str; 3] = ["core", "option", "Option"];
pub const RESULT: &[&str] = &["core", "result", "Result"];
-30
View File
@@ -1,30 +0,0 @@
#![deny(clippy::invalid_paths)]
#![allow(clippy::missing_clippy_version_attribute, clippy::unnecessary_def_path)]
mod paths {
// Good path
pub const ANY_TRAIT: [&str; 3] = ["std", "any", "Any"];
// Path to method on inherent impl of a primitive type
pub const F32_EPSILON: [&str; 4] = ["core", "f32", "<impl f32>", "EPSILON"];
// Path to method on inherent impl
pub const ARC_PTR_EQ: [&str; 4] = ["alloc", "sync", "Arc", "ptr_eq"];
// Path with empty segment
pub const TRANSMUTE: [&str; 4] = ["core", "intrinsics", "", "transmute"];
//~^ invalid_paths
// Path with bad crate
pub const BAD_CRATE_PATH: [&str; 2] = ["bad", "path"];
//~^ invalid_paths
// Path with bad module
pub const BAD_MOD_PATH: [&str; 2] = ["std", "xxx"];
//~^ invalid_paths
// Path to method on an enum inherent impl
pub const OPTION_IS_SOME: [&str; 4] = ["core", "option", "Option", "is_some"];
}
fn main() {}
-26
View File
@@ -1,26 +0,0 @@
error: invalid path
--> tests/ui-internal/invalid_paths.rs:15:5
|
LL | pub const TRANSMUTE: [&str; 4] = ["core", "intrinsics", "", "transmute"];
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: the lint level is defined here
--> tests/ui-internal/invalid_paths.rs:1:9
|
LL | #![deny(clippy::invalid_paths)]
| ^^^^^^^^^^^^^^^^^^^^^
error: invalid path
--> tests/ui-internal/invalid_paths.rs:19:5
|
LL | pub const BAD_CRATE_PATH: [&str; 2] = ["bad", "path"];
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: invalid path
--> tests/ui-internal/invalid_paths.rs:23:5
|
LL | pub const BAD_MOD_PATH: [&str; 2] = ["std", "xxx"];
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to 3 previous errors
@@ -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() {}
+14 -71
View File
@@ -1,77 +1,20 @@
//@aux-build:paths.rs
#![deny(clippy::unnecessary_def_path)]
#![feature(rustc_private)]
#![allow(clippy::unnecessary_map_or)]
extern crate clippy_utils;
extern crate paths;
extern crate rustc_hir;
extern crate rustc_lint;
extern crate rustc_middle;
extern crate rustc_span;
use clippy_utils::paths::PathLookup;
use clippy_utils::{PathNS, 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
+39 -81
View File
@@ -1,100 +1,58 @@
error: use of a def path to a diagnostic item
--> tests/ui-internal/unnecessary_def_path.rs:37:13
error: a diagnostic name exists for this path: sym::Option
--> tests/ui-internal/unnecessary_def_path.rs:6:29
|
LL | let _ = match_type(cx, ty, &OPTION);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::Option)`
LL | static OPTION: PathLookup = type_path!(core::option::Option);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: the lint level is defined here
--> tests/ui-internal/unnecessary_def_path.rs:2:9
|
LL | #![deny(clippy::unnecessary_def_path)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= help: remove the `PathLookup` and use utilities such as `cx.tcx.is_diagnostic_item` instead
= help: see also https://doc.rust-lang.org/nightly/nightly-rustc/?search=diag&filter-crate=clippy_utils
= note: `-D clippy::unnecessary-def-path` implied by `-D warnings`
= help: to override `-D warnings` add `#[allow(clippy::unnecessary_def_path)]`
error: use of a def path to a diagnostic item
--> tests/ui-internal/unnecessary_def_path.rs:39:13
error: a language item exists for this path: LangItem::OptionSome
--> tests/ui-internal/unnecessary_def_path.rs:8:27
|
LL | let _ = match_type(cx, ty, RESULT);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::Result)`
error: use of a def path to a diagnostic item
--> tests/ui-internal/unnecessary_def_path.rs:41:13
LL | static SOME: PathLookup = type_path!(core::option::Option::Some);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
LL | let _ = match_type(cx, ty, &["core", "result", "Result"]);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::Result)`
= help: remove the `PathLookup` and use utilities such as `cx.tcx.lang_items` instead
= help: see also https://doc.rust-lang.org/nightly/nightly-rustc/?search=lang&filter-crate=clippy_utils
error: use of a def path to a diagnostic item
--> tests/ui-internal/unnecessary_def_path.rs:46:13
error: a diagnostic name exists for this path: sym::Result
--> tests/ui-internal/unnecessary_def_path.rs:11:29
|
LL | let _ = clippy_utils::ty::match_type(cx, ty, rc_path);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::Rc)`
error: use of a def path to a diagnostic item
--> tests/ui-internal/unnecessary_def_path.rs:49:13
LL | static RESULT: PathLookup = type_path!(core::result::Result);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
LL | let _ = match_type(cx, ty, &paths::OPTION);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::Option)`
= help: remove the `PathLookup` and use utilities such as `cx.tcx.is_diagnostic_item` instead
= help: see also https://doc.rust-lang.org/nightly/nightly-rustc/?search=diag&filter-crate=clippy_utils
error: use of a def path to a diagnostic item
--> tests/ui-internal/unnecessary_def_path.rs:51:13
error: a diagnostic name exists for this path: sym::Result
--> tests/ui-internal/unnecessary_def_path.rs:13:37
|
LL | let _ = match_type(cx, ty, paths::RESULT);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::Result)`
error: use of a def path to a `LangItem`
--> tests/ui-internal/unnecessary_def_path.rs:54:13
LL | static RESULT_VIA_STD: PathLookup = type_path!(std::result::Result);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
LL | let _ = match_type(cx, ty, &["alloc", "boxed", "Box"]);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_lang_item(cx, ty, LangItem::OwnedBox)`
= help: remove the `PathLookup` and use utilities such as `cx.tcx.is_diagnostic_item` instead
= help: see also https://doc.rust-lang.org/nightly/nightly-rustc/?search=diag&filter-crate=clippy_utils
error: use of a def path to a diagnostic item
--> tests/ui-internal/unnecessary_def_path.rs:56:13
error: a diagnostic name exists for this path: sym::vec_new
--> tests/ui-internal/unnecessary_def_path.rs:16:30
|
LL | let _ = match_type(cx, ty, &["core", "mem", "maybe_uninit", "MaybeUninit", "uninit"]);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::maybe_uninit_uninit)`
error: use of a def path to a `LangItem`
--> tests/ui-internal/unnecessary_def_path.rs:59:13
LL | static VEC_NEW: PathLookup = value_path!(alloc::vec::Vec::new);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
LL | let _ = match_def_path(cx, did, &["alloc", "boxed", "Box"]);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `cx.tcx.lang_items().get(LangItem::OwnedBox) == Some(did)`
= help: remove the `PathLookup` and use utilities such as `cx.tcx.is_diagnostic_item` instead
= help: see also https://doc.rust-lang.org/nightly/nightly-rustc/?search=diag&filter-crate=clippy_utils
error: use of a def path to a diagnostic item
--> tests/ui-internal/unnecessary_def_path.rs:61:13
error: a diagnostic name exists for this path: sym::vec_macro
--> tests/ui-internal/unnecessary_def_path.rs:19:32
|
LL | let _ = match_def_path(cx, did, &["core", "option", "Option"]);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `cx.tcx.is_diagnostic_item(sym::Option, did)`
error: use of a def path to a `LangItem`
--> tests/ui-internal/unnecessary_def_path.rs:63:13
LL | static VEC_MACRO: PathLookup = macro_path!(std::vec);
| ^^^^^^^^^^^^^^^^^^^^^
|
LL | let _ = match_def_path(cx, did, &["core", "option", "Option", "Some"]);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `cx.tcx.lang_items().get(LangItem::OptionSome) == Some(did)`
|
= help: if this `DefId` came from a constructor expression or pattern then the parent `DefId` should be used instead
= help: remove the `PathLookup` and use utilities such as `cx.tcx.is_diagnostic_item` instead
= help: see also https://doc.rust-lang.org/nightly/nightly-rustc/?search=diag&filter-crate=clippy_utils
error: use of a def path to a diagnostic item
--> tests/ui-internal/unnecessary_def_path.rs:66:13
|
LL | let _ = match_trait_method(cx, expr, &["core", "convert", "AsRef"]);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_trait_method(cx, expr, sym::AsRef)`
error: use of a def path to a diagnostic item
--> tests/ui-internal/unnecessary_def_path.rs:69:13
|
LL | let _ = is_expr_path_def_path(cx, expr, &["core", "option", "Option"]);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_path_diagnostic_item(cx, expr, sym::Option)`
error: use of a def path to a `LangItem`
--> tests/ui-internal/unnecessary_def_path.rs:71:13
|
LL | let _ = is_expr_path_def_path(cx, expr, &["core", "iter", "traits", "Iterator", "next"]);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `path_res(cx, expr).opt_def_id().map_or(false, |id| cx.tcx.lang_items().get(LangItem::IteratorNext) == Some(id))`
error: use of a def path to a `LangItem`
--> tests/ui-internal/unnecessary_def_path.rs:73:13
|
LL | let _ = is_expr_path_def_path(cx, expr, &["core", "option", "Option", "Some"]);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_res_lang_ctor(cx, path_res(cx, expr), LangItem::OptionSome)`
error: aborting due to 15 previous errors
error: aborting due to 6 previous errors
@@ -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;
+6 -3
View File
@@ -1,12 +1,15 @@
[[disallowed-types]]
path = "std::result::Result::Err"
[[disallowed-macros]]
path = "bool"
[[disallowed-methods]]
path = "std::process::current_exe"
[[disallowed-methods]]
path = ""
[[disallowed-types]]
path = "std::result::Result::Err"
# negative test
[[disallowed-methods]]
@@ -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
-2
View File
@@ -1,8 +1,6 @@
if let StmtKind::Let(local) = stmt.kind
&& let Some(init) = local.init
&& let ExprKind::Cast(expr, cast_ty) = init.kind
&& let TyKind::Path(ref qpath) = cast_ty.kind
&& match_qpath(qpath, &["char"])
&& let ExprKind::Lit(ref lit) = expr.kind
&& let LitKind::Int(69, LitIntType::Unsuffixed) = lit.node
&& let PatKind::Binding(BindingMode::NONE, _, name, None) = local.pat.kind
+2 -8
View File
@@ -14,8 +14,6 @@ if let ExprKind::Block(block, None) = expr.kind
&& name1.as_str() == "_t"
&& let StmtKind::Semi(e) = block.stmts[2].kind
&& let ExprKind::Unary(UnOp::Neg, inner) = e.kind
&& let ExprKind::Path(ref qpath) = inner.kind
&& match_qpath(qpath, &["x"])
&& block.expr.is_none()
{
// report your lint here
@@ -25,18 +23,14 @@ if let ExprKind::Block(block, None) = expr.kind
&& let StmtKind::Let(local) = block.stmts[0].kind
&& let Some(init) = local.init
&& let ExprKind::Call(func, args) = init.kind
&& let ExprKind::Path(ref qpath) = func.kind
&& match_qpath(qpath, &["String", "new"])
&& is_path_diagnostic_item(cx, func, sym::string_new)
&& args.is_empty()
&& let PatKind::Binding(BindingMode::NONE, _, name, None) = local.pat.kind
&& name.as_str() == "expr"
&& let Some(trailing_expr) = block.expr
&& let ExprKind::Call(func1, args1) = trailing_expr.kind
&& let ExprKind::Path(ref qpath1) = func1.kind
&& match_qpath(qpath1, &["drop"])
&& is_path_diagnostic_item(cx, func1, sym::mem_drop)
&& args1.len() == 1
&& let ExprKind::Path(ref qpath2) = args1[0].kind
&& match_qpath(qpath2, &["expr"])
{
// report your lint here
}
+1 -2
View File
@@ -1,8 +1,7 @@
if let StmtKind::Let(local) = stmt.kind
&& let Some(init) = local.init
&& let ExprKind::Call(func, args) = init.kind
&& let ExprKind::Path(ref qpath) = func.kind
&& match_qpath(qpath, &["{{root}}", "std", "cmp", "min"])
&& is_path_diagnostic_item(cx, func, sym::cmp_min)
&& args.len() == 2
&& let ExprKind::Lit(ref lit) = args[0].kind
&& let LitKind::Int(3, LitIntType::Unsuffixed) = lit.node
+1 -3
View File
@@ -31,10 +31,8 @@ if let StmtKind::Let(local) = stmt.kind
if let ExprKind::If(cond, then, Some(else_expr)) = expr.kind
&& let ExprKind::Let(let_expr) = cond.kind
&& let PatKind::Expr(lit_expr) = let_expr.pat.kind
&& let PatExprKind::Lit{ref lit, negated } = lit_expr.kind
&& let PatExprKind::Lit { ref lit, negated } = lit_expr.kind
&& let LitKind::Bool(true) = lit.node
&& let ExprKind::Path(ref qpath) = let_expr.init.kind
&& match_qpath(qpath, &["a"])
&& let ExprKind::Block(block, None) = then.kind
&& block.stmts.is_empty()
&& block.expr.is_none()
+1 -4
View File
@@ -1,11 +1,8 @@
if let StmtKind::Let(local) = stmt.kind
&& let Some(init) = local.init
&& let ExprKind::Call(func, args) = init.kind
&& let ExprKind::Path(ref qpath) = func.kind
&& match_qpath(qpath, &["std", "mem", "transmute"])
&& is_path_diagnostic_item(cx, func, sym::transmute)
&& args.len() == 1
&& let ExprKind::Path(ref qpath1) = args[0].kind
&& match_qpath(qpath1, &["ZPTR"])
&& let PatKind::Wild = local.pat.kind
{
// report your lint here
+1 -7
View File
@@ -14,8 +14,6 @@ if let Some(higher::ForLoop { pat: pat, arg: arg, body: body, .. }) = higher::Fo
&& block.stmts.len() == 1
&& let StmtKind::Let(local) = block.stmts[0].kind
&& let Some(init) = local.init
&& let ExprKind::Path(ref qpath1) = init.kind
&& match_qpath(qpath1, &["y"])
&& let PatKind::Binding(BindingMode::NONE, _, name1, None) = local.pat.kind
&& name1.as_str() == "z"
&& block.expr.is_none()
@@ -64,8 +62,6 @@ if let Some(higher::ForLoop { pat: pat, arg: arg, body: body, .. }) = higher::Fo
// report your lint here
}
if let Some(higher::While { condition: condition, body: body }) = higher::While::hir(expr)
&& let ExprKind::Path(ref qpath) = condition.kind
&& match_qpath(qpath, &["a"])
&& let ExprKind::Block(block, None) = body.kind
&& block.stmts.len() == 1
&& let StmtKind::Semi(e) = block.stmts[0].kind
@@ -77,10 +73,8 @@ if let Some(higher::While { condition: condition, body: body }) = higher::While:
}
if let Some(higher::WhileLet { let_pat: let_pat, let_expr: let_expr, if_then: if_then }) = higher::WhileLet::hir(expr)
&& let PatKind::Expr(lit_expr) = let_pat.kind
&& let PatExprKind::Lit{ref lit, negated } = lit_expr.kind
&& let PatExprKind::Lit { ref lit, negated } = lit_expr.kind
&& let LitKind::Bool(true) = lit.node
&& let ExprKind::Path(ref qpath) = let_expr.kind
&& match_qpath(qpath, &["a"])
&& let ExprKind::Block(block, None) = if_then.kind
&& block.stmts.len() == 1
&& let StmtKind::Semi(e) = block.stmts[0].kind
+3 -8
View File
@@ -7,12 +7,10 @@ if let StmtKind::Let(local) = stmt.kind
&& block.stmts.len() == 1
&& let StmtKind::Semi(e) = block.stmts[0].kind
&& let ExprKind::Call(func, args) = e.kind
&& let ExprKind::Path(ref qpath) = func.kind
&& match_qpath(qpath, &["$crate", "io", "_print"])
&& paths::STD_IO_STDIO__PRINT.matches_path(cx, func) // Add the path to `clippy_utils::paths` if needed
&& args.len() == 1
&& let ExprKind::Call(func1, args1) = args[0].kind
&& let ExprKind::Path(ref qpath1) = func1.kind
&& match_qpath(qpath1, &["format_arguments", "new_v1"])
&& paths::CORE_FMT_ARGUMENTS_NEW_V1.matches_path(cx, func1) // Add the path to `clippy_utils::paths` if needed
&& args1.len() == 2
&& let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner) = args1[0].kind
&& let ExprKind::Array(elements) = inner.kind
@@ -27,12 +25,9 @@ if let StmtKind::Let(local) = stmt.kind
&& let ExprKind::Array(elements1) = inner1.kind
&& elements1.len() == 1
&& let ExprKind::Call(func2, args2) = elements1[0].kind
&& let ExprKind::Path(ref qpath2) = func2.kind
&& match_qpath(qpath2, &["format_argument", "new_display"])
&& paths::CORE_FMT_RT_ARGUMENT_NEW_DISPLAY.matches_path(cx, func2) // Add the path to `clippy_utils::paths` if needed
&& args2.len() == 1
&& let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner2) = args2[0].kind
&& let ExprKind::Path(ref qpath3) = inner2.kind
&& match_qpath(qpath3, &["x"])
&& block.expr.is_none()
&& let PatKind::Binding(BindingMode::NONE, _, name, None) = local.pat.kind
&& name.as_str() == "print_text"
+3 -8
View File
@@ -17,12 +17,10 @@ if let Some(higher::ForLoop { pat: pat, arg: arg, body: body, .. }) = higher::Fo
&& block1.stmts.len() == 1
&& let StmtKind::Semi(e1) = block1.stmts[0].kind
&& let ExprKind::Call(func, args) = e1.kind
&& let ExprKind::Path(ref qpath1) = func.kind
&& match_qpath(qpath1, &["$crate", "io", "_print"])
&& paths::STD_IO_STDIO__PRINT.matches_path(cx, func) // Add the path to `clippy_utils::paths` if needed
&& args.len() == 1
&& let ExprKind::Call(func1, args1) = args[0].kind
&& let ExprKind::Path(ref qpath2) = func1.kind
&& match_qpath(qpath2, &["format_arguments", "new_v1"])
&& paths::CORE_FMT_ARGUMENTS_NEW_V1.matches_path(cx, func1) // Add the path to `clippy_utils::paths` if needed
&& args1.len() == 2
&& let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner) = args1[0].kind
&& let ExprKind::Array(elements) = inner.kind
@@ -37,12 +35,9 @@ if let Some(higher::ForLoop { pat: pat, arg: arg, body: body, .. }) = higher::Fo
&& let ExprKind::Array(elements1) = inner1.kind
&& elements1.len() == 1
&& let ExprKind::Call(func2, args2) = elements1[0].kind
&& let ExprKind::Path(ref qpath3) = func2.kind
&& match_qpath(qpath3, &["format_argument", "new_display"])
&& paths::CORE_FMT_RT_ARGUMENT_NEW_DISPLAY.matches_path(cx, func2) // Add the path to `clippy_utils::paths` if needed
&& args2.len() == 1
&& let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner2) = args2[0].kind
&& let ExprKind::Path(ref qpath4) = inner2.kind
&& match_qpath(qpath4, &["i"])
&& block1.expr.is_none()
&& block.expr.is_none()
{
+2 -4
View File
@@ -5,13 +5,13 @@ if let StmtKind::Let(local) = stmt.kind
&& let LitKind::Int(42, LitIntType::Unsuffixed) = lit.node
&& arms.len() == 3
&& let PatKind::Expr(lit_expr) = arms[0].pat.kind
&& let PatExprKind::Lit{ref lit1, negated } = lit_expr.kind
&& let PatExprKind::Lit { ref lit1, negated } = lit_expr.kind
&& let LitKind::Int(16, LitIntType::Unsuffixed) = lit1.node
&& arms[0].guard.is_none()
&& let ExprKind::Lit(ref lit2) = arms[0].body.kind
&& let LitKind::Int(5, LitIntType::Unsuffixed) = lit2.node
&& let PatKind::Expr(lit_expr1) = arms[1].pat.kind
&& let PatExprKind::Lit{ref lit3, negated1 } = lit_expr1.kind
&& let PatExprKind::Lit { ref lit3, negated1 } = lit_expr1.kind
&& let LitKind::Int(17, LitIntType::Unsuffixed) = lit3.node
&& arms[1].guard.is_none()
&& let ExprKind::Block(block, None) = arms[1].body.kind
@@ -23,8 +23,6 @@ if let StmtKind::Let(local) = stmt.kind
&& let PatKind::Binding(BindingMode::NONE, _, name, None) = local1.pat.kind
&& name.as_str() == "x"
&& let Some(trailing_expr) = block.expr
&& let ExprKind::Path(ref qpath) = trailing_expr.kind
&& match_qpath(qpath, &["x"])
&& let PatKind::Wild = arms[2].pat.kind
&& arms[2].guard.is_none()
&& let ExprKind::Lit(ref lit5) = arms[2].body.kind
+2 -7
View File
@@ -1,5 +1,4 @@
if let ExprKind::Struct(qpath, fields, None) = expr.kind
&& match_qpath(qpath, &["Test"])
&& fields.len() == 1
&& fields[0].ident.as_str() == "field"
&& let ExprKind::If(cond, then, Some(else_expr)) = fields[0].expr.kind
@@ -20,11 +19,10 @@ if let ExprKind::Struct(qpath, fields, None) = expr.kind
// report your lint here
}
if let PatKind::Struct(ref qpath, fields, false) = arm.pat.kind
&& match_qpath(qpath, &["Test"])
&& fields.len() == 1
&& fields[0].ident.as_str() == "field"
&& let PatKind::Expr(lit_expr) = fields[0].pat.kind
&& let PatExprKind::Lit{ref lit, negated } = lit_expr.kind
&& let PatExprKind::Lit { ref lit, negated } = lit_expr.kind
&& let LitKind::Int(1, LitIntType::Unsuffixed) = lit.node
&& arm.guard.is_none()
&& let ExprKind::Block(block, None) = arm.body.kind
@@ -34,10 +32,9 @@ if let PatKind::Struct(ref qpath, fields, false) = arm.pat.kind
// report your lint here
}
if let PatKind::TupleStruct(ref qpath, fields, None) = arm.pat.kind
&& match_qpath(qpath, &["TestTuple"])
&& fields.len() == 1
&& let PatKind::Expr(lit_expr) = fields[0].kind
&& let PatExprKind::Lit{ref lit, negated } = lit_expr.kind
&& let PatExprKind::Lit { ref lit, negated } = lit_expr.kind
&& let LitKind::Int(1, LitIntType::Unsuffixed) = lit.node
&& arm.guard.is_none()
&& let ExprKind::Block(block, None) = arm.body.kind
@@ -48,8 +45,6 @@ if let PatKind::TupleStruct(ref qpath, fields, None) = arm.pat.kind
}
if let ExprKind::MethodCall(method_name, receiver, args, _) = expr.kind
&& method_name.ident.as_str() == "test"
&& let ExprKind::Path(ref qpath) = receiver.kind
&& match_qpath(qpath, &["test_method_call"])
&& args.is_empty()
{
// report your lint here
@@ -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
-2
View File
@@ -1,7 +1,5 @@
#![allow(clippy::legacy_numeric_constants, unused_imports)]
use std::{i32, i128, u32, u128};
fn main() {
let _ = 1u32.checked_add(1).unwrap_or(u32::max_value());
//~^ manual_saturating_arithmetic
+24 -24
View File
@@ -1,5 +1,5 @@
error: manual saturating arithmetic
--> tests/ui/manual_saturating_arithmetic.rs:6:13
--> tests/ui/manual_saturating_arithmetic.rs:4:13
|
LL | let _ = 1u32.checked_add(1).unwrap_or(u32::max_value());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_add`: `1u32.saturating_add(1)`
@@ -8,19 +8,19 @@ LL | let _ = 1u32.checked_add(1).unwrap_or(u32::max_value());
= help: to override `-D warnings` add `#[allow(clippy::manual_saturating_arithmetic)]`
error: manual saturating arithmetic
--> tests/ui/manual_saturating_arithmetic.rs:8:13
--> tests/ui/manual_saturating_arithmetic.rs:6:13
|
LL | let _ = 1u32.checked_add(1).unwrap_or(u32::MAX);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_add`: `1u32.saturating_add(1)`
error: manual saturating arithmetic
--> tests/ui/manual_saturating_arithmetic.rs:10:13
--> tests/ui/manual_saturating_arithmetic.rs:8:13
|
LL | let _ = 1u8.checked_add(1).unwrap_or(255);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_add`: `1u8.saturating_add(1)`
error: manual saturating arithmetic
--> tests/ui/manual_saturating_arithmetic.rs:12:13
--> tests/ui/manual_saturating_arithmetic.rs:10:13
|
LL | let _ = 1u128
| _____________^
@@ -30,49 +30,49 @@ LL | | .unwrap_or(340_282_366_920_938_463_463_374_607_431_768_211_455);
| |_______________________________________________________________________^ help: consider using `saturating_add`: `1u128.saturating_add(1)`
error: manual saturating arithmetic
--> tests/ui/manual_saturating_arithmetic.rs:18:13
--> tests/ui/manual_saturating_arithmetic.rs:16:13
|
LL | let _ = 1u32.checked_mul(1).unwrap_or(u32::MAX);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_mul`: `1u32.saturating_mul(1)`
error: manual saturating arithmetic
--> tests/ui/manual_saturating_arithmetic.rs:21:13
--> tests/ui/manual_saturating_arithmetic.rs:19:13
|
LL | let _ = 1u32.checked_sub(1).unwrap_or(u32::min_value());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_sub`: `1u32.saturating_sub(1)`
error: manual saturating arithmetic
--> tests/ui/manual_saturating_arithmetic.rs:23:13
--> tests/ui/manual_saturating_arithmetic.rs:21:13
|
LL | let _ = 1u32.checked_sub(1).unwrap_or(u32::MIN);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_sub`: `1u32.saturating_sub(1)`
error: manual saturating arithmetic
--> tests/ui/manual_saturating_arithmetic.rs:25:13
--> tests/ui/manual_saturating_arithmetic.rs:23:13
|
LL | let _ = 1u8.checked_sub(1).unwrap_or(0);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_sub`: `1u8.saturating_sub(1)`
error: manual saturating arithmetic
--> tests/ui/manual_saturating_arithmetic.rs:30:13
--> tests/ui/manual_saturating_arithmetic.rs:28:13
|
LL | let _ = 1i32.checked_add(1).unwrap_or(i32::max_value());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_add`: `1i32.saturating_add(1)`
error: manual saturating arithmetic
--> tests/ui/manual_saturating_arithmetic.rs:32:13
--> tests/ui/manual_saturating_arithmetic.rs:30:13
|
LL | let _ = 1i32.checked_add(1).unwrap_or(i32::MAX);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_add`: `1i32.saturating_add(1)`
error: manual saturating arithmetic
--> tests/ui/manual_saturating_arithmetic.rs:34:13
--> tests/ui/manual_saturating_arithmetic.rs:32:13
|
LL | let _ = 1i8.checked_add(1).unwrap_or(127);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_add`: `1i8.saturating_add(1)`
error: manual saturating arithmetic
--> tests/ui/manual_saturating_arithmetic.rs:36:13
--> tests/ui/manual_saturating_arithmetic.rs:34:13
|
LL | let _ = 1i128
| _____________^
@@ -82,25 +82,25 @@ LL | | .unwrap_or(170_141_183_460_469_231_731_687_303_715_884_105_727);
| |_______________________________________________________________________^ help: consider using `saturating_add`: `1i128.saturating_add(1)`
error: manual saturating arithmetic
--> tests/ui/manual_saturating_arithmetic.rs:40:13
--> tests/ui/manual_saturating_arithmetic.rs:38:13
|
LL | let _ = 1i32.checked_add(-1).unwrap_or(i32::min_value());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_add`: `1i32.saturating_add(-1)`
error: manual saturating arithmetic
--> tests/ui/manual_saturating_arithmetic.rs:42:13
--> tests/ui/manual_saturating_arithmetic.rs:40:13
|
LL | let _ = 1i32.checked_add(-1).unwrap_or(i32::MIN);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_add`: `1i32.saturating_add(-1)`
error: manual saturating arithmetic
--> tests/ui/manual_saturating_arithmetic.rs:44:13
--> tests/ui/manual_saturating_arithmetic.rs:42:13
|
LL | let _ = 1i8.checked_add(-1).unwrap_or(-128);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_add`: `1i8.saturating_add(-1)`
error: manual saturating arithmetic
--> tests/ui/manual_saturating_arithmetic.rs:46:13
--> tests/ui/manual_saturating_arithmetic.rs:44:13
|
LL | let _ = 1i128
| _____________^
@@ -110,25 +110,25 @@ LL | | .unwrap_or(-170_141_183_460_469_231_731_687_303_715_884_105_728);
| |________________________________________________________________________^ help: consider using `saturating_add`: `1i128.saturating_add(-1)`
error: manual saturating arithmetic
--> tests/ui/manual_saturating_arithmetic.rs:54:13
--> tests/ui/manual_saturating_arithmetic.rs:52:13
|
LL | let _ = 1i32.checked_sub(1).unwrap_or(i32::min_value());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_sub`: `1i32.saturating_sub(1)`
error: manual saturating arithmetic
--> tests/ui/manual_saturating_arithmetic.rs:56:13
--> tests/ui/manual_saturating_arithmetic.rs:54:13
|
LL | let _ = 1i32.checked_sub(1).unwrap_or(i32::MIN);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_sub`: `1i32.saturating_sub(1)`
error: manual saturating arithmetic
--> tests/ui/manual_saturating_arithmetic.rs:58:13
--> tests/ui/manual_saturating_arithmetic.rs:56:13
|
LL | let _ = 1i8.checked_sub(1).unwrap_or(-128);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_sub`: `1i8.saturating_sub(1)`
error: manual saturating arithmetic
--> tests/ui/manual_saturating_arithmetic.rs:60:13
--> tests/ui/manual_saturating_arithmetic.rs:58:13
|
LL | let _ = 1i128
| _____________^
@@ -138,25 +138,25 @@ LL | | .unwrap_or(-170_141_183_460_469_231_731_687_303_715_884_105_728);
| |________________________________________________________________________^ help: consider using `saturating_sub`: `1i128.saturating_sub(1)`
error: manual saturating arithmetic
--> tests/ui/manual_saturating_arithmetic.rs:64:13
--> tests/ui/manual_saturating_arithmetic.rs:62:13
|
LL | let _ = 1i32.checked_sub(-1).unwrap_or(i32::max_value());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_sub`: `1i32.saturating_sub(-1)`
error: manual saturating arithmetic
--> tests/ui/manual_saturating_arithmetic.rs:66:13
--> tests/ui/manual_saturating_arithmetic.rs:64:13
|
LL | let _ = 1i32.checked_sub(-1).unwrap_or(i32::MAX);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_sub`: `1i32.saturating_sub(-1)`
error: manual saturating arithmetic
--> tests/ui/manual_saturating_arithmetic.rs:68:13
--> tests/ui/manual_saturating_arithmetic.rs:66:13
|
LL | let _ = 1i8.checked_sub(-1).unwrap_or(127);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_sub`: `1i8.saturating_sub(-1)`
error: manual saturating arithmetic
--> tests/ui/manual_saturating_arithmetic.rs:70:13
--> tests/ui/manual_saturating_arithmetic.rs:68:13
|
LL | let _ = 1i128
| _____________^