mirror of
https://github.com/rust-lang/rust.git
synced 2025-01-19 11:12:43 +00:00
Auto merge of #9566 - smoelius:diagnostic-item-path, r=dswij
Expand internal lint `unnecessary_def_path` This PR does essentially two things: * Separates the internal lints into modules by pass. (`internal_lints.rs` was over 1400 lines, which is a little unruly IMHO.) * ~Adds a new~ Expands the `unnecessary_def_path` internal lint to flag hardcoded paths to diagnostic and language items. My understanding is that the latter is currently done by reviewers. Automating this process should make things easier for both reviewers and contributors. I could make the first bullet a separate PR, or remove it entirely, if desired. changelog: Add internal lint `diagnostic_item_path`
This commit is contained in:
commit
332b5b3d8b
@ -1,7 +1,7 @@
|
||||
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then};
|
||||
use clippy_utils::eq_expr_value;
|
||||
use clippy_utils::source::snippet_opt;
|
||||
use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
|
||||
use clippy_utils::{eq_expr_value, get_trait_def_id, paths};
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::ast::LitKind;
|
||||
use rustc_errors::Applicability;
|
||||
@ -483,7 +483,9 @@ impl<'a, 'tcx> Visitor<'tcx> for NonminimalBoolVisitor<'a, 'tcx> {
|
||||
|
||||
fn implements_ord<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) -> bool {
|
||||
let ty = cx.typeck_results().expr_ty(expr);
|
||||
get_trait_def_id(cx, &paths::ORD).map_or(false, |id| implements_trait(cx, ty, id, &[]))
|
||||
cx.tcx
|
||||
.get_diagnostic_item(sym::Ord)
|
||||
.map_or(false, |id| implements_trait(cx, ty, id, &[]))
|
||||
}
|
||||
|
||||
struct NotSimplificationVisitor<'a, 'tcx> {
|
||||
|
@ -1,9 +1,10 @@
|
||||
use clippy_utils::diagnostics::span_lint_and_help;
|
||||
use clippy_utils::ty::implements_trait;
|
||||
use clippy_utils::{get_trait_def_id, if_sequence, in_constant, is_else_clause, paths, SpanlessEq};
|
||||
use clippy_utils::{if_sequence, in_constant, is_else_clause, SpanlessEq};
|
||||
use rustc_hir::{BinOpKind, Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::sym;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
@ -106,7 +107,10 @@ impl<'tcx> LateLintPass<'tcx> for ComparisonChain {
|
||||
|
||||
// Check that the type being compared implements `core::cmp::Ord`
|
||||
let ty = cx.typeck_results().expr_ty(lhs1);
|
||||
let is_ord = get_trait_def_id(cx, &paths::ORD).map_or(false, |id| implements_trait(cx, ty, id, &[]));
|
||||
let is_ord = cx
|
||||
.tcx
|
||||
.get_diagnostic_item(sym::Ord)
|
||||
.map_or(false, |id| implements_trait(cx, ty, id, &[]));
|
||||
|
||||
if !is_ord {
|
||||
return;
|
||||
|
@ -7,14 +7,14 @@ use rustc_middle::{
|
||||
lint::in_external_macro,
|
||||
ty::{self, Ty},
|
||||
};
|
||||
use rustc_span::{sym, Span};
|
||||
use rustc_span::{sym, Span, Symbol};
|
||||
|
||||
use clippy_utils::attrs::is_proc_macro;
|
||||
use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then};
|
||||
use clippy_utils::source::snippet_opt;
|
||||
use clippy_utils::ty::is_must_use_ty;
|
||||
use clippy_utils::visitors::for_each_expr;
|
||||
use clippy_utils::{match_def_path, return_ty, trait_ref_of_method};
|
||||
use clippy_utils::{return_ty, trait_ref_of_method};
|
||||
|
||||
use core::ops::ControlFlow;
|
||||
|
||||
@ -181,7 +181,7 @@ fn is_mutable_pat(cx: &LateContext<'_>, pat: &hir::Pat<'_>, tys: &mut DefIdSet)
|
||||
}
|
||||
}
|
||||
|
||||
static KNOWN_WRAPPER_TYS: &[&[&str]] = &[&["alloc", "rc", "Rc"], &["std", "sync", "Arc"]];
|
||||
static KNOWN_WRAPPER_TYS: &[Symbol] = &[sym::Rc, sym::Arc];
|
||||
|
||||
fn is_mutable_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, span: Span, tys: &mut DefIdSet) -> bool {
|
||||
match *ty.kind() {
|
||||
@ -189,7 +189,9 @@ fn is_mutable_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, span: Span, tys: &m
|
||||
ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Str => false,
|
||||
ty::Adt(adt, substs) => {
|
||||
tys.insert(adt.did()) && !ty.is_freeze(cx.tcx.at(span), cx.param_env)
|
||||
|| KNOWN_WRAPPER_TYS.iter().any(|path| match_def_path(cx, adt.did(), path))
|
||||
|| KNOWN_WRAPPER_TYS
|
||||
.iter()
|
||||
.any(|&sym| cx.tcx.is_diagnostic_item(sym, adt.did()))
|
||||
&& substs.types().any(|ty| is_mutable_ty(cx, ty, span, tys))
|
||||
},
|
||||
ty::Tuple(substs) => substs.iter().any(|ty| is_mutable_ty(cx, ty, span, tys)),
|
||||
|
@ -1,5 +1,5 @@
|
||||
use clippy_utils::diagnostics::span_lint_and_help;
|
||||
use clippy_utils::ty::{is_must_use_ty, match_type};
|
||||
use clippy_utils::ty::{is_must_use_ty, is_type_diagnostic_item, match_type};
|
||||
use clippy_utils::{is_must_use_func_call, paths};
|
||||
use if_chain::if_chain;
|
||||
use rustc_hir::{Local, PatKind};
|
||||
@ -7,6 +7,7 @@ use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_middle::ty::subst::GenericArgKind;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::{sym, Symbol};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
@ -99,10 +100,9 @@ declare_clippy_lint! {
|
||||
|
||||
declare_lint_pass!(LetUnderscore => [LET_UNDERSCORE_MUST_USE, LET_UNDERSCORE_LOCK, LET_UNDERSCORE_DROP]);
|
||||
|
||||
const SYNC_GUARD_PATHS: [&[&str]; 6] = [
|
||||
&paths::MUTEX_GUARD,
|
||||
&paths::RWLOCK_READ_GUARD,
|
||||
&paths::RWLOCK_WRITE_GUARD,
|
||||
const SYNC_GUARD_SYMS: [Symbol; 3] = [sym::MutexGuard, sym::RwLockReadGuard, sym::RwLockWriteGuard];
|
||||
|
||||
const SYNC_GUARD_PATHS: [&[&str]; 3] = [
|
||||
&paths::PARKING_LOT_MUTEX_GUARD,
|
||||
&paths::PARKING_LOT_RWLOCK_READ_GUARD,
|
||||
&paths::PARKING_LOT_RWLOCK_WRITE_GUARD,
|
||||
@ -121,7 +121,10 @@ impl<'tcx> LateLintPass<'tcx> for LetUnderscore {
|
||||
let init_ty = cx.typeck_results().expr_ty(init);
|
||||
let contains_sync_guard = init_ty.walk().any(|inner| match inner.unpack() {
|
||||
GenericArgKind::Type(inner_ty) => {
|
||||
SYNC_GUARD_PATHS.iter().any(|path| match_type(cx, inner_ty, path))
|
||||
SYNC_GUARD_SYMS
|
||||
.iter()
|
||||
.any(|&sym| is_type_diagnostic_item(cx, inner_ty, sym))
|
||||
|| SYNC_GUARD_PATHS.iter().any(|path| match_type(cx, inner_ty, path))
|
||||
},
|
||||
|
||||
GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false,
|
||||
@ -134,7 +137,7 @@ impl<'tcx> LateLintPass<'tcx> for LetUnderscore {
|
||||
"non-binding let on a synchronization lock",
|
||||
None,
|
||||
"consider using an underscore-prefixed named \
|
||||
binding or dropping explicitly with `std::mem::drop`"
|
||||
binding or dropping explicitly with `std::mem::drop`",
|
||||
);
|
||||
} else if init_ty.needs_drop(cx.tcx, cx.param_env) {
|
||||
span_lint_and_help(
|
||||
@ -144,7 +147,7 @@ impl<'tcx> LateLintPass<'tcx> for LetUnderscore {
|
||||
"non-binding `let` on a type that implements `Drop`",
|
||||
None,
|
||||
"consider using an underscore-prefixed named \
|
||||
binding or dropping explicitly with `std::mem::drop`"
|
||||
binding or dropping explicitly with `std::mem::drop`",
|
||||
);
|
||||
} else if is_must_use_ty(cx, cx.typeck_results().expr_ty(init)) {
|
||||
span_lint_and_help(
|
||||
@ -153,7 +156,7 @@ impl<'tcx> LateLintPass<'tcx> for LetUnderscore {
|
||||
local.span,
|
||||
"non-binding let on an expression with `#[must_use]` type",
|
||||
None,
|
||||
"consider explicitly using expression value"
|
||||
"consider explicitly using expression value",
|
||||
);
|
||||
} else if is_must_use_func_call(cx, init) {
|
||||
span_lint_and_help(
|
||||
@ -162,7 +165,7 @@ impl<'tcx> LateLintPass<'tcx> for LetUnderscore {
|
||||
local.span,
|
||||
"non-binding let on a result of a `#[must_use]` function",
|
||||
None,
|
||||
"consider explicitly using function result"
|
||||
"consider explicitly using function result",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -3,20 +3,20 @@
|
||||
// Manual edits will be overwritten.
|
||||
|
||||
store.register_group(true, "clippy::internal", Some("clippy_internal"), vec![
|
||||
LintId::of(utils::internal_lints::CLIPPY_LINTS_INTERNAL),
|
||||
LintId::of(utils::internal_lints::COLLAPSIBLE_SPAN_LINT_CALLS),
|
||||
LintId::of(utils::internal_lints::COMPILER_LINT_FUNCTIONS),
|
||||
LintId::of(utils::internal_lints::DEFAULT_DEPRECATION_REASON),
|
||||
LintId::of(utils::internal_lints::DEFAULT_LINT),
|
||||
LintId::of(utils::internal_lints::IF_CHAIN_STYLE),
|
||||
LintId::of(utils::internal_lints::INTERNING_DEFINED_SYMBOL),
|
||||
LintId::of(utils::internal_lints::INVALID_CLIPPY_VERSION_ATTRIBUTE),
|
||||
LintId::of(utils::internal_lints::INVALID_PATHS),
|
||||
LintId::of(utils::internal_lints::LINT_WITHOUT_LINT_PASS),
|
||||
LintId::of(utils::internal_lints::MISSING_CLIPPY_VERSION_ATTRIBUTE),
|
||||
LintId::of(utils::internal_lints::MISSING_MSRV_ATTR_IMPL),
|
||||
LintId::of(utils::internal_lints::OUTER_EXPN_EXPN_DATA),
|
||||
LintId::of(utils::internal_lints::PRODUCE_ICE),
|
||||
LintId::of(utils::internal_lints::UNNECESSARY_DEF_PATH),
|
||||
LintId::of(utils::internal_lints::UNNECESSARY_SYMBOL_STR),
|
||||
LintId::of(utils::internal_lints::clippy_lints_internal::CLIPPY_LINTS_INTERNAL),
|
||||
LintId::of(utils::internal_lints::collapsible_calls::COLLAPSIBLE_SPAN_LINT_CALLS),
|
||||
LintId::of(utils::internal_lints::compiler_lint_functions::COMPILER_LINT_FUNCTIONS),
|
||||
LintId::of(utils::internal_lints::if_chain_style::IF_CHAIN_STYLE),
|
||||
LintId::of(utils::internal_lints::interning_defined_symbol::INTERNING_DEFINED_SYMBOL),
|
||||
LintId::of(utils::internal_lints::interning_defined_symbol::UNNECESSARY_SYMBOL_STR),
|
||||
LintId::of(utils::internal_lints::invalid_paths::INVALID_PATHS),
|
||||
LintId::of(utils::internal_lints::lint_without_lint_pass::DEFAULT_DEPRECATION_REASON),
|
||||
LintId::of(utils::internal_lints::lint_without_lint_pass::DEFAULT_LINT),
|
||||
LintId::of(utils::internal_lints::lint_without_lint_pass::INVALID_CLIPPY_VERSION_ATTRIBUTE),
|
||||
LintId::of(utils::internal_lints::lint_without_lint_pass::LINT_WITHOUT_LINT_PASS),
|
||||
LintId::of(utils::internal_lints::lint_without_lint_pass::MISSING_CLIPPY_VERSION_ATTRIBUTE),
|
||||
LintId::of(utils::internal_lints::msrv_attr_impl::MISSING_MSRV_ATTR_IMPL),
|
||||
LintId::of(utils::internal_lints::outer_expn_data_pass::OUTER_EXPN_EXPN_DATA),
|
||||
LintId::of(utils::internal_lints::produce_ice::PRODUCE_ICE),
|
||||
LintId::of(utils::internal_lints::unnecessary_def_path::UNNECESSARY_DEF_PATH),
|
||||
])
|
||||
|
@ -4,37 +4,37 @@
|
||||
|
||||
store.register_lints(&[
|
||||
#[cfg(feature = "internal")]
|
||||
utils::internal_lints::CLIPPY_LINTS_INTERNAL,
|
||||
utils::internal_lints::clippy_lints_internal::CLIPPY_LINTS_INTERNAL,
|
||||
#[cfg(feature = "internal")]
|
||||
utils::internal_lints::COLLAPSIBLE_SPAN_LINT_CALLS,
|
||||
utils::internal_lints::collapsible_calls::COLLAPSIBLE_SPAN_LINT_CALLS,
|
||||
#[cfg(feature = "internal")]
|
||||
utils::internal_lints::COMPILER_LINT_FUNCTIONS,
|
||||
utils::internal_lints::compiler_lint_functions::COMPILER_LINT_FUNCTIONS,
|
||||
#[cfg(feature = "internal")]
|
||||
utils::internal_lints::DEFAULT_DEPRECATION_REASON,
|
||||
utils::internal_lints::if_chain_style::IF_CHAIN_STYLE,
|
||||
#[cfg(feature = "internal")]
|
||||
utils::internal_lints::DEFAULT_LINT,
|
||||
utils::internal_lints::interning_defined_symbol::INTERNING_DEFINED_SYMBOL,
|
||||
#[cfg(feature = "internal")]
|
||||
utils::internal_lints::IF_CHAIN_STYLE,
|
||||
utils::internal_lints::interning_defined_symbol::UNNECESSARY_SYMBOL_STR,
|
||||
#[cfg(feature = "internal")]
|
||||
utils::internal_lints::INTERNING_DEFINED_SYMBOL,
|
||||
utils::internal_lints::invalid_paths::INVALID_PATHS,
|
||||
#[cfg(feature = "internal")]
|
||||
utils::internal_lints::INVALID_CLIPPY_VERSION_ATTRIBUTE,
|
||||
utils::internal_lints::lint_without_lint_pass::DEFAULT_DEPRECATION_REASON,
|
||||
#[cfg(feature = "internal")]
|
||||
utils::internal_lints::INVALID_PATHS,
|
||||
utils::internal_lints::lint_without_lint_pass::DEFAULT_LINT,
|
||||
#[cfg(feature = "internal")]
|
||||
utils::internal_lints::LINT_WITHOUT_LINT_PASS,
|
||||
utils::internal_lints::lint_without_lint_pass::INVALID_CLIPPY_VERSION_ATTRIBUTE,
|
||||
#[cfg(feature = "internal")]
|
||||
utils::internal_lints::MISSING_CLIPPY_VERSION_ATTRIBUTE,
|
||||
utils::internal_lints::lint_without_lint_pass::LINT_WITHOUT_LINT_PASS,
|
||||
#[cfg(feature = "internal")]
|
||||
utils::internal_lints::MISSING_MSRV_ATTR_IMPL,
|
||||
utils::internal_lints::lint_without_lint_pass::MISSING_CLIPPY_VERSION_ATTRIBUTE,
|
||||
#[cfg(feature = "internal")]
|
||||
utils::internal_lints::OUTER_EXPN_EXPN_DATA,
|
||||
utils::internal_lints::msrv_attr_impl::MISSING_MSRV_ATTR_IMPL,
|
||||
#[cfg(feature = "internal")]
|
||||
utils::internal_lints::PRODUCE_ICE,
|
||||
utils::internal_lints::outer_expn_data_pass::OUTER_EXPN_EXPN_DATA,
|
||||
#[cfg(feature = "internal")]
|
||||
utils::internal_lints::UNNECESSARY_DEF_PATH,
|
||||
utils::internal_lints::produce_ice::PRODUCE_ICE,
|
||||
#[cfg(feature = "internal")]
|
||||
utils::internal_lints::UNNECESSARY_SYMBOL_STR,
|
||||
utils::internal_lints::unnecessary_def_path::UNNECESSARY_DEF_PATH,
|
||||
almost_complete_letter_range::ALMOST_COMPLETE_LETTER_RANGE,
|
||||
approx_const::APPROX_CONSTANT,
|
||||
as_conversions::AS_CONVERSIONS,
|
||||
|
@ -528,17 +528,23 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
// all the internal lints
|
||||
#[cfg(feature = "internal")]
|
||||
{
|
||||
store.register_early_pass(|| Box::new(utils::internal_lints::ClippyLintsInternal));
|
||||
store.register_early_pass(|| Box::new(utils::internal_lints::ProduceIce));
|
||||
store.register_late_pass(|_| Box::new(utils::internal_lints::CollapsibleCalls));
|
||||
store.register_late_pass(|_| Box::new(utils::internal_lints::CompilerLintFunctions::new()));
|
||||
store.register_late_pass(|_| Box::new(utils::internal_lints::IfChainStyle));
|
||||
store.register_late_pass(|_| Box::new(utils::internal_lints::InvalidPaths));
|
||||
store.register_late_pass(|_| Box::<utils::internal_lints::InterningDefinedSymbol>::default());
|
||||
store.register_late_pass(|_| Box::<utils::internal_lints::LintWithoutLintPass>::default());
|
||||
store.register_late_pass(|_| Box::new(utils::internal_lints::UnnecessaryDefPath));
|
||||
store.register_late_pass(|_| Box::new(utils::internal_lints::OuterExpnDataPass));
|
||||
store.register_late_pass(|_| Box::new(utils::internal_lints::MsrvAttrImpl));
|
||||
store.register_early_pass(|| Box::new(utils::internal_lints::clippy_lints_internal::ClippyLintsInternal));
|
||||
store.register_early_pass(|| Box::new(utils::internal_lints::produce_ice::ProduceIce));
|
||||
store.register_late_pass(|_| Box::new(utils::internal_lints::collapsible_calls::CollapsibleCalls));
|
||||
store.register_late_pass(|_| {
|
||||
Box::new(utils::internal_lints::compiler_lint_functions::CompilerLintFunctions::new())
|
||||
});
|
||||
store.register_late_pass(|_| Box::new(utils::internal_lints::if_chain_style::IfChainStyle));
|
||||
store.register_late_pass(|_| Box::new(utils::internal_lints::invalid_paths::InvalidPaths));
|
||||
store.register_late_pass(|_| {
|
||||
Box::<utils::internal_lints::interning_defined_symbol::InterningDefinedSymbol>::default()
|
||||
});
|
||||
store.register_late_pass(|_| {
|
||||
Box::<utils::internal_lints::lint_without_lint_pass::LintWithoutLintPass>::default()
|
||||
});
|
||||
store.register_late_pass(|_| Box::<utils::internal_lints::unnecessary_def_path::UnnecessaryDefPath>::default());
|
||||
store.register_late_pass(|_| Box::new(utils::internal_lints::outer_expn_data_pass::OuterExpnDataPass));
|
||||
store.register_late_pass(|_| Box::new(utils::internal_lints::msrv_attr_impl::MsrvAttrImpl));
|
||||
}
|
||||
|
||||
let arithmetic_side_effects_allowed = conf.arithmetic_side_effects_allowed.clone();
|
||||
|
@ -3,7 +3,7 @@ use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then};
|
||||
use clippy_utils::source::snippet;
|
||||
use clippy_utils::ty::has_iter_method;
|
||||
use clippy_utils::visitors::is_local_used;
|
||||
use clippy_utils::{contains_name, higher, is_integer_const, match_trait_method, paths, sugg, SpanlessEq};
|
||||
use clippy_utils::{contains_name, higher, is_integer_const, sugg, SpanlessEq};
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::ast;
|
||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
||||
@ -263,7 +263,8 @@ impl<'a, 'tcx> VarVisitor<'a, 'tcx> {
|
||||
match res {
|
||||
Res::Local(hir_id) => {
|
||||
let parent_def_id = self.cx.tcx.hir().get_parent_item(expr.hir_id);
|
||||
let extent = self.cx
|
||||
let extent = self
|
||||
.cx
|
||||
.tcx
|
||||
.region_scope_tree(parent_def_id)
|
||||
.var_scope(hir_id.local_id)
|
||||
@ -274,11 +275,12 @@ impl<'a, 'tcx> VarVisitor<'a, 'tcx> {
|
||||
(Some(extent), self.cx.typeck_results().node_type(seqexpr.hir_id)),
|
||||
);
|
||||
} else {
|
||||
self.indexed_indirectly.insert(seqvar.segments[0].ident.name, Some(extent));
|
||||
self.indexed_indirectly
|
||||
.insert(seqvar.segments[0].ident.name, Some(extent));
|
||||
}
|
||||
return false; // no need to walk further *on the variable*
|
||||
}
|
||||
Res::Def(DefKind::Static (_)| DefKind::Const, ..) => {
|
||||
return false; // no need to walk further *on the variable*
|
||||
},
|
||||
Res::Def(DefKind::Static(_) | DefKind::Const, ..) => {
|
||||
if index_used_directly {
|
||||
self.indexed_directly.insert(
|
||||
seqvar.segments[0].ident.name,
|
||||
@ -287,8 +289,8 @@ impl<'a, 'tcx> VarVisitor<'a, 'tcx> {
|
||||
} else {
|
||||
self.indexed_indirectly.insert(seqvar.segments[0].ident.name, None);
|
||||
}
|
||||
return false; // no need to walk further *on the variable*
|
||||
}
|
||||
return false; // no need to walk further *on the variable*
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
@ -302,17 +304,26 @@ impl<'a, 'tcx> Visitor<'tcx> for VarVisitor<'a, 'tcx> {
|
||||
if_chain! {
|
||||
// a range index op
|
||||
if let ExprKind::MethodCall(meth, args_0, [args_1, ..], _) = &expr.kind;
|
||||
if (meth.ident.name == sym::index && match_trait_method(self.cx, expr, &paths::INDEX))
|
||||
|| (meth.ident.name == sym::index_mut && match_trait_method(self.cx, expr, &paths::INDEX_MUT));
|
||||
if let Some(trait_id) = self
|
||||
.cx
|
||||
.typeck_results()
|
||||
.type_dependent_def_id(expr.hir_id)
|
||||
.and_then(|def_id| self.cx.tcx.trait_of_item(def_id));
|
||||
if (meth.ident.name == sym::index && self.cx.tcx.lang_items().index_trait() == Some(trait_id))
|
||||
|| (meth.ident.name == sym::index_mut && self.cx.tcx.lang_items().index_mut_trait() == Some(trait_id));
|
||||
if !self.check(args_1, args_0, expr);
|
||||
then { return }
|
||||
then {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if_chain! {
|
||||
// an index op
|
||||
if let ExprKind::Index(seqexpr, idx) = expr.kind;
|
||||
if !self.check(idx, seqexpr, expr);
|
||||
then { return }
|
||||
then {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if_chain! {
|
||||
|
@ -1,6 +1,5 @@
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::match_function_call;
|
||||
use clippy_utils::paths::FUTURE_FROM_GENERATOR;
|
||||
use clippy_utils::match_function_call_with_def_id;
|
||||
use clippy_utils::source::{position_before_rarrow, snippet_block, snippet_opt};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
@ -140,9 +139,9 @@ fn future_output_ty<'tcx>(trait_ref: &'tcx TraitRef<'tcx>) -> Option<&'tcx Ty<'t
|
||||
if args.bindings.len() == 1;
|
||||
let binding = &args.bindings[0];
|
||||
if binding.ident.name == sym::Output;
|
||||
if let TypeBindingKind::Equality{term: Term::Ty(output)} = binding.kind;
|
||||
if let TypeBindingKind::Equality { term: Term::Ty(output) } = binding.kind;
|
||||
then {
|
||||
return Some(output)
|
||||
return Some(output);
|
||||
}
|
||||
}
|
||||
|
||||
@ -175,9 +174,16 @@ fn captures_all_lifetimes(inputs: &[Ty<'_>], output_lifetimes: &[LifetimeName])
|
||||
fn desugared_async_block<'tcx>(cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) -> Option<&'tcx Body<'tcx>> {
|
||||
if_chain! {
|
||||
if let Some(block_expr) = block.expr;
|
||||
if let Some(args) = match_function_call(cx, block_expr, &FUTURE_FROM_GENERATOR);
|
||||
if let Some(args) = cx
|
||||
.tcx
|
||||
.lang_items()
|
||||
.from_generator_fn()
|
||||
.and_then(|def_id| match_function_call_with_def_id(cx, block_expr, def_id));
|
||||
if args.len() == 1;
|
||||
if let Expr{kind: ExprKind::Closure(&Closure { body, .. }), ..} = args[0];
|
||||
if let Expr {
|
||||
kind: ExprKind::Closure(&Closure { body, .. }),
|
||||
..
|
||||
} = args[0];
|
||||
let closure_body = cx.tcx.hir().body(body);
|
||||
if closure_body.generator_kind == Some(GeneratorKind::Async(AsyncGeneratorKind::Block));
|
||||
then {
|
||||
|
@ -12,9 +12,9 @@ use std::ops::Deref;
|
||||
|
||||
use clippy_utils::{
|
||||
diagnostics::{span_lint_and_then, span_lint_hir_and_then},
|
||||
eq_expr_value, get_trait_def_id,
|
||||
eq_expr_value,
|
||||
higher::If,
|
||||
is_diag_trait_item, is_trait_method, meets_msrv, msrvs, path_res, path_to_local_id, paths, peel_blocks,
|
||||
is_diag_trait_item, is_trait_method, meets_msrv, msrvs, path_res, path_to_local_id, peel_blocks,
|
||||
peel_blocks_with_stmt,
|
||||
sugg::Sugg,
|
||||
ty::implements_trait,
|
||||
@ -190,7 +190,11 @@ impl TypeClampability {
|
||||
fn is_clampable<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<TypeClampability> {
|
||||
if ty.is_floating_point() {
|
||||
Some(TypeClampability::Float)
|
||||
} else if get_trait_def_id(cx, &paths::ORD).map_or(false, |id| implements_trait(cx, ty, id, &[])) {
|
||||
} else if cx
|
||||
.tcx
|
||||
.get_diagnostic_item(sym::Ord)
|
||||
.map_or(false, |id| implements_trait(cx, ty, id, &[]))
|
||||
{
|
||||
Some(TypeClampability::Ord)
|
||||
} else {
|
||||
None
|
||||
|
@ -1,14 +1,13 @@
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::{expr_block, snippet};
|
||||
use clippy_utils::ty::{implements_trait, match_type, peel_mid_ty_refs};
|
||||
use clippy_utils::{
|
||||
is_lint_allowed, is_unit_expr, is_wild, paths, peel_blocks, peel_hir_pat_refs, peel_n_hir_expr_refs,
|
||||
};
|
||||
use clippy_utils::ty::{implements_trait, is_type_diagnostic_item, peel_mid_ty_refs};
|
||||
use clippy_utils::{is_lint_allowed, is_unit_expr, is_wild, peel_blocks, peel_hir_pat_refs, peel_n_hir_expr_refs};
|
||||
use core::cmp::max;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Arm, BindingAnnotation, Block, Expr, ExprKind, Pat, PatKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
use rustc_span::sym;
|
||||
|
||||
use super::{MATCH_BOOL, SINGLE_MATCH, SINGLE_MATCH_ELSE};
|
||||
|
||||
@ -156,10 +155,10 @@ fn pat_in_candidate_enum<'a>(cx: &LateContext<'a>, ty: Ty<'a>, pat: &Pat<'_>) ->
|
||||
/// Returns `true` if the given type is an enum we know won't be expanded in the future
|
||||
fn in_candidate_enum<'a>(cx: &LateContext<'a>, ty: Ty<'_>) -> bool {
|
||||
// list of candidate `Enum`s we know will never get any more members
|
||||
let candidates = [&paths::COW, &paths::OPTION, &paths::RESULT];
|
||||
let candidates = [sym::Cow, sym::Option, sym::Result];
|
||||
|
||||
for candidate_ty in candidates {
|
||||
if match_type(cx, ty, candidate_ty) {
|
||||
if is_type_diagnostic_item(cx, ty, candidate_ty) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -102,9 +102,7 @@ use bind_instead_of_map::BindInsteadOfMap;
|
||||
use clippy_utils::consts::{constant, Constant};
|
||||
use clippy_utils::diagnostics::{span_lint, span_lint_and_help};
|
||||
use clippy_utils::ty::{contains_adt_constructor, implements_trait, is_copy, is_type_diagnostic_item};
|
||||
use clippy_utils::{
|
||||
contains_return, get_trait_def_id, is_trait_method, iter_input_pats, meets_msrv, msrvs, paths, return_ty,
|
||||
};
|
||||
use clippy_utils::{contains_return, is_trait_method, iter_input_pats, meets_msrv, msrvs, return_ty};
|
||||
use if_chain::if_chain;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def::Res;
|
||||
@ -3372,7 +3370,9 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
|
||||
then {
|
||||
let first_arg_span = first_arg_ty.span;
|
||||
let first_arg_ty = hir_ty_to_ty(cx.tcx, first_arg_ty);
|
||||
let self_ty = TraitRef::identity(cx.tcx, item.def_id.to_def_id()).self_ty().skip_binder();
|
||||
let self_ty = TraitRef::identity(cx.tcx, item.def_id.to_def_id())
|
||||
.self_ty()
|
||||
.skip_binder();
|
||||
wrong_self_convention::check(
|
||||
cx,
|
||||
item.ident.name.as_str(),
|
||||
@ -3380,7 +3380,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
|
||||
first_arg_ty,
|
||||
first_arg_span,
|
||||
false,
|
||||
true
|
||||
true,
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -3389,7 +3389,9 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
|
||||
if item.ident.name == sym::new;
|
||||
if let TraitItemKind::Fn(_, _) = item.kind;
|
||||
let ret_ty = return_ty(cx, item.hir_id());
|
||||
let self_ty = TraitRef::identity(cx.tcx, item.def_id.to_def_id()).self_ty().skip_binder();
|
||||
let self_ty = TraitRef::identity(cx.tcx, item.def_id.to_def_id())
|
||||
.self_ty()
|
||||
.skip_binder();
|
||||
if !ret_ty.contains(self_ty);
|
||||
|
||||
then {
|
||||
@ -3846,12 +3848,12 @@ impl SelfKind {
|
||||
return m == mutability && t == parent_ty;
|
||||
}
|
||||
|
||||
let trait_path = match mutability {
|
||||
hir::Mutability::Not => &paths::ASREF_TRAIT,
|
||||
hir::Mutability::Mut => &paths::ASMUT_TRAIT,
|
||||
let trait_sym = match mutability {
|
||||
hir::Mutability::Not => sym::AsRef,
|
||||
hir::Mutability::Mut => sym::AsMut,
|
||||
};
|
||||
|
||||
let Some(trait_def_id) = get_trait_def_id(cx, trait_path) else {
|
||||
let Some(trait_def_id) = cx.tcx.get_diagnostic_item(trait_sym) else {
|
||||
return false
|
||||
};
|
||||
implements_trait(cx, ty, trait_def_id, &[parent_ty.into()])
|
||||
|
@ -32,8 +32,7 @@ pub(super) fn check<'tcx>(
|
||||
return;
|
||||
}
|
||||
|
||||
let deref_aliases: [&[&str]; 9] = [
|
||||
&paths::DEREF_TRAIT_METHOD,
|
||||
let deref_aliases: [&[&str]; 8] = [
|
||||
&paths::DEREF_MUT_TRAIT_METHOD,
|
||||
&paths::CSTRING_AS_C_STR,
|
||||
&paths::OS_STRING_AS_OS_STR,
|
||||
@ -45,12 +44,14 @@ pub(super) fn check<'tcx>(
|
||||
];
|
||||
|
||||
let is_deref = match map_arg.kind {
|
||||
hir::ExprKind::Path(ref expr_qpath) => cx
|
||||
.qpath_res(expr_qpath, map_arg.hir_id)
|
||||
.opt_def_id()
|
||||
.map_or(false, |fun_def_id| {
|
||||
deref_aliases.iter().any(|path| match_def_path(cx, fun_def_id, path))
|
||||
}),
|
||||
hir::ExprKind::Path(ref expr_qpath) => {
|
||||
cx.qpath_res(expr_qpath, map_arg.hir_id)
|
||||
.opt_def_id()
|
||||
.map_or(false, |fun_def_id| {
|
||||
cx.tcx.is_diagnostic_item(sym::deref_method, fun_def_id)
|
||||
|| deref_aliases.iter().any(|path| match_def_path(cx, fun_def_id, path))
|
||||
})
|
||||
},
|
||||
hir::ExprKind::Closure(&hir::Closure { body, .. }) => {
|
||||
let closure_body = cx.tcx.hir().body(body);
|
||||
let closure_expr = peel_blocks(closure_body.value);
|
||||
@ -68,7 +69,8 @@ pub(super) fn check<'tcx>(
|
||||
if let [ty::adjustment::Adjust::Deref(None), ty::adjustment::Adjust::Borrow(_)] = *adj;
|
||||
then {
|
||||
let method_did = cx.typeck_results().type_dependent_def_id(closure_expr.hir_id).unwrap();
|
||||
deref_aliases.iter().any(|path| match_def_path(cx, method_did, path))
|
||||
cx.tcx.is_diagnostic_item(sym::deref_method, method_did)
|
||||
|| deref_aliases.iter().any(|path| match_def_path(cx, method_did, path))
|
||||
} else {
|
||||
false
|
||||
}
|
||||
|
@ -1,14 +1,14 @@
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::eager_or_lazy::switch_to_lazy_eval;
|
||||
use clippy_utils::source::{snippet, snippet_with_macro_callsite};
|
||||
use clippy_utils::ty::{implements_trait, match_type};
|
||||
use clippy_utils::{contains_return, is_trait_item, last_path_segment, paths};
|
||||
use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
|
||||
use clippy_utils::{contains_return, is_trait_item, last_path_segment};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::source_map::Span;
|
||||
use rustc_span::symbol::{kw, sym};
|
||||
use rustc_span::symbol::{kw, sym, Symbol};
|
||||
use std::borrow::Cow;
|
||||
|
||||
use super::OR_FUN_CALL;
|
||||
@ -88,11 +88,11 @@ pub(super) fn check<'tcx>(
|
||||
fun_span: Option<Span>,
|
||||
) {
|
||||
// (path, fn_has_argument, methods, suffix)
|
||||
const KNOW_TYPES: [(&[&str], bool, &[&str], &str); 4] = [
|
||||
(&paths::BTREEMAP_ENTRY, false, &["or_insert"], "with"),
|
||||
(&paths::HASHMAP_ENTRY, false, &["or_insert"], "with"),
|
||||
(&paths::OPTION, false, &["map_or", "ok_or", "or", "unwrap_or"], "else"),
|
||||
(&paths::RESULT, true, &["or", "unwrap_or"], "else"),
|
||||
const KNOW_TYPES: [(Symbol, bool, &[&str], &str); 4] = [
|
||||
(sym::BTreeEntry, false, &["or_insert"], "with"),
|
||||
(sym::HashMapEntry, false, &["or_insert"], "with"),
|
||||
(sym::Option, false, &["map_or", "ok_or", "or", "unwrap_or"], "else"),
|
||||
(sym::Result, true, &["or", "unwrap_or"], "else"),
|
||||
];
|
||||
|
||||
if_chain! {
|
||||
@ -104,7 +104,7 @@ pub(super) fn check<'tcx>(
|
||||
let self_ty = cx.typeck_results().expr_ty(self_expr);
|
||||
|
||||
if let Some(&(_, fn_has_arguments, poss, suffix)) =
|
||||
KNOW_TYPES.iter().find(|&&i| match_type(cx, self_ty, i.0));
|
||||
KNOW_TYPES.iter().find(|&&i| is_type_diagnostic_item(cx, self_ty, i.0));
|
||||
|
||||
if poss.contains(&name);
|
||||
|
||||
@ -121,10 +121,9 @@ pub(super) fn check<'tcx>(
|
||||
macro_expanded_snipped = snippet(cx, snippet_span, "..");
|
||||
match macro_expanded_snipped.strip_prefix("$crate::vec::") {
|
||||
Some(stripped) => Cow::from(stripped),
|
||||
None => macro_expanded_snipped
|
||||
None => macro_expanded_snipped,
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
not_macro_argument_snippet
|
||||
}
|
||||
};
|
||||
|
@ -1,11 +1,11 @@
|
||||
use clippy_utils::diagnostics::span_lint;
|
||||
use clippy_utils::ty::implements_trait;
|
||||
use clippy_utils::{self, get_trait_def_id, paths};
|
||||
use if_chain::if_chain;
|
||||
use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::sym;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
@ -47,18 +47,16 @@ declare_lint_pass!(NoNegCompOpForPartialOrd => [NEG_CMP_OP_ON_PARTIAL_ORD]);
|
||||
impl<'tcx> LateLintPass<'tcx> for NoNegCompOpForPartialOrd {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
if_chain! {
|
||||
|
||||
if !in_external_macro(cx.sess(), expr.span);
|
||||
if let ExprKind::Unary(UnOp::Not, inner) = expr.kind;
|
||||
if let ExprKind::Binary(ref op, left, _) = inner.kind;
|
||||
if let BinOpKind::Le | BinOpKind::Ge | BinOpKind::Lt | BinOpKind::Gt = op.node;
|
||||
|
||||
then {
|
||||
|
||||
let ty = cx.typeck_results().expr_ty(left);
|
||||
|
||||
let implements_ord = {
|
||||
if let Some(id) = get_trait_def_id(cx, &paths::ORD) {
|
||||
if let Some(id) = cx.tcx.get_diagnostic_item(sym::Ord) {
|
||||
implements_trait(cx, ty, id, &[])
|
||||
} else {
|
||||
return;
|
||||
@ -81,7 +79,7 @@ impl<'tcx> LateLintPass<'tcx> for NoNegCompOpForPartialOrd {
|
||||
"the use of negated comparison operators on partially ordered \
|
||||
types produces code that is hard to read and refactor, please \
|
||||
consider using the `partial_cmp` method instead, to make it \
|
||||
clear that the two values could be incomparable"
|
||||
clear that the two values could be incomparable",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::source::snippet;
|
||||
use clippy_utils::ty::{implements_trait, is_copy};
|
||||
use clippy_utils::{match_any_def_paths, path_def_id, paths};
|
||||
use clippy_utils::{match_def_path, path_def_id, paths};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp};
|
||||
use rustc_lint::LateContext;
|
||||
@ -49,13 +49,15 @@ fn check_op(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>, left: bool)
|
||||
(arg, arg.span)
|
||||
},
|
||||
ExprKind::Call(path, [arg])
|
||||
if path_def_id(cx, path)
|
||||
.and_then(|id| match_any_def_paths(cx, id, &[&paths::FROM_STR_METHOD, &paths::FROM_FROM]))
|
||||
.map_or(false, |idx| match idx {
|
||||
0 => true,
|
||||
1 => !is_copy(cx, typeck.expr_ty(expr)),
|
||||
_ => false,
|
||||
}) =>
|
||||
if path_def_id(cx, path).map_or(false, |id| {
|
||||
if match_def_path(cx, id, &paths::FROM_STR_METHOD) {
|
||||
true
|
||||
} else if cx.tcx.lang_items().from_fn() == Some(id) {
|
||||
!is_copy(cx, typeck.expr_ty(expr))
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}) =>
|
||||
{
|
||||
(arg, arg.span)
|
||||
},
|
||||
|
@ -1,5 +1,4 @@
|
||||
use clippy_utils::diagnostics::{span_lint, span_lint_and_help};
|
||||
use clippy_utils::{get_trait_def_id, paths};
|
||||
use if_chain::if_chain;
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::{Closure, Expr, ExprKind, StmtKind};
|
||||
@ -7,7 +6,7 @@ use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty;
|
||||
use rustc_middle::ty::{GenericPredicates, PredicateKind, ProjectionPredicate, TraitPredicate};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::{BytePos, Span};
|
||||
use rustc_span::{sym, BytePos, Span};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
@ -80,7 +79,7 @@ fn get_args_to_check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Ve
|
||||
let fn_sig = cx.tcx.fn_sig(def_id);
|
||||
let generics = cx.tcx.predicates_of(def_id);
|
||||
let fn_mut_preds = get_trait_predicates_for_trait_id(cx, generics, cx.tcx.lang_items().fn_mut_trait());
|
||||
let ord_preds = get_trait_predicates_for_trait_id(cx, generics, get_trait_def_id(cx, &paths::ORD));
|
||||
let ord_preds = get_trait_predicates_for_trait_id(cx, generics, cx.tcx.get_diagnostic_item(sym::Ord));
|
||||
let partial_ord_preds =
|
||||
get_trait_predicates_for_trait_id(cx, generics, cx.tcx.lang_items().partial_ord_trait());
|
||||
// Trying to call erase_late_bound_regions on fn_sig.inputs() gives the following error
|
||||
@ -99,11 +98,15 @@ fn get_args_to_check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Ve
|
||||
if trait_pred.self_ty() == inp;
|
||||
if let Some(return_ty_pred) = get_projection_pred(cx, generics, *trait_pred);
|
||||
then {
|
||||
if ord_preds.iter().any(|ord| Some(ord.self_ty()) == return_ty_pred.term.ty()) {
|
||||
if ord_preds
|
||||
.iter()
|
||||
.any(|ord| Some(ord.self_ty()) == return_ty_pred.term.ty())
|
||||
{
|
||||
args_to_check.push((i, "Ord".to_string()));
|
||||
} else if partial_ord_preds.iter().any(|pord| {
|
||||
pord.self_ty() == return_ty_pred.term.ty().unwrap()
|
||||
}) {
|
||||
} else if partial_ord_preds
|
||||
.iter()
|
||||
.any(|pord| pord.self_ty() == return_ty_pred.term.ty().unwrap())
|
||||
{
|
||||
args_to_check.push((i, "PartialOrd".to_string()));
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,49 @@
|
||||
use clippy_utils::diagnostics::span_lint;
|
||||
use rustc_ast::ast::{Crate, ItemKind, ModKind};
|
||||
use rustc_lint::{EarlyContext, EarlyLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for various things we like to keep tidy in clippy.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// We like to pretend we're an example of tidy code.
|
||||
///
|
||||
/// ### Example
|
||||
/// Wrong ordering of the util::paths constants.
|
||||
pub CLIPPY_LINTS_INTERNAL,
|
||||
internal,
|
||||
"various things that will negatively affect your clippy experience"
|
||||
}
|
||||
|
||||
declare_lint_pass!(ClippyLintsInternal => [CLIPPY_LINTS_INTERNAL]);
|
||||
|
||||
impl EarlyLintPass for ClippyLintsInternal {
|
||||
fn check_crate(&mut self, cx: &EarlyContext<'_>, krate: &Crate) {
|
||||
if let Some(utils) = krate.items.iter().find(|item| item.ident.name.as_str() == "utils") {
|
||||
if let ItemKind::Mod(_, ModKind::Loaded(ref items, ..)) = utils.kind {
|
||||
if let Some(paths) = items.iter().find(|item| item.ident.name.as_str() == "paths") {
|
||||
if let ItemKind::Mod(_, ModKind::Loaded(ref items, ..)) = paths.kind {
|
||||
let mut last_name: Option<&str> = None;
|
||||
for item in items {
|
||||
let name = item.ident.as_str();
|
||||
if let Some(last_name) = last_name {
|
||||
if *last_name > *name {
|
||||
span_lint(
|
||||
cx,
|
||||
CLIPPY_LINTS_INTERNAL,
|
||||
item.span,
|
||||
"this constant should be before the previous constant due to lexical \
|
||||
ordering",
|
||||
);
|
||||
}
|
||||
}
|
||||
last_name = Some(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
245
clippy_lints/src/utils/internal_lints/collapsible_calls.rs
Normal file
245
clippy_lints/src/utils/internal_lints/collapsible_calls.rs
Normal file
@ -0,0 +1,245 @@
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::snippet;
|
||||
use clippy_utils::{is_expr_path_def_path, is_lint_allowed, peel_blocks_with_stmt, SpanlessEq};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::{Closure, Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
|
||||
use std::borrow::{Borrow, Cow};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Lints `span_lint_and_then` function calls, where the
|
||||
/// closure argument has only one statement and that statement is a method
|
||||
/// call to `span_suggestion`, `span_help`, `span_note` (using the same
|
||||
/// span), `help` or `note`.
|
||||
///
|
||||
/// These usages of `span_lint_and_then` should be replaced with one of the
|
||||
/// wrapper functions `span_lint_and_sugg`, span_lint_and_help`, or
|
||||
/// `span_lint_and_note`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Using the wrapper `span_lint_and_*` functions, is more
|
||||
/// convenient, readable and less error prone.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust,ignore
|
||||
/// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| {
|
||||
/// diag.span_suggestion(
|
||||
/// expr.span,
|
||||
/// help_msg,
|
||||
/// sugg.to_string(),
|
||||
/// Applicability::MachineApplicable,
|
||||
/// );
|
||||
/// });
|
||||
/// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| {
|
||||
/// diag.span_help(expr.span, help_msg);
|
||||
/// });
|
||||
/// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| {
|
||||
/// diag.help(help_msg);
|
||||
/// });
|
||||
/// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| {
|
||||
/// diag.span_note(expr.span, note_msg);
|
||||
/// });
|
||||
/// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| {
|
||||
/// diag.note(note_msg);
|
||||
/// });
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust,ignore
|
||||
/// span_lint_and_sugg(
|
||||
/// cx,
|
||||
/// TEST_LINT,
|
||||
/// expr.span,
|
||||
/// lint_msg,
|
||||
/// help_msg,
|
||||
/// sugg.to_string(),
|
||||
/// Applicability::MachineApplicable,
|
||||
/// );
|
||||
/// span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), help_msg);
|
||||
/// span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, None, help_msg);
|
||||
/// span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), note_msg);
|
||||
/// span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, None, note_msg);
|
||||
/// ```
|
||||
pub COLLAPSIBLE_SPAN_LINT_CALLS,
|
||||
internal,
|
||||
"found collapsible `span_lint_and_then` calls"
|
||||
}
|
||||
|
||||
declare_lint_pass!(CollapsibleCalls => [COLLAPSIBLE_SPAN_LINT_CALLS]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for CollapsibleCalls {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
|
||||
if is_lint_allowed(cx, COLLAPSIBLE_SPAN_LINT_CALLS, expr.hir_id) {
|
||||
return;
|
||||
}
|
||||
|
||||
if_chain! {
|
||||
if let ExprKind::Call(func, and_then_args) = expr.kind;
|
||||
if is_expr_path_def_path(cx, func, &["clippy_utils", "diagnostics", "span_lint_and_then"]);
|
||||
if and_then_args.len() == 5;
|
||||
if let ExprKind::Closure(&Closure { body, .. }) = &and_then_args[4].kind;
|
||||
let body = cx.tcx.hir().body(body);
|
||||
let only_expr = peel_blocks_with_stmt(body.value);
|
||||
if let ExprKind::MethodCall(ps, recv, span_call_args, _) = &only_expr.kind;
|
||||
if let ExprKind::Path(..) = recv.kind;
|
||||
then {
|
||||
let and_then_snippets = get_and_then_snippets(cx, and_then_args);
|
||||
let mut sle = SpanlessEq::new(cx).deny_side_effects();
|
||||
match ps.ident.as_str() {
|
||||
"span_suggestion" if sle.eq_expr(&and_then_args[2], &span_call_args[0]) => {
|
||||
suggest_suggestion(
|
||||
cx,
|
||||
expr,
|
||||
&and_then_snippets,
|
||||
&span_suggestion_snippets(cx, span_call_args),
|
||||
);
|
||||
},
|
||||
"span_help" if sle.eq_expr(&and_then_args[2], &span_call_args[0]) => {
|
||||
let help_snippet = snippet(cx, span_call_args[1].span, r#""...""#);
|
||||
suggest_help(cx, expr, &and_then_snippets, help_snippet.borrow(), true);
|
||||
},
|
||||
"span_note" if sle.eq_expr(&and_then_args[2], &span_call_args[0]) => {
|
||||
let note_snippet = snippet(cx, span_call_args[1].span, r#""...""#);
|
||||
suggest_note(cx, expr, &and_then_snippets, note_snippet.borrow(), true);
|
||||
},
|
||||
"help" => {
|
||||
let help_snippet = snippet(cx, span_call_args[0].span, r#""...""#);
|
||||
suggest_help(cx, expr, &and_then_snippets, help_snippet.borrow(), false);
|
||||
},
|
||||
"note" => {
|
||||
let note_snippet = snippet(cx, span_call_args[0].span, r#""...""#);
|
||||
suggest_note(cx, expr, &and_then_snippets, note_snippet.borrow(), false);
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct AndThenSnippets<'a> {
|
||||
cx: Cow<'a, str>,
|
||||
lint: Cow<'a, str>,
|
||||
span: Cow<'a, str>,
|
||||
msg: Cow<'a, str>,
|
||||
}
|
||||
|
||||
fn get_and_then_snippets<'a, 'hir>(cx: &LateContext<'_>, and_then_snippets: &'hir [Expr<'hir>]) -> AndThenSnippets<'a> {
|
||||
let cx_snippet = snippet(cx, and_then_snippets[0].span, "cx");
|
||||
let lint_snippet = snippet(cx, and_then_snippets[1].span, "..");
|
||||
let span_snippet = snippet(cx, and_then_snippets[2].span, "span");
|
||||
let msg_snippet = snippet(cx, and_then_snippets[3].span, r#""...""#);
|
||||
|
||||
AndThenSnippets {
|
||||
cx: cx_snippet,
|
||||
lint: lint_snippet,
|
||||
span: span_snippet,
|
||||
msg: msg_snippet,
|
||||
}
|
||||
}
|
||||
|
||||
struct SpanSuggestionSnippets<'a> {
|
||||
help: Cow<'a, str>,
|
||||
sugg: Cow<'a, str>,
|
||||
applicability: Cow<'a, str>,
|
||||
}
|
||||
|
||||
fn span_suggestion_snippets<'a, 'hir>(
|
||||
cx: &LateContext<'_>,
|
||||
span_call_args: &'hir [Expr<'hir>],
|
||||
) -> SpanSuggestionSnippets<'a> {
|
||||
let help_snippet = snippet(cx, span_call_args[1].span, r#""...""#);
|
||||
let sugg_snippet = snippet(cx, span_call_args[2].span, "..");
|
||||
let applicability_snippet = snippet(cx, span_call_args[3].span, "Applicability::MachineApplicable");
|
||||
|
||||
SpanSuggestionSnippets {
|
||||
help: help_snippet,
|
||||
sugg: sugg_snippet,
|
||||
applicability: applicability_snippet,
|
||||
}
|
||||
}
|
||||
|
||||
fn suggest_suggestion(
|
||||
cx: &LateContext<'_>,
|
||||
expr: &Expr<'_>,
|
||||
and_then_snippets: &AndThenSnippets<'_>,
|
||||
span_suggestion_snippets: &SpanSuggestionSnippets<'_>,
|
||||
) {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
COLLAPSIBLE_SPAN_LINT_CALLS,
|
||||
expr.span,
|
||||
"this call is collapsible",
|
||||
"collapse into",
|
||||
format!(
|
||||
"span_lint_and_sugg({}, {}, {}, {}, {}, {}, {})",
|
||||
and_then_snippets.cx,
|
||||
and_then_snippets.lint,
|
||||
and_then_snippets.span,
|
||||
and_then_snippets.msg,
|
||||
span_suggestion_snippets.help,
|
||||
span_suggestion_snippets.sugg,
|
||||
span_suggestion_snippets.applicability
|
||||
),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
|
||||
fn suggest_help(
|
||||
cx: &LateContext<'_>,
|
||||
expr: &Expr<'_>,
|
||||
and_then_snippets: &AndThenSnippets<'_>,
|
||||
help: &str,
|
||||
with_span: bool,
|
||||
) {
|
||||
let option_span = if with_span {
|
||||
format!("Some({})", and_then_snippets.span)
|
||||
} else {
|
||||
"None".to_string()
|
||||
};
|
||||
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
COLLAPSIBLE_SPAN_LINT_CALLS,
|
||||
expr.span,
|
||||
"this call is collapsible",
|
||||
"collapse into",
|
||||
format!(
|
||||
"span_lint_and_help({}, {}, {}, {}, {}, {help})",
|
||||
and_then_snippets.cx, and_then_snippets.lint, and_then_snippets.span, and_then_snippets.msg, &option_span,
|
||||
),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
|
||||
fn suggest_note(
|
||||
cx: &LateContext<'_>,
|
||||
expr: &Expr<'_>,
|
||||
and_then_snippets: &AndThenSnippets<'_>,
|
||||
note: &str,
|
||||
with_span: bool,
|
||||
) {
|
||||
let note_span = if with_span {
|
||||
format!("Some({})", and_then_snippets.span)
|
||||
} else {
|
||||
"None".to_string()
|
||||
};
|
||||
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
COLLAPSIBLE_SPAN_LINT_CALLS,
|
||||
expr.span,
|
||||
"this call is collapsible",
|
||||
"collapse into",
|
||||
format!(
|
||||
"span_lint_and_note({}, {}, {}, {}, {note_span}, {note})",
|
||||
and_then_snippets.cx, and_then_snippets.lint, and_then_snippets.span, and_then_snippets.msg,
|
||||
),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
@ -0,0 +1,77 @@
|
||||
use clippy_utils::diagnostics::span_lint_and_help;
|
||||
use clippy_utils::ty::match_type;
|
||||
use clippy_utils::{is_lint_allowed, paths};
|
||||
use if_chain::if_chain;
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for calls to `cx.span_lint*` and suggests to use the `utils::*`
|
||||
/// variant of the function.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// The `utils::*` variants also add a link to the Clippy documentation to the
|
||||
/// warning/error messages.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust,ignore
|
||||
/// cx.span_lint(LINT_NAME, "message");
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust,ignore
|
||||
/// utils::span_lint(cx, LINT_NAME, "message");
|
||||
/// ```
|
||||
pub COMPILER_LINT_FUNCTIONS,
|
||||
internal,
|
||||
"usage of the lint functions of the compiler instead of the utils::* variant"
|
||||
}
|
||||
|
||||
impl_lint_pass!(CompilerLintFunctions => [COMPILER_LINT_FUNCTIONS]);
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub struct CompilerLintFunctions {
|
||||
map: FxHashMap<&'static str, &'static str>,
|
||||
}
|
||||
|
||||
impl CompilerLintFunctions {
|
||||
#[must_use]
|
||||
pub fn new() -> Self {
|
||||
let mut map = FxHashMap::default();
|
||||
map.insert("span_lint", "utils::span_lint");
|
||||
map.insert("struct_span_lint", "utils::span_lint");
|
||||
map.insert("lint", "utils::span_lint");
|
||||
map.insert("span_lint_note", "utils::span_lint_and_note");
|
||||
map.insert("span_lint_help", "utils::span_lint_and_help");
|
||||
Self { map }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for CompilerLintFunctions {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
if is_lint_allowed(cx, COMPILER_LINT_FUNCTIONS, expr.hir_id) {
|
||||
return;
|
||||
}
|
||||
|
||||
if_chain! {
|
||||
if let ExprKind::MethodCall(path, self_arg, _, _) = &expr.kind;
|
||||
let fn_name = path.ident;
|
||||
if let Some(sugg) = self.map.get(fn_name.as_str());
|
||||
let ty = cx.typeck_results().expr_ty(self_arg).peel_refs();
|
||||
if match_type(cx, ty, &paths::EARLY_CONTEXT) || match_type(cx, ty, &paths::LATE_CONTEXT);
|
||||
then {
|
||||
span_lint_and_help(
|
||||
cx,
|
||||
COMPILER_LINT_FUNCTIONS,
|
||||
path.ident.span,
|
||||
"usage of a compiler lint function",
|
||||
None,
|
||||
&format!("please use the Clippy variant of this function: `{sugg}`"),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
164
clippy_lints/src/utils/internal_lints/if_chain_style.rs
Normal file
164
clippy_lints/src/utils/internal_lints/if_chain_style.rs
Normal file
@ -0,0 +1,164 @@
|
||||
use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
|
||||
use clippy_utils::{higher, is_else_clause, is_expn_of};
|
||||
use if_chain::if_chain;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::{BinOpKind, Block, Expr, ExprKind, HirId, Local, Node, Stmt, StmtKind};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::{BytePos, Span};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// Finds unidiomatic usage of `if_chain!`
|
||||
pub IF_CHAIN_STYLE,
|
||||
internal,
|
||||
"non-idiomatic `if_chain!` usage"
|
||||
}
|
||||
|
||||
declare_lint_pass!(IfChainStyle => [IF_CHAIN_STYLE]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for IfChainStyle {
|
||||
fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx hir::Block<'_>) {
|
||||
let (local, after, if_chain_span) = if_chain! {
|
||||
if let [Stmt { kind: StmtKind::Local(local), .. }, after @ ..] = block.stmts;
|
||||
if let Some(if_chain_span) = is_expn_of(block.span, "if_chain");
|
||||
then { (local, after, if_chain_span) } else { return }
|
||||
};
|
||||
if is_first_if_chain_expr(cx, block.hir_id, if_chain_span) {
|
||||
span_lint(
|
||||
cx,
|
||||
IF_CHAIN_STYLE,
|
||||
if_chain_local_span(cx, local, if_chain_span),
|
||||
"`let` expression should be above the `if_chain!`",
|
||||
);
|
||||
} else if local.span.ctxt() == block.span.ctxt() && is_if_chain_then(after, block.expr, if_chain_span) {
|
||||
span_lint(
|
||||
cx,
|
||||
IF_CHAIN_STYLE,
|
||||
if_chain_local_span(cx, local, if_chain_span),
|
||||
"`let` expression should be inside `then { .. }`",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
|
||||
let (cond, then, els) = if let Some(higher::IfOrIfLet { cond, r#else, then }) = higher::IfOrIfLet::hir(expr) {
|
||||
(cond, then, r#else.is_some())
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
let ExprKind::Block(then_block, _) = then.kind else { return };
|
||||
let if_chain_span = is_expn_of(expr.span, "if_chain");
|
||||
if !els {
|
||||
check_nested_if_chains(cx, expr, then_block, if_chain_span);
|
||||
}
|
||||
let Some(if_chain_span) = if_chain_span else { return };
|
||||
// check for `if a && b;`
|
||||
if_chain! {
|
||||
if let ExprKind::Binary(op, _, _) = cond.kind;
|
||||
if op.node == BinOpKind::And;
|
||||
if cx.sess().source_map().is_multiline(cond.span);
|
||||
then {
|
||||
span_lint(cx, IF_CHAIN_STYLE, cond.span, "`if a && b;` should be `if a; if b;`");
|
||||
}
|
||||
}
|
||||
if is_first_if_chain_expr(cx, expr.hir_id, if_chain_span)
|
||||
&& is_if_chain_then(then_block.stmts, then_block.expr, if_chain_span)
|
||||
{
|
||||
span_lint(cx, IF_CHAIN_STYLE, expr.span, "`if_chain!` only has one `if`");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_nested_if_chains(
|
||||
cx: &LateContext<'_>,
|
||||
if_expr: &Expr<'_>,
|
||||
then_block: &Block<'_>,
|
||||
if_chain_span: Option<Span>,
|
||||
) {
|
||||
#[rustfmt::skip]
|
||||
let (head, tail) = match *then_block {
|
||||
Block { stmts, expr: Some(tail), .. } => (stmts, tail),
|
||||
Block {
|
||||
stmts: &[
|
||||
ref head @ ..,
|
||||
Stmt { kind: StmtKind::Expr(tail) | StmtKind::Semi(tail), .. }
|
||||
],
|
||||
..
|
||||
} => (head, tail),
|
||||
_ => return,
|
||||
};
|
||||
if_chain! {
|
||||
if let Some(higher::IfOrIfLet { r#else: None, .. }) = higher::IfOrIfLet::hir(tail);
|
||||
let sm = cx.sess().source_map();
|
||||
if head
|
||||
.iter()
|
||||
.all(|stmt| matches!(stmt.kind, StmtKind::Local(..)) && !sm.is_multiline(stmt.span));
|
||||
if if_chain_span.is_some() || !is_else_clause(cx.tcx, if_expr);
|
||||
then {
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
let (span, msg) = match (if_chain_span, is_expn_of(tail.span, "if_chain")) {
|
||||
(None, Some(_)) => (if_expr.span, "this `if` can be part of the inner `if_chain!`"),
|
||||
(Some(_), None) => (tail.span, "this `if` can be part of the outer `if_chain!`"),
|
||||
(Some(a), Some(b)) if a != b => (b, "this `if_chain!` can be merged with the outer `if_chain!`"),
|
||||
_ => return,
|
||||
};
|
||||
span_lint_and_then(cx, IF_CHAIN_STYLE, span, msg, |diag| {
|
||||
let (span, msg) = match head {
|
||||
[] => return,
|
||||
[stmt] => (stmt.span, "this `let` statement can also be in the `if_chain!`"),
|
||||
[a, .., b] => (
|
||||
a.span.to(b.span),
|
||||
"these `let` statements can also be in the `if_chain!`",
|
||||
),
|
||||
};
|
||||
diag.span_help(span, msg);
|
||||
});
|
||||
}
|
||||
|
||||
fn is_first_if_chain_expr(cx: &LateContext<'_>, hir_id: HirId, if_chain_span: Span) -> bool {
|
||||
cx.tcx
|
||||
.hir()
|
||||
.parent_iter(hir_id)
|
||||
.find(|(_, node)| {
|
||||
#[rustfmt::skip]
|
||||
!matches!(node, Node::Expr(Expr { kind: ExprKind::Block(..), .. }) | Node::Stmt(_))
|
||||
})
|
||||
.map_or(false, |(id, _)| {
|
||||
is_expn_of(cx.tcx.hir().span(id), "if_chain") != Some(if_chain_span)
|
||||
})
|
||||
}
|
||||
|
||||
/// Checks a trailing slice of statements and expression of a `Block` to see if they are part
|
||||
/// of the `then {..}` portion of an `if_chain!`
|
||||
fn is_if_chain_then(stmts: &[Stmt<'_>], expr: Option<&Expr<'_>>, if_chain_span: Span) -> bool {
|
||||
let span = if let [stmt, ..] = stmts {
|
||||
stmt.span
|
||||
} else if let Some(expr) = expr {
|
||||
expr.span
|
||||
} else {
|
||||
// empty `then {}`
|
||||
return true;
|
||||
};
|
||||
is_expn_of(span, "if_chain").map_or(true, |span| span != if_chain_span)
|
||||
}
|
||||
|
||||
/// Creates a `Span` for `let x = ..;` in an `if_chain!` call.
|
||||
fn if_chain_local_span(cx: &LateContext<'_>, local: &Local<'_>, if_chain_span: Span) -> Span {
|
||||
let mut span = local.pat.span;
|
||||
if let Some(init) = local.init {
|
||||
span = span.to(init.span);
|
||||
}
|
||||
span.adjust(if_chain_span.ctxt().outer_expn());
|
||||
let sm = cx.sess().source_map();
|
||||
let span = sm.span_extend_to_prev_str(span, "let", false, true).unwrap_or(span);
|
||||
let span = sm.span_extend_to_next_char(span, ';', false);
|
||||
Span::new(
|
||||
span.lo() - BytePos(3),
|
||||
span.hi() + BytePos(1),
|
||||
span.ctxt(),
|
||||
span.parent(),
|
||||
)
|
||||
}
|
@ -0,0 +1,239 @@
|
||||
use clippy_utils::consts::{constant_simple, Constant};
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::snippet;
|
||||
use clippy_utils::ty::match_type;
|
||||
use clippy_utils::{def_path_res, is_expn_of, match_def_path, paths};
|
||||
use if_chain::if_chain;
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::mir::interpret::ConstValue;
|
||||
use rustc_middle::ty::{self};
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::symbol::Symbol;
|
||||
|
||||
use std::borrow::Cow;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for interning symbols that have already been pre-interned and defined as constants.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// It's faster and easier to use the symbol constant.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust,ignore
|
||||
/// let _ = sym!(f32);
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust,ignore
|
||||
/// let _ = sym::f32;
|
||||
/// ```
|
||||
pub INTERNING_DEFINED_SYMBOL,
|
||||
internal,
|
||||
"interning a symbol that is pre-interned and defined as a constant"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for unnecessary conversion from Symbol to a string.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// It's faster use symbols directly instead of strings.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust,ignore
|
||||
/// symbol.as_str() == "clippy";
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust,ignore
|
||||
/// symbol == sym::clippy;
|
||||
/// ```
|
||||
pub UNNECESSARY_SYMBOL_STR,
|
||||
internal,
|
||||
"unnecessary conversion between Symbol and string"
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct InterningDefinedSymbol {
|
||||
// Maps the symbol value to the constant DefId.
|
||||
symbol_map: FxHashMap<u32, DefId>,
|
||||
}
|
||||
|
||||
impl_lint_pass!(InterningDefinedSymbol => [INTERNING_DEFINED_SYMBOL, UNNECESSARY_SYMBOL_STR]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for InterningDefinedSymbol {
|
||||
fn check_crate(&mut self, cx: &LateContext<'_>) {
|
||||
if !self.symbol_map.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
for &module in &[&paths::KW_MODULE, &paths::SYM_MODULE] {
|
||||
if let Some(def_id) = def_path_res(cx, module, None).opt_def_id() {
|
||||
for item in cx.tcx.module_children(def_id).iter() {
|
||||
if_chain! {
|
||||
if let Res::Def(DefKind::Const, item_def_id) = item.res;
|
||||
let ty = cx.tcx.type_of(item_def_id);
|
||||
if match_type(cx, ty, &paths::SYMBOL);
|
||||
if let Ok(ConstValue::Scalar(value)) = cx.tcx.const_eval_poly(item_def_id);
|
||||
if let Ok(value) = value.to_u32();
|
||||
then {
|
||||
self.symbol_map.insert(value, item_def_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
if_chain! {
|
||||
if let ExprKind::Call(func, [arg]) = &expr.kind;
|
||||
if let ty::FnDef(def_id, _) = cx.typeck_results().expr_ty(func).kind();
|
||||
if match_def_path(cx, *def_id, &paths::SYMBOL_INTERN);
|
||||
if let Some(Constant::Str(arg)) = constant_simple(cx, cx.typeck_results(), arg);
|
||||
let value = Symbol::intern(&arg).as_u32();
|
||||
if let Some(&def_id) = self.symbol_map.get(&value);
|
||||
then {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
INTERNING_DEFINED_SYMBOL,
|
||||
is_expn_of(expr.span, "sym").unwrap_or(expr.span),
|
||||
"interning a defined symbol",
|
||||
"try",
|
||||
cx.tcx.def_path_str(def_id),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
if let ExprKind::Binary(op, left, right) = expr.kind {
|
||||
if matches!(op.node, BinOpKind::Eq | BinOpKind::Ne) {
|
||||
let data = [
|
||||
(left, self.symbol_str_expr(left, cx)),
|
||||
(right, self.symbol_str_expr(right, cx)),
|
||||
];
|
||||
match data {
|
||||
// both operands are a symbol string
|
||||
[(_, Some(left)), (_, Some(right))] => {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
UNNECESSARY_SYMBOL_STR,
|
||||
expr.span,
|
||||
"unnecessary `Symbol` to string conversion",
|
||||
"try",
|
||||
format!(
|
||||
"{} {} {}",
|
||||
left.as_symbol_snippet(cx),
|
||||
op.node.as_str(),
|
||||
right.as_symbol_snippet(cx),
|
||||
),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
},
|
||||
// one of the operands is a symbol string
|
||||
[(expr, Some(symbol)), _] | [_, (expr, Some(symbol))] => {
|
||||
// creating an owned string for comparison
|
||||
if matches!(symbol, SymbolStrExpr::Expr { is_to_owned: true, .. }) {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
UNNECESSARY_SYMBOL_STR,
|
||||
expr.span,
|
||||
"unnecessary string allocation",
|
||||
"try",
|
||||
format!("{}.as_str()", symbol.as_symbol_snippet(cx)),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
},
|
||||
// nothing found
|
||||
[(_, None), (_, None)] => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl InterningDefinedSymbol {
|
||||
fn symbol_str_expr<'tcx>(&self, expr: &'tcx Expr<'tcx>, cx: &LateContext<'tcx>) -> Option<SymbolStrExpr<'tcx>> {
|
||||
static IDENT_STR_PATHS: &[&[&str]] = &[&paths::IDENT_AS_STR, &paths::TO_STRING_METHOD];
|
||||
static SYMBOL_STR_PATHS: &[&[&str]] = &[
|
||||
&paths::SYMBOL_AS_STR,
|
||||
&paths::SYMBOL_TO_IDENT_STRING,
|
||||
&paths::TO_STRING_METHOD,
|
||||
];
|
||||
let call = if_chain! {
|
||||
if let ExprKind::AddrOf(_, _, e) = expr.kind;
|
||||
if let ExprKind::Unary(UnOp::Deref, e) = e.kind;
|
||||
then { e } else { expr }
|
||||
};
|
||||
if_chain! {
|
||||
// is a method call
|
||||
if let ExprKind::MethodCall(_, item, [], _) = call.kind;
|
||||
if let Some(did) = cx.typeck_results().type_dependent_def_id(call.hir_id);
|
||||
let ty = cx.typeck_results().expr_ty(item);
|
||||
// ...on either an Ident or a Symbol
|
||||
if let Some(is_ident) = if match_type(cx, ty, &paths::SYMBOL) {
|
||||
Some(false)
|
||||
} else if match_type(cx, ty, &paths::IDENT) {
|
||||
Some(true)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
// ...which converts it to a string
|
||||
let paths = if is_ident { IDENT_STR_PATHS } else { SYMBOL_STR_PATHS };
|
||||
if let Some(path) = paths.iter().find(|path| match_def_path(cx, did, path));
|
||||
then {
|
||||
let is_to_owned = path.last().unwrap().ends_with("string");
|
||||
return Some(SymbolStrExpr::Expr {
|
||||
item,
|
||||
is_ident,
|
||||
is_to_owned,
|
||||
});
|
||||
}
|
||||
}
|
||||
// is a string constant
|
||||
if let Some(Constant::Str(s)) = constant_simple(cx, cx.typeck_results(), expr) {
|
||||
let value = Symbol::intern(&s).as_u32();
|
||||
// ...which matches a symbol constant
|
||||
if let Some(&def_id) = self.symbol_map.get(&value) {
|
||||
return Some(SymbolStrExpr::Const(def_id));
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
enum SymbolStrExpr<'tcx> {
|
||||
/// a string constant with a corresponding symbol constant
|
||||
Const(DefId),
|
||||
/// a "symbol to string" expression like `symbol.as_str()`
|
||||
Expr {
|
||||
/// part that evaluates to `Symbol` or `Ident`
|
||||
item: &'tcx Expr<'tcx>,
|
||||
is_ident: bool,
|
||||
/// whether an owned `String` is created like `to_ident_string()`
|
||||
is_to_owned: bool,
|
||||
},
|
||||
}
|
||||
|
||||
impl<'tcx> SymbolStrExpr<'tcx> {
|
||||
/// Returns a snippet that evaluates to a `Symbol` and is const if possible
|
||||
fn as_symbol_snippet(&self, cx: &LateContext<'_>) -> Cow<'tcx, str> {
|
||||
match *self {
|
||||
Self::Const(def_id) => cx.tcx.def_path_str(def_id).into(),
|
||||
Self::Expr { item, is_ident, .. } => {
|
||||
let mut snip = snippet(cx, item.span.source_callsite(), "..");
|
||||
if is_ident {
|
||||
// get `Ident.name`
|
||||
snip.to_mut().push_str(".name");
|
||||
}
|
||||
snip
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
108
clippy_lints/src/utils/internal_lints/invalid_paths.rs
Normal file
108
clippy_lints/src/utils/internal_lints/invalid_paths.rs
Normal file
@ -0,0 +1,108 @@
|
||||
use clippy_utils::consts::{constant_simple, Constant};
|
||||
use clippy_utils::def_path_res;
|
||||
use clippy_utils::diagnostics::span_lint;
|
||||
use if_chain::if_chain;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_hir::Item;
|
||||
use rustc_hir_analysis::hir_ty_to_ty;
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty::{self, fast_reject::SimplifiedTypeGen, FloatTy};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::symbol::Symbol;
|
||||
|
||||
declare_clippy_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 INVALID_PATHS,
|
||||
internal,
|
||||
"invalid path"
|
||||
}
|
||||
|
||||
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_chain! {
|
||||
if mod_name.as_str() == "paths";
|
||||
if let hir::ItemKind::Const(ty, body_id) = item.kind;
|
||||
let ty = hir_ty_to_ty(cx.tcx, ty);
|
||||
if let ty::Array(el_ty, _) = &ty.kind();
|
||||
if let ty::Ref(_, el_ty, _) = &el_ty.kind();
|
||||
if el_ty.is_str();
|
||||
let body = cx.tcx.hir().body(body_id);
|
||||
let typeck_results = cx.tcx.typeck_body(body_id);
|
||||
if let Some(Constant::Vec(path)) = constant_simple(cx, typeck_results, body.value);
|
||||
let path: Vec<&str> = path
|
||||
.iter()
|
||||
.map(|x| {
|
||||
if let Constant::Str(s) = x {
|
||||
s.as_str()
|
||||
} else {
|
||||
// We checked the type of the constant above
|
||||
unreachable!()
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
if !check_path(cx, &path[..]);
|
||||
then {
|
||||
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, path, None) != Res::Err {
|
||||
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 = [
|
||||
SimplifiedTypeGen::FloatSimplifiedType(FloatTy::F32),
|
||||
SimplifiedTypeGen::FloatSimplifiedType(FloatTy::F64),
|
||||
SimplifiedTypeGen::SliceSimplifiedType,
|
||||
SimplifiedTypeGen::StrSimplifiedType,
|
||||
]
|
||||
.iter()
|
||||
.flat_map(|&ty| cx.tcx.incoherent_impls(ty));
|
||||
for item_def_id in lang_items.items().iter().flatten().chain(incoherent_impls) {
|
||||
let lang_item_path = cx.get_def_path(*item_def_id);
|
||||
if path_syms.starts_with(&lang_item_path) {
|
||||
if 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
|
||||
}
|
342
clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs
Normal file
342
clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs
Normal file
@ -0,0 +1,342 @@
|
||||
use crate::utils::internal_lints::metadata_collector::is_deprecated_lint;
|
||||
use clippy_utils::diagnostics::{span_lint, span_lint_and_help};
|
||||
use clippy_utils::macros::root_macro_call_first_node;
|
||||
use clippy_utils::{is_lint_allowed, match_def_path, paths};
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast as ast;
|
||||
use rustc_ast::ast::LitKind;
|
||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_hir::hir_id::CRATE_HIR_ID;
|
||||
use rustc_hir::intravisit::Visitor;
|
||||
use rustc_hir::{ExprKind, HirId, Item, MutTy, Mutability, Path, TyKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::hir::nested_filter;
|
||||
use rustc_semver::RustcVersion;
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::source_map::Spanned;
|
||||
use rustc_span::symbol::Symbol;
|
||||
use rustc_span::{sym, Span};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Ensures every lint is associated to a `LintPass`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// The compiler only knows lints via a `LintPass`. Without
|
||||
/// putting a lint to a `LintPass::get_lints()`'s return, the compiler will not
|
||||
/// know the name of the lint.
|
||||
///
|
||||
/// ### Known problems
|
||||
/// Only checks for lints associated using the
|
||||
/// `declare_lint_pass!`, `impl_lint_pass!`, and `lint_array!` macros.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust,ignore
|
||||
/// declare_lint! { pub LINT_1, ... }
|
||||
/// declare_lint! { pub LINT_2, ... }
|
||||
/// declare_lint! { pub FORGOTTEN_LINT, ... }
|
||||
/// // ...
|
||||
/// declare_lint_pass!(Pass => [LINT_1, LINT_2]);
|
||||
/// // missing FORGOTTEN_LINT
|
||||
/// ```
|
||||
pub LINT_WITHOUT_LINT_PASS,
|
||||
internal,
|
||||
"declaring a lint without associating it in a LintPass"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for cases of an auto-generated lint without an updated description,
|
||||
/// i.e. `default lint description`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Indicates that the lint is not finished.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust,ignore
|
||||
/// declare_lint! { pub COOL_LINT, nursery, "default lint description" }
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust,ignore
|
||||
/// declare_lint! { pub COOL_LINT, nursery, "a great new lint" }
|
||||
/// ```
|
||||
pub DEFAULT_LINT,
|
||||
internal,
|
||||
"found 'default lint description' in a lint declaration"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for invalid `clippy::version` attributes.
|
||||
///
|
||||
/// Valid values are:
|
||||
/// * "pre 1.29.0"
|
||||
/// * any valid semantic version
|
||||
pub INVALID_CLIPPY_VERSION_ATTRIBUTE,
|
||||
internal,
|
||||
"found an invalid `clippy::version` attribute"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for declared clippy lints without the `clippy::version` attribute.
|
||||
///
|
||||
pub MISSING_CLIPPY_VERSION_ATTRIBUTE,
|
||||
internal,
|
||||
"found clippy lint without `clippy::version` attribute"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for cases of an auto-generated deprecated lint without an updated reason,
|
||||
/// i.e. `"default deprecation note"`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Indicates that the documentation is incomplete.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust,ignore
|
||||
/// declare_deprecated_lint! {
|
||||
/// /// ### What it does
|
||||
/// /// Nothing. This lint has been deprecated.
|
||||
/// ///
|
||||
/// /// ### Deprecation reason
|
||||
/// /// TODO
|
||||
/// #[clippy::version = "1.63.0"]
|
||||
/// pub COOL_LINT,
|
||||
/// "default deprecation note"
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust,ignore
|
||||
/// declare_deprecated_lint! {
|
||||
/// /// ### What it does
|
||||
/// /// Nothing. This lint has been deprecated.
|
||||
/// ///
|
||||
/// /// ### Deprecation reason
|
||||
/// /// This lint has been replaced by `cooler_lint`
|
||||
/// #[clippy::version = "1.63.0"]
|
||||
/// pub COOL_LINT,
|
||||
/// "this lint has been replaced by `cooler_lint`"
|
||||
/// }
|
||||
/// ```
|
||||
pub DEFAULT_DEPRECATION_REASON,
|
||||
internal,
|
||||
"found 'default deprecation note' in a deprecated lint declaration"
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct LintWithoutLintPass {
|
||||
declared_lints: FxHashMap<Symbol, Span>,
|
||||
registered_lints: FxHashSet<Symbol>,
|
||||
}
|
||||
|
||||
impl_lint_pass!(LintWithoutLintPass => [DEFAULT_LINT, LINT_WITHOUT_LINT_PASS, INVALID_CLIPPY_VERSION_ATTRIBUTE, MISSING_CLIPPY_VERSION_ATTRIBUTE, DEFAULT_DEPRECATION_REASON]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for LintWithoutLintPass {
|
||||
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
|
||||
if is_lint_allowed(cx, DEFAULT_LINT, item.hir_id())
|
||||
|| is_lint_allowed(cx, DEFAULT_DEPRECATION_REASON, item.hir_id())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if let hir::ItemKind::Static(ty, Mutability::Not, body_id) = item.kind {
|
||||
let is_lint_ref_ty = is_lint_ref_type(cx, ty);
|
||||
if is_deprecated_lint(cx, ty) || is_lint_ref_ty {
|
||||
check_invalid_clippy_version_attribute(cx, item);
|
||||
|
||||
let expr = &cx.tcx.hir().body(body_id).value;
|
||||
let fields;
|
||||
if is_lint_ref_ty {
|
||||
if let ExprKind::AddrOf(_, _, inner_exp) = expr.kind
|
||||
&& let ExprKind::Struct(_, struct_fields, _) = inner_exp.kind {
|
||||
fields = struct_fields;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
} else if let ExprKind::Struct(_, struct_fields, _) = expr.kind {
|
||||
fields = struct_fields;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
let field = fields
|
||||
.iter()
|
||||
.find(|f| f.ident.as_str() == "desc")
|
||||
.expect("lints must have a description field");
|
||||
|
||||
if let ExprKind::Lit(Spanned {
|
||||
node: LitKind::Str(ref sym, _),
|
||||
..
|
||||
}) = field.expr.kind
|
||||
{
|
||||
let sym_str = sym.as_str();
|
||||
if is_lint_ref_ty {
|
||||
if sym_str == "default lint description" {
|
||||
span_lint(
|
||||
cx,
|
||||
DEFAULT_LINT,
|
||||
item.span,
|
||||
&format!("the lint `{}` has the default lint description", item.ident.name),
|
||||
);
|
||||
}
|
||||
|
||||
self.declared_lints.insert(item.ident.name, item.span);
|
||||
} else if sym_str == "default deprecation note" {
|
||||
span_lint(
|
||||
cx,
|
||||
DEFAULT_DEPRECATION_REASON,
|
||||
item.span,
|
||||
&format!("the lint `{}` has the default deprecation reason", item.ident.name),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if let Some(macro_call) = root_macro_call_first_node(cx, item) {
|
||||
if !matches!(
|
||||
cx.tcx.item_name(macro_call.def_id).as_str(),
|
||||
"impl_lint_pass" | "declare_lint_pass"
|
||||
) {
|
||||
return;
|
||||
}
|
||||
if let hir::ItemKind::Impl(hir::Impl {
|
||||
of_trait: None,
|
||||
items: impl_item_refs,
|
||||
..
|
||||
}) = item.kind
|
||||
{
|
||||
let mut collector = LintCollector {
|
||||
output: &mut self.registered_lints,
|
||||
cx,
|
||||
};
|
||||
let body_id = cx.tcx.hir().body_owned_by(
|
||||
cx.tcx.hir().local_def_id(
|
||||
impl_item_refs
|
||||
.iter()
|
||||
.find(|iiref| iiref.ident.as_str() == "get_lints")
|
||||
.expect("LintPass needs to implement get_lints")
|
||||
.id
|
||||
.hir_id(),
|
||||
),
|
||||
);
|
||||
collector.visit_expr(cx.tcx.hir().body(body_id).value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_crate_post(&mut self, cx: &LateContext<'tcx>) {
|
||||
if is_lint_allowed(cx, LINT_WITHOUT_LINT_PASS, CRATE_HIR_ID) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (lint_name, &lint_span) in &self.declared_lints {
|
||||
// When using the `declare_tool_lint!` macro, the original `lint_span`'s
|
||||
// file points to "<rustc macros>".
|
||||
// `compiletest-rs` thinks that's an error in a different file and
|
||||
// just ignores it. This causes the test in compile-fail/lint_pass
|
||||
// not able to capture the error.
|
||||
// Therefore, we need to climb the macro expansion tree and find the
|
||||
// actual span that invoked `declare_tool_lint!`:
|
||||
let lint_span = lint_span.ctxt().outer_expn_data().call_site;
|
||||
|
||||
if !self.registered_lints.contains(lint_name) {
|
||||
span_lint(
|
||||
cx,
|
||||
LINT_WITHOUT_LINT_PASS,
|
||||
lint_span,
|
||||
&format!("the lint `{lint_name}` is not added to any `LintPass`"),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn is_lint_ref_type<'tcx>(cx: &LateContext<'tcx>, ty: &hir::Ty<'_>) -> bool {
|
||||
if let TyKind::Rptr(
|
||||
_,
|
||||
MutTy {
|
||||
ty: inner,
|
||||
mutbl: Mutability::Not,
|
||||
},
|
||||
) = ty.kind
|
||||
{
|
||||
if let TyKind::Path(ref path) = inner.kind {
|
||||
if let Res::Def(DefKind::Struct, def_id) = cx.qpath_res(path, inner.hir_id) {
|
||||
return match_def_path(cx, def_id, &paths::LINT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
fn check_invalid_clippy_version_attribute(cx: &LateContext<'_>, item: &'_ Item<'_>) {
|
||||
if let Some(value) = extract_clippy_version_value(cx, item) {
|
||||
// The `sym!` macro doesn't work as it only expects a single token.
|
||||
// It's better to keep it this way and have a direct `Symbol::intern` call here.
|
||||
if value == Symbol::intern("pre 1.29.0") {
|
||||
return;
|
||||
}
|
||||
|
||||
if RustcVersion::parse(value.as_str()).is_err() {
|
||||
span_lint_and_help(
|
||||
cx,
|
||||
INVALID_CLIPPY_VERSION_ATTRIBUTE,
|
||||
item.span,
|
||||
"this item has an invalid `clippy::version` attribute",
|
||||
None,
|
||||
"please use a valid semantic version, see `doc/adding_lints.md`",
|
||||
);
|
||||
}
|
||||
} else {
|
||||
span_lint_and_help(
|
||||
cx,
|
||||
MISSING_CLIPPY_VERSION_ATTRIBUTE,
|
||||
item.span,
|
||||
"this lint is missing the `clippy::version` attribute or version value",
|
||||
None,
|
||||
"please use a `clippy::version` attribute, see `doc/adding_lints.md`",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// This function extracts the version value of a `clippy::version` attribute if the given value has
|
||||
/// one
|
||||
pub(super) fn extract_clippy_version_value(cx: &LateContext<'_>, item: &'_ Item<'_>) -> Option<Symbol> {
|
||||
let attrs = cx.tcx.hir().attrs(item.hir_id());
|
||||
attrs.iter().find_map(|attr| {
|
||||
if_chain! {
|
||||
// Identify attribute
|
||||
if let ast::AttrKind::Normal(ref attr_kind) = &attr.kind;
|
||||
if let [tool_name, attr_name] = &attr_kind.item.path.segments[..];
|
||||
if tool_name.ident.name == sym::clippy;
|
||||
if attr_name.ident.name == sym::version;
|
||||
if let Some(version) = attr.value_str();
|
||||
then { Some(version) } else { None }
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
struct LintCollector<'a, 'tcx> {
|
||||
output: &'a mut FxHashSet<Symbol>,
|
||||
cx: &'a LateContext<'tcx>,
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> Visitor<'tcx> for LintCollector<'a, 'tcx> {
|
||||
type NestedFilter = nested_filter::All;
|
||||
|
||||
fn visit_path(&mut self, path: &'tcx Path<'_>, _: HirId) {
|
||||
if path.segments.len() == 1 {
|
||||
self.output.insert(path.segments[0].ident.name);
|
||||
}
|
||||
}
|
||||
|
||||
fn nested_visit_map(&mut self) -> Self::Map {
|
||||
self.cx.tcx.hir()
|
||||
}
|
||||
}
|
@ -8,7 +8,7 @@
|
||||
//! a simple mistake)
|
||||
|
||||
use crate::renamed_lints::RENAMED_LINTS;
|
||||
use crate::utils::internal_lints::{extract_clippy_version_value, is_lint_ref_type};
|
||||
use crate::utils::internal_lints::lint_without_lint_pass::{extract_clippy_version_value, is_lint_ref_type};
|
||||
|
||||
use clippy_utils::diagnostics::span_lint;
|
||||
use clippy_utils::ty::{match_type, walk_ptrs_ty_depth};
|
||||
@ -532,7 +532,11 @@ fn parse_config_field_doc(doc_comment: &str) -> Option<(Vec<String>, String)> {
|
||||
|
||||
// Extract lints
|
||||
doc_comment.make_ascii_lowercase();
|
||||
let lints: Vec<String> = doc_comment.split_off(DOC_START.len()).split(", ").map(str::to_string).collect();
|
||||
let lints: Vec<String> = doc_comment
|
||||
.split_off(DOC_START.len())
|
||||
.split(", ")
|
||||
.map(str::to_string)
|
||||
.collect();
|
||||
|
||||
// Format documentation correctly
|
||||
// split off leading `.` from lint name list and indent for correct formatting
|
||||
|
63
clippy_lints/src/utils/internal_lints/msrv_attr_impl.rs
Normal file
63
clippy_lints/src/utils/internal_lints/msrv_attr_impl.rs
Normal file
@ -0,0 +1,63 @@
|
||||
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 if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir_analysis::hir_ty_to_ty;
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::ty::{self, subst::GenericArgKind};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Check that the `extract_msrv_attr!` macro is used, when a lint has a MSRV.
|
||||
///
|
||||
pub MISSING_MSRV_ATTR_IMPL,
|
||||
internal,
|
||||
"checking if all necessary steps were taken when adding a MSRV to a lint"
|
||||
}
|
||||
|
||||
declare_lint_pass!(MsrvAttrImpl => [MISSING_MSRV_ATTR_IMPL]);
|
||||
|
||||
impl LateLintPass<'_> for MsrvAttrImpl {
|
||||
fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) {
|
||||
if_chain! {
|
||||
if let hir::ItemKind::Impl(hir::Impl {
|
||||
of_trait: Some(lint_pass_trait_ref),
|
||||
self_ty,
|
||||
items,
|
||||
..
|
||||
}) = &item.kind;
|
||||
if let Some(lint_pass_trait_def_id) = lint_pass_trait_ref.trait_def_id();
|
||||
let is_late_pass = match_def_path(cx, lint_pass_trait_def_id, &paths::LATE_LINT_PASS);
|
||||
if is_late_pass || match_def_path(cx, lint_pass_trait_def_id, &paths::EARLY_LINT_PASS);
|
||||
let self_ty = hir_ty_to_ty(cx.tcx, self_ty);
|
||||
if let ty::Adt(self_ty_def, _) = self_ty.kind();
|
||||
if self_ty_def.is_struct();
|
||||
if self_ty_def.all_fields().any(|f| {
|
||||
cx.tcx
|
||||
.type_of(f.did)
|
||||
.walk()
|
||||
.filter(|t| matches!(t.unpack(), GenericArgKind::Type(_)))
|
||||
.any(|t| match_type(cx, t.expect_ty(), &paths::RUSTC_VERSION))
|
||||
});
|
||||
if !items.iter().any(|item| item.ident.name == sym!(enter_lint_attrs));
|
||||
then {
|
||||
let context = if is_late_pass { "LateContext" } else { "EarlyContext" };
|
||||
let lint_pass = if is_late_pass { "LateLintPass" } else { "EarlyLintPass" };
|
||||
let span = cx.sess().source_map().span_through_char(item.span, '{');
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
MISSING_MSRV_ATTR_IMPL,
|
||||
span,
|
||||
&format!("`extract_msrv_attr!` macro missing from `{lint_pass}` implementation"),
|
||||
&format!("add `extract_msrv_attr!({context})` to the `{lint_pass}` implementation"),
|
||||
format!("{}\n extract_msrv_attr!({context});", snippet(cx, span, "..")),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::ty::match_type;
|
||||
use clippy_utils::{is_lint_allowed, method_calls, paths};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::symbol::Symbol;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for calls to `cx.outer().expn_data()` and suggests to use
|
||||
/// the `cx.outer_expn_data()`
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// `cx.outer_expn_data()` is faster and more concise.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust,ignore
|
||||
/// expr.span.ctxt().outer().expn_data()
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust,ignore
|
||||
/// expr.span.ctxt().outer_expn_data()
|
||||
/// ```
|
||||
pub OUTER_EXPN_EXPN_DATA,
|
||||
internal,
|
||||
"using `cx.outer_expn().expn_data()` instead of `cx.outer_expn_data()`"
|
||||
}
|
||||
|
||||
declare_lint_pass!(OuterExpnDataPass => [OUTER_EXPN_EXPN_DATA]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for OuterExpnDataPass {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
|
||||
if is_lint_allowed(cx, OUTER_EXPN_EXPN_DATA, expr.hir_id) {
|
||||
return;
|
||||
}
|
||||
|
||||
let (method_names, arg_lists, spans) = method_calls(expr, 2);
|
||||
let method_names: Vec<&str> = method_names.iter().map(Symbol::as_str).collect();
|
||||
if_chain! {
|
||||
if let ["expn_data", "outer_expn"] = method_names.as_slice();
|
||||
let (self_arg, args) = arg_lists[1];
|
||||
if args.is_empty();
|
||||
let self_ty = cx.typeck_results().expr_ty(self_arg).peel_refs();
|
||||
if match_type(cx, self_ty, &paths::SYNTAX_CONTEXT);
|
||||
then {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
OUTER_EXPN_EXPN_DATA,
|
||||
spans[1].with_hi(expr.span.hi()),
|
||||
"usage of `outer_expn().expn_data()`",
|
||||
"try",
|
||||
"outer_expn_data()".to_string(),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
37
clippy_lints/src/utils/internal_lints/produce_ice.rs
Normal file
37
clippy_lints/src/utils/internal_lints/produce_ice.rs
Normal file
@ -0,0 +1,37 @@
|
||||
use rustc_ast::ast::NodeId;
|
||||
use rustc_ast::visit::FnKind;
|
||||
use rustc_lint::{EarlyContext, EarlyLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::Span;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Not an actual lint. This lint is only meant for testing our customized internal compiler
|
||||
/// error message by calling `panic`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// ICE in large quantities can damage your teeth
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust,ignore
|
||||
/// 🍦🍦🍦🍦🍦
|
||||
/// ```
|
||||
pub PRODUCE_ICE,
|
||||
internal,
|
||||
"this message should not appear anywhere as we ICE before and don't emit the lint"
|
||||
}
|
||||
|
||||
declare_lint_pass!(ProduceIce => [PRODUCE_ICE]);
|
||||
|
||||
impl EarlyLintPass for ProduceIce {
|
||||
fn check_fn(&mut self, _: &EarlyContext<'_>, fn_kind: FnKind<'_>, _: Span, _: NodeId) {
|
||||
assert!(!is_trigger_fn(fn_kind), "Would you like some help with that?");
|
||||
}
|
||||
}
|
||||
|
||||
fn is_trigger_fn(fn_kind: FnKind<'_>) -> bool {
|
||||
match fn_kind {
|
||||
FnKind::Fn(_, ident, ..) => ident.name.as_str() == "it_looks_like_you_are_trying_to_kill_clippy",
|
||||
FnKind::Closure(..) => false,
|
||||
}
|
||||
}
|
343
clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs
Normal file
343
clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs
Normal file
@ -0,0 +1,343 @@
|
||||
use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then};
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::{def_path_res, is_lint_allowed, match_any_def_paths, peel_hir_expr_refs};
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::ast::LitKind;
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def::{DefKind, Namespace, Res};
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::{Expr, ExprKind, Local, Mutability, Node};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::mir::interpret::{Allocation, ConstValue, GlobalAlloc};
|
||||
use rustc_middle::ty::{self, AssocKind, DefIdTree, Ty};
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::symbol::{Ident, Symbol};
|
||||
use rustc_span::Span;
|
||||
|
||||
use std::str;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for usages of def paths when a diagnostic item or a `LangItem` could be used.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// The path for an item is subject to change and is less efficient to look up than a
|
||||
/// diagnostic item or a `LangItem`.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust,ignore
|
||||
/// utils::match_type(cx, ty, &paths::VEC)
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust,ignore
|
||||
/// utils::is_type_diagnostic_item(cx, ty, sym::Vec)
|
||||
/// ```
|
||||
pub UNNECESSARY_DEF_PATH,
|
||||
internal,
|
||||
"using a def path when a diagnostic item or a `LangItem` is available"
|
||||
}
|
||||
|
||||
impl_lint_pass!(UnnecessaryDefPath => [UNNECESSARY_DEF_PATH]);
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct UnnecessaryDefPath {
|
||||
array_def_ids: FxHashSet<(DefId, Span)>,
|
||||
linted_def_ids: FxHashSet<DefId>,
|
||||
}
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for UnnecessaryDefPath {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::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(Symbol),
|
||||
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_chain! {
|
||||
if let [cx_arg, def_arg, args @ ..] = args;
|
||||
if let ExprKind::Path(path) = &func.kind;
|
||||
if let Some(id) = cx.qpath_res(path, func.hir_id).opt_def_id();
|
||||
if 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
|
||||
if let Some(segments) = path_to_matched_type(cx, item_arg);
|
||||
let segments: Vec<&str> = segments.iter().map(|sym| &**sym).collect();
|
||||
if let Some(def_id) = inherent_def_path_res(cx, &segments[..]);
|
||||
then {
|
||||
// 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 has_ctor = match cx.tcx.def_kind(def_id) {
|
||||
DefKind::Struct => {
|
||||
let variant = cx.tcx.adt_def(def_id).non_enum_variant();
|
||||
variant.ctor_def_id.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_def_id.is_some() && variant.fields.iter().all(|f| f.vis.is_public())
|
||||
},
|
||||
_ => false,
|
||||
};
|
||||
|
||||
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().require(LangItem::{item}).ok() == 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().require(LangItem::{item}).ok() == 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",
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
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 };
|
||||
|
||||
if let Some(def_id) = inherent_def_path_res(cx, &path.iter().map(AsRef::as_ref).collect::<Vec<_>>()) {
|
||||
self.array_def_ids.insert((def_id, span));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn path_to_matched_type(cx: &LateContext<'_>, expr: &hir::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) => {
|
||||
let parent_id = cx.tcx.hir().get_parent_node(hir_id);
|
||||
if let Some(Node::Local(Local { init: Some(init), .. })) = cx.tcx.hir().find(parent_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),
|
||||
),
|
||||
Res::Def(DefKind::Const, def_id) => match cx.tcx.const_eval_poly(def_id).ok()? {
|
||||
ConstValue::ByRef { alloc, offset } if offset.bytes() == 0 => {
|
||||
read_mir_alloc_def_path(cx, alloc.inner(), cx.tcx.type_of(def_id))
|
||||
},
|
||||
_ => 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().values().next()?;
|
||||
if let GlobalAlloc::Memory(alloc) = cx.tcx.global_alloc(alloc) {
|
||||
(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()
|
||||
.values()
|
||||
.map(|&alloc| {
|
||||
if let GlobalAlloc::Memory(alloc) = cx.tcx.global_alloc(alloc) {
|
||||
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 {
|
||||
if let LitKind::Str(sym, _) = lit.node {
|
||||
return Some((*sym.as_str()).to_owned());
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
// def_path_res will match field names before anything else, but for this we want to match
|
||||
// inherent functions first.
|
||||
fn inherent_def_path_res(cx: &LateContext<'_>, segments: &[&str]) -> Option<DefId> {
|
||||
def_path_res(cx, segments, None).opt_def_id().map(|def_id| {
|
||||
if cx.tcx.def_kind(def_id) == DefKind::Field {
|
||||
let method_name = *segments.last().unwrap();
|
||||
cx.tcx
|
||||
.def_key(def_id)
|
||||
.parent
|
||||
.and_then(|parent_idx| {
|
||||
cx.tcx
|
||||
.inherent_impls(DefId {
|
||||
index: parent_idx,
|
||||
krate: def_id.krate,
|
||||
})
|
||||
.iter()
|
||||
.find_map(|impl_id| {
|
||||
cx.tcx.associated_items(*impl_id).find_by_name_and_kind(
|
||||
cx.tcx,
|
||||
Ident::from_str(method_name),
|
||||
AssocKind::Fn,
|
||||
*impl_id,
|
||||
)
|
||||
})
|
||||
})
|
||||
.map_or(def_id, |item| item.def_id)
|
||||
} else {
|
||||
def_id
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn get_lang_item_name(cx: &LateContext<'_>, def_id: DefId) -> Option<Symbol> {
|
||||
if let Some(lang_item) = cx.tcx.lang_items().items().iter().position(|id| *id == Some(def_id)) {
|
||||
let lang_items = def_path_res(cx, &["rustc_hir", "lang_items", "LangItem"], Some(Namespace::TypeNS)).def_id();
|
||||
let item_name = cx
|
||||
.tcx
|
||||
.adt_def(lang_items)
|
||||
.variants()
|
||||
.iter()
|
||||
.nth(lang_item)
|
||||
.unwrap()
|
||||
.name;
|
||||
Some(item_name)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
@ -1766,6 +1766,7 @@ pub fn any_parent_is_automatically_derived(tcx: TyCtxt<'_>, node: HirId) -> bool
|
||||
/// ```rust,ignore
|
||||
/// if let Some(args) = match_function_call(cx, cmp_max_call, &paths::CMP_MAX);
|
||||
/// ```
|
||||
/// This function is deprecated. Use [`match_function_call_with_def_id`].
|
||||
pub fn match_function_call<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &'tcx Expr<'_>,
|
||||
@ -1783,6 +1784,22 @@ pub fn match_function_call<'tcx>(
|
||||
None
|
||||
}
|
||||
|
||||
pub fn match_function_call_with_def_id<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &'tcx Expr<'_>,
|
||||
fun_def_id: DefId,
|
||||
) -> Option<&'tcx [Expr<'tcx>]> {
|
||||
if_chain! {
|
||||
if let ExprKind::Call(fun, args) = expr.kind;
|
||||
if let ExprKind::Path(ref qpath) = fun.kind;
|
||||
if cx.qpath_res(qpath, fun.hir_id).opt_def_id() == Some(fun_def_id);
|
||||
then {
|
||||
return Some(args);
|
||||
}
|
||||
};
|
||||
None
|
||||
}
|
||||
|
||||
/// Checks if the given `DefId` matches any of the paths. Returns the index of matching path, if
|
||||
/// any.
|
||||
///
|
||||
|
@ -16,25 +16,17 @@ pub const APPLICABILITY_VALUES: [[&str; 3]; 4] = [
|
||||
#[cfg(feature = "internal")]
|
||||
pub const DIAGNOSTIC_BUILDER: [&str; 3] = ["rustc_errors", "diagnostic_builder", "DiagnosticBuilder"];
|
||||
pub const ARC_PTR_EQ: [&str; 4] = ["alloc", "sync", "Arc", "ptr_eq"];
|
||||
pub const ASMUT_TRAIT: [&str; 3] = ["core", "convert", "AsMut"];
|
||||
pub const ASREF_TRAIT: [&str; 3] = ["core", "convert", "AsRef"];
|
||||
pub const BTREEMAP_CONTAINS_KEY: [&str; 6] = ["alloc", "collections", "btree", "map", "BTreeMap", "contains_key"];
|
||||
pub const BTREEMAP_ENTRY: [&str; 6] = ["alloc", "collections", "btree", "map", "entry", "Entry"];
|
||||
pub const BTREEMAP_INSERT: [&str; 6] = ["alloc", "collections", "btree", "map", "BTreeMap", "insert"];
|
||||
pub const BTREESET_ITER: [&str; 6] = ["alloc", "collections", "btree", "set", "BTreeSet", "iter"];
|
||||
pub const CLONE_TRAIT_METHOD: [&str; 4] = ["core", "clone", "Clone", "clone"];
|
||||
pub const COW: [&str; 3] = ["alloc", "borrow", "Cow"];
|
||||
pub const CORE_ITER_COLLECT: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "collect"];
|
||||
pub const CORE_ITER_CLONED: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "cloned"];
|
||||
pub const CORE_ITER_COPIED: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "copied"];
|
||||
pub const CORE_ITER_FILTER: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "filter"];
|
||||
pub const CORE_ITER_INTO_ITER: [&str; 6] = ["core", "iter", "traits", "collect", "IntoIterator", "into_iter"];
|
||||
pub const CSTRING_AS_C_STR: [&str; 5] = ["alloc", "ffi", "c_str", "CString", "as_c_str"];
|
||||
pub const DEFAULT_TRAIT_METHOD: [&str; 4] = ["core", "default", "Default", "default"];
|
||||
pub const DEREF_MUT_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "DerefMut", "deref_mut"];
|
||||
/// Preferably use the diagnostic item `sym::deref_method` where possible
|
||||
pub const DEREF_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "Deref", "deref"];
|
||||
pub const DISPLAY_TRAIT: [&str; 3] = ["core", "fmt", "Display"];
|
||||
#[cfg(feature = "internal")]
|
||||
pub const EARLY_CONTEXT: [&str; 2] = ["rustc_lint", "EarlyContext"];
|
||||
#[cfg(feature = "internal")]
|
||||
@ -42,30 +34,22 @@ pub const EARLY_LINT_PASS: [&str; 3] = ["rustc_lint", "passes", "EarlyLintPass"]
|
||||
pub const EXIT: [&str; 3] = ["std", "process", "exit"];
|
||||
pub const F32_EPSILON: [&str; 4] = ["core", "f32", "<impl f32>", "EPSILON"];
|
||||
pub const F64_EPSILON: [&str; 4] = ["core", "f64", "<impl f64>", "EPSILON"];
|
||||
pub const FILE: [&str; 3] = ["std", "fs", "File"];
|
||||
pub const FILE_TYPE: [&str; 3] = ["std", "fs", "FileType"];
|
||||
pub const FROM_FROM: [&str; 4] = ["core", "convert", "From", "from"];
|
||||
pub const FROM_ITERATOR_METHOD: [&str; 6] = ["core", "iter", "traits", "collect", "FromIterator", "from_iter"];
|
||||
pub const FROM_STR_METHOD: [&str; 5] = ["core", "str", "traits", "FromStr", "from_str"];
|
||||
pub const FUTURE_FROM_GENERATOR: [&str; 3] = ["core", "future", "from_generator"];
|
||||
#[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 HASHMAP_CONTAINS_KEY: [&str; 6] = ["std", "collections", "hash", "map", "HashMap", "contains_key"];
|
||||
pub const HASHMAP_ENTRY: [&str; 5] = ["std", "collections", "hash", "map", "Entry"];
|
||||
pub const HASHMAP_INSERT: [&str; 6] = ["std", "collections", "hash", "map", "HashMap", "insert"];
|
||||
pub const HASHSET_ITER: [&str; 6] = ["std", "collections", "hash", "set", "HashSet", "iter"];
|
||||
#[cfg(feature = "internal")]
|
||||
pub const IDENT: [&str; 3] = ["rustc_span", "symbol", "Ident"];
|
||||
#[cfg(feature = "internal")]
|
||||
pub const IDENT_AS_STR: [&str; 4] = ["rustc_span", "symbol", "Ident", "as_str"];
|
||||
pub const INDEX: [&str; 3] = ["core", "ops", "Index"];
|
||||
pub const INDEX_MUT: [&str; 3] = ["core", "ops", "IndexMut"];
|
||||
pub const INSERT_STR: [&str; 4] = ["alloc", "string", "String", "insert_str"];
|
||||
pub const ITER_COUNT: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "count"];
|
||||
pub const ITER_EMPTY: [&str; 5] = ["core", "iter", "sources", "empty", "Empty"];
|
||||
pub const ITER_REPEAT: [&str; 5] = ["core", "iter", "sources", "repeat", "repeat"];
|
||||
pub const ITERTOOLS_NEXT_TUPLE: [&str; 3] = ["itertools", "Itertools", "next_tuple"];
|
||||
#[cfg(feature = "internal")]
|
||||
pub const KW_MODULE: [&str; 3] = ["rustc_span", "symbol", "kw"];
|
||||
@ -76,13 +60,7 @@ pub const LATE_LINT_PASS: [&str; 3] = ["rustc_lint", "passes", "LateLintPass"];
|
||||
#[cfg(feature = "internal")]
|
||||
pub const LINT: [&str; 2] = ["rustc_lint_defs", "Lint"];
|
||||
pub const MEM_SWAP: [&str; 3] = ["core", "mem", "swap"];
|
||||
pub const MUTEX_GUARD: [&str; 4] = ["std", "sync", "mutex", "MutexGuard"];
|
||||
pub const OPEN_OPTIONS: [&str; 3] = ["std", "fs", "OpenOptions"];
|
||||
/// Preferably use the diagnostic item `sym::Option` where possible
|
||||
pub const OPTION: [&str; 3] = ["core", "option", "Option"];
|
||||
pub const OPTION_NONE: [&str; 4] = ["core", "option", "Option", "None"];
|
||||
pub const OPTION_SOME: [&str; 4] = ["core", "option", "Option", "Some"];
|
||||
pub const ORD: [&str; 3] = ["core", "cmp", "Ord"];
|
||||
pub const OS_STRING_AS_OS_STR: [&str; 5] = ["std", "ffi", "os_str", "OsString", "as_os_str"];
|
||||
pub const OS_STR_TO_OS_STRING: [&str; 5] = ["std", "ffi", "os_str", "OsStr", "to_os_string"];
|
||||
pub const PARKING_LOT_MUTEX_GUARD: [&str; 3] = ["lock_api", "mutex", "MutexGuard"];
|
||||
@ -95,8 +73,6 @@ pub const PERMISSIONS: [&str; 3] = ["std", "fs", "Permissions"];
|
||||
#[cfg_attr(not(unix), allow(clippy::invalid_paths))]
|
||||
pub const PERMISSIONS_FROM_MODE: [&str; 6] = ["std", "os", "unix", "fs", "PermissionsExt", "from_mode"];
|
||||
pub const POLL: [&str; 4] = ["core", "task", "poll", "Poll"];
|
||||
pub const POLL_PENDING: [&str; 5] = ["core", "task", "poll", "Poll", "Pending"];
|
||||
pub const POLL_READY: [&str; 5] = ["core", "task", "poll", "Poll", "Ready"];
|
||||
pub const PTR_COPY: [&str; 3] = ["core", "intrinsics", "copy"];
|
||||
pub const PTR_COPY_NONOVERLAPPING: [&str; 3] = ["core", "intrinsics", "copy_nonoverlapping"];
|
||||
pub const PTR_EQ: [&str; 3] = ["core", "ptr", "eq"];
|
||||
@ -125,14 +101,8 @@ pub const REGEX_BYTES_NEW: [&str; 4] = ["regex", "re_bytes", "Regex", "new"];
|
||||
pub const REGEX_BYTES_SET_NEW: [&str; 5] = ["regex", "re_set", "bytes", "RegexSet", "new"];
|
||||
pub const REGEX_NEW: [&str; 4] = ["regex", "re_unicode", "Regex", "new"];
|
||||
pub const REGEX_SET_NEW: [&str; 5] = ["regex", "re_set", "unicode", "RegexSet", "new"];
|
||||
/// Preferably use the diagnostic item `sym::Result` where possible
|
||||
pub const RESULT: [&str; 3] = ["core", "result", "Result"];
|
||||
pub const RESULT_ERR: [&str; 4] = ["core", "result", "Result", "Err"];
|
||||
pub const RESULT_OK: [&str; 4] = ["core", "result", "Result", "Ok"];
|
||||
#[cfg(feature = "internal")]
|
||||
pub const RUSTC_VERSION: [&str; 2] = ["rustc_semver", "RustcVersion"];
|
||||
pub const RWLOCK_READ_GUARD: [&str; 4] = ["std", "sync", "rwlock", "RwLockReadGuard"];
|
||||
pub const RWLOCK_WRITE_GUARD: [&str; 4] = ["std", "sync", "rwlock", "RwLockWriteGuard"];
|
||||
pub const SERDE_DESERIALIZE: [&str; 3] = ["serde", "de", "Deserialize"];
|
||||
pub const SERDE_DE_VISITOR: [&str; 3] = ["serde", "de", "Visitor"];
|
||||
pub const SLICE_FROM_RAW_PARTS: [&str; 4] = ["core", "slice", "raw", "from_raw_parts"];
|
||||
|
@ -1,4 +1,4 @@
|
||||
thread 'rustc' panicked at 'Would you like some help with that?', clippy_lints/src/utils/internal_lints.rs
|
||||
thread 'rustc' panicked at 'Would you like some help with that?', clippy_lints/src/utils/internal_lints/produce_ice.rs:28:9
|
||||
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
|
||||
|
||||
error: internal compiler error: unexpected panic
|
||||
|
@ -1,5 +1,5 @@
|
||||
#![warn(clippy::internal)]
|
||||
#![allow(clippy::missing_clippy_version_attribute)]
|
||||
#![allow(clippy::missing_clippy_version_attribute, clippy::unnecessary_def_path)]
|
||||
|
||||
mod paths {
|
||||
// Good path
|
||||
|
@ -28,9 +28,9 @@ use rustc_hir::Expr;
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty::Ty;
|
||||
|
||||
#[allow(unused)]
|
||||
#[allow(unused, clippy::unnecessary_def_path)]
|
||||
static OPTION: [&str; 3] = ["core", "option", "Option"];
|
||||
#[allow(unused)]
|
||||
#[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<'_>) {
|
||||
@ -38,7 +38,7 @@ fn _f<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, did: DefId, expr: &Expr<'_>) {
|
||||
let _ = is_type_diagnostic_item(cx, ty, sym::Result);
|
||||
let _ = is_type_diagnostic_item(cx, ty, sym::Result);
|
||||
|
||||
#[allow(unused)]
|
||||
#[allow(unused, clippy::unnecessary_def_path)]
|
||||
let rc_path = &["alloc", "rc", "Rc"];
|
||||
let _ = is_type_diagnostic_item(cx, ty, sym::Rc);
|
||||
|
||||
|
@ -28,9 +28,9 @@ use rustc_hir::Expr;
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty::Ty;
|
||||
|
||||
#[allow(unused)]
|
||||
#[allow(unused, clippy::unnecessary_def_path)]
|
||||
static OPTION: [&str; 3] = ["core", "option", "Option"];
|
||||
#[allow(unused)]
|
||||
#[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<'_>) {
|
||||
@ -38,7 +38,7 @@ fn _f<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, did: DefId, expr: &Expr<'_>) {
|
||||
let _ = match_type(cx, ty, RESULT);
|
||||
let _ = match_type(cx, ty, &["core", "result", "Result"]);
|
||||
|
||||
#[allow(unused)]
|
||||
#[allow(unused, clippy::unnecessary_def_path)]
|
||||
let rc_path = &["alloc", "rc", "Rc"];
|
||||
let _ = clippy_utils::ty::match_type(cx, ty, rc_path);
|
||||
|
||||
|
16
tests/ui-internal/unnecessary_def_path_hardcoded_path.rs
Normal file
16
tests/ui-internal/unnecessary_def_path_hardcoded_path.rs
Normal file
@ -0,0 +1,16 @@
|
||||
#![feature(rustc_private)]
|
||||
#![allow(unused)]
|
||||
#![warn(clippy::unnecessary_def_path)]
|
||||
|
||||
extern crate rustc_hir;
|
||||
|
||||
use rustc_hir::LangItem;
|
||||
|
||||
fn main() {
|
||||
const DEREF_TRAIT: [&str; 4] = ["core", "ops", "deref", "Deref"];
|
||||
const DEREF_MUT_TRAIT: [&str; 4] = ["core", "ops", "deref", "DerefMut"];
|
||||
const DEREF_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "Deref", "deref"];
|
||||
|
||||
// Don't lint, not yet a diagnostic or language item
|
||||
const DEREF_MUT_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "DerefMut", "deref_mut"];
|
||||
}
|
27
tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr
Normal file
27
tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr
Normal file
@ -0,0 +1,27 @@
|
||||
error: hardcoded path to a language item
|
||||
--> $DIR/unnecessary_def_path_hardcoded_path.rs:11:40
|
||||
|
|
||||
LL | const DEREF_MUT_TRAIT: [&str; 4] = ["core", "ops", "deref", "DerefMut"];
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: convert all references to use `LangItem::DerefMut`
|
||||
= note: `-D clippy::unnecessary-def-path` implied by `-D warnings`
|
||||
|
||||
error: hardcoded path to a diagnostic item
|
||||
--> $DIR/unnecessary_def_path_hardcoded_path.rs:12:43
|
||||
|
|
||||
LL | const DEREF_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "Deref", "deref"];
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: convert all references to use `sym::deref_method`
|
||||
|
||||
error: hardcoded path to a diagnostic item
|
||||
--> $DIR/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`
|
||||
|
||||
error: aborting due to 3 previous errors
|
||||
|
Loading…
Reference in New Issue
Block a user