mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-27 01:04:03 +00:00
Merge commit '2b2190cb5667cdd276a24ef8b9f3692209c54a89' into clippyup
This commit is contained in:
commit
ad76883ff9
@ -965,7 +965,7 @@ Released 2021-09-09
|
||||
[#7407](https://github.com/rust-lang/rust-clippy/pull/7407)
|
||||
* [`redundant_allocation`]: Now additionally supports the `Arc<>` type
|
||||
[#7308](https://github.com/rust-lang/rust-clippy/pull/7308)
|
||||
* [`blacklisted_name`]: Now allows blacklisted names in test code
|
||||
* [`disallowed_names`]: Now allows disallowed names in test code
|
||||
[#7379](https://github.com/rust-lang/rust-clippy/pull/7379)
|
||||
* [`redundant_closure`]: Suggests `&mut` for `FnMut`
|
||||
[#7437](https://github.com/rust-lang/rust-clippy/pull/7437)
|
||||
@ -2066,7 +2066,7 @@ Released 2020-08-27
|
||||
[#5692](https://github.com/rust-lang/rust-clippy/pull/5692)
|
||||
* [`if_same_then_else`]: Don't assume multiplication is always commutative
|
||||
[#5702](https://github.com/rust-lang/rust-clippy/pull/5702)
|
||||
* [`blacklisted_name`]: Remove `bar` from the default configuration
|
||||
* [`disallowed_names`]: Remove `bar` from the default configuration
|
||||
[#5712](https://github.com/rust-lang/rust-clippy/pull/5712)
|
||||
* [`redundant_pattern_matching`]: Avoid suggesting non-`const fn` calls in const contexts
|
||||
[#5724](https://github.com/rust-lang/rust-clippy/pull/5724)
|
||||
@ -3522,6 +3522,7 @@ Released 2018-09-13
|
||||
[`derive_partial_eq_without_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_partial_eq_without_eq
|
||||
[`disallowed_method`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_method
|
||||
[`disallowed_methods`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_methods
|
||||
[`disallowed_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_names
|
||||
[`disallowed_script_idents`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_script_idents
|
||||
[`disallowed_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_type
|
||||
[`disallowed_types`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_types
|
||||
@ -3685,6 +3686,7 @@ Released 2018-09-13
|
||||
[`manual_find`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_find
|
||||
[`manual_find_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_find_map
|
||||
[`manual_flatten`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_flatten
|
||||
[`manual_instant_elapsed`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_instant_elapsed
|
||||
[`manual_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_map
|
||||
[`manual_memcpy`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_memcpy
|
||||
[`manual_non_exhaustive`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_non_exhaustive
|
||||
@ -3816,11 +3818,13 @@ Released 2018-09-13
|
||||
[`or_then_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#or_then_unwrap
|
||||
[`out_of_bounds_indexing`]: https://rust-lang.github.io/rust-clippy/master/index.html#out_of_bounds_indexing
|
||||
[`overflow_check_conditional`]: https://rust-lang.github.io/rust-clippy/master/index.html#overflow_check_conditional
|
||||
[`overly_complex_bool_expr`]: https://rust-lang.github.io/rust-clippy/master/index.html#overly_complex_bool_expr
|
||||
[`panic`]: https://rust-lang.github.io/rust-clippy/master/index.html#panic
|
||||
[`panic_in_result_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#panic_in_result_fn
|
||||
[`panic_params`]: https://rust-lang.github.io/rust-clippy/master/index.html#panic_params
|
||||
[`panicking_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#panicking_unwrap
|
||||
[`partialeq_ne_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#partialeq_ne_impl
|
||||
[`partialeq_to_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#partialeq_to_none
|
||||
[`path_buf_push_overwrite`]: https://rust-lang.github.io/rust-clippy/master/index.html#path_buf_push_overwrite
|
||||
[`pattern_type_mismatch`]: https://rust-lang.github.io/rust-clippy/master/index.html#pattern_type_mismatch
|
||||
[`possible_missing_comma`]: https://rust-lang.github.io/rust-clippy/master/index.html#possible_missing_comma
|
||||
|
@ -30,10 +30,10 @@ All contributors are expected to follow the [Rust Code of Conduct].
|
||||
## The Clippy book
|
||||
|
||||
If you're new to Clippy and don't know where to start the [Clippy book] includes
|
||||
a developer guide and is a good place to start your journey.
|
||||
a [developer guide] and is a good place to start your journey.
|
||||
|
||||
<!-- FIXME: Link to the deployed book, once it is deployed through CI -->
|
||||
[Clippy book]: book/src
|
||||
[Clippy book]: https://doc.rust-lang.org/nightly/clippy/index.html
|
||||
[developer guide]: https://doc.rust-lang.org/nightly/clippy/development/index.html
|
||||
|
||||
## High level approach
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "clippy"
|
||||
version = "0.1.64"
|
||||
version = "0.1.65"
|
||||
description = "A bunch of helpful lints to avoid common pitfalls in Rust"
|
||||
repository = "https://github.com/rust-lang/rust-clippy"
|
||||
readme = "README.md"
|
||||
|
@ -144,7 +144,7 @@ value` mapping e.g.
|
||||
|
||||
```toml
|
||||
avoid-breaking-exported-api = false
|
||||
blacklisted-names = ["toto", "tata", "titi"]
|
||||
disallowed-names = ["toto", "tata", "titi"]
|
||||
cognitive-complexity-threshold = 30
|
||||
```
|
||||
|
||||
|
@ -7,7 +7,7 @@ basic `variable = value` mapping eg.
|
||||
|
||||
```toml
|
||||
avoid-breaking-exported-api = false
|
||||
blacklisted-names = ["toto", "tata", "titi"]
|
||||
disallowed-names = ["toto", "tata", "titi"]
|
||||
cognitive-complexity-threshold = 30
|
||||
```
|
||||
|
||||
|
@ -438,7 +438,7 @@ fn setup_mod_file(path: &Path, lint: &LintData<'_>) -> io::Result<&'static str>
|
||||
let mut lint_context = None;
|
||||
|
||||
let mut iter = rustc_lexer::tokenize(&file_contents).map(|t| {
|
||||
let range = offset..offset + t.len;
|
||||
let range = offset..offset + t.len as usize;
|
||||
offset = range.end;
|
||||
|
||||
LintDeclSearchResult {
|
||||
|
@ -836,7 +836,7 @@ pub(crate) struct LintDeclSearchResult<'a> {
|
||||
fn parse_contents(contents: &str, module: &str, lints: &mut Vec<Lint>) {
|
||||
let mut offset = 0usize;
|
||||
let mut iter = tokenize(contents).map(|t| {
|
||||
let range = offset..offset + t.len;
|
||||
let range = offset..offset + t.len as usize;
|
||||
offset = range.end;
|
||||
|
||||
LintDeclSearchResult {
|
||||
@ -899,7 +899,7 @@ fn parse_contents(contents: &str, module: &str, lints: &mut Vec<Lint>) {
|
||||
fn parse_deprecated_contents(contents: &str, lints: &mut Vec<DeprecatedLint>) {
|
||||
let mut offset = 0usize;
|
||||
let mut iter = tokenize(contents).map(|t| {
|
||||
let range = offset..offset + t.len;
|
||||
let range = offset..offset + t.len as usize;
|
||||
offset = range.end;
|
||||
|
||||
LintDeclSearchResult {
|
||||
@ -946,7 +946,7 @@ fn parse_renamed_contents(contents: &str, lints: &mut Vec<RenamedLint>) {
|
||||
for line in contents.lines() {
|
||||
let mut offset = 0usize;
|
||||
let mut iter = tokenize(line).map(|t| {
|
||||
let range = offset..offset + t.len;
|
||||
let range = offset..offset + t.len as usize;
|
||||
offset = range.end;
|
||||
|
||||
LintDeclSearchResult {
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "clippy_lints"
|
||||
version = "0.1.64"
|
||||
version = "0.1.65"
|
||||
description = "A bunch of helpful lints to avoid common pitfalls in Rust"
|
||||
repository = "https://github.com/rust-lang/rust-clippy"
|
||||
readme = "README.md"
|
||||
|
@ -53,13 +53,14 @@ impl<'tcx> LateLintPass<'tcx> for AssertionsOnResultStates {
|
||||
if result_type_with_refs != result_type {
|
||||
return;
|
||||
} else if let Res::Local(binding_id) = path_res(cx, recv)
|
||||
&& local_used_after_expr(cx, binding_id, recv) {
|
||||
&& local_used_after_expr(cx, binding_id, recv)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
let mut app = Applicability::MachineApplicable;
|
||||
match method_segment.ident.as_str() {
|
||||
"is_ok" if has_debug_impl(cx, substs.type_at(1)) => {
|
||||
"is_ok" if type_suitable_to_unwrap(cx, substs.type_at(1)) => {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
ASSERTIONS_ON_RESULT_STATES,
|
||||
@ -73,7 +74,7 @@ impl<'tcx> LateLintPass<'tcx> for AssertionsOnResultStates {
|
||||
app,
|
||||
);
|
||||
}
|
||||
"is_err" if has_debug_impl(cx, substs.type_at(0)) => {
|
||||
"is_err" if type_suitable_to_unwrap(cx, substs.type_at(0)) => {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
ASSERTIONS_ON_RESULT_STATES,
|
||||
@ -99,3 +100,7 @@ fn has_debug_impl<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
|
||||
.get_diagnostic_item(sym::Debug)
|
||||
.map_or(false, |debug| implements_trait(cx, ty, debug, &[]))
|
||||
}
|
||||
|
||||
fn type_suitable_to_unwrap<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
|
||||
has_debug_impl(cx, ty) && !ty.is_unit() && !ty.is_never()
|
||||
}
|
||||
|
@ -64,7 +64,7 @@ declare_clippy_lint! {
|
||||
/// if a {}
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub LOGIC_BUG,
|
||||
pub OVERLY_COMPLEX_BOOL_EXPR,
|
||||
correctness,
|
||||
"boolean expressions that contain terminals which can be eliminated"
|
||||
}
|
||||
@ -72,7 +72,7 @@ declare_clippy_lint! {
|
||||
// For each pairs, both orders are considered.
|
||||
const METHODS_WITH_NEGATION: [(&str, &str); 2] = [("is_some", "is_none"), ("is_err", "is_ok")];
|
||||
|
||||
declare_lint_pass!(NonminimalBool => [NONMINIMAL_BOOL, LOGIC_BUG]);
|
||||
declare_lint_pass!(NonminimalBool => [NONMINIMAL_BOOL, OVERLY_COMPLEX_BOOL_EXPR]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for NonminimalBool {
|
||||
fn check_fn(
|
||||
@ -396,7 +396,7 @@ impl<'a, 'tcx> NonminimalBoolVisitor<'a, 'tcx> {
|
||||
if stats.terminals[i] != 0 && simplified_stats.terminals[i] == 0 {
|
||||
span_lint_hir_and_then(
|
||||
self.cx,
|
||||
LOGIC_BUG,
|
||||
OVERLY_COMPLEX_BOOL_EXPR,
|
||||
e.hir_id,
|
||||
e.span,
|
||||
"this boolean expression contains a logic bug",
|
||||
|
@ -37,7 +37,7 @@ pub(super) fn check(
|
||||
span,
|
||||
&format!("casting the result of `{cast_from}::abs()` to {cast_to}"),
|
||||
"replace with",
|
||||
format!("{}.unsigned_abs()", Sugg::hir(cx, &args[0], "..")),
|
||||
format!("{}.unsigned_abs()", Sugg::hir(cx, &args[0], "..").maybe_par()),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
|
@ -270,10 +270,7 @@ fn get_types_from_cast<'a>(
|
||||
let limit_from: Option<(&Expr<'_>, &str)> = call_from_cast.or_else(|| {
|
||||
if_chain! {
|
||||
// `from_type::from, to_type::max_value()`
|
||||
if let ExprKind::Call(from_func, args) = &expr.kind;
|
||||
// `to_type::max_value()`
|
||||
if args.len() == 1;
|
||||
if let limit = &args[0];
|
||||
if let ExprKind::Call(from_func, [limit]) = &expr.kind;
|
||||
// `from_type::from`
|
||||
if let ExprKind::Path(ref path) = &from_func.kind;
|
||||
if let Some(from_sym) = get_implementing_type(path, INTS, "from");
|
||||
|
@ -34,7 +34,7 @@ declare_lint_pass!(CreateDir => [CREATE_DIR]);
|
||||
impl LateLintPass<'_> for CreateDir {
|
||||
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||
if_chain! {
|
||||
if let ExprKind::Call(func, args) = expr.kind;
|
||||
if let ExprKind::Call(func, [arg, ..]) = expr.kind;
|
||||
if let ExprKind::Path(ref path) = func.kind;
|
||||
if let Some(def_id) = cx.qpath_res(path, func.hir_id).opt_def_id();
|
||||
if match_def_path(cx, def_id, &paths::STD_FS_CREATE_DIR);
|
||||
@ -45,7 +45,7 @@ impl LateLintPass<'_> for CreateDir {
|
||||
expr.span,
|
||||
"calling `std::fs::create_dir` where there may be a better way",
|
||||
"consider calling `std::fs::create_dir_all` instead",
|
||||
format!("create_dir_all({})", snippet(cx, args[0].span, "..")),
|
||||
format!("create_dir_all({})", snippet(cx, arg.span, "..")),
|
||||
Applicability::MaybeIncorrect,
|
||||
)
|
||||
}
|
||||
|
@ -1,7 +1,9 @@
|
||||
use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_sugg};
|
||||
use clippy_utils::source::snippet_with_macro_callsite;
|
||||
use clippy_utils::ty::{has_drop, is_copy};
|
||||
use clippy_utils::{any_parent_is_automatically_derived, contains_name, get_parent_expr, match_def_path, paths};
|
||||
use clippy_utils::{
|
||||
any_parent_is_automatically_derived, contains_name, get_parent_expr, is_from_proc_macro, match_def_path, paths,
|
||||
};
|
||||
use if_chain::if_chain;
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_errors::Applicability;
|
||||
@ -94,6 +96,7 @@ impl<'tcx> LateLintPass<'tcx> for Default {
|
||||
if let QPath::Resolved(None, _path) = qpath;
|
||||
let expr_ty = cx.typeck_results().expr_ty(expr);
|
||||
if let ty::Adt(def, ..) = expr_ty.kind();
|
||||
if !is_from_proc_macro(cx, expr);
|
||||
then {
|
||||
// TODO: Work out a way to put "whatever the imported way of referencing
|
||||
// this type in this file" rather than a fully-qualified type.
|
||||
|
@ -1,7 +1,7 @@
|
||||
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then};
|
||||
use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
|
||||
use clippy_utils::sugg::has_enclosing_paren;
|
||||
use clippy_utils::ty::{expr_sig, peel_mid_ty_refs, variant_of_res};
|
||||
use clippy_utils::ty::{expr_sig, peel_mid_ty_refs, ty_sig, variant_of_res};
|
||||
use clippy_utils::{get_parent_expr, is_lint_allowed, path_to_local, walk_to_expr_usage};
|
||||
use rustc_ast::util::parser::{PREC_POSTFIX, PREC_PREFIX};
|
||||
use rustc_data_structures::fx::FxIndexMap;
|
||||
@ -15,9 +15,9 @@ use rustc_hir::{
|
||||
use rustc_infer::infer::TyCtxtInferExt;
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability};
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitable, TypeckResults};
|
||||
use rustc_middle::ty::{self, Binder, BoundVariableKind, List, Ty, TyCtxt, TypeVisitable, TypeckResults};
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::{symbol::sym, Span, Symbol};
|
||||
use rustc_span::{symbol::sym, Span, Symbol, DUMMY_SP};
|
||||
use rustc_trait_selection::infer::InferCtxtExt;
|
||||
|
||||
declare_clippy_lint! {
|
||||
@ -183,24 +183,24 @@ enum State {
|
||||
},
|
||||
DerefedBorrow(DerefedBorrow),
|
||||
ExplicitDeref {
|
||||
// Span and id of the top-level deref expression if the parent expression is a borrow.
|
||||
deref_span_id: Option<(Span, HirId)>,
|
||||
mutability: Option<Mutability>,
|
||||
},
|
||||
ExplicitDerefField {
|
||||
name: Symbol,
|
||||
},
|
||||
Reborrow {
|
||||
deref_span: Span,
|
||||
deref_hir_id: HirId,
|
||||
mutability: Mutability,
|
||||
},
|
||||
Borrow {
|
||||
mutability: Mutability,
|
||||
},
|
||||
Borrow,
|
||||
}
|
||||
|
||||
// A reference operation considered by this lint pass
|
||||
enum RefOp {
|
||||
Method(Mutability),
|
||||
Deref,
|
||||
AddrOf,
|
||||
AddrOf(Mutability),
|
||||
}
|
||||
|
||||
struct RefPat {
|
||||
@ -263,7 +263,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing {
|
||||
));
|
||||
} else if position.is_deref_stable() {
|
||||
self.state = Some((
|
||||
State::ExplicitDeref { deref_span_id: None },
|
||||
State::ExplicitDeref { mutability: None },
|
||||
StateData { span: expr.span, hir_id: expr.hir_id, position },
|
||||
));
|
||||
}
|
||||
@ -289,7 +289,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing {
|
||||
},
|
||||
));
|
||||
},
|
||||
RefOp::AddrOf => {
|
||||
RefOp::AddrOf(mutability) => {
|
||||
// Find the number of times the borrow is auto-derefed.
|
||||
let mut iter = adjustments.iter();
|
||||
let mut deref_count = 0usize;
|
||||
@ -357,9 +357,13 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing {
|
||||
}),
|
||||
StateData { span: expr.span, hir_id: expr.hir_id, position },
|
||||
));
|
||||
} else if position.is_deref_stable() {
|
||||
} else if position.is_deref_stable()
|
||||
// Auto-deref doesn't combine with other adjustments
|
||||
&& next_adjust.map_or(true, |a| matches!(a.kind, Adjust::Deref(_) | Adjust::Borrow(_)))
|
||||
&& iter.all(|a| matches!(a.kind, Adjust::Deref(_) | Adjust::Borrow(_)))
|
||||
{
|
||||
self.state = Some((
|
||||
State::Borrow,
|
||||
State::Borrow { mutability },
|
||||
StateData {
|
||||
span: expr.span,
|
||||
hir_id: expr.hir_id,
|
||||
@ -395,7 +399,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing {
|
||||
data,
|
||||
));
|
||||
},
|
||||
(Some((State::DerefedBorrow(state), data)), RefOp::AddrOf) if state.count != 0 => {
|
||||
(Some((State::DerefedBorrow(state), data)), RefOp::AddrOf(_)) if state.count != 0 => {
|
||||
self.state = Some((
|
||||
State::DerefedBorrow(DerefedBorrow {
|
||||
count: state.count - 1,
|
||||
@ -404,12 +408,12 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing {
|
||||
data,
|
||||
));
|
||||
},
|
||||
(Some((State::DerefedBorrow(state), data)), RefOp::AddrOf) => {
|
||||
(Some((State::DerefedBorrow(state), data)), RefOp::AddrOf(mutability)) => {
|
||||
let position = data.position;
|
||||
report(cx, expr, State::DerefedBorrow(state), data);
|
||||
if position.is_deref_stable() {
|
||||
self.state = Some((
|
||||
State::Borrow,
|
||||
State::Borrow { mutability },
|
||||
StateData {
|
||||
span: expr.span,
|
||||
hir_id: expr.hir_id,
|
||||
@ -430,43 +434,28 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing {
|
||||
));
|
||||
} else if position.is_deref_stable() {
|
||||
self.state = Some((
|
||||
State::ExplicitDeref { deref_span_id: None },
|
||||
State::ExplicitDeref { mutability: None },
|
||||
StateData { span: expr.span, hir_id: expr.hir_id, position },
|
||||
));
|
||||
}
|
||||
},
|
||||
|
||||
(Some((State::Borrow, data)), RefOp::Deref) => {
|
||||
(Some((State::Borrow { mutability }, data)), RefOp::Deref) => {
|
||||
if typeck.expr_ty(sub_expr).is_ref() {
|
||||
self.state = Some((
|
||||
State::Reborrow {
|
||||
deref_span: expr.span,
|
||||
deref_hir_id: expr.hir_id,
|
||||
},
|
||||
data,
|
||||
));
|
||||
self.state = Some((State::Reborrow { mutability }, data));
|
||||
} else {
|
||||
self.state = Some((
|
||||
State::ExplicitDeref {
|
||||
deref_span_id: Some((expr.span, expr.hir_id)),
|
||||
mutability: Some(mutability),
|
||||
},
|
||||
data,
|
||||
));
|
||||
}
|
||||
},
|
||||
(
|
||||
Some((
|
||||
State::Reborrow {
|
||||
deref_span,
|
||||
deref_hir_id,
|
||||
},
|
||||
data,
|
||||
)),
|
||||
RefOp::Deref,
|
||||
) => {
|
||||
(Some((State::Reborrow { mutability }, data)), RefOp::Deref) => {
|
||||
self.state = Some((
|
||||
State::ExplicitDeref {
|
||||
deref_span_id: Some((deref_span, deref_hir_id)),
|
||||
mutability: Some(mutability),
|
||||
},
|
||||
data,
|
||||
));
|
||||
@ -573,7 +562,7 @@ fn try_parse_ref_op<'tcx>(
|
||||
ExprKind::Unary(UnOp::Deref, sub_expr) if !typeck.expr_ty(sub_expr).is_unsafe_ptr() => {
|
||||
return Some((RefOp::Deref, sub_expr));
|
||||
},
|
||||
ExprKind::AddrOf(BorrowKind::Ref, _, sub_expr) => return Some((RefOp::AddrOf, sub_expr)),
|
||||
ExprKind::AddrOf(BorrowKind::Ref, mutability, sub_expr) => return Some((RefOp::AddrOf(mutability), sub_expr)),
|
||||
_ => return None,
|
||||
};
|
||||
if tcx.is_diagnostic_item(sym::deref_method, def_id) {
|
||||
@ -609,18 +598,21 @@ enum Position {
|
||||
Postfix,
|
||||
Deref,
|
||||
/// Any other location which will trigger auto-deref to a specific time.
|
||||
DerefStable(i8),
|
||||
/// Contains the precedence of the parent expression and whether the target type is sized.
|
||||
DerefStable(i8, bool),
|
||||
/// Any other location which will trigger auto-reborrowing.
|
||||
/// Contains the precedence of the parent expression.
|
||||
ReborrowStable(i8),
|
||||
/// Contains the precedence of the parent expression.
|
||||
Other(i8),
|
||||
}
|
||||
impl Position {
|
||||
fn is_deref_stable(self) -> bool {
|
||||
matches!(self, Self::DerefStable(_))
|
||||
matches!(self, Self::DerefStable(..))
|
||||
}
|
||||
|
||||
fn is_reborrow_stable(self) -> bool {
|
||||
matches!(self, Self::DerefStable(_) | Self::ReborrowStable(_))
|
||||
matches!(self, Self::DerefStable(..) | Self::ReborrowStable(_))
|
||||
}
|
||||
|
||||
fn can_auto_borrow(self) -> bool {
|
||||
@ -628,7 +620,7 @@ impl Position {
|
||||
}
|
||||
|
||||
fn lint_explicit_deref(self) -> bool {
|
||||
matches!(self, Self::Other(_) | Self::DerefStable(_) | Self::ReborrowStable(_))
|
||||
matches!(self, Self::Other(_) | Self::DerefStable(..) | Self::ReborrowStable(_))
|
||||
}
|
||||
|
||||
fn precedence(self) -> i8 {
|
||||
@ -639,7 +631,7 @@ impl Position {
|
||||
| Self::FieldAccess(_)
|
||||
| Self::Postfix => PREC_POSTFIX,
|
||||
Self::Deref => PREC_PREFIX,
|
||||
Self::DerefStable(p) | Self::ReborrowStable(p) | Self::Other(p) => p,
|
||||
Self::DerefStable(p, _) | Self::ReborrowStable(p) | Self::Other(p) => p,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -659,7 +651,7 @@ fn walk_parents<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> (Position, &
|
||||
}
|
||||
match parent {
|
||||
Node::Local(Local { ty: Some(ty), span, .. }) if span.ctxt() == ctxt => {
|
||||
Some(binding_ty_auto_deref_stability(ty, precedence))
|
||||
Some(binding_ty_auto_deref_stability(cx, ty, precedence, List::empty()))
|
||||
},
|
||||
Node::Item(&Item {
|
||||
kind: ItemKind::Static(..) | ItemKind::Const(..),
|
||||
@ -680,11 +672,7 @@ fn walk_parents<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> (Position, &
|
||||
..
|
||||
}) if span.ctxt() == ctxt => {
|
||||
let ty = cx.tcx.type_of(def_id);
|
||||
Some(if ty.is_ref() {
|
||||
Position::DerefStable(precedence)
|
||||
} else {
|
||||
Position::Other(precedence)
|
||||
})
|
||||
Some(ty_auto_deref_stability(cx, ty, precedence).position_for_result(cx))
|
||||
},
|
||||
|
||||
Node::Item(&Item {
|
||||
@ -705,45 +693,38 @@ fn walk_parents<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> (Position, &
|
||||
span,
|
||||
..
|
||||
}) if span.ctxt() == ctxt => {
|
||||
let output = cx.tcx.fn_sig(def_id.to_def_id()).skip_binder().output();
|
||||
Some(if !output.is_ref() {
|
||||
Position::Other(precedence)
|
||||
} else if output.has_placeholders() || output.has_opaque_types() {
|
||||
Position::ReborrowStable(precedence)
|
||||
} else {
|
||||
Position::DerefStable(precedence)
|
||||
})
|
||||
let output = cx
|
||||
.tcx
|
||||
.erase_late_bound_regions(cx.tcx.fn_sig(def_id.to_def_id()).output());
|
||||
Some(ty_auto_deref_stability(cx, output, precedence).position_for_result(cx))
|
||||
},
|
||||
|
||||
Node::Expr(parent) if parent.span.ctxt() == ctxt => match parent.kind {
|
||||
ExprKind::Ret(_) => {
|
||||
let owner_id = cx.tcx.hir().body_owner(cx.enclosing_body.unwrap());
|
||||
Some(
|
||||
if let Node::Expr(Expr {
|
||||
kind: ExprKind::Closure(&Closure { fn_decl, .. }),
|
||||
..
|
||||
}) = cx.tcx.hir().get(owner_id)
|
||||
if let Node::Expr(
|
||||
closure_expr @ Expr {
|
||||
kind: ExprKind::Closure(closure),
|
||||
..
|
||||
},
|
||||
) = cx.tcx.hir().get(owner_id)
|
||||
{
|
||||
match fn_decl.output {
|
||||
FnRetTy::Return(ty) => binding_ty_auto_deref_stability(ty, precedence),
|
||||
FnRetTy::DefaultReturn(_) => Position::Other(precedence),
|
||||
}
|
||||
closure_result_position(cx, closure, cx.typeck_results().expr_ty(closure_expr), precedence)
|
||||
} else {
|
||||
let output = cx
|
||||
.tcx
|
||||
.fn_sig(cx.tcx.hir().local_def_id(owner_id))
|
||||
.skip_binder()
|
||||
.output();
|
||||
if !output.is_ref() {
|
||||
Position::Other(precedence)
|
||||
} else if output.has_placeholders() || output.has_opaque_types() {
|
||||
Position::ReborrowStable(precedence)
|
||||
} else {
|
||||
Position::DerefStable(precedence)
|
||||
}
|
||||
.erase_late_bound_regions(cx.tcx.fn_sig(cx.tcx.hir().local_def_id(owner_id)).output());
|
||||
ty_auto_deref_stability(cx, output, precedence).position_for_result(cx)
|
||||
},
|
||||
)
|
||||
},
|
||||
ExprKind::Closure(closure) => Some(closure_result_position(
|
||||
cx,
|
||||
closure,
|
||||
cx.typeck_results().expr_ty(parent),
|
||||
precedence,
|
||||
)),
|
||||
ExprKind::Call(func, _) if func.hir_id == child_id => {
|
||||
(child_id == e.hir_id).then_some(Position::Callee)
|
||||
},
|
||||
@ -755,8 +736,9 @@ fn walk_parents<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> (Position, &
|
||||
.map(|(hir_ty, ty)| match hir_ty {
|
||||
// Type inference for closures can depend on how they're called. Only go by the explicit
|
||||
// types here.
|
||||
Some(ty) => binding_ty_auto_deref_stability(ty, precedence),
|
||||
None => param_auto_deref_stability(ty.skip_binder(), precedence),
|
||||
Some(hir_ty) => binding_ty_auto_deref_stability(cx, hir_ty, precedence, ty.bound_vars()),
|
||||
None => ty_auto_deref_stability(cx, cx.tcx.erase_late_bound_regions(ty), precedence)
|
||||
.position_for_arg(),
|
||||
}),
|
||||
ExprKind::MethodCall(_, args, _) => {
|
||||
let id = cx.typeck_results().type_dependent_def_id(parent.hir_id).unwrap();
|
||||
@ -797,7 +779,12 @@ fn walk_parents<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> (Position, &
|
||||
Position::MethodReceiver
|
||||
}
|
||||
} else {
|
||||
param_auto_deref_stability(cx.tcx.fn_sig(id).skip_binder().inputs()[i], precedence)
|
||||
ty_auto_deref_stability(
|
||||
cx,
|
||||
cx.tcx.erase_late_bound_regions(cx.tcx.fn_sig(id).input(i)),
|
||||
precedence,
|
||||
)
|
||||
.position_for_arg()
|
||||
}
|
||||
})
|
||||
},
|
||||
@ -808,7 +795,9 @@ fn walk_parents<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> (Position, &
|
||||
.find(|f| f.expr.hir_id == child_id)
|
||||
.zip(variant)
|
||||
.and_then(|(field, variant)| variant.fields.iter().find(|f| f.name == field.ident.name))
|
||||
.map(|field| param_auto_deref_stability(cx.tcx.type_of(field.did), precedence))
|
||||
.map(|field| {
|
||||
ty_auto_deref_stability(cx, cx.tcx.type_of(field.did), precedence).position_for_arg()
|
||||
})
|
||||
},
|
||||
ExprKind::Field(child, name) if child.hir_id == e.hir_id => Some(Position::FieldAccess(name.name)),
|
||||
ExprKind::Unary(UnOp::Deref, child) if child.hir_id == e.hir_id => Some(Position::Deref),
|
||||
@ -831,6 +820,26 @@ fn walk_parents<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> (Position, &
|
||||
(position, adjustments)
|
||||
}
|
||||
|
||||
fn closure_result_position<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
closure: &'tcx Closure<'_>,
|
||||
ty: Ty<'tcx>,
|
||||
precedence: i8,
|
||||
) -> Position {
|
||||
match closure.fn_decl.output {
|
||||
FnRetTy::Return(hir_ty) => {
|
||||
if let Some(sig) = ty_sig(cx, ty)
|
||||
&& let Some(output) = sig.output()
|
||||
{
|
||||
binding_ty_auto_deref_stability(cx, hir_ty, precedence, output.bound_vars())
|
||||
} else {
|
||||
Position::Other(precedence)
|
||||
}
|
||||
},
|
||||
FnRetTy::DefaultReturn(_) => Position::Other(precedence),
|
||||
}
|
||||
}
|
||||
|
||||
// Checks the stability of auto-deref when assigned to a binding with the given explicit type.
|
||||
//
|
||||
// e.g.
|
||||
@ -840,7 +849,12 @@ fn walk_parents<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> (Position, &
|
||||
//
|
||||
// Here `y1` and `y2` would resolve to different types, so the type `&Box<_>` is not stable when
|
||||
// switching to auto-dereferencing.
|
||||
fn binding_ty_auto_deref_stability(ty: &hir::Ty<'_>, precedence: i8) -> Position {
|
||||
fn binding_ty_auto_deref_stability<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
ty: &'tcx hir::Ty<'_>,
|
||||
precedence: i8,
|
||||
binder_args: &'tcx List<BoundVariableKind>,
|
||||
) -> Position {
|
||||
let TyKind::Rptr(_, ty) = &ty.kind else {
|
||||
return Position::Other(precedence);
|
||||
};
|
||||
@ -870,21 +884,33 @@ fn binding_ty_auto_deref_stability(ty: &hir::Ty<'_>, precedence: i8) -> Position
|
||||
{
|
||||
Position::ReborrowStable(precedence)
|
||||
} else {
|
||||
Position::DerefStable(precedence)
|
||||
Position::DerefStable(
|
||||
precedence,
|
||||
cx.tcx
|
||||
.erase_late_bound_regions(Binder::bind_with_vars(
|
||||
cx.typeck_results().node_type(ty.ty.hir_id),
|
||||
binder_args,
|
||||
))
|
||||
.is_sized(cx.tcx.at(DUMMY_SP), cx.param_env.without_caller_bounds()),
|
||||
)
|
||||
}
|
||||
},
|
||||
TyKind::Slice(_)
|
||||
| TyKind::Array(..)
|
||||
| TyKind::BareFn(_)
|
||||
| TyKind::Never
|
||||
TyKind::Slice(_) => Position::DerefStable(precedence, false),
|
||||
TyKind::Array(..) | TyKind::Ptr(_) | TyKind::BareFn(_) => Position::DerefStable(precedence, true),
|
||||
TyKind::Never
|
||||
| TyKind::Tup(_)
|
||||
| TyKind::Ptr(_)
|
||||
| TyKind::TraitObject(..)
|
||||
| TyKind::Path(_) => Position::DerefStable(precedence),
|
||||
TyKind::OpaqueDef(..)
|
||||
| TyKind::Infer
|
||||
| TyKind::Typeof(..)
|
||||
| TyKind::Err => Position::ReborrowStable(precedence),
|
||||
| TyKind::Path(_) => Position::DerefStable(
|
||||
precedence,
|
||||
cx.tcx
|
||||
.erase_late_bound_regions(Binder::bind_with_vars(
|
||||
cx.typeck_results().node_type(ty.ty.hir_id),
|
||||
binder_args,
|
||||
))
|
||||
.is_sized(cx.tcx.at(DUMMY_SP), cx.param_env.without_caller_bounds()),
|
||||
),
|
||||
TyKind::OpaqueDef(..) | TyKind::Infer | TyKind::Typeof(..) | TyKind::TraitObject(..) | TyKind::Err => {
|
||||
Position::ReborrowStable(precedence)
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -920,10 +946,39 @@ fn ty_contains_infer(ty: &hir::Ty<'_>) -> bool {
|
||||
v.0
|
||||
}
|
||||
|
||||
struct TyPosition<'tcx> {
|
||||
position: Position,
|
||||
ty: Option<Ty<'tcx>>,
|
||||
}
|
||||
impl From<Position> for TyPosition<'_> {
|
||||
fn from(position: Position) -> Self {
|
||||
Self { position, ty: None }
|
||||
}
|
||||
}
|
||||
impl<'tcx> TyPosition<'tcx> {
|
||||
fn new_deref_stable_for_result(precedence: i8, ty: Ty<'tcx>) -> Self {
|
||||
Self {
|
||||
position: Position::ReborrowStable(precedence),
|
||||
ty: Some(ty),
|
||||
}
|
||||
}
|
||||
fn position_for_result(self, cx: &LateContext<'tcx>) -> Position {
|
||||
match (self.position, self.ty) {
|
||||
(Position::ReborrowStable(precedence), Some(ty)) => {
|
||||
Position::DerefStable(precedence, ty.is_sized(cx.tcx.at(DUMMY_SP), cx.param_env))
|
||||
},
|
||||
(position, _) => position,
|
||||
}
|
||||
}
|
||||
fn position_for_arg(self) -> Position {
|
||||
self.position
|
||||
}
|
||||
}
|
||||
|
||||
// Checks whether a type is stable when switching to auto dereferencing,
|
||||
fn param_auto_deref_stability(ty: Ty<'_>, precedence: i8) -> Position {
|
||||
fn ty_auto_deref_stability<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, precedence: i8) -> TyPosition<'tcx> {
|
||||
let ty::Ref(_, mut ty, _) = *ty.kind() else {
|
||||
return Position::Other(precedence);
|
||||
return Position::Other(precedence).into();
|
||||
};
|
||||
|
||||
loop {
|
||||
@ -932,35 +987,38 @@ fn param_auto_deref_stability(ty: Ty<'_>, precedence: i8) -> Position {
|
||||
ty = ref_ty;
|
||||
continue;
|
||||
},
|
||||
ty::Infer(_)
|
||||
| ty::Error(_)
|
||||
| ty::Param(_)
|
||||
| ty::Bound(..)
|
||||
| ty::Opaque(..)
|
||||
| ty::Placeholder(_)
|
||||
| ty::Dynamic(..) => Position::ReborrowStable(precedence),
|
||||
ty::Adt(..) if ty.has_placeholders() || ty.has_param_types_or_consts() => {
|
||||
Position::ReborrowStable(precedence)
|
||||
ty::Param(_) => TyPosition::new_deref_stable_for_result(precedence, ty),
|
||||
ty::Infer(_) | ty::Error(_) | ty::Bound(..) | ty::Opaque(..) | ty::Placeholder(_) | ty::Dynamic(..) => {
|
||||
Position::ReborrowStable(precedence).into()
|
||||
},
|
||||
ty::Adt(..)
|
||||
| ty::Bool
|
||||
ty::Adt(..) if ty.has_placeholders() || ty.has_opaque_types() => {
|
||||
Position::ReborrowStable(precedence).into()
|
||||
},
|
||||
ty::Adt(_, substs) if substs.has_param_types_or_consts() => {
|
||||
TyPosition::new_deref_stable_for_result(precedence, ty)
|
||||
},
|
||||
ty::Bool
|
||||
| ty::Char
|
||||
| ty::Int(_)
|
||||
| ty::Uint(_)
|
||||
| ty::Float(_)
|
||||
| ty::Foreign(_)
|
||||
| ty::Str
|
||||
| ty::Array(..)
|
||||
| ty::Slice(..)
|
||||
| ty::Float(_)
|
||||
| ty::RawPtr(..)
|
||||
| ty::FnPtr(_) => Position::DerefStable(precedence, true).into(),
|
||||
ty::Str | ty::Slice(..) => Position::DerefStable(precedence, false).into(),
|
||||
ty::Adt(..)
|
||||
| ty::Foreign(_)
|
||||
| ty::FnDef(..)
|
||||
| ty::FnPtr(_)
|
||||
| ty::Closure(..)
|
||||
| ty::Generator(..)
|
||||
| ty::GeneratorWitness(..)
|
||||
| ty::Closure(..)
|
||||
| ty::Never
|
||||
| ty::Tuple(_)
|
||||
| ty::Projection(_) => Position::DerefStable(precedence),
|
||||
| ty::Projection(_) => Position::DerefStable(
|
||||
precedence,
|
||||
ty.is_sized(cx.tcx.at(DUMMY_SP), cx.param_env.without_caller_bounds()),
|
||||
)
|
||||
.into(),
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -1040,34 +1098,64 @@ fn report<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data
|
||||
diag.span_suggestion(data.span, "change this to", sugg, app);
|
||||
});
|
||||
},
|
||||
State::ExplicitDeref { deref_span_id } => {
|
||||
let (span, hir_id, precedence) = if let Some((span, hir_id)) = deref_span_id
|
||||
State::ExplicitDeref { mutability } => {
|
||||
if matches!(
|
||||
expr.kind,
|
||||
ExprKind::Block(..)
|
||||
| ExprKind::ConstBlock(_)
|
||||
| ExprKind::If(..)
|
||||
| ExprKind::Loop(..)
|
||||
| ExprKind::Match(..)
|
||||
) && matches!(data.position, Position::DerefStable(_, true))
|
||||
{
|
||||
// Rustc bug: auto deref doesn't work on block expression when targeting sized types.
|
||||
return;
|
||||
}
|
||||
|
||||
let (prefix, precedence) = if let Some(mutability) = mutability
|
||||
&& !cx.typeck_results().expr_ty(expr).is_ref()
|
||||
{
|
||||
(span, hir_id, PREC_PREFIX)
|
||||
let prefix = match mutability {
|
||||
Mutability::Not => "&",
|
||||
Mutability::Mut => "&mut ",
|
||||
};
|
||||
(prefix, 0)
|
||||
} else {
|
||||
(data.span, data.hir_id, data.position.precedence())
|
||||
("", data.position.precedence())
|
||||
};
|
||||
span_lint_hir_and_then(
|
||||
cx,
|
||||
EXPLICIT_AUTO_DEREF,
|
||||
hir_id,
|
||||
span,
|
||||
data.hir_id,
|
||||
data.span,
|
||||
"deref which would be done by auto-deref",
|
||||
|diag| {
|
||||
let mut app = Applicability::MachineApplicable;
|
||||
let (snip, snip_is_macro) = snippet_with_context(cx, expr.span, span.ctxt(), "..", &mut app);
|
||||
let (snip, snip_is_macro) = snippet_with_context(cx, expr.span, data.span.ctxt(), "..", &mut app);
|
||||
let sugg =
|
||||
if !snip_is_macro && expr.precedence().order() < precedence && !has_enclosing_paren(&snip) {
|
||||
format!("({})", snip)
|
||||
format!("{}({})", prefix, snip)
|
||||
} else {
|
||||
snip.into()
|
||||
format!("{}{}", prefix, snip)
|
||||
};
|
||||
diag.span_suggestion(span, "try this", sugg, app);
|
||||
diag.span_suggestion(data.span, "try this", sugg, app);
|
||||
},
|
||||
);
|
||||
},
|
||||
State::ExplicitDerefField { .. } => {
|
||||
if matches!(
|
||||
expr.kind,
|
||||
ExprKind::Block(..)
|
||||
| ExprKind::ConstBlock(_)
|
||||
| ExprKind::If(..)
|
||||
| ExprKind::Loop(..)
|
||||
| ExprKind::Match(..)
|
||||
) && matches!(data.position, Position::DerefStable(_, true))
|
||||
{
|
||||
// Rustc bug: auto deref doesn't work on block expression when targeting sized types.
|
||||
return;
|
||||
}
|
||||
|
||||
span_lint_hir_and_then(
|
||||
cx,
|
||||
EXPLICIT_AUTO_DEREF,
|
||||
@ -1081,7 +1169,7 @@ fn report<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data
|
||||
},
|
||||
);
|
||||
},
|
||||
State::Borrow | State::Reborrow { .. } => (),
|
||||
State::Borrow { .. } | State::Reborrow { .. } => (),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6,7 +6,7 @@ use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for usage of blacklisted names for variables, such
|
||||
/// Checks for usage of disallowed names for variables, such
|
||||
/// as `foo`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
@ -18,21 +18,21 @@ declare_clippy_lint! {
|
||||
/// let foo = 3.14;
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub BLACKLISTED_NAME,
|
||||
pub DISALLOWED_NAMES,
|
||||
style,
|
||||
"usage of a blacklisted/placeholder name"
|
||||
"usage of a disallowed/placeholder name"
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct BlacklistedName {
|
||||
blacklist: FxHashSet<String>,
|
||||
pub struct DisallowedNames {
|
||||
disallow: FxHashSet<String>,
|
||||
test_modules_deep: u32,
|
||||
}
|
||||
|
||||
impl BlacklistedName {
|
||||
pub fn new(blacklist: FxHashSet<String>) -> Self {
|
||||
impl DisallowedNames {
|
||||
pub fn new(disallow: FxHashSet<String>) -> Self {
|
||||
Self {
|
||||
blacklist,
|
||||
disallow,
|
||||
test_modules_deep: 0,
|
||||
}
|
||||
}
|
||||
@ -42,9 +42,9 @@ impl BlacklistedName {
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(BlacklistedName => [BLACKLISTED_NAME]);
|
||||
impl_lint_pass!(DisallowedNames => [DISALLOWED_NAMES]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for BlacklistedName {
|
||||
impl<'tcx> LateLintPass<'tcx> for DisallowedNames {
|
||||
fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
|
||||
if is_test_module_or_function(cx.tcx, item) {
|
||||
self.test_modules_deep = self.test_modules_deep.saturating_add(1);
|
||||
@ -58,12 +58,12 @@ impl<'tcx> LateLintPass<'tcx> for BlacklistedName {
|
||||
}
|
||||
|
||||
if let PatKind::Binding(.., ident, _) = pat.kind {
|
||||
if self.blacklist.contains(&ident.name.to_string()) {
|
||||
if self.disallow.contains(&ident.name.to_string()) {
|
||||
span_lint(
|
||||
cx,
|
||||
BLACKLISTED_NAME,
|
||||
DISALLOWED_NAMES,
|
||||
ident.span,
|
||||
&format!("use of a blacklisted/placeholder name `{}`", ident.name),
|
||||
&format!("use of a disallowed/placeholder name `{}`", ident.name),
|
||||
);
|
||||
}
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_note};
|
||||
use clippy_utils::is_span_if;
|
||||
use clippy_utils::source::snippet_opt;
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::ast::{BinOpKind, Block, Expr, ExprKind, StmtKind, UnOp};
|
||||
@ -297,12 +298,11 @@ fn check_array(cx: &EarlyContext<'_>, expr: &Expr) {
|
||||
fn check_missing_else(cx: &EarlyContext<'_>, first: &Expr, second: &Expr) {
|
||||
if_chain! {
|
||||
if !first.span.from_expansion() && !second.span.from_expansion();
|
||||
if let ExprKind::If(cond_expr, ..) = &first.kind;
|
||||
if matches!(first.kind, ExprKind::If(..));
|
||||
if is_block(second) || is_if(second);
|
||||
|
||||
// Proc-macros can give weird spans. Make sure this is actually an `if`.
|
||||
if let Some(if_snip) = snippet_opt(cx, first.span.until(cond_expr.span));
|
||||
if if_snip.starts_with("if");
|
||||
if is_span_if(cx, first.span);
|
||||
|
||||
// If there is a line break between the two expressions, don't lint.
|
||||
// If there is a non-whitespace character, this span came from a proc-macro.
|
||||
|
@ -46,7 +46,7 @@ declare_lint_pass!(FromStrRadix10 => [FROM_STR_RADIX_10]);
|
||||
impl<'tcx> LateLintPass<'tcx> for FromStrRadix10 {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, exp: &Expr<'tcx>) {
|
||||
if_chain! {
|
||||
if let ExprKind::Call(maybe_path, arguments) = &exp.kind;
|
||||
if let ExprKind::Call(maybe_path, [src, radix]) = &exp.kind;
|
||||
if let ExprKind::Path(QPath::TypeRelative(ty, pathseg)) = &maybe_path.kind;
|
||||
|
||||
// check if the first part of the path is some integer primitive
|
||||
@ -60,20 +60,19 @@ impl<'tcx> LateLintPass<'tcx> for FromStrRadix10 {
|
||||
if pathseg.ident.name.as_str() == "from_str_radix";
|
||||
|
||||
// check if the second argument is a primitive `10`
|
||||
if arguments.len() == 2;
|
||||
if let ExprKind::Lit(lit) = &arguments[1].kind;
|
||||
if let ExprKind::Lit(lit) = &radix.kind;
|
||||
if let rustc_ast::ast::LitKind::Int(10, _) = lit.node;
|
||||
|
||||
then {
|
||||
let expr = if let ExprKind::AddrOf(_, _, expr) = &arguments[0].kind {
|
||||
let expr = if let ExprKind::AddrOf(_, _, expr) = &src.kind {
|
||||
let ty = cx.typeck_results().expr_ty(expr);
|
||||
if is_ty_stringish(cx, ty) {
|
||||
expr
|
||||
} else {
|
||||
&arguments[0]
|
||||
&src
|
||||
}
|
||||
} else {
|
||||
&arguments[0]
|
||||
&src
|
||||
};
|
||||
|
||||
let sugg = Sugg::hir_with_applicability(
|
||||
|
@ -15,11 +15,10 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
|
||||
LintId::of(await_holding_invalid::AWAIT_HOLDING_INVALID_TYPE),
|
||||
LintId::of(await_holding_invalid::AWAIT_HOLDING_LOCK),
|
||||
LintId::of(await_holding_invalid::AWAIT_HOLDING_REFCELL_REF),
|
||||
LintId::of(blacklisted_name::BLACKLISTED_NAME),
|
||||
LintId::of(blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS),
|
||||
LintId::of(bool_assert_comparison::BOOL_ASSERT_COMPARISON),
|
||||
LintId::of(booleans::LOGIC_BUG),
|
||||
LintId::of(booleans::NONMINIMAL_BOOL),
|
||||
LintId::of(booleans::OVERLY_COMPLEX_BOOL_EXPR),
|
||||
LintId::of(borrow_deref_ref::BORROW_DEREF_REF),
|
||||
LintId::of(bytes_count_to_len::BYTES_COUNT_TO_LEN),
|
||||
LintId::of(casts::CAST_ABS_TO_UNSIGNED),
|
||||
@ -46,6 +45,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
|
||||
LintId::of(derive::DERIVE_ORD_XOR_PARTIAL_ORD),
|
||||
LintId::of(derive::DERIVE_PARTIAL_EQ_WITHOUT_EQ),
|
||||
LintId::of(disallowed_methods::DISALLOWED_METHODS),
|
||||
LintId::of(disallowed_names::DISALLOWED_NAMES),
|
||||
LintId::of(disallowed_types::DISALLOWED_TYPES),
|
||||
LintId::of(doc::MISSING_SAFETY_DOC),
|
||||
LintId::of(doc::NEEDLESS_DOCTEST_MAIN),
|
||||
@ -144,7 +144,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
|
||||
LintId::of(matches::MATCH_STR_CASE_MISMATCH),
|
||||
LintId::of(matches::NEEDLESS_MATCH),
|
||||
LintId::of(matches::REDUNDANT_PATTERN_MATCHING),
|
||||
LintId::of(matches::SIGNIFICANT_DROP_IN_SCRUTINEE),
|
||||
LintId::of(matches::SINGLE_MATCH),
|
||||
LintId::of(matches::WILDCARD_IN_OR_PATTERNS),
|
||||
LintId::of(mem_replace::MEM_REPLACE_OPTION_WITH_NONE),
|
||||
@ -267,6 +266,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
|
||||
LintId::of(option_env_unwrap::OPTION_ENV_UNWRAP),
|
||||
LintId::of(overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL),
|
||||
LintId::of(partialeq_ne_impl::PARTIALEQ_NE_IMPL),
|
||||
LintId::of(partialeq_to_none::PARTIALEQ_TO_NONE),
|
||||
LintId::of(precedence::PRECEDENCE),
|
||||
LintId::of(ptr::CMP_NULL),
|
||||
LintId::of(ptr::INVALID_NULL_PTR_USAGE),
|
||||
|
@ -8,7 +8,7 @@ store.register_group(true, "clippy::correctness", Some("clippy_correctness"), ve
|
||||
LintId::of(attrs::DEPRECATED_SEMVER),
|
||||
LintId::of(attrs::MISMATCHED_TARGET_OS),
|
||||
LintId::of(attrs::USELESS_ATTRIBUTE),
|
||||
LintId::of(booleans::LOGIC_BUG),
|
||||
LintId::of(booleans::OVERLY_COMPLEX_BOOL_EXPR),
|
||||
LintId::of(casts::CAST_REF_TO_MUT),
|
||||
LintId::of(casts::CAST_SLICE_DIFFERENT_SIZES),
|
||||
LintId::of(copies::IFS_SAME_COND),
|
||||
|
@ -55,11 +55,10 @@ store.register_lints(&[
|
||||
await_holding_invalid::AWAIT_HOLDING_INVALID_TYPE,
|
||||
await_holding_invalid::AWAIT_HOLDING_LOCK,
|
||||
await_holding_invalid::AWAIT_HOLDING_REFCELL_REF,
|
||||
blacklisted_name::BLACKLISTED_NAME,
|
||||
blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS,
|
||||
bool_assert_comparison::BOOL_ASSERT_COMPARISON,
|
||||
booleans::LOGIC_BUG,
|
||||
booleans::NONMINIMAL_BOOL,
|
||||
booleans::OVERLY_COMPLEX_BOOL_EXPR,
|
||||
borrow_as_ptr::BORROW_AS_PTR,
|
||||
borrow_deref_ref::BORROW_DEREF_REF,
|
||||
bytecount::NAIVE_BYTECOUNT,
|
||||
@ -116,6 +115,7 @@ store.register_lints(&[
|
||||
derive::EXPL_IMPL_CLONE_ON_COPY,
|
||||
derive::UNSAFE_DERIVE_DESERIALIZE,
|
||||
disallowed_methods::DISALLOWED_METHODS,
|
||||
disallowed_names::DISALLOWED_NAMES,
|
||||
disallowed_script_idents::DISALLOWED_SCRIPT_IDENTS,
|
||||
disallowed_types::DISALLOWED_TYPES,
|
||||
doc::DOC_MARKDOWN,
|
||||
@ -244,6 +244,7 @@ store.register_lints(&[
|
||||
manual_assert::MANUAL_ASSERT,
|
||||
manual_async_fn::MANUAL_ASYNC_FN,
|
||||
manual_bits::MANUAL_BITS,
|
||||
manual_instant_elapsed::MANUAL_INSTANT_ELAPSED,
|
||||
manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE,
|
||||
manual_ok_or::MANUAL_OK_OR,
|
||||
manual_rem_euclid::MANUAL_REM_EUCLID,
|
||||
@ -453,6 +454,7 @@ store.register_lints(&[
|
||||
panic_unimplemented::UNIMPLEMENTED,
|
||||
panic_unimplemented::UNREACHABLE,
|
||||
partialeq_ne_impl::PARTIALEQ_NE_IMPL,
|
||||
partialeq_to_none::PARTIALEQ_TO_NONE,
|
||||
pass_by_ref_or_value::LARGE_TYPES_PASSED_BY_VALUE,
|
||||
pass_by_ref_or_value::TRIVIALLY_COPY_PASS_BY_REF,
|
||||
path_buf_push_overwrite::PATH_BUF_PUSH_OVERWRITE,
|
||||
|
@ -13,6 +13,7 @@ store.register_group(true, "clippy::nursery", Some("clippy_nursery"), vec![
|
||||
LintId::of(future_not_send::FUTURE_NOT_SEND),
|
||||
LintId::of(index_refutable_slice::INDEX_REFUTABLE_SLICE),
|
||||
LintId::of(let_if_seq::USELESS_LET_IF_SEQ),
|
||||
LintId::of(matches::SIGNIFICANT_DROP_IN_SCRUTINEE),
|
||||
LintId::of(methods::ITER_WITH_DRAIN),
|
||||
LintId::of(missing_const_for_fn::MISSING_CONST_FOR_FN),
|
||||
LintId::of(mutable_debug_assertion::DEBUG_ASSERT_WITH_MUT_CALL),
|
||||
|
@ -49,6 +49,7 @@ store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![
|
||||
LintId::of(loops::EXPLICIT_ITER_LOOP),
|
||||
LintId::of(macro_use::MACRO_USE_IMPORTS),
|
||||
LintId::of(manual_assert::MANUAL_ASSERT),
|
||||
LintId::of(manual_instant_elapsed::MANUAL_INSTANT_ELAPSED),
|
||||
LintId::of(manual_ok_or::MANUAL_OK_OR),
|
||||
LintId::of(matches::MATCH_BOOL),
|
||||
LintId::of(matches::MATCH_ON_VEC_ITEMS),
|
||||
|
@ -4,7 +4,6 @@
|
||||
|
||||
store.register_group(true, "clippy::style", Some("clippy_style"), vec![
|
||||
LintId::of(assertions_on_constants::ASSERTIONS_ON_CONSTANTS),
|
||||
LintId::of(blacklisted_name::BLACKLISTED_NAME),
|
||||
LintId::of(blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS),
|
||||
LintId::of(bool_assert_comparison::BOOL_ASSERT_COMPARISON),
|
||||
LintId::of(casts::FN_TO_NUMERIC_CAST),
|
||||
@ -17,6 +16,7 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![
|
||||
LintId::of(dereference::NEEDLESS_BORROW),
|
||||
LintId::of(derive::DERIVE_PARTIAL_EQ_WITHOUT_EQ),
|
||||
LintId::of(disallowed_methods::DISALLOWED_METHODS),
|
||||
LintId::of(disallowed_names::DISALLOWED_NAMES),
|
||||
LintId::of(disallowed_types::DISALLOWED_TYPES),
|
||||
LintId::of(doc::MISSING_SAFETY_DOC),
|
||||
LintId::of(doc::NEEDLESS_DOCTEST_MAIN),
|
||||
@ -100,6 +100,7 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![
|
||||
LintId::of(operators::ASSIGN_OP_PATTERN),
|
||||
LintId::of(operators::OP_REF),
|
||||
LintId::of(operators::PTR_EQ),
|
||||
LintId::of(partialeq_to_none::PARTIALEQ_TO_NONE),
|
||||
LintId::of(ptr::CMP_NULL),
|
||||
LintId::of(ptr::PTR_ARG),
|
||||
LintId::of(question_mark::QUESTION_MARK),
|
||||
|
@ -22,7 +22,6 @@ store.register_group(true, "clippy::suspicious", Some("clippy_suspicious"), vec!
|
||||
LintId::of(loops::EMPTY_LOOP),
|
||||
LintId::of(loops::FOR_LOOPS_OVER_FALLIBLES),
|
||||
LintId::of(loops::MUT_RANGE_BOUND),
|
||||
LintId::of(matches::SIGNIFICANT_DROP_IN_SCRUTINEE),
|
||||
LintId::of(methods::NO_EFFECT_REPLACE),
|
||||
LintId::of(methods::SUSPICIOUS_MAP),
|
||||
LintId::of(mut_key::MUTABLE_KEY_TYPE),
|
||||
|
@ -178,7 +178,6 @@ mod assertions_on_result_states;
|
||||
mod async_yields_async;
|
||||
mod attrs;
|
||||
mod await_holding_invalid;
|
||||
mod blacklisted_name;
|
||||
mod blocks_in_if_conditions;
|
||||
mod bool_assert_comparison;
|
||||
mod booleans;
|
||||
@ -206,6 +205,7 @@ mod dereference;
|
||||
mod derivable_impls;
|
||||
mod derive;
|
||||
mod disallowed_methods;
|
||||
mod disallowed_names;
|
||||
mod disallowed_script_idents;
|
||||
mod disallowed_types;
|
||||
mod doc;
|
||||
@ -274,6 +274,7 @@ mod main_recursion;
|
||||
mod manual_assert;
|
||||
mod manual_async_fn;
|
||||
mod manual_bits;
|
||||
mod manual_instant_elapsed;
|
||||
mod manual_non_exhaustive;
|
||||
mod manual_ok_or;
|
||||
mod manual_rem_euclid;
|
||||
@ -332,6 +333,7 @@ mod overflow_check_conditional;
|
||||
mod panic_in_result_fn;
|
||||
mod panic_unimplemented;
|
||||
mod partialeq_ne_impl;
|
||||
mod partialeq_to_none;
|
||||
mod pass_by_ref_or_value;
|
||||
mod path_buf_push_overwrite;
|
||||
mod pattern_type_mismatch;
|
||||
@ -487,7 +489,7 @@ pub fn read_conf(sess: &Session) -> Conf {
|
||||
},
|
||||
};
|
||||
|
||||
let TryConf { conf, errors } = utils::conf::read(&file_name);
|
||||
let TryConf { conf, errors, warnings } = utils::conf::read(&file_name);
|
||||
// all conf errors are non-fatal, we just use the default conf in case of error
|
||||
for error in errors {
|
||||
sess.err(&format!(
|
||||
@ -497,6 +499,15 @@ pub fn read_conf(sess: &Session) -> Conf {
|
||||
));
|
||||
}
|
||||
|
||||
for warning in warnings {
|
||||
sess.struct_warn(&format!(
|
||||
"error reading Clippy's configuration file `{}`: {}",
|
||||
file_name.display(),
|
||||
format_error(warning)
|
||||
))
|
||||
.emit();
|
||||
}
|
||||
|
||||
conf
|
||||
}
|
||||
|
||||
@ -675,8 +686,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
store.register_late_pass(|| Box::new(swap::Swap));
|
||||
store.register_late_pass(|| Box::new(overflow_check_conditional::OverflowCheckConditional));
|
||||
store.register_late_pass(|| Box::new(new_without_default::NewWithoutDefault::default()));
|
||||
let blacklisted_names = conf.blacklisted_names.iter().cloned().collect::<FxHashSet<_>>();
|
||||
store.register_late_pass(move || Box::new(blacklisted_name::BlacklistedName::new(blacklisted_names.clone())));
|
||||
let disallowed_names = conf.disallowed_names.iter().cloned().collect::<FxHashSet<_>>();
|
||||
store.register_late_pass(move || Box::new(disallowed_names::DisallowedNames::new(disallowed_names.clone())));
|
||||
let too_many_arguments_threshold = conf.too_many_arguments_threshold;
|
||||
let too_many_lines_threshold = conf.too_many_lines_threshold;
|
||||
store.register_late_pass(move || {
|
||||
@ -921,6 +932,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
store.register_late_pass(move || Box::new(operators::Operators::new(verbose_bit_mask_threshold)));
|
||||
store.register_late_pass(|| Box::new(invalid_utf8_in_unchecked::InvalidUtf8InUnchecked));
|
||||
store.register_late_pass(|| Box::new(std_instead_of_core::StdReexports::default()));
|
||||
store.register_late_pass(|| Box::new(manual_instant_elapsed::ManualInstantElapsed));
|
||||
store.register_late_pass(|| Box::new(partialeq_to_none::PartialeqToNone));
|
||||
// add lints here, do not remove this comment, it's used in `new_lint`
|
||||
}
|
||||
|
||||
|
@ -119,11 +119,9 @@ fn build_manual_memcpy_suggestion<'tcx>(
|
||||
|
||||
let print_limit = |end: &Expr<'_>, end_str: &str, base: &Expr<'_>, sugg: MinifyingSugg<'static>| {
|
||||
if_chain! {
|
||||
if let ExprKind::MethodCall(method, len_args, _) = end.kind;
|
||||
if let ExprKind::MethodCall(method, [recv], _) = end.kind;
|
||||
if method.ident.name == sym::len;
|
||||
if len_args.len() == 1;
|
||||
if let Some(arg) = len_args.get(0);
|
||||
if path_to_local(arg) == path_to_local(base);
|
||||
if path_to_local(recv) == path_to_local(base);
|
||||
then {
|
||||
if sugg.to_string() == end_str {
|
||||
sugg::EMPTY.into()
|
||||
@ -343,10 +341,8 @@ fn get_slice_like_element_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Opti
|
||||
|
||||
fn fetch_cloned_expr<'tcx>(expr: &'tcx Expr<'tcx>) -> &'tcx Expr<'tcx> {
|
||||
if_chain! {
|
||||
if let ExprKind::MethodCall(method, args, _) = expr.kind;
|
||||
if let ExprKind::MethodCall(method, [arg], _) = expr.kind;
|
||||
if method.ident.name == sym::clone;
|
||||
if args.len() == 1;
|
||||
if let Some(arg) = args.get(0);
|
||||
then { arg } else { expr }
|
||||
}
|
||||
}
|
||||
|
@ -188,10 +188,9 @@ pub(super) fn check<'tcx>(
|
||||
|
||||
fn is_len_call(expr: &Expr<'_>, var: Symbol) -> bool {
|
||||
if_chain! {
|
||||
if let ExprKind::MethodCall(method, len_args, _) = expr.kind;
|
||||
if len_args.len() == 1;
|
||||
if let ExprKind::MethodCall(method, [recv], _) = expr.kind;
|
||||
if method.ident.name == sym::len;
|
||||
if let ExprKind::Path(QPath::Resolved(_, path)) = len_args[0].kind;
|
||||
if let ExprKind::Path(QPath::Resolved(_, path)) = recv.kind;
|
||||
if path.segments.len() == 1;
|
||||
if path.segments[0].ident.name == var;
|
||||
then {
|
||||
|
69
src/tools/clippy/clippy_lints/src/manual_instant_elapsed.rs
Normal file
69
src/tools/clippy/clippy_lints/src/manual_instant_elapsed.rs
Normal file
@ -0,0 +1,69 @@
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{BinOpKind, Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::source_map::Spanned;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Lints subtraction between `Instant::now()` and another `Instant`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// It is easy to accidentally write `prev_instant - Instant::now()`, which will always be 0ns
|
||||
/// as `Instant` subtraction saturates.
|
||||
///
|
||||
/// `prev_instant.elapsed()` also more clearly signals intention.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// use std::time::Instant;
|
||||
/// let prev_instant = Instant::now();
|
||||
/// let duration = Instant::now() - prev_instant;
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// use std::time::Instant;
|
||||
/// let prev_instant = Instant::now();
|
||||
/// let duration = prev_instant.elapsed();
|
||||
/// ```
|
||||
#[clippy::version = "1.64.0"]
|
||||
pub MANUAL_INSTANT_ELAPSED,
|
||||
pedantic,
|
||||
"subtraction between `Instant::now()` and previous `Instant`"
|
||||
}
|
||||
|
||||
declare_lint_pass!(ManualInstantElapsed => [MANUAL_INSTANT_ELAPSED]);
|
||||
|
||||
impl LateLintPass<'_> for ManualInstantElapsed {
|
||||
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) {
|
||||
if let ExprKind::Binary(Spanned {node: BinOpKind::Sub, ..}, lhs, rhs) = expr.kind
|
||||
&& check_instant_now_call(cx, lhs)
|
||||
&& let ty_resolved = cx.typeck_results().expr_ty(rhs)
|
||||
&& let rustc_middle::ty::Adt(def, _) = ty_resolved.kind()
|
||||
&& clippy_utils::match_def_path(cx, def.did(), &clippy_utils::paths::INSTANT)
|
||||
&& let Some(sugg) = clippy_utils::sugg::Sugg::hir_opt(cx, rhs)
|
||||
{
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
MANUAL_INSTANT_ELAPSED,
|
||||
expr.span,
|
||||
"manual implementation of `Instant::elapsed`",
|
||||
"try",
|
||||
format!("{}.elapsed()", sugg.maybe_par()),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_instant_now_call(cx: &LateContext<'_>, expr_block: &'_ Expr<'_>) -> bool {
|
||||
if let ExprKind::Call(fn_expr, []) = expr_block.kind
|
||||
&& let Some(fn_id) = clippy_utils::path_def_id(cx, fn_expr)
|
||||
&& clippy_utils::match_def_path(cx, fn_id, &clippy_utils::paths::INSTANT_NOW)
|
||||
{
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
@ -47,17 +47,14 @@ impl<'tcx> LateLintPass<'tcx> for ManualOkOr {
|
||||
}
|
||||
|
||||
if_chain! {
|
||||
if let ExprKind::MethodCall(method_segment, args, _) = scrutinee.kind;
|
||||
if let ExprKind::MethodCall(method_segment, [receiver, or_expr, map_expr], _) = scrutinee.kind;
|
||||
if method_segment.ident.name == sym!(map_or);
|
||||
if args.len() == 3;
|
||||
let method_receiver = &args[0];
|
||||
let ty = cx.typeck_results().expr_ty(method_receiver);
|
||||
let ty = cx.typeck_results().expr_ty(receiver);
|
||||
if is_type_diagnostic_item(cx, ty, sym::Option);
|
||||
let or_expr = &args[1];
|
||||
if is_ok_wrapping(cx, &args[2]);
|
||||
if is_ok_wrapping(cx, map_expr);
|
||||
if let ExprKind::Call(Expr { kind: ExprKind::Path(err_path), .. }, &[ref err_arg]) = or_expr.kind;
|
||||
if is_lang_ctor(cx, err_path, ResultErr);
|
||||
if let Some(method_receiver_snippet) = snippet_opt(cx, method_receiver.span);
|
||||
if let Some(method_receiver_snippet) = snippet_opt(cx, receiver.span);
|
||||
if let Some(err_arg_snippet) = snippet_opt(cx, err_arg.span);
|
||||
if let Some(indent) = indent_of(cx, scrutinee.span);
|
||||
then {
|
||||
|
@ -113,10 +113,10 @@ impl<'tcx> LateLintPass<'tcx> for MapErrIgnore {
|
||||
}
|
||||
|
||||
// check if this is a method call (e.g. x.foo())
|
||||
if let ExprKind::MethodCall(method, args, _) = e.kind {
|
||||
if let ExprKind::MethodCall(method, [_, arg], _) = e.kind {
|
||||
// only work if the method name is `map_err` and there are only 2 arguments (e.g. x.map_err(|_|[1]
|
||||
// Enum::Variant[2]))
|
||||
if method.ident.as_str() == "map_err" && args.len() == 2 {
|
||||
if method.ident.name == sym!(map_err) {
|
||||
// make sure the first argument is a closure, and grab the CaptureRef, BodyId, and fn_decl_span
|
||||
// fields
|
||||
if let ExprKind::Closure(&Closure {
|
||||
@ -124,7 +124,7 @@ impl<'tcx> LateLintPass<'tcx> for MapErrIgnore {
|
||||
body,
|
||||
fn_decl_span,
|
||||
..
|
||||
}) = args[1].kind
|
||||
}) = arg.kind
|
||||
{
|
||||
// check if this is by Reference (meaning there's no move statement)
|
||||
if capture_clause == CaptureBy::Ref {
|
||||
|
@ -97,11 +97,7 @@ declare_clippy_lint! {
|
||||
declare_lint_pass!(MapUnit => [OPTION_MAP_UNIT_FN, RESULT_MAP_UNIT_FN]);
|
||||
|
||||
fn is_unit_type(ty: Ty<'_>) -> bool {
|
||||
match ty.kind() {
|
||||
ty::Tuple(slice) => slice.is_empty(),
|
||||
ty::Never => true,
|
||||
_ => false,
|
||||
}
|
||||
ty.is_unit() || ty.is_never()
|
||||
}
|
||||
|
||||
fn is_unit_function(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool {
|
||||
|
@ -72,10 +72,10 @@ fn is_ref_some_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> Option<BindingAnnotat
|
||||
if is_lang_ctor(cx, qpath, LangItem::OptionSome);
|
||||
if let PatKind::Binding(rb, .., ident, _) = first_pat.kind;
|
||||
if rb == BindingAnnotation::Ref || rb == BindingAnnotation::RefMut;
|
||||
if let ExprKind::Call(e, args) = peel_blocks(arm.body).kind;
|
||||
if let ExprKind::Call(e, [arg]) = peel_blocks(arm.body).kind;
|
||||
if let ExprKind::Path(ref some_path) = e.kind;
|
||||
if is_lang_ctor(cx, some_path, LangItem::OptionSome) && args.len() == 1;
|
||||
if let ExprKind::Path(QPath::Resolved(_, path2)) = args[0].kind;
|
||||
if is_lang_ctor(cx, some_path, LangItem::OptionSome);
|
||||
if let ExprKind::Path(QPath::Resolved(_, path2)) = arg.kind;
|
||||
if path2.segments.len() == 1 && ident.name == path2.segments[0].ident.name;
|
||||
then {
|
||||
return Some(rb)
|
||||
|
@ -21,8 +21,8 @@ mod single_match;
|
||||
mod try_err;
|
||||
mod wild_in_or_pats;
|
||||
|
||||
use clippy_utils::source::{snippet_opt, span_starts_with, walk_span_to_context};
|
||||
use clippy_utils::{higher, in_constant, meets_msrv, msrvs};
|
||||
use clippy_utils::source::{snippet_opt, walk_span_to_context};
|
||||
use clippy_utils::{higher, in_constant, is_span_match, meets_msrv, msrvs};
|
||||
use rustc_hir::{Arm, Expr, ExprKind, Local, MatchSource, Pat};
|
||||
use rustc_lexer::{tokenize, TokenKind};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
@ -835,7 +835,7 @@ declare_clippy_lint! {
|
||||
/// ```
|
||||
#[clippy::version = "1.60.0"]
|
||||
pub SIGNIFICANT_DROP_IN_SCRUTINEE,
|
||||
suspicious,
|
||||
nursery,
|
||||
"warns when a temporary of a type with a drop with a significant side-effect might have a surprising lifetime"
|
||||
}
|
||||
|
||||
@ -949,7 +949,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches {
|
||||
let from_expansion = expr.span.from_expansion();
|
||||
|
||||
if let ExprKind::Match(ex, arms, source) = expr.kind {
|
||||
if source == MatchSource::Normal && !span_starts_with(cx, expr.span, "match") {
|
||||
if source == MatchSource::Normal && !is_span_match(cx, expr.span) {
|
||||
return;
|
||||
}
|
||||
if matches!(source, MatchSource::Normal | MatchSource::ForLoopDesugar) {
|
||||
|
@ -23,12 +23,10 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, scrutine
|
||||
// val,
|
||||
// };
|
||||
if_chain! {
|
||||
if let ExprKind::Call(match_fun, try_args) = scrutinee.kind;
|
||||
if let ExprKind::Call(match_fun, [try_arg, ..]) = scrutinee.kind;
|
||||
if let ExprKind::Path(ref match_fun_path) = match_fun.kind;
|
||||
if matches!(match_fun_path, QPath::LangItem(LangItem::TryTraitBranch, ..));
|
||||
if let Some(try_arg) = try_args.get(0);
|
||||
if let ExprKind::Call(err_fun, err_args) = try_arg.kind;
|
||||
if let Some(err_arg) = err_args.get(0);
|
||||
if let ExprKind::Call(err_fun, [err_arg, ..]) = try_arg.kind;
|
||||
if let ExprKind::Path(ref err_fun_path) = err_fun.kind;
|
||||
if is_lang_ctor(cx, err_fun_path, ResultErr);
|
||||
if let Some(return_ty) = find_return_type(cx, &expr.kind);
|
||||
|
@ -163,8 +163,7 @@ fn check_replace_with_uninit(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<'
|
||||
}
|
||||
|
||||
if_chain! {
|
||||
if let ExprKind::Call(repl_func, repl_args) = src.kind;
|
||||
if repl_args.is_empty();
|
||||
if let ExprKind::Call(repl_func, []) = src.kind;
|
||||
if let ExprKind::Path(ref repl_func_qpath) = repl_func.kind;
|
||||
if let Some(repl_def_id) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id();
|
||||
then {
|
||||
@ -246,11 +245,10 @@ impl<'tcx> LateLintPass<'tcx> for MemReplace {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
if_chain! {
|
||||
// Check that `expr` is a call to `mem::replace()`
|
||||
if let ExprKind::Call(func, func_args) = expr.kind;
|
||||
if let ExprKind::Call(func, [dest, src]) = expr.kind;
|
||||
if let ExprKind::Path(ref func_qpath) = func.kind;
|
||||
if let Some(def_id) = cx.qpath_res(func_qpath, func.hir_id).opt_def_id();
|
||||
if cx.tcx.is_diagnostic_item(sym::mem_replace, def_id);
|
||||
if let [dest, src] = func_args;
|
||||
then {
|
||||
check_replace_option_with_none(cx, src, dest, expr.span);
|
||||
check_replace_with_uninit(cx, src, dest, expr.span);
|
||||
|
@ -4,7 +4,7 @@ use clippy_utils::source::snippet_with_context;
|
||||
use clippy_utils::sugg;
|
||||
use clippy_utils::ty::is_copy;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{BindingAnnotation, Expr, ExprKind, MatchSource, Node, PatKind};
|
||||
use rustc_hir::{BindingAnnotation, Expr, ExprKind, MatchSource, Node, PatKind, QPath};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty::{self, adjustment::Adjust};
|
||||
use rustc_span::symbol::{sym, Symbol};
|
||||
@ -86,6 +86,11 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, method_name: Symbol,
|
||||
{
|
||||
return;
|
||||
},
|
||||
// ? is a Call, makes sure not to rec *x?, but rather (*x)?
|
||||
ExprKind::Call(hir_callee, _) => matches!(
|
||||
hir_callee.kind,
|
||||
ExprKind::Path(QPath::LangItem(rustc_hir::LangItem::TryTraitBranch, _, _))
|
||||
),
|
||||
ExprKind::MethodCall(_, [self_arg, ..], _) if expr.hir_id == self_arg.hir_id => true,
|
||||
ExprKind::Match(_, _, MatchSource::TryDesugar | MatchSource::AwaitDesugar)
|
||||
| ExprKind::Field(..)
|
||||
|
@ -12,9 +12,9 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr
|
||||
let obj_ty = cx.typeck_results().expr_ty(recv).peel_refs();
|
||||
|
||||
let mess = if is_type_diagnostic_item(cx, obj_ty, sym::Option) {
|
||||
Some((EXPECT_USED, "an Option", "None"))
|
||||
Some((EXPECT_USED, "an Option", "None", ""))
|
||||
} else if is_type_diagnostic_item(cx, obj_ty, sym::Result) {
|
||||
Some((EXPECT_USED, "a Result", "Err"))
|
||||
Some((EXPECT_USED, "a Result", "Err", "an "))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
@ -23,14 +23,14 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some((lint, kind, none_value)) = mess {
|
||||
if let Some((lint, kind, none_value, none_prefix)) = mess {
|
||||
span_lint_and_help(
|
||||
cx,
|
||||
lint,
|
||||
expr.span,
|
||||
&format!("used `expect()` on `{}` value", kind,),
|
||||
&format!("used `expect()` on `{kind}` value"),
|
||||
None,
|
||||
&format!("if this value is an `{}`, it will panic", none_value,),
|
||||
&format!("if this value is {none_prefix}`{none_value}`, it will panic"),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -204,6 +204,17 @@ declare_clippy_lint! {
|
||||
/// option.expect("more helpful message");
|
||||
/// result.expect("more helpful message");
|
||||
/// ```
|
||||
///
|
||||
/// If [expect_used](#expect_used) is enabled, instead:
|
||||
/// ```rust,ignore
|
||||
/// # let option = Some(1);
|
||||
/// # let result: Result<usize, ()> = Ok(1);
|
||||
/// option?;
|
||||
///
|
||||
/// // or
|
||||
///
|
||||
/// result?;
|
||||
/// ```
|
||||
#[clippy::version = "1.45.0"]
|
||||
pub UNWRAP_USED,
|
||||
restriction,
|
||||
|
@ -1,20 +1,20 @@
|
||||
use clippy_utils::diagnostics::span_lint_and_help;
|
||||
use clippy_utils::is_in_test_function;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use clippy_utils::{is_in_test_function, is_lint_allowed};
|
||||
use rustc_hir as hir;
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::sym;
|
||||
|
||||
use super::UNWRAP_USED;
|
||||
use super::{EXPECT_USED, UNWRAP_USED};
|
||||
|
||||
/// lint use of `unwrap()` for `Option`s and `Result`s
|
||||
pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>, allow_unwrap_in_tests: bool) {
|
||||
let obj_ty = cx.typeck_results().expr_ty(recv).peel_refs();
|
||||
|
||||
let mess = if is_type_diagnostic_item(cx, obj_ty, sym::Option) {
|
||||
Some((UNWRAP_USED, "an Option", "None"))
|
||||
Some((UNWRAP_USED, "an Option", "None", ""))
|
||||
} else if is_type_diagnostic_item(cx, obj_ty, sym::Result) {
|
||||
Some((UNWRAP_USED, "a Result", "Err"))
|
||||
Some((UNWRAP_USED, "a Result", "Err", "an "))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
@ -23,18 +23,23 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some((lint, kind, none_value)) = mess {
|
||||
if let Some((lint, kind, none_value, none_prefix)) = mess {
|
||||
let help = if is_lint_allowed(cx, EXPECT_USED, expr.hir_id) {
|
||||
format!(
|
||||
"if you don't want to handle the `{none_value}` case gracefully, consider \
|
||||
using `expect()` to provide a better panic message"
|
||||
)
|
||||
} else {
|
||||
format!("if this value is {none_prefix}`{none_value}`, it will panic")
|
||||
};
|
||||
|
||||
span_lint_and_help(
|
||||
cx,
|
||||
lint,
|
||||
expr.span,
|
||||
&format!("used `unwrap()` on `{}` value", kind,),
|
||||
&format!("used `unwrap()` on `{kind}` value"),
|
||||
None,
|
||||
&format!(
|
||||
"if you don't want to handle the `{}` case gracefully, consider \
|
||||
using `expect()` to provide a better panic message",
|
||||
none_value,
|
||||
),
|
||||
&help,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,9 @@
|
||||
use clippy_utils::diagnostics::span_lint;
|
||||
use clippy_utils::qualify_min_const_fn::is_min_const_fn;
|
||||
use clippy_utils::ty::has_drop;
|
||||
use clippy_utils::{fn_has_unsatisfiable_preds, is_entrypoint_fn, meets_msrv, msrvs, trait_ref_of_method};
|
||||
use clippy_utils::{
|
||||
fn_has_unsatisfiable_preds, is_entrypoint_fn, is_from_proc_macro, meets_msrv, msrvs, trait_ref_of_method,
|
||||
};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def_id::CRATE_DEF_ID;
|
||||
use rustc_hir::intravisit::FnKind;
|
||||
@ -86,10 +88,10 @@ impl MissingConstForFn {
|
||||
impl<'tcx> LateLintPass<'tcx> for MissingConstForFn {
|
||||
fn check_fn(
|
||||
&mut self,
|
||||
cx: &LateContext<'_>,
|
||||
kind: FnKind<'_>,
|
||||
cx: &LateContext<'tcx>,
|
||||
kind: FnKind<'tcx>,
|
||||
_: &FnDecl<'_>,
|
||||
_: &Body<'_>,
|
||||
body: &Body<'tcx>,
|
||||
span: Span,
|
||||
hir_id: HirId,
|
||||
) {
|
||||
@ -124,7 +126,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn {
|
||||
FnKind::Method(_, sig, ..) => {
|
||||
if trait_ref_of_method(cx, def_id).is_some()
|
||||
|| already_const(sig.header)
|
||||
|| method_accepts_dropable(cx, sig.decl.inputs)
|
||||
|| method_accepts_droppable(cx, sig.decl.inputs)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@ -144,6 +146,10 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn {
|
||||
}
|
||||
}
|
||||
|
||||
if is_from_proc_macro(cx, &(&kind, body, hir_id, span)) {
|
||||
return;
|
||||
}
|
||||
|
||||
let mir = cx.tcx.optimized_mir(def_id);
|
||||
|
||||
if let Err((span, err)) = is_min_const_fn(cx.tcx, mir, self.msrv) {
|
||||
@ -159,7 +165,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn {
|
||||
|
||||
/// Returns true if any of the method parameters is a type that implements `Drop`. The method
|
||||
/// can't be made const then, because `drop` can't be const-evaluated.
|
||||
fn method_accepts_dropable(cx: &LateContext<'_>, param_tys: &[hir::Ty<'_>]) -> bool {
|
||||
fn method_accepts_droppable(cx: &LateContext<'_>, param_tys: &[hir::Ty<'_>]) -> bool {
|
||||
// If any of the params are droppable, return true
|
||||
param_tys.iter().any(|hir_ty| {
|
||||
let ty_ty = hir_ty_to_ty(cx.tcx, hir_ty);
|
||||
|
@ -7,7 +7,8 @@
|
||||
|
||||
use clippy_utils::attrs::is_doc_hidden;
|
||||
use clippy_utils::diagnostics::span_lint;
|
||||
use rustc_ast::ast;
|
||||
use clippy_utils::is_from_proc_macro;
|
||||
use rustc_ast::ast::{self, MetaItem, MetaItemKind};
|
||||
use rustc_hir as hir;
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::ty::DefIdTree;
|
||||
@ -57,6 +58,20 @@ impl MissingDoc {
|
||||
*self.doc_hidden_stack.last().expect("empty doc_hidden_stack")
|
||||
}
|
||||
|
||||
fn has_include(meta: Option<MetaItem>) -> bool {
|
||||
if_chain! {
|
||||
if let Some(meta) = meta;
|
||||
if let MetaItemKind::List(list) = meta.kind;
|
||||
if let Some(meta) = list.get(0);
|
||||
if let Some(name) = meta.ident();
|
||||
then {
|
||||
name.name == sym::include
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_missing_docs_attrs(
|
||||
&self,
|
||||
cx: &LateContext<'_>,
|
||||
@ -80,7 +95,9 @@ impl MissingDoc {
|
||||
return;
|
||||
}
|
||||
|
||||
let has_doc = attrs.iter().any(|a| a.doc_str().is_some());
|
||||
let has_doc = attrs
|
||||
.iter()
|
||||
.any(|a| a.doc_str().is_some() || Self::has_include(a.meta()));
|
||||
if !has_doc {
|
||||
span_lint(
|
||||
cx,
|
||||
@ -141,14 +158,18 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc {
|
||||
let (article, desc) = cx.tcx.article_and_description(it.def_id.to_def_id());
|
||||
|
||||
let attrs = cx.tcx.hir().attrs(it.hir_id());
|
||||
self.check_missing_docs_attrs(cx, attrs, it.span, article, desc);
|
||||
if !is_from_proc_macro(cx, it) {
|
||||
self.check_missing_docs_attrs(cx, attrs, it.span, article, desc);
|
||||
}
|
||||
}
|
||||
|
||||
fn check_trait_item(&mut self, cx: &LateContext<'tcx>, trait_item: &'tcx hir::TraitItem<'_>) {
|
||||
let (article, desc) = cx.tcx.article_and_description(trait_item.def_id.to_def_id());
|
||||
|
||||
let attrs = cx.tcx.hir().attrs(trait_item.hir_id());
|
||||
self.check_missing_docs_attrs(cx, attrs, trait_item.span, article, desc);
|
||||
if !is_from_proc_macro(cx, trait_item) {
|
||||
self.check_missing_docs_attrs(cx, attrs, trait_item.span, article, desc);
|
||||
}
|
||||
}
|
||||
|
||||
fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx hir::ImplItem<'_>) {
|
||||
@ -163,18 +184,24 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc {
|
||||
|
||||
let (article, desc) = cx.tcx.article_and_description(impl_item.def_id.to_def_id());
|
||||
let attrs = cx.tcx.hir().attrs(impl_item.hir_id());
|
||||
self.check_missing_docs_attrs(cx, attrs, impl_item.span, article, desc);
|
||||
if !is_from_proc_macro(cx, impl_item) {
|
||||
self.check_missing_docs_attrs(cx, attrs, impl_item.span, article, desc);
|
||||
}
|
||||
}
|
||||
|
||||
fn check_field_def(&mut self, cx: &LateContext<'tcx>, sf: &'tcx hir::FieldDef<'_>) {
|
||||
if !sf.is_positional() {
|
||||
let attrs = cx.tcx.hir().attrs(sf.hir_id);
|
||||
self.check_missing_docs_attrs(cx, attrs, sf.span, "a", "struct field");
|
||||
if !is_from_proc_macro(cx, sf) {
|
||||
self.check_missing_docs_attrs(cx, attrs, sf.span, "a", "struct field");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_variant(&mut self, cx: &LateContext<'tcx>, v: &'tcx hir::Variant<'_>) {
|
||||
let attrs = cx.tcx.hir().attrs(v.id);
|
||||
self.check_missing_docs_attrs(cx, attrs, v.span, "a", "variant");
|
||||
if !is_from_proc_macro(cx, v) {
|
||||
self.check_missing_docs_attrs(cx, attrs, v.span, "a", "variant");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
104
src/tools/clippy/clippy_lints/src/partialeq_to_none.rs
Normal file
104
src/tools/clippy/clippy_lints/src/partialeq_to_none.rs
Normal file
@ -0,0 +1,104 @@
|
||||
use clippy_utils::{
|
||||
diagnostics::span_lint_and_sugg, is_lang_ctor, peel_hir_expr_refs, peel_ref_operators, sugg,
|
||||
ty::is_type_diagnostic_item,
|
||||
};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{BinOpKind, Expr, ExprKind, LangItem};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::sym;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
///
|
||||
/// Checks for binary comparisons to a literal `Option::None`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
///
|
||||
/// A programmer checking if some `foo` is `None` via a comparison `foo == None`
|
||||
/// is usually inspired from other programming languages (e.g. `foo is None`
|
||||
/// in Python).
|
||||
/// Checking if a value of type `Option<T>` is (not) equal to `None` in that
|
||||
/// way relies on `T: PartialEq` to do the comparison, which is unneeded.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// fn foo(f: Option<u32>) -> &'static str {
|
||||
/// if f != None { "yay" } else { "nay" }
|
||||
/// }
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// fn foo(f: Option<u32>) -> &'static str {
|
||||
/// if f.is_some() { "yay" } else { "nay" }
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.64.0"]
|
||||
pub PARTIALEQ_TO_NONE,
|
||||
style,
|
||||
"Binary comparison to `Option<T>::None` relies on `T: PartialEq`, which is unneeded"
|
||||
}
|
||||
declare_lint_pass!(PartialeqToNone => [PARTIALEQ_TO_NONE]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for PartialeqToNone {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
|
||||
// Skip expanded code, as we have no control over it anyway...
|
||||
if e.span.from_expansion() {
|
||||
return;
|
||||
}
|
||||
|
||||
// If the expression is of type `Option`
|
||||
let is_ty_option =
|
||||
|expr: &Expr<'_>| is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr).peel_refs(), sym::Option);
|
||||
|
||||
// If the expression is a literal `Option::None`
|
||||
let is_none_ctor = |expr: &Expr<'_>| {
|
||||
matches!(&peel_hir_expr_refs(expr).0.kind,
|
||||
ExprKind::Path(p) if is_lang_ctor(cx, p, LangItem::OptionNone))
|
||||
};
|
||||
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
|
||||
if let ExprKind::Binary(op, left_side, right_side) = e.kind {
|
||||
// All other comparisons (e.g. `>= None`) have special meaning wrt T
|
||||
let is_eq = match op.node {
|
||||
BinOpKind::Eq => true,
|
||||
BinOpKind::Ne => false,
|
||||
_ => return,
|
||||
};
|
||||
|
||||
// We are only interested in comparisons between `Option` and a literal `Option::None`
|
||||
let scrutinee = match (
|
||||
is_none_ctor(left_side) && is_ty_option(right_side),
|
||||
is_none_ctor(right_side) && is_ty_option(left_side),
|
||||
) {
|
||||
(true, false) => right_side,
|
||||
(false, true) => left_side,
|
||||
_ => return,
|
||||
};
|
||||
|
||||
// Peel away refs/derefs (as long as we don't cross manual deref impls), as
|
||||
// autoref/autoderef will take care of those
|
||||
let sugg = format!(
|
||||
"{}.{}",
|
||||
sugg::Sugg::hir_with_applicability(cx, peel_ref_operators(cx, scrutinee), "..", &mut applicability)
|
||||
.maybe_par(),
|
||||
if is_eq { "is_none()" } else { "is_some()" }
|
||||
);
|
||||
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
PARTIALEQ_TO_NONE,
|
||||
e.span,
|
||||
"binary comparison to literal `Option::None`",
|
||||
if is_eq {
|
||||
"use `Option::is_none()` instead"
|
||||
} else {
|
||||
"use `Option::is_some()` instead"
|
||||
},
|
||||
sugg,
|
||||
applicability,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
@ -46,11 +46,9 @@ declare_lint_pass!(PathBufPushOverwrite => [PATH_BUF_PUSH_OVERWRITE]);
|
||||
impl<'tcx> LateLintPass<'tcx> for PathBufPushOverwrite {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
if_chain! {
|
||||
if let ExprKind::MethodCall(path, args, _) = expr.kind;
|
||||
if let ExprKind::MethodCall(path, [recv, get_index_arg], _) = expr.kind;
|
||||
if path.ident.name == sym!(push);
|
||||
if args.len() == 2;
|
||||
if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&args[0]).peel_refs(), sym::PathBuf);
|
||||
if let Some(get_index_arg) = args.get(1);
|
||||
if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv).peel_refs(), sym::PathBuf);
|
||||
if let ExprKind::Lit(ref lit) = get_index_arg.kind;
|
||||
if let LitKind::Str(ref path_lit, _) = lit.node;
|
||||
if let pushed_path = Path::new(path_lit.as_str());
|
||||
|
@ -86,8 +86,7 @@ fn check_is_none_or_err_and_early_return<'tcx>(cx: &LateContext<'tcx>, expr: &Ex
|
||||
if_chain! {
|
||||
if let Some(higher::If { cond, then, r#else }) = higher::If::hir(expr);
|
||||
if !is_else_clause(cx.tcx, expr);
|
||||
if let ExprKind::MethodCall(segment, args, _) = &cond.kind;
|
||||
if let Some(caller) = args.get(0);
|
||||
if let ExprKind::MethodCall(segment, [caller, ..], _) = &cond.kind;
|
||||
let caller_ty = cx.typeck_results().expr_ty(caller);
|
||||
let if_block = IfBlockType::IfIs(caller, caller_ty, segment.ident.name, then, r#else);
|
||||
if is_early_return(sym::Option, cx, &if_block) || is_early_return(sym::Result, cx, &if_block);
|
||||
|
@ -385,24 +385,24 @@ fn check_range_zip_with_len(cx: &LateContext<'_>, path: &PathSegment<'_>, args:
|
||||
if path.ident.as_str() == "zip";
|
||||
if let [iter, zip_arg] = args;
|
||||
// `.iter()` call
|
||||
if let ExprKind::MethodCall(iter_path, iter_args, _) = iter.kind;
|
||||
if let ExprKind::MethodCall(iter_path, [iter_caller, ..], _) = iter.kind;
|
||||
if iter_path.ident.name == sym::iter;
|
||||
// range expression in `.zip()` call: `0..x.len()`
|
||||
if let Some(higher::Range { start: Some(start), end: Some(end), .. }) = higher::Range::hir(zip_arg);
|
||||
if is_integer_const(cx, start, 0);
|
||||
// `.len()` call
|
||||
if let ExprKind::MethodCall(len_path, len_args, _) = end.kind;
|
||||
if len_path.ident.name == sym::len && len_args.len() == 1;
|
||||
if let ExprKind::MethodCall(len_path, [len_caller], _) = end.kind;
|
||||
if len_path.ident.name == sym::len;
|
||||
// `.iter()` and `.len()` called on same `Path`
|
||||
if let ExprKind::Path(QPath::Resolved(_, iter_path)) = iter_args[0].kind;
|
||||
if let ExprKind::Path(QPath::Resolved(_, len_path)) = len_args[0].kind;
|
||||
if SpanlessEq::new(cx).eq_path_segments(&iter_path.segments, &len_path.segments);
|
||||
if let ExprKind::Path(QPath::Resolved(_, iter_path)) = iter_caller.kind;
|
||||
if let ExprKind::Path(QPath::Resolved(_, len_path)) = len_caller.kind;
|
||||
if SpanlessEq::new(cx).eq_path_segments(iter_path.segments, len_path.segments);
|
||||
then {
|
||||
span_lint(cx,
|
||||
RANGE_ZIP_WITH_LEN,
|
||||
span,
|
||||
&format!("it is more idiomatic to use `{}.iter().enumerate()`",
|
||||
snippet(cx, iter_args[0].span, "_"))
|
||||
snippet(cx, iter_caller.span, "_"))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::sugg::Sugg;
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::ast;
|
||||
use rustc_ast::visit as ast_visit;
|
||||
@ -69,7 +69,7 @@ impl EarlyLintPass for RedundantClosureCall {
|
||||
if_chain! {
|
||||
if let ast::ExprKind::Call(ref paren, _) = expr.kind;
|
||||
if let ast::ExprKind::Paren(ref closure) = paren.kind;
|
||||
if let ast::ExprKind::Closure(_, _, _, _, ref decl, ref block, _) = closure.kind;
|
||||
if let ast::ExprKind::Closure(_, _, ref r#async, _, ref decl, ref block, _) = closure.kind;
|
||||
then {
|
||||
let mut visitor = ReturnVisitor::new();
|
||||
visitor.visit_expr(block);
|
||||
@ -81,10 +81,19 @@ impl EarlyLintPass for RedundantClosureCall {
|
||||
"try not to call a closure in the expression where it is declared",
|
||||
|diag| {
|
||||
if decl.inputs.is_empty() {
|
||||
let mut app = Applicability::MachineApplicable;
|
||||
let hint =
|
||||
snippet_with_applicability(cx, block.span, "..", &mut app).into_owned();
|
||||
diag.span_suggestion(expr.span, "try doing something like", hint, app);
|
||||
let app = Applicability::MachineApplicable;
|
||||
let mut hint = Sugg::ast(cx, block, "..");
|
||||
|
||||
if r#async.is_async() {
|
||||
// `async x` is a syntax error, so it becomes `async { x }`
|
||||
if !matches!(block.kind, ast::ExprKind::Block(_, _)) {
|
||||
hint = hint.blockify();
|
||||
}
|
||||
|
||||
hint = hint.asyncify();
|
||||
}
|
||||
|
||||
diag.span_suggestion(expr.span, "try doing something like", hint.to_string(), app);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
@ -57,21 +57,20 @@ declare_lint_pass!(Regex => [INVALID_REGEX, TRIVIAL_REGEX]);
|
||||
impl<'tcx> LateLintPass<'tcx> for Regex {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
if_chain! {
|
||||
if let ExprKind::Call(fun, args) = expr.kind;
|
||||
if let ExprKind::Call(fun, [arg]) = expr.kind;
|
||||
if let ExprKind::Path(ref qpath) = fun.kind;
|
||||
if args.len() == 1;
|
||||
if let Some(def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id();
|
||||
then {
|
||||
if match_def_path(cx, def_id, &paths::REGEX_NEW) ||
|
||||
match_def_path(cx, def_id, &paths::REGEX_BUILDER_NEW) {
|
||||
check_regex(cx, &args[0], true);
|
||||
check_regex(cx, arg, true);
|
||||
} else if match_def_path(cx, def_id, &paths::REGEX_BYTES_NEW) ||
|
||||
match_def_path(cx, def_id, &paths::REGEX_BYTES_BUILDER_NEW) {
|
||||
check_regex(cx, &args[0], false);
|
||||
check_regex(cx, arg, false);
|
||||
} else if match_def_path(cx, def_id, &paths::REGEX_SET_NEW) {
|
||||
check_set(cx, &args[0], true);
|
||||
check_set(cx, arg, true);
|
||||
} else if match_def_path(cx, def_id, &paths::REGEX_BYTES_SET_NEW) {
|
||||
check_set(cx, &args[0], false);
|
||||
check_set(cx, arg, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
#[rustfmt::skip]
|
||||
pub static RENAMED_LINTS: &[(&str, &str)] = &[
|
||||
("clippy::blacklisted_name", "clippy::disallowed_names"),
|
||||
("clippy::block_in_if_condition_expr", "clippy::blocks_in_if_conditions"),
|
||||
("clippy::block_in_if_condition_stmt", "clippy::blocks_in_if_conditions"),
|
||||
("clippy::box_vec", "clippy::box_collection"),
|
||||
@ -14,6 +15,7 @@ pub static RENAMED_LINTS: &[(&str, &str)] = &[
|
||||
("clippy::for_loop_over_result", "clippy::for_loops_over_fallibles"),
|
||||
("clippy::identity_conversion", "clippy::useless_conversion"),
|
||||
("clippy::if_let_some_result", "clippy::match_result_ok"),
|
||||
("clippy::logic_bug", "clippy::overly_complex_bool_expr"),
|
||||
("clippy::new_without_default_derive", "clippy::new_without_default"),
|
||||
("clippy::option_and_then_some", "clippy::bind_instead_of_map"),
|
||||
("clippy::option_expect_used", "clippy::expect_used"),
|
||||
|
@ -233,15 +233,10 @@ impl<'a, 'tcx> VectorInitializationVisitor<'a, 'tcx> {
|
||||
/// Returns `true` if give expression is `repeat(0).take(...)`
|
||||
fn is_repeat_take(&self, expr: &Expr<'_>) -> bool {
|
||||
if_chain! {
|
||||
if let ExprKind::MethodCall(take_path, take_args, _) = expr.kind;
|
||||
if let ExprKind::MethodCall(take_path, [recv, len_arg, ..], _) = expr.kind;
|
||||
if take_path.ident.name == sym!(take);
|
||||
|
||||
// Check that take is applied to `repeat(0)`
|
||||
if let Some(repeat_expr) = take_args.get(0);
|
||||
if self.is_repeat_zero(repeat_expr);
|
||||
|
||||
if let Some(len_arg) = take_args.get(1);
|
||||
|
||||
if self.is_repeat_zero(recv);
|
||||
then {
|
||||
// Check that len expression is equals to `with_capacity` expression
|
||||
if SpanlessEq::new(self.cx).eq_expr(len_arg, self.vec_alloc.len_expr) {
|
||||
|
@ -97,12 +97,11 @@ struct LintDetection {
|
||||
|
||||
fn detect_stable_sort_primitive(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<LintDetection> {
|
||||
if_chain! {
|
||||
if let ExprKind::MethodCall(method_name, args, _) = &expr.kind;
|
||||
if let Some(slice) = &args.get(0);
|
||||
if let ExprKind::MethodCall(method_name, [slice, args @ ..], _) = &expr.kind;
|
||||
if let Some(method) = SortingKind::from_stable_name(method_name.ident.name.as_str());
|
||||
if let Some(slice_type) = is_slice_of_primitives(cx, slice);
|
||||
then {
|
||||
let args_str = args.iter().skip(1).map(|arg| Sugg::hir(cx, arg, "..").to_string()).collect::<Vec<String>>().join(", ");
|
||||
let args_str = args.iter().map(|arg| Sugg::hir(cx, arg, "..").to_string()).collect::<Vec<String>>().join(", ");
|
||||
Some(LintDetection { slice_name: Sugg::hir(cx, slice, "..").to_string(), method, method_args: args_str, slice_type })
|
||||
} else {
|
||||
None
|
||||
|
@ -103,7 +103,7 @@ impl<'tcx> LateLintPass<'tcx> for UnitTypes {
|
||||
let_unit_value::check(cx, local);
|
||||
}
|
||||
|
||||
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
|
||||
unit_cmp::check(cx, expr);
|
||||
unit_arg::check(cx, expr);
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::is_from_proc_macro;
|
||||
use clippy_utils::source::{indent_of, reindent_multiline, snippet_opt};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
@ -7,7 +8,7 @@ use rustc_lint::LateContext;
|
||||
|
||||
use super::{utils, UNIT_ARG};
|
||||
|
||||
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
|
||||
if expr.span.from_expansion() {
|
||||
return;
|
||||
}
|
||||
@ -44,7 +45,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
if !args_to_recover.is_empty() {
|
||||
if !args_to_recover.is_empty() && !is_from_proc_macro(cx, expr) {
|
||||
lint_unit_args(cx, expr, &args_to_recover);
|
||||
}
|
||||
},
|
||||
|
@ -59,17 +59,17 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion {
|
||||
ExprKind::Ret(Some(e)) | ExprKind::Break(_, Some(e)) => e,
|
||||
_ => return,
|
||||
};
|
||||
if let ExprKind::Call(_, args) = e.kind {
|
||||
self.try_desugar_arm.push(args[0].hir_id);
|
||||
if let ExprKind::Call(_, [arg, ..]) = e.kind {
|
||||
self.try_desugar_arm.push(arg.hir_id);
|
||||
}
|
||||
},
|
||||
|
||||
ExprKind::MethodCall(name, .., args, _) => {
|
||||
ExprKind::MethodCall(name, .., [recv, ..], _) => {
|
||||
if is_trait_method(cx, e, sym::Into) && name.ident.as_str() == "into" {
|
||||
let a = cx.typeck_results().expr_ty(e);
|
||||
let b = cx.typeck_results().expr_ty(&args[0]);
|
||||
let b = cx.typeck_results().expr_ty(recv);
|
||||
if same_type_and_consts(a, b) {
|
||||
let sugg = snippet_with_macro_callsite(cx, args[0].span, "<expr>").to_string();
|
||||
let sugg = snippet_with_macro_callsite(cx, recv.span, "<expr>").to_string();
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
USELESS_CONVERSION,
|
||||
@ -90,9 +90,9 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion {
|
||||
}
|
||||
}
|
||||
let a = cx.typeck_results().expr_ty(e);
|
||||
let b = cx.typeck_results().expr_ty(&args[0]);
|
||||
let b = cx.typeck_results().expr_ty(recv);
|
||||
if same_type_and_consts(a, b) {
|
||||
let sugg = snippet(cx, args[0].span, "<expr>").into_owned();
|
||||
let sugg = snippet(cx, recv.span, "<expr>").into_owned();
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
USELESS_CONVERSION,
|
||||
@ -107,7 +107,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion {
|
||||
if_chain! {
|
||||
if is_trait_method(cx, e, sym::TryInto) && name.ident.name == sym::try_into;
|
||||
let a = cx.typeck_results().expr_ty(e);
|
||||
let b = cx.typeck_results().expr_ty(&args[0]);
|
||||
let b = cx.typeck_results().expr_ty(recv);
|
||||
if is_type_diagnostic_item(cx, a, sym::Result);
|
||||
if let ty::Adt(_, substs) = a.kind();
|
||||
if let Some(a_type) = substs.types().next();
|
||||
@ -126,14 +126,13 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion {
|
||||
}
|
||||
},
|
||||
|
||||
ExprKind::Call(path, args) => {
|
||||
ExprKind::Call(path, [arg]) => {
|
||||
if_chain! {
|
||||
if args.len() == 1;
|
||||
if let ExprKind::Path(ref qpath) = path.kind;
|
||||
if let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id();
|
||||
then {
|
||||
let a = cx.typeck_results().expr_ty(e);
|
||||
let b = cx.typeck_results().expr_ty(&args[0]);
|
||||
let b = cx.typeck_results().expr_ty(arg);
|
||||
if_chain! {
|
||||
if match_def_path(cx, def_id, &paths::TRY_FROM);
|
||||
if is_type_diagnostic_item(cx, a, sym::Result);
|
||||
@ -159,7 +158,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion {
|
||||
if same_type_and_consts(a, b);
|
||||
|
||||
then {
|
||||
let sugg = Sugg::hir_with_macro_callsite(cx, &args[0], "<expr>").maybe_par();
|
||||
let sugg = Sugg::hir_with_macro_callsite(cx, arg, "<expr>").maybe_par();
|
||||
let sugg_msg =
|
||||
format!("consider removing `{}()`", snippet(cx, path.span, "From::from"));
|
||||
span_lint_and_sugg(
|
||||
|
@ -30,7 +30,7 @@ const DEFAULT_DOC_VALID_IDENTS: &[&str] = &[
|
||||
"MinGW",
|
||||
"CamelCase",
|
||||
];
|
||||
const DEFAULT_BLACKLISTED_NAMES: &[&str] = &["foo", "baz", "quux"];
|
||||
const DEFAULT_DISALLOWED_NAMES: &[&str] = &["foo", "baz", "quux"];
|
||||
|
||||
/// Holds information used by `MISSING_ENFORCED_IMPORT_RENAMES` lint.
|
||||
#[derive(Clone, Debug, Deserialize)]
|
||||
@ -68,6 +68,7 @@ pub enum DisallowedType {
|
||||
pub struct TryConf {
|
||||
pub conf: Conf,
|
||||
pub errors: Vec<Box<dyn Error>>,
|
||||
pub warnings: Vec<Box<dyn Error>>,
|
||||
}
|
||||
|
||||
impl TryConf {
|
||||
@ -75,6 +76,7 @@ impl TryConf {
|
||||
Self {
|
||||
conf: Conf::default(),
|
||||
errors: vec![Box::new(error)],
|
||||
warnings: vec![],
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -90,14 +92,14 @@ impl fmt::Display for ConfError {
|
||||
|
||||
impl Error for ConfError {}
|
||||
|
||||
fn conf_error(s: String) -> Box<dyn Error> {
|
||||
Box::new(ConfError(s))
|
||||
fn conf_error(s: impl Into<String>) -> Box<dyn Error> {
|
||||
Box::new(ConfError(s.into()))
|
||||
}
|
||||
|
||||
macro_rules! define_Conf {
|
||||
($(
|
||||
$(#[doc = $doc:literal])+
|
||||
$(#[conf_deprecated($dep:literal)])?
|
||||
$(#[conf_deprecated($dep:literal, $new_conf:ident)])?
|
||||
($name:ident: $ty:ty = $default:expr),
|
||||
)*) => {
|
||||
/// Clippy lint configuration
|
||||
@ -137,17 +139,29 @@ macro_rules! define_Conf {
|
||||
|
||||
fn visit_map<V>(self, mut map: V) -> Result<Self::Value, V::Error> where V: MapAccess<'de> {
|
||||
let mut errors = Vec::new();
|
||||
let mut warnings = Vec::new();
|
||||
$(let mut $name = None;)*
|
||||
// could get `Field` here directly, but get `str` first for diagnostics
|
||||
while let Some(name) = map.next_key::<&str>()? {
|
||||
match Field::deserialize(name.into_deserializer())? {
|
||||
$(Field::$name => {
|
||||
$(errors.push(conf_error(format!("deprecated field `{}`. {}", name, $dep)));)?
|
||||
$(warnings.push(conf_error(format!("deprecated field `{}`. {}", name, $dep)));)?
|
||||
match map.next_value() {
|
||||
Err(e) => errors.push(conf_error(e.to_string())),
|
||||
Ok(value) => match $name {
|
||||
Some(_) => errors.push(conf_error(format!("duplicate field `{}`", name))),
|
||||
None => $name = Some(value),
|
||||
None => {
|
||||
$name = Some(value);
|
||||
// $new_conf is the same as one of the defined `$name`s, so
|
||||
// this variable is defined in line 2 of this function.
|
||||
$(match $new_conf {
|
||||
Some(_) => errors.push(conf_error(concat!(
|
||||
"duplicate field `", stringify!($new_conf),
|
||||
"` (provided as `", stringify!($name), "`)"
|
||||
))),
|
||||
None => $new_conf = $name.clone(),
|
||||
})?
|
||||
},
|
||||
}
|
||||
}
|
||||
})*
|
||||
@ -156,7 +170,7 @@ macro_rules! define_Conf {
|
||||
}
|
||||
}
|
||||
let conf = Conf { $($name: $name.unwrap_or_else(defaults::$name),)* };
|
||||
Ok(TryConf { conf, errors })
|
||||
Ok(TryConf { conf, errors, warnings })
|
||||
}
|
||||
}
|
||||
|
||||
@ -203,12 +217,11 @@ define_Conf! {
|
||||
///
|
||||
/// The minimum rust version that the project supports
|
||||
(msrv: Option<String> = None),
|
||||
/// Lint: BLACKLISTED_NAME.
|
||||
/// DEPRECATED LINT: BLACKLISTED_NAME.
|
||||
///
|
||||
/// The list of blacklisted names to lint about. NB: `bar` is not here since it has legitimate uses. The value
|
||||
/// `".."` can be used as part of the list to indicate, that the configured values should be appended to the
|
||||
/// default configuration of Clippy. By default any configuraction will replace the default value.
|
||||
(blacklisted_names: Vec<String> = super::DEFAULT_BLACKLISTED_NAMES.iter().map(ToString::to_string).collect()),
|
||||
/// Use the Disallowed Names lint instead
|
||||
#[conf_deprecated("Please use `disallowed-names` instead", disallowed_names)]
|
||||
(blacklisted_names: Vec<String> = Vec::new()),
|
||||
/// Lint: COGNITIVE_COMPLEXITY.
|
||||
///
|
||||
/// The maximum cognitive complexity a function can have
|
||||
@ -216,8 +229,14 @@ define_Conf! {
|
||||
/// DEPRECATED LINT: CYCLOMATIC_COMPLEXITY.
|
||||
///
|
||||
/// Use the Cognitive Complexity lint instead.
|
||||
#[conf_deprecated("Please use `cognitive-complexity-threshold` instead")]
|
||||
(cyclomatic_complexity_threshold: Option<u64> = None),
|
||||
#[conf_deprecated("Please use `cognitive-complexity-threshold` instead", cognitive_complexity_threshold)]
|
||||
(cyclomatic_complexity_threshold: u64 = 25),
|
||||
/// Lint: DISALLOWED_NAMES.
|
||||
///
|
||||
/// The list of disallowed names to lint about. NB: `bar` is not here since it has legitimate uses. The value
|
||||
/// `".."` can be used as part of the list to indicate, that the configured values should be appended to the
|
||||
/// default configuration of Clippy. By default any configuration will replace the default value.
|
||||
(disallowed_names: Vec<String> = super::DEFAULT_DISALLOWED_NAMES.iter().map(ToString::to_string).collect()),
|
||||
/// Lint: DOC_MARKDOWN.
|
||||
///
|
||||
/// The list of words this lint should not consider as identifiers needing ticks. The value
|
||||
@ -420,7 +439,7 @@ pub fn read(path: &Path) -> TryConf {
|
||||
match toml::from_str::<TryConf>(&content) {
|
||||
Ok(mut conf) => {
|
||||
extend_vec_if_indicator_present(&mut conf.conf.doc_valid_idents, DEFAULT_DOC_VALID_IDENTS);
|
||||
extend_vec_if_indicator_present(&mut conf.conf.blacklisted_names, DEFAULT_BLACKLISTED_NAMES);
|
||||
extend_vec_if_indicator_present(&mut conf.conf.disallowed_names, DEFAULT_DISALLOWED_NAMES);
|
||||
|
||||
conf
|
||||
},
|
||||
|
@ -496,12 +496,14 @@ impl<'tcx> LateLintPass<'tcx> for LintWithoutLintPass {
|
||||
cx,
|
||||
};
|
||||
let body_id = cx.tcx.hir().body_owned_by(
|
||||
impl_item_refs
|
||||
.iter()
|
||||
.find(|iiref| iiref.ident.as_str() == "get_lints")
|
||||
.expect("LintPass needs to implement get_lints")
|
||||
.id
|
||||
.hir_id(),
|
||||
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);
|
||||
}
|
||||
@ -569,7 +571,7 @@ fn check_invalid_clippy_version_attribute(cx: &LateContext<'_>, item: &'_ Item<'
|
||||
item.span,
|
||||
"this item has an invalid `clippy::version` attribute",
|
||||
None,
|
||||
"please use a valid sematic version, see `doc/adding_lints.md`",
|
||||
"please use a valid semantic version, see `doc/adding_lints.md`",
|
||||
);
|
||||
}
|
||||
} else {
|
||||
|
@ -619,7 +619,7 @@ impl<'hir> LateLintPass<'hir> for MetadataCollector {
|
||||
if_chain! {
|
||||
// item validation
|
||||
if is_lint_ref_type(cx, ty);
|
||||
// blacklist check
|
||||
// disallow check
|
||||
let lint_name = sym_to_string(item.ident.name).to_ascii_lowercase();
|
||||
if !BLACK_LISTED_LINTS.contains(&lint_name.as_str());
|
||||
// metadata extraction
|
||||
@ -644,7 +644,7 @@ impl<'hir> LateLintPass<'hir> for MetadataCollector {
|
||||
|
||||
if_chain! {
|
||||
if is_deprecated_lint(cx, ty);
|
||||
// blacklist check
|
||||
// disallow check
|
||||
let lint_name = sym_to_string(item.ident.name).to_ascii_lowercase();
|
||||
if !BLACK_LISTED_LINTS.contains(&lint_name.as_str());
|
||||
// Metadata the little we can get from a deprecated lint
|
||||
|
@ -61,10 +61,10 @@ impl<'tcx> LateLintPass<'tcx> for VerboseFileReads {
|
||||
|
||||
fn is_file_read_to_end<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
|
||||
if_chain! {
|
||||
if let ExprKind::MethodCall(method_name, exprs, _) = expr.kind;
|
||||
if let ExprKind::MethodCall(method_name, [recv, ..], _) = expr.kind;
|
||||
if method_name.ident.as_str() == "read_to_end";
|
||||
if let ExprKind::Path(QPath::Resolved(None, _)) = &exprs[0].kind;
|
||||
let ty = cx.typeck_results().expr_ty(&exprs[0]);
|
||||
if let ExprKind::Path(QPath::Resolved(None, _)) = &recv.kind;
|
||||
let ty = cx.typeck_results().expr_ty(recv);
|
||||
if match_type(cx, ty, &paths::FILE);
|
||||
then {
|
||||
return true
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "clippy_utils"
|
||||
version = "0.1.64"
|
||||
version = "0.1.65"
|
||||
edition = "2021"
|
||||
publish = false
|
||||
|
||||
|
329
src/tools/clippy/clippy_utils/src/check_proc_macro.rs
Normal file
329
src/tools/clippy/clippy_utils/src/check_proc_macro.rs
Normal file
@ -0,0 +1,329 @@
|
||||
//! This module handles checking if the span given is from a proc-macro or not.
|
||||
//!
|
||||
//! Proc-macros are capable of setting the span of every token they output to a few possible spans.
|
||||
//! This includes spans we can detect easily as coming from a proc-macro (e.g. the call site
|
||||
//! or the def site), and spans we can't easily detect as such (e.g. the span of any token
|
||||
//! passed into the proc macro). This capability means proc-macros are capable of generating code
|
||||
//! with a span that looks like it was written by the user, but which should not be linted by clippy
|
||||
//! as it was generated by an external macro.
|
||||
//!
|
||||
//! That brings us to this module. The current approach is to determine a small bit of text which
|
||||
//! must exist at both the start and the end of an item (e.g. an expression or a path) assuming the
|
||||
//! code was written, and check if the span contains that text. Note this will only work correctly
|
||||
//! if the span is not from a `macro_rules` based macro.
|
||||
|
||||
use rustc_ast::ast::{IntTy, LitIntType, LitKind, StrStyle, UintTy};
|
||||
use rustc_hir::{
|
||||
intravisit::FnKind, Block, BlockCheckMode, Body, Closure, Destination, Expr, ExprKind, FieldDef, FnHeader, HirId,
|
||||
Impl, ImplItem, ImplItemKind, IsAuto, Item, ItemKind, LoopSource, MatchSource, Node, QPath, TraitItem,
|
||||
TraitItemKind, UnOp, UnsafeSource, Unsafety, Variant, VariantData, YieldSource,
|
||||
};
|
||||
use rustc_lint::{LateContext, LintContext};
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
use rustc_session::Session;
|
||||
use rustc_span::{Span, Symbol};
|
||||
use rustc_target::spec::abi::Abi;
|
||||
|
||||
/// The search pattern to look for. Used by `span_matches_pat`
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum Pat {
|
||||
/// A single string.
|
||||
Str(&'static str),
|
||||
/// Any of the given strings.
|
||||
MultiStr(&'static [&'static str]),
|
||||
/// The string representation of the symbol.
|
||||
Sym(Symbol),
|
||||
/// Any decimal or hexadecimal digit depending on the location.
|
||||
Num,
|
||||
}
|
||||
|
||||
/// Checks if the start and the end of the span's text matches the patterns. This will return false
|
||||
/// if the span crosses multiple files or if source is not available.
|
||||
fn span_matches_pat(sess: &Session, span: Span, start_pat: Pat, end_pat: Pat) -> bool {
|
||||
let pos = sess.source_map().lookup_byte_offset(span.lo());
|
||||
let Some(ref src) = pos.sf.src else {
|
||||
return false;
|
||||
};
|
||||
let end = span.hi() - pos.sf.start_pos;
|
||||
src.get(pos.pos.0 as usize..end.0 as usize).map_or(false, |s| {
|
||||
// Spans can be wrapped in a mixture or parenthesis, whitespace, and trailing commas.
|
||||
let start_str = s.trim_start_matches(|c: char| c.is_whitespace() || c == '(');
|
||||
let end_str = s.trim_end_matches(|c: char| c.is_whitespace() || c == ')' || c == ',');
|
||||
(match start_pat {
|
||||
Pat::Str(text) => start_str.starts_with(text),
|
||||
Pat::MultiStr(texts) => texts.iter().any(|s| start_str.starts_with(s)),
|
||||
Pat::Sym(sym) => start_str.starts_with(sym.as_str()),
|
||||
Pat::Num => start_str.as_bytes().first().map_or(false, u8::is_ascii_digit),
|
||||
} && match end_pat {
|
||||
Pat::Str(text) => end_str.ends_with(text),
|
||||
Pat::MultiStr(texts) => texts.iter().any(|s| start_str.ends_with(s)),
|
||||
Pat::Sym(sym) => end_str.ends_with(sym.as_str()),
|
||||
Pat::Num => end_str.as_bytes().last().map_or(false, u8::is_ascii_hexdigit),
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/// Get the search patterns to use for the given literal
|
||||
fn lit_search_pat(lit: &LitKind) -> (Pat, Pat) {
|
||||
match lit {
|
||||
LitKind::Str(_, StrStyle::Cooked) => (Pat::Str("\""), Pat::Str("\"")),
|
||||
LitKind::Str(_, StrStyle::Raw(0)) => (Pat::Str("r"), Pat::Str("\"")),
|
||||
LitKind::Str(_, StrStyle::Raw(_)) => (Pat::Str("r#"), Pat::Str("#")),
|
||||
LitKind::ByteStr(_) => (Pat::Str("b\""), Pat::Str("\"")),
|
||||
LitKind::Byte(_) => (Pat::Str("b'"), Pat::Str("'")),
|
||||
LitKind::Char(_) => (Pat::Str("'"), Pat::Str("'")),
|
||||
LitKind::Int(_, LitIntType::Signed(IntTy::Isize)) => (Pat::Num, Pat::Str("isize")),
|
||||
LitKind::Int(_, LitIntType::Unsigned(UintTy::Usize)) => (Pat::Num, Pat::Str("usize")),
|
||||
LitKind::Int(..) => (Pat::Num, Pat::Num),
|
||||
LitKind::Float(..) => (Pat::Num, Pat::Str("")),
|
||||
LitKind::Bool(true) => (Pat::Str("true"), Pat::Str("true")),
|
||||
LitKind::Bool(false) => (Pat::Str("false"), Pat::Str("false")),
|
||||
_ => (Pat::Str(""), Pat::Str("")),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the search patterns to use for the given path
|
||||
fn qpath_search_pat(path: &QPath<'_>) -> (Pat, Pat) {
|
||||
match path {
|
||||
QPath::Resolved(ty, path) => {
|
||||
let start = if ty.is_some() {
|
||||
Pat::Str("<")
|
||||
} else {
|
||||
path.segments
|
||||
.first()
|
||||
.map_or(Pat::Str(""), |seg| Pat::Sym(seg.ident.name))
|
||||
};
|
||||
let end = path.segments.last().map_or(Pat::Str(""), |seg| {
|
||||
if seg.args.is_some() {
|
||||
Pat::Str(">")
|
||||
} else {
|
||||
Pat::Sym(seg.ident.name)
|
||||
}
|
||||
});
|
||||
(start, end)
|
||||
},
|
||||
QPath::TypeRelative(_, name) => (Pat::Str(""), Pat::Sym(name.ident.name)),
|
||||
QPath::LangItem(..) => (Pat::Str(""), Pat::Str("")),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the search patterns to use for the given expression
|
||||
fn expr_search_pat(tcx: TyCtxt<'_>, e: &Expr<'_>) -> (Pat, Pat) {
|
||||
match e.kind {
|
||||
ExprKind::Box(e) => (Pat::Str("box"), expr_search_pat(tcx, e).1),
|
||||
ExprKind::ConstBlock(_) => (Pat::Str("const"), Pat::Str("}")),
|
||||
ExprKind::Tup([]) => (Pat::Str(")"), Pat::Str("(")),
|
||||
ExprKind::Unary(UnOp::Deref, e) => (Pat::Str("*"), expr_search_pat(tcx, e).1),
|
||||
ExprKind::Unary(UnOp::Not, e) => (Pat::Str("!"), expr_search_pat(tcx, e).1),
|
||||
ExprKind::Unary(UnOp::Neg, e) => (Pat::Str("-"), expr_search_pat(tcx, e).1),
|
||||
ExprKind::Lit(ref lit) => lit_search_pat(&lit.node),
|
||||
ExprKind::Array(_) | ExprKind::Repeat(..) => (Pat::Str("["), Pat::Str("]")),
|
||||
ExprKind::Call(e, []) | ExprKind::MethodCall(_, [e], _) => (expr_search_pat(tcx, e).0, Pat::Str("(")),
|
||||
ExprKind::Call(first, [.., last])
|
||||
| ExprKind::MethodCall(_, [first, .., last], _)
|
||||
| ExprKind::Binary(_, first, last)
|
||||
| ExprKind::Tup([first, .., last])
|
||||
| ExprKind::Assign(first, last, _)
|
||||
| ExprKind::AssignOp(_, first, last) => (expr_search_pat(tcx, first).0, expr_search_pat(tcx, last).1),
|
||||
ExprKind::Tup([e]) | ExprKind::DropTemps(e) => expr_search_pat(tcx, e),
|
||||
ExprKind::Cast(e, _) | ExprKind::Type(e, _) => (expr_search_pat(tcx, e).0, Pat::Str("")),
|
||||
ExprKind::Let(let_expr) => (Pat::Str("let"), expr_search_pat(tcx, let_expr.init).1),
|
||||
ExprKind::If(..) => (Pat::Str("if"), Pat::Str("}")),
|
||||
ExprKind::Loop(_, Some(_), _, _) | ExprKind::Block(_, Some(_)) => (Pat::Str("'"), Pat::Str("}")),
|
||||
ExprKind::Loop(_, None, LoopSource::Loop, _) => (Pat::Str("loop"), Pat::Str("}")),
|
||||
ExprKind::Loop(_, None, LoopSource::While, _) => (Pat::Str("while"), Pat::Str("}")),
|
||||
ExprKind::Loop(_, None, LoopSource::ForLoop, _) | ExprKind::Match(_, _, MatchSource::ForLoopDesugar) => {
|
||||
(Pat::Str("for"), Pat::Str("}"))
|
||||
},
|
||||
ExprKind::Match(_, _, MatchSource::Normal) => (Pat::Str("match"), Pat::Str("}")),
|
||||
ExprKind::Match(e, _, MatchSource::TryDesugar) => (expr_search_pat(tcx, e).0, Pat::Str("?")),
|
||||
ExprKind::Match(e, _, MatchSource::AwaitDesugar) | ExprKind::Yield(e, YieldSource::Await { .. }) => {
|
||||
(expr_search_pat(tcx, e).0, Pat::Str("await"))
|
||||
},
|
||||
ExprKind::Closure(&Closure { body, .. }) => (Pat::Str(""), expr_search_pat(tcx, &tcx.hir().body(body).value).1),
|
||||
ExprKind::Block(
|
||||
Block {
|
||||
rules: BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided),
|
||||
..
|
||||
},
|
||||
None,
|
||||
) => (Pat::Str("unsafe"), Pat::Str("}")),
|
||||
ExprKind::Block(_, None) => (Pat::Str("{"), Pat::Str("}")),
|
||||
ExprKind::Field(e, name) => (expr_search_pat(tcx, e).0, Pat::Sym(name.name)),
|
||||
ExprKind::Index(e, _) => (expr_search_pat(tcx, e).0, Pat::Str("]")),
|
||||
ExprKind::Path(ref path) => qpath_search_pat(path),
|
||||
ExprKind::AddrOf(_, _, e) => (Pat::Str("&"), expr_search_pat(tcx, e).1),
|
||||
ExprKind::Break(Destination { label: None, .. }, None) => (Pat::Str("break"), Pat::Str("break")),
|
||||
ExprKind::Break(Destination { label: Some(name), .. }, None) => (Pat::Str("break"), Pat::Sym(name.ident.name)),
|
||||
ExprKind::Break(_, Some(e)) => (Pat::Str("break"), expr_search_pat(tcx, e).1),
|
||||
ExprKind::Continue(Destination { label: None, .. }) => (Pat::Str("continue"), Pat::Str("continue")),
|
||||
ExprKind::Continue(Destination { label: Some(name), .. }) => (Pat::Str("continue"), Pat::Sym(name.ident.name)),
|
||||
ExprKind::Ret(None) => (Pat::Str("return"), Pat::Str("return")),
|
||||
ExprKind::Ret(Some(e)) => (Pat::Str("return"), expr_search_pat(tcx, e).1),
|
||||
ExprKind::Struct(path, _, _) => (qpath_search_pat(path).0, Pat::Str("}")),
|
||||
ExprKind::Yield(e, YieldSource::Yield) => (Pat::Str("yield"), expr_search_pat(tcx, e).1),
|
||||
_ => (Pat::Str(""), Pat::Str("")),
|
||||
}
|
||||
}
|
||||
|
||||
fn fn_header_search_pat(header: FnHeader) -> Pat {
|
||||
if header.is_async() {
|
||||
Pat::Str("async")
|
||||
} else if header.is_const() {
|
||||
Pat::Str("const")
|
||||
} else if header.is_unsafe() {
|
||||
Pat::Str("unsafe")
|
||||
} else if header.abi != Abi::Rust {
|
||||
Pat::Str("extern")
|
||||
} else {
|
||||
Pat::MultiStr(&["fn", "extern"])
|
||||
}
|
||||
}
|
||||
|
||||
fn item_search_pat(item: &Item<'_>) -> (Pat, Pat) {
|
||||
let (start_pat, end_pat) = match &item.kind {
|
||||
ItemKind::ExternCrate(_) => (Pat::Str("extern"), Pat::Str(";")),
|
||||
ItemKind::Static(..) => (Pat::Str("static"), Pat::Str(";")),
|
||||
ItemKind::Const(..) => (Pat::Str("const"), Pat::Str(";")),
|
||||
ItemKind::Fn(sig, ..) => (fn_header_search_pat(sig.header), Pat::Str("")),
|
||||
ItemKind::ForeignMod { .. } => (Pat::Str("extern"), Pat::Str("}")),
|
||||
ItemKind::TyAlias(..) | ItemKind::OpaqueTy(_) => (Pat::Str("type"), Pat::Str(";")),
|
||||
ItemKind::Enum(..) => (Pat::Str("enum"), Pat::Str("}")),
|
||||
ItemKind::Struct(VariantData::Struct(..), _) => (Pat::Str("struct"), Pat::Str("}")),
|
||||
ItemKind::Struct(..) => (Pat::Str("struct"), Pat::Str(";")),
|
||||
ItemKind::Union(..) => (Pat::Str("union"), Pat::Str("}")),
|
||||
ItemKind::Trait(_, Unsafety::Unsafe, ..)
|
||||
| ItemKind::Impl(Impl {
|
||||
unsafety: Unsafety::Unsafe,
|
||||
..
|
||||
}) => (Pat::Str("unsafe"), Pat::Str("}")),
|
||||
ItemKind::Trait(IsAuto::Yes, ..) => (Pat::Str("auto"), Pat::Str("}")),
|
||||
ItemKind::Trait(..) => (Pat::Str("trait"), Pat::Str("}")),
|
||||
ItemKind::Impl(_) => (Pat::Str("impl"), Pat::Str("}")),
|
||||
_ => return (Pat::Str(""), Pat::Str("")),
|
||||
};
|
||||
if item.vis_span.is_empty() {
|
||||
(start_pat, end_pat)
|
||||
} else {
|
||||
(Pat::Str("pub"), end_pat)
|
||||
}
|
||||
}
|
||||
|
||||
fn trait_item_search_pat(item: &TraitItem<'_>) -> (Pat, Pat) {
|
||||
match &item.kind {
|
||||
TraitItemKind::Const(..) => (Pat::Str("const"), Pat::Str(";")),
|
||||
TraitItemKind::Type(..) => (Pat::Str("type"), Pat::Str(";")),
|
||||
TraitItemKind::Fn(sig, ..) => (fn_header_search_pat(sig.header), Pat::Str("")),
|
||||
}
|
||||
}
|
||||
|
||||
fn impl_item_search_pat(item: &ImplItem<'_>) -> (Pat, Pat) {
|
||||
let (start_pat, end_pat) = match &item.kind {
|
||||
ImplItemKind::Const(..) => (Pat::Str("const"), Pat::Str(";")),
|
||||
ImplItemKind::TyAlias(..) => (Pat::Str("type"), Pat::Str(";")),
|
||||
ImplItemKind::Fn(sig, ..) => (fn_header_search_pat(sig.header), Pat::Str("")),
|
||||
};
|
||||
if item.vis_span.is_empty() {
|
||||
(start_pat, end_pat)
|
||||
} else {
|
||||
(Pat::Str("pub"), end_pat)
|
||||
}
|
||||
}
|
||||
|
||||
fn field_def_search_pat(def: &FieldDef<'_>) -> (Pat, Pat) {
|
||||
if def.vis_span.is_empty() {
|
||||
if def.is_positional() {
|
||||
(Pat::Str(""), Pat::Str(""))
|
||||
} else {
|
||||
(Pat::Sym(def.ident.name), Pat::Str(""))
|
||||
}
|
||||
} else {
|
||||
(Pat::Str("pub"), Pat::Str(""))
|
||||
}
|
||||
}
|
||||
|
||||
fn variant_search_pat(v: &Variant<'_>) -> (Pat, Pat) {
|
||||
match v.data {
|
||||
VariantData::Struct(..) => (Pat::Sym(v.ident.name), Pat::Str("}")),
|
||||
VariantData::Tuple(..) => (Pat::Sym(v.ident.name), Pat::Str("")),
|
||||
VariantData::Unit(..) => (Pat::Sym(v.ident.name), Pat::Sym(v.ident.name)),
|
||||
}
|
||||
}
|
||||
|
||||
fn fn_kind_pat(tcx: TyCtxt<'_>, kind: &FnKind<'_>, body: &Body<'_>, hir_id: HirId) -> (Pat, Pat) {
|
||||
let (start_pat, end_pat) = match kind {
|
||||
FnKind::ItemFn(.., header) => (fn_header_search_pat(*header), Pat::Str("")),
|
||||
FnKind::Method(.., sig) => (fn_header_search_pat(sig.header), Pat::Str("")),
|
||||
FnKind::Closure => return (Pat::Str(""), expr_search_pat(tcx, &body.value).1),
|
||||
};
|
||||
let start_pat = match tcx.hir().get(hir_id) {
|
||||
Node::Item(Item { vis_span, .. }) | Node::ImplItem(ImplItem { vis_span, .. }) => {
|
||||
if vis_span.is_empty() {
|
||||
start_pat
|
||||
} else {
|
||||
Pat::Str("pub")
|
||||
}
|
||||
},
|
||||
Node::TraitItem(_) => start_pat,
|
||||
_ => Pat::Str(""),
|
||||
};
|
||||
(start_pat, end_pat)
|
||||
}
|
||||
|
||||
pub trait WithSearchPat {
|
||||
type Context: LintContext;
|
||||
fn search_pat(&self, cx: &Self::Context) -> (Pat, Pat);
|
||||
fn span(&self) -> Span;
|
||||
}
|
||||
macro_rules! impl_with_search_pat {
|
||||
($cx:ident: $ty:ident with $fn:ident $(($tcx:ident))?) => {
|
||||
impl<'cx> WithSearchPat for $ty<'cx> {
|
||||
type Context = $cx<'cx>;
|
||||
#[allow(unused_variables)]
|
||||
fn search_pat(&self, cx: &Self::Context) -> (Pat, Pat) {
|
||||
$(let $tcx = cx.tcx;)?
|
||||
$fn($($tcx,)? self)
|
||||
}
|
||||
fn span(&self) -> Span {
|
||||
self.span
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
impl_with_search_pat!(LateContext: Expr with expr_search_pat(tcx));
|
||||
impl_with_search_pat!(LateContext: Item with item_search_pat);
|
||||
impl_with_search_pat!(LateContext: TraitItem with trait_item_search_pat);
|
||||
impl_with_search_pat!(LateContext: ImplItem with impl_item_search_pat);
|
||||
impl_with_search_pat!(LateContext: FieldDef with field_def_search_pat);
|
||||
impl_with_search_pat!(LateContext: Variant with variant_search_pat);
|
||||
|
||||
impl<'cx> WithSearchPat for (&FnKind<'cx>, &Body<'cx>, HirId, Span) {
|
||||
type Context = LateContext<'cx>;
|
||||
|
||||
fn search_pat(&self, cx: &Self::Context) -> (Pat, Pat) {
|
||||
fn_kind_pat(cx.tcx, self.0, self.1, self.2)
|
||||
}
|
||||
|
||||
fn span(&self) -> Span {
|
||||
self.3
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks if the item likely came from a proc-macro.
|
||||
///
|
||||
/// This should be called after `in_external_macro` and the initial pattern matching of the ast as
|
||||
/// it is significantly slower than both of those.
|
||||
pub fn is_from_proc_macro<T: WithSearchPat>(cx: &T::Context, item: &T) -> bool {
|
||||
let (start_pat, end_pat) = item.search_pat(cx);
|
||||
!span_matches_pat(cx.sess(), item.span(), start_pat, end_pat)
|
||||
}
|
||||
|
||||
/// Checks if the span actually refers to a match expression
|
||||
pub fn is_span_match(cx: &impl LintContext, span: Span) -> bool {
|
||||
span_matches_pat(cx.sess(), span, Pat::Str("match"), Pat::Str("}"))
|
||||
}
|
||||
|
||||
/// Checks if the span actually refers to an if expression
|
||||
pub fn is_span_if(cx: &impl LintContext, span: Span) -> bool {
|
||||
span_matches_pat(cx.sess(), span, Pat::Str("if"), Pat::Str("}"))
|
||||
}
|
@ -39,6 +39,7 @@ pub mod sym_helper;
|
||||
|
||||
pub mod ast_utils;
|
||||
pub mod attrs;
|
||||
mod check_proc_macro;
|
||||
pub mod comparisons;
|
||||
pub mod consts;
|
||||
pub mod diagnostics;
|
||||
@ -59,6 +60,7 @@ pub mod usage;
|
||||
pub mod visitors;
|
||||
|
||||
pub use self::attrs::*;
|
||||
pub use self::check_proc_macro::{is_from_proc_macro, is_span_if, is_span_match};
|
||||
pub use self::hir_utils::{
|
||||
both, count_eq, eq_expr_value, hash_expr, hash_stmt, over, HirEqInterExpr, SpanlessEq, SpanlessHash,
|
||||
};
|
||||
|
@ -223,10 +223,12 @@ impl<'a> NumericLiteral<'a> {
|
||||
|
||||
fn split_suffix<'a>(src: &'a str, lit_kind: &LitKind) -> (&'a str, Option<&'a str>) {
|
||||
debug_assert!(lit_kind.is_numeric());
|
||||
lit_suffix_length(lit_kind).map_or((src, None), |suffix_length| {
|
||||
let (unsuffixed, suffix) = src.split_at(src.len() - suffix_length);
|
||||
(unsuffixed, Some(suffix))
|
||||
})
|
||||
lit_suffix_length(lit_kind)
|
||||
.and_then(|suffix_length| src.len().checked_sub(suffix_length))
|
||||
.map_or((src, None), |split_pos| {
|
||||
let (unsuffixed, suffix) = src.split_at(split_pos);
|
||||
(unsuffixed, Some(suffix))
|
||||
})
|
||||
}
|
||||
|
||||
fn lit_suffix_length(lit_kind: &LitKind) -> Option<usize> {
|
||||
|
@ -194,3 +194,5 @@ pub const VEC_RESIZE: [&str; 4] = ["alloc", "vec", "Vec", "resize"];
|
||||
pub const WEAK_ARC: [&str; 3] = ["alloc", "sync", "Weak"];
|
||||
pub const WEAK_RC: [&str; 3] = ["alloc", "rc", "Weak"];
|
||||
pub const PTR_NON_NULL: [&str; 4] = ["core", "ptr", "non_null", "NonNull"];
|
||||
pub const INSTANT_NOW: [&str; 4] = ["std", "time", "Instant", "now"];
|
||||
pub const INSTANT: [&str; 3] = ["std", "time", "Instant"];
|
||||
|
@ -11,24 +11,6 @@ use rustc_span::source_map::SourceMap;
|
||||
use rustc_span::{BytePos, Pos, Span, SpanData, SyntaxContext};
|
||||
use std::borrow::Cow;
|
||||
|
||||
/// Checks if the span starts with the given text. This will return false if the span crosses
|
||||
/// multiple files or if source is not available.
|
||||
///
|
||||
/// This is used to check for proc macros giving unhelpful spans to things.
|
||||
pub fn span_starts_with<T: LintContext>(cx: &T, span: Span, text: &str) -> bool {
|
||||
fn helper(sm: &SourceMap, span: Span, text: &str) -> bool {
|
||||
let pos = sm.lookup_byte_offset(span.lo());
|
||||
let Some(ref src) = pos.sf.src else {
|
||||
return false;
|
||||
};
|
||||
let end = span.hi() - pos.sf.start_pos;
|
||||
src.get(pos.pos.0 as usize..end.0 as usize)
|
||||
// Expression spans can include wrapping parenthesis. Remove them first.
|
||||
.map_or(false, |s| s.trim_start_matches('(').starts_with(text))
|
||||
}
|
||||
helper(cx.sess().source_map(), span, text)
|
||||
}
|
||||
|
||||
/// Like `snippet_block`, but add braces if the expr is not an `ExprKind::Block`.
|
||||
/// Also takes an `Option<String>` which can be put inside the braces.
|
||||
pub fn expr_block<'a, T: LintContext>(
|
||||
|
@ -315,6 +315,12 @@ impl<'a> Sugg<'a> {
|
||||
Sugg::NonParen(Cow::Owned(format!("{{ {} }}", self)))
|
||||
}
|
||||
|
||||
/// Convenience method to prefix the expression with the `async` keyword.
|
||||
/// Can be used after `blockify` to create an async block.
|
||||
pub fn asyncify(self) -> Sugg<'static> {
|
||||
Sugg::NonParen(Cow::Owned(format!("async {}", self)))
|
||||
}
|
||||
|
||||
/// Convenience method to create the `<lhs>..<rhs>` or `<lhs>...<rhs>`
|
||||
/// suggestion.
|
||||
pub fn range(self, end: &Self, limit: ast::RangeLimits) -> Sugg<'static> {
|
||||
|
@ -503,7 +503,7 @@ pub fn all_predicates_of(tcx: TyCtxt<'_>, id: DefId) -> impl Iterator<Item = &(P
|
||||
pub enum ExprFnSig<'tcx> {
|
||||
Sig(Binder<'tcx, FnSig<'tcx>>, Option<DefId>),
|
||||
Closure(Option<&'tcx FnDecl<'tcx>>, Binder<'tcx, FnSig<'tcx>>),
|
||||
Trait(Binder<'tcx, Ty<'tcx>>, Option<Binder<'tcx, Ty<'tcx>>>),
|
||||
Trait(Binder<'tcx, Ty<'tcx>>, Option<Binder<'tcx, Ty<'tcx>>>, Option<DefId>),
|
||||
}
|
||||
impl<'tcx> ExprFnSig<'tcx> {
|
||||
/// Gets the argument type at the given offset. This will return `None` when the index is out of
|
||||
@ -518,7 +518,7 @@ impl<'tcx> ExprFnSig<'tcx> {
|
||||
}
|
||||
},
|
||||
Self::Closure(_, sig) => Some(sig.input(0).map_bound(|ty| ty.tuple_fields()[i])),
|
||||
Self::Trait(inputs, _) => Some(inputs.map_bound(|ty| ty.tuple_fields()[i])),
|
||||
Self::Trait(inputs, _, _) => Some(inputs.map_bound(|ty| ty.tuple_fields()[i])),
|
||||
}
|
||||
}
|
||||
|
||||
@ -541,7 +541,7 @@ impl<'tcx> ExprFnSig<'tcx> {
|
||||
decl.and_then(|decl| decl.inputs.get(i)),
|
||||
sig.input(0).map_bound(|ty| ty.tuple_fields()[i]),
|
||||
)),
|
||||
Self::Trait(inputs, _) => Some((None, inputs.map_bound(|ty| ty.tuple_fields()[i]))),
|
||||
Self::Trait(inputs, _, _) => Some((None, inputs.map_bound(|ty| ty.tuple_fields()[i]))),
|
||||
}
|
||||
}
|
||||
|
||||
@ -550,12 +550,16 @@ impl<'tcx> ExprFnSig<'tcx> {
|
||||
pub fn output(self) -> Option<Binder<'tcx, Ty<'tcx>>> {
|
||||
match self {
|
||||
Self::Sig(sig, _) | Self::Closure(_, sig) => Some(sig.output()),
|
||||
Self::Trait(_, output) => output,
|
||||
Self::Trait(_, output, _) => output,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn predicates_id(&self) -> Option<DefId> {
|
||||
if let ExprFnSig::Sig(_, id) = *self { id } else { None }
|
||||
if let ExprFnSig::Sig(_, id) | ExprFnSig::Trait(_, _, id) = *self {
|
||||
id
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -568,7 +572,8 @@ pub fn expr_sig<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) -> Option<ExprFnS
|
||||
}
|
||||
}
|
||||
|
||||
fn ty_sig<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<ExprFnSig<'tcx>> {
|
||||
/// If the type is function like, get the signature for it.
|
||||
pub fn ty_sig<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<ExprFnSig<'tcx>> {
|
||||
if ty.is_box() {
|
||||
return ty_sig(cx, ty.boxed_ty());
|
||||
}
|
||||
@ -580,7 +585,7 @@ fn ty_sig<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<ExprFnSig<'tcx>>
|
||||
Some(ExprFnSig::Closure(decl, subs.as_closure().sig()))
|
||||
},
|
||||
ty::FnDef(id, subs) => Some(ExprFnSig::Sig(cx.tcx.bound_fn_sig(id).subst(cx.tcx, subs), Some(id))),
|
||||
ty::Opaque(id, _) => ty_sig(cx, cx.tcx.type_of(id)),
|
||||
ty::Opaque(id, _) => sig_from_bounds(cx, ty, cx.tcx.item_bounds(id), cx.tcx.opt_parent(id)),
|
||||
ty::FnPtr(sig) => Some(ExprFnSig::Sig(sig, None)),
|
||||
ty::Dynamic(bounds, _) => {
|
||||
let lang_items = cx.tcx.lang_items();
|
||||
@ -594,26 +599,31 @@ fn ty_sig<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<ExprFnSig<'tcx>>
|
||||
.projection_bounds()
|
||||
.find(|p| lang_items.fn_once_output().map_or(false, |id| id == p.item_def_id()))
|
||||
.map(|p| p.map_bound(|p| p.term.ty().unwrap()));
|
||||
Some(ExprFnSig::Trait(bound.map_bound(|b| b.substs.type_at(0)), output))
|
||||
Some(ExprFnSig::Trait(bound.map_bound(|b| b.substs.type_at(0)), output, None))
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
},
|
||||
ty::Projection(proj) => match cx.tcx.try_normalize_erasing_regions(cx.param_env, ty) {
|
||||
Ok(normalized_ty) if normalized_ty != ty => ty_sig(cx, normalized_ty),
|
||||
_ => sig_for_projection(cx, proj).or_else(|| sig_from_bounds(cx, ty)),
|
||||
_ => sig_for_projection(cx, proj).or_else(|| sig_from_bounds(cx, ty, cx.param_env.caller_bounds(), None)),
|
||||
},
|
||||
ty::Param(_) => sig_from_bounds(cx, ty),
|
||||
ty::Param(_) => sig_from_bounds(cx, ty, cx.param_env.caller_bounds(), None),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn sig_from_bounds<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<ExprFnSig<'tcx>> {
|
||||
fn sig_from_bounds<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
ty: Ty<'tcx>,
|
||||
predicates: &'tcx [Predicate<'tcx>],
|
||||
predicates_id: Option<DefId>,
|
||||
) -> Option<ExprFnSig<'tcx>> {
|
||||
let mut inputs = None;
|
||||
let mut output = None;
|
||||
let lang_items = cx.tcx.lang_items();
|
||||
|
||||
for (pred, _) in all_predicates_of(cx.tcx, cx.typeck_results().hir_owner.to_def_id()) {
|
||||
for pred in predicates {
|
||||
match pred.kind().skip_binder() {
|
||||
PredicateKind::Trait(p)
|
||||
if (lang_items.fn_trait() == Some(p.def_id())
|
||||
@ -621,11 +631,12 @@ fn sig_from_bounds<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<ExprFnS
|
||||
|| lang_items.fn_once_trait() == Some(p.def_id()))
|
||||
&& p.self_ty() == ty =>
|
||||
{
|
||||
if inputs.is_some() {
|
||||
let i = pred.kind().rebind(p.trait_ref.substs.type_at(1));
|
||||
if inputs.map_or(false, |inputs| i != inputs) {
|
||||
// Multiple different fn trait impls. Is this even allowed?
|
||||
return None;
|
||||
}
|
||||
inputs = Some(pred.kind().rebind(p.trait_ref.substs.type_at(1)));
|
||||
inputs = Some(i);
|
||||
},
|
||||
PredicateKind::Projection(p)
|
||||
if Some(p.projection_ty.item_def_id) == lang_items.fn_once_output()
|
||||
@ -641,7 +652,7 @@ fn sig_from_bounds<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<ExprFnS
|
||||
}
|
||||
}
|
||||
|
||||
inputs.map(|ty| ExprFnSig::Trait(ty, output))
|
||||
inputs.map(|ty| ExprFnSig::Trait(ty, output, predicates_id))
|
||||
}
|
||||
|
||||
fn sig_for_projection<'tcx>(cx: &LateContext<'tcx>, ty: ProjectionTy<'tcx>) -> Option<ExprFnSig<'tcx>> {
|
||||
@ -661,14 +672,15 @@ fn sig_for_projection<'tcx>(cx: &LateContext<'tcx>, ty: ProjectionTy<'tcx>) -> O
|
||||
|| lang_items.fn_mut_trait() == Some(p.def_id())
|
||||
|| lang_items.fn_once_trait() == Some(p.def_id())) =>
|
||||
{
|
||||
if inputs.is_some() {
|
||||
let i = pred
|
||||
.map_bound(|pred| pred.kind().rebind(p.trait_ref.substs.type_at(1)))
|
||||
.subst(cx.tcx, ty.substs);
|
||||
|
||||
if inputs.map_or(false, |inputs| inputs != i) {
|
||||
// Multiple different fn trait impls. Is this even allowed?
|
||||
return None;
|
||||
}
|
||||
inputs = Some(
|
||||
pred.map_bound(|pred| pred.kind().rebind(p.trait_ref.substs.type_at(1)))
|
||||
.subst(cx.tcx, ty.substs),
|
||||
);
|
||||
inputs = Some(i);
|
||||
},
|
||||
PredicateKind::Projection(p) if Some(p.projection_ty.item_def_id) == lang_items.fn_once_output() => {
|
||||
if output.is_some() {
|
||||
@ -684,7 +696,7 @@ fn sig_for_projection<'tcx>(cx: &LateContext<'tcx>, ty: ProjectionTy<'tcx>) -> O
|
||||
}
|
||||
}
|
||||
|
||||
inputs.map(|ty| ExprFnSig::Trait(ty, output))
|
||||
inputs.map(|ty| ExprFnSig::Trait(ty, output, None))
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
|
@ -1,3 +1,3 @@
|
||||
[toolchain]
|
||||
channel = "nightly-2022-07-28"
|
||||
channel = "nightly-2022-08-11"
|
||||
components = ["cargo", "llvm-tools-preview", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"]
|
||||
|
@ -16,7 +16,7 @@ note: the lint level is defined here
|
||||
LL | #![deny(clippy::internal)]
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
= note: `#[deny(clippy::invalid_clippy_version_attribute)]` implied by `#[deny(clippy::internal)]`
|
||||
= help: please use a valid sematic version, see `doc/adding_lints.md`
|
||||
= help: please use a valid semantic version, see `doc/adding_lints.md`
|
||||
= note: this error originates in the macro `$crate::declare_tool_lint` which comes from the expansion of the macro `declare_tool_lint` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error: this item has an invalid `clippy::version` attribute
|
||||
@ -31,7 +31,7 @@ LL | | report_in_external_macro: true
|
||||
LL | | }
|
||||
| |_^
|
||||
|
|
||||
= help: please use a valid sematic version, see `doc/adding_lints.md`
|
||||
= help: please use a valid semantic version, see `doc/adding_lints.md`
|
||||
= note: this error originates in the macro `$crate::declare_tool_lint` which comes from the expansion of the macro `declare_tool_lint` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error: this lint is missing the `clippy::version` attribute or version value
|
||||
|
@ -1 +1 @@
|
||||
blacklisted-names = 42
|
||||
disallowed-names = 42
|
||||
|
@ -1,4 +1,4 @@
|
||||
error: error reading Clippy's configuration file `$DIR/clippy.toml`: invalid type: integer `42`, expected a sequence for key `blacklisted-names`
|
||||
error: error reading Clippy's configuration file `$DIR/clippy.toml`: invalid type: integer `42`, expected a sequence for key `disallowed-names`
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
|
@ -1,16 +0,0 @@
|
||||
error: use of a blacklisted/placeholder name `foo`
|
||||
--> $DIR/blacklisted_names.rs:5:9
|
||||
|
|
||||
LL | let foo = "bar";
|
||||
| ^^^
|
||||
|
|
||||
= note: `-D clippy::blacklisted-name` implied by `-D warnings`
|
||||
|
||||
error: use of a blacklisted/placeholder name `ducks`
|
||||
--> $DIR/blacklisted_names.rs:7:9
|
||||
|
|
||||
LL | let ducks = ["quack", "quack"];
|
||||
| ^^^^^
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
@ -1 +0,0 @@
|
||||
blacklisted-names = ["ducks", ".."]
|
@ -1,10 +0,0 @@
|
||||
error: use of a blacklisted/placeholder name `ducks`
|
||||
--> $DIR/blacklisted_names.rs:7:9
|
||||
|
|
||||
LL | let ducks = ["quack", "quack"];
|
||||
| ^^^^^
|
||||
|
|
||||
= note: `-D clippy::blacklisted-name` implied by `-D warnings`
|
||||
|
||||
error: aborting due to previous error
|
||||
|
@ -1 +0,0 @@
|
||||
blacklisted-names = ["ducks"]
|
@ -1,5 +1,6 @@
|
||||
# that one is an error
|
||||
cyclomatic-complexity-threshold = 42
|
||||
# Expect errors from these deprecated configs
|
||||
cyclomatic-complexity-threshold = 2
|
||||
blacklisted-names = [ "..", "wibble" ]
|
||||
|
||||
# that one is white-listed
|
||||
[third-party]
|
||||
|
@ -1 +1,11 @@
|
||||
fn main() {}
|
||||
|
||||
#[warn(clippy::cognitive_complexity)]
|
||||
fn cognitive_complexity() {
|
||||
let x = vec![1, 2, 3];
|
||||
for i in x {
|
||||
if i == 1 {
|
||||
println!("{}", i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,15 @@
|
||||
error: error reading Clippy's configuration file `$DIR/clippy.toml`: deprecated field `cyclomatic-complexity-threshold`. Please use `cognitive-complexity-threshold` instead
|
||||
warning: error reading Clippy's configuration file `$DIR/clippy.toml`: deprecated field `cyclomatic-complexity-threshold`. Please use `cognitive-complexity-threshold` instead
|
||||
|
||||
error: aborting due to previous error
|
||||
warning: error reading Clippy's configuration file `$DIR/clippy.toml`: deprecated field `blacklisted-names`. Please use `disallowed-names` instead
|
||||
|
||||
error: the function has a cognitive complexity of (3/2)
|
||||
--> $DIR/conf_deprecated_key.rs:4:4
|
||||
|
|
||||
LL | fn cognitive_complexity() {
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: `-D clippy::cognitive-complexity` implied by `-D warnings`
|
||||
= help: you could split it up into multiple smaller functions
|
||||
|
||||
error: aborting due to previous error; 2 warnings emitted
|
||||
|
||||
|
@ -0,0 +1 @@
|
||||
disallowed-names = ["ducks", ".."]
|
@ -1,9 +1,9 @@
|
||||
#[warn(clippy::blacklisted_name)]
|
||||
#[warn(clippy::disallowed_names)]
|
||||
|
||||
fn main() {
|
||||
// `foo` is part of the default configuration
|
||||
let foo = "bar";
|
||||
// `ducks` was unrightfully blacklisted
|
||||
// `ducks` was unrightfully disallowed
|
||||
let ducks = ["quack", "quack"];
|
||||
// `fox` is okay
|
||||
let fox = ["what", "does", "the", "fox", "say", "?"];
|
@ -0,0 +1,16 @@
|
||||
error: use of a disallowed/placeholder name `foo`
|
||||
--> $DIR/disallowed_names.rs:5:9
|
||||
|
|
||||
LL | let foo = "bar";
|
||||
| ^^^
|
||||
|
|
||||
= note: `-D clippy::disallowed-names` implied by `-D warnings`
|
||||
|
||||
error: use of a disallowed/placeholder name `ducks`
|
||||
--> $DIR/disallowed_names.rs:7:9
|
||||
|
|
||||
LL | let ducks = ["quack", "quack"];
|
||||
| ^^^^^
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
@ -0,0 +1 @@
|
||||
disallowed-names = ["ducks"]
|
@ -1,9 +1,9 @@
|
||||
#[warn(clippy::blacklisted_name)]
|
||||
#[warn(clippy::disallowed_names)]
|
||||
|
||||
fn main() {
|
||||
// `foo` is part of the default configuration
|
||||
let foo = "bar";
|
||||
// `ducks` was unrightfully blacklisted
|
||||
// `ducks` was unrightfully disallowed
|
||||
let ducks = ["quack", "quack"];
|
||||
// `fox` is okay
|
||||
let fox = ["what", "does", "the", "fox", "say", "?"];
|
@ -0,0 +1,10 @@
|
||||
error: use of a disallowed/placeholder name `ducks`
|
||||
--> $DIR/disallowed_names.rs:7:9
|
||||
|
|
||||
LL | let ducks = ["quack", "quack"];
|
||||
| ^^^^^
|
||||
|
|
||||
= note: `-D clippy::disallowed-names` implied by `-D warnings`
|
||||
|
||||
error: aborting due to previous error
|
||||
|
@ -0,0 +1,5 @@
|
||||
cognitive-complexity-threshold = 2
|
||||
# This is the deprecated name for the same key
|
||||
cyclomatic-complexity-threshold = 3
|
||||
# Check we get duplication warning regardless of order
|
||||
cognitive-complexity-threshold = 4
|
@ -0,0 +1 @@
|
||||
fn main() {}
|
@ -0,0 +1,8 @@
|
||||
error: error reading Clippy's configuration file `$DIR/clippy.toml`: duplicate field `cognitive_complexity_threshold` (provided as `cyclomatic_complexity_threshold`)
|
||||
|
||||
error: error reading Clippy's configuration file `$DIR/clippy.toml`: duplicate field `cognitive-complexity-threshold`
|
||||
|
||||
warning: error reading Clippy's configuration file `$DIR/clippy.toml`: deprecated field `cyclomatic-complexity-threshold`. Please use `cognitive-complexity-threshold` instead
|
||||
|
||||
error: aborting due to 2 previous errors; 1 warning emitted
|
||||
|
@ -5,7 +5,7 @@ LL | let _ = opt.expect("");
|
||||
| ^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: `-D clippy::expect-used` implied by `-D warnings`
|
||||
= help: if this value is an `None`, it will panic
|
||||
= help: if this value is `None`, it will panic
|
||||
|
||||
error: used `expect()` on `a Result` value
|
||||
--> $DIR/expect_used.rs:11:13
|
||||
|
@ -1 +0,0 @@
|
||||
blacklisted-names = ["toto", "tata", "titi"]
|
@ -1,46 +0,0 @@
|
||||
error: use of a blacklisted/placeholder name `toto`
|
||||
--> $DIR/conf_french_blacklisted_name.rs:6:9
|
||||
|
|
||||
LL | fn test(toto: ()) {}
|
||||
| ^^^^
|
||||
|
|
||||
= note: `-D clippy::blacklisted-name` implied by `-D warnings`
|
||||
|
||||
error: use of a blacklisted/placeholder name `toto`
|
||||
--> $DIR/conf_french_blacklisted_name.rs:9:9
|
||||
|
|
||||
LL | let toto = 42;
|
||||
| ^^^^
|
||||
|
||||
error: use of a blacklisted/placeholder name `tata`
|
||||
--> $DIR/conf_french_blacklisted_name.rs:10:9
|
||||
|
|
||||
LL | let tata = 42;
|
||||
| ^^^^
|
||||
|
||||
error: use of a blacklisted/placeholder name `titi`
|
||||
--> $DIR/conf_french_blacklisted_name.rs:11:9
|
||||
|
|
||||
LL | let titi = 42;
|
||||
| ^^^^
|
||||
|
||||
error: use of a blacklisted/placeholder name `toto`
|
||||
--> $DIR/conf_french_blacklisted_name.rs:17:10
|
||||
|
|
||||
LL | (toto, Some(tata), titi @ Some(_)) => (),
|
||||
| ^^^^
|
||||
|
||||
error: use of a blacklisted/placeholder name `tata`
|
||||
--> $DIR/conf_french_blacklisted_name.rs:17:21
|
||||
|
|
||||
LL | (toto, Some(tata), titi @ Some(_)) => (),
|
||||
| ^^^^
|
||||
|
||||
error: use of a blacklisted/placeholder name `titi`
|
||||
--> $DIR/conf_french_blacklisted_name.rs:17:28
|
||||
|
|
||||
LL | (toto, Some(tata), titi @ Some(_)) => (),
|
||||
| ^^^^
|
||||
|
||||
error: aborting due to 7 previous errors
|
||||
|
1
src/tools/clippy/tests/ui-toml/toml_disallow/clippy.toml
Normal file
1
src/tools/clippy/tests/ui-toml/toml_disallow/clippy.toml
Normal file
@ -0,0 +1 @@
|
||||
disallowed-names = ["toto", "tata", "titi"]
|
@ -1,7 +1,7 @@
|
||||
#![allow(dead_code)]
|
||||
#![allow(clippy::single_match)]
|
||||
#![allow(unused_variables)]
|
||||
#![warn(clippy::blacklisted_name)]
|
||||
#![warn(clippy::disallowed_names)]
|
||||
|
||||
fn test(toto: ()) {}
|
||||
|
@ -0,0 +1,46 @@
|
||||
error: use of a disallowed/placeholder name `toto`
|
||||
--> $DIR/conf_french_disallowed_name.rs:6:9
|
||||
|
|
||||
LL | fn test(toto: ()) {}
|
||||
| ^^^^
|
||||
|
|
||||
= note: `-D clippy::disallowed-names` implied by `-D warnings`
|
||||
|
||||
error: use of a disallowed/placeholder name `toto`
|
||||
--> $DIR/conf_french_disallowed_name.rs:9:9
|
||||
|
|
||||
LL | let toto = 42;
|
||||
| ^^^^
|
||||
|
||||
error: use of a disallowed/placeholder name `tata`
|
||||
--> $DIR/conf_french_disallowed_name.rs:10:9
|
||||
|
|
||||
LL | let tata = 42;
|
||||
| ^^^^
|
||||
|
||||
error: use of a disallowed/placeholder name `titi`
|
||||
--> $DIR/conf_french_disallowed_name.rs:11:9
|
||||
|
|
||||
LL | let titi = 42;
|
||||
| ^^^^
|
||||
|
||||
error: use of a disallowed/placeholder name `toto`
|
||||
--> $DIR/conf_french_disallowed_name.rs:17:10
|
||||
|
|
||||
LL | (toto, Some(tata), titi @ Some(_)) => (),
|
||||
| ^^^^
|
||||
|
||||
error: use of a disallowed/placeholder name `tata`
|
||||
--> $DIR/conf_french_disallowed_name.rs:17:21
|
||||
|
|
||||
LL | (toto, Some(tata), titi @ Some(_)) => (),
|
||||
| ^^^^
|
||||
|
||||
error: use of a disallowed/placeholder name `titi`
|
||||
--> $DIR/conf_french_disallowed_name.rs:17:28
|
||||
|
|
||||
LL | (toto, Some(tata), titi @ Some(_)) => (),
|
||||
| ^^^^
|
||||
|
||||
error: aborting due to 7 previous errors
|
||||
|
@ -12,6 +12,7 @@ error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown fie
|
||||
cognitive-complexity-threshold
|
||||
cyclomatic-complexity-threshold
|
||||
disallowed-methods
|
||||
disallowed-names
|
||||
disallowed-types
|
||||
doc-valid-idents
|
||||
enable-raw-pointer-heuristic-for-send
|
||||
|
@ -27,6 +27,14 @@ fn main() {
|
||||
let r: Result<Foo, Foo> = Ok(Foo);
|
||||
assert!(r.is_ok());
|
||||
|
||||
// test ok with some messages
|
||||
let r: Result<Foo, DebugFoo> = Ok(Foo);
|
||||
assert!(r.is_ok(), "oops");
|
||||
|
||||
// test ok with unit error
|
||||
let r: Result<Foo, ()> = Ok(Foo);
|
||||
assert!(r.is_ok());
|
||||
|
||||
// test temporary ok
|
||||
fn get_ok() -> Result<Foo, DebugFoo> {
|
||||
Ok(Foo)
|
||||
|
@ -27,6 +27,14 @@ fn main() {
|
||||
let r: Result<Foo, Foo> = Ok(Foo);
|
||||
assert!(r.is_ok());
|
||||
|
||||
// test ok with some messages
|
||||
let r: Result<Foo, DebugFoo> = Ok(Foo);
|
||||
assert!(r.is_ok(), "oops");
|
||||
|
||||
// test ok with unit error
|
||||
let r: Result<Foo, ()> = Ok(Foo);
|
||||
assert!(r.is_ok());
|
||||
|
||||
// test temporary ok
|
||||
fn get_ok() -> Result<Foo, DebugFoo> {
|
||||
Ok(Foo)
|
||||
|
@ -7,31 +7,31 @@ LL | assert!(r.is_ok());
|
||||
= note: `-D clippy::assertions-on-result-states` implied by `-D warnings`
|
||||
|
||||
error: called `assert!` with `Result::is_ok`
|
||||
--> $DIR/assertions_on_result_states.rs:34:5
|
||||
--> $DIR/assertions_on_result_states.rs:42:5
|
||||
|
|
||||
LL | assert!(get_ok().is_ok());
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `get_ok().unwrap()`
|
||||
|
||||
error: called `assert!` with `Result::is_ok`
|
||||
--> $DIR/assertions_on_result_states.rs:37:5
|
||||
--> $DIR/assertions_on_result_states.rs:45:5
|
||||
|
|
||||
LL | assert!(get_ok_macro!().is_ok());
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `get_ok_macro!().unwrap()`
|
||||
|
||||
error: called `assert!` with `Result::is_ok`
|
||||
--> $DIR/assertions_on_result_states.rs:50:5
|
||||
--> $DIR/assertions_on_result_states.rs:58:5
|
||||
|
|
||||
LL | assert!(r.is_ok());
|
||||
| ^^^^^^^^^^^^^^^^^^ help: replace with: `r.unwrap()`
|
||||
|
||||
error: called `assert!` with `Result::is_ok`
|
||||
--> $DIR/assertions_on_result_states.rs:56:9
|
||||
--> $DIR/assertions_on_result_states.rs:64:9
|
||||
|
|
||||
LL | assert!(r.is_ok());
|
||||
| ^^^^^^^^^^^^^^^^^^ help: replace with: `r.unwrap()`
|
||||
|
||||
error: called `assert!` with `Result::is_err`
|
||||
--> $DIR/assertions_on_result_states.rs:64:5
|
||||
--> $DIR/assertions_on_result_states.rs:72:5
|
||||
|
|
||||
LL | assert!(r.is_err());
|
||||
| ^^^^^^^^^^^^^^^^^^^ help: replace with: `r.unwrap_err()`
|
||||
|
@ -1,88 +0,0 @@
|
||||
error: use of a blacklisted/placeholder name `foo`
|
||||
--> $DIR/blacklisted_name.rs:11:9
|
||||
|
|
||||
LL | fn test(foo: ()) {}
|
||||
| ^^^
|
||||
|
|
||||
= note: `-D clippy::blacklisted-name` implied by `-D warnings`
|
||||
|
||||
error: use of a blacklisted/placeholder name `foo`
|
||||
--> $DIR/blacklisted_name.rs:14:9
|
||||
|
|
||||
LL | let foo = 42;
|
||||
| ^^^
|
||||
|
||||
error: use of a blacklisted/placeholder name `baz`
|
||||
--> $DIR/blacklisted_name.rs:15:9
|
||||
|
|
||||
LL | let baz = 42;
|
||||
| ^^^
|
||||
|
||||
error: use of a blacklisted/placeholder name `quux`
|
||||
--> $DIR/blacklisted_name.rs:16:9
|
||||
|
|
||||
LL | let quux = 42;
|
||||
| ^^^^
|
||||
|
||||
error: use of a blacklisted/placeholder name `foo`
|
||||
--> $DIR/blacklisted_name.rs:27:10
|
||||
|
|
||||
LL | (foo, Some(baz), quux @ Some(_)) => (),
|
||||
| ^^^
|
||||
|
||||
error: use of a blacklisted/placeholder name `baz`
|
||||
--> $DIR/blacklisted_name.rs:27:20
|
||||
|
|
||||
LL | (foo, Some(baz), quux @ Some(_)) => (),
|
||||
| ^^^
|
||||
|
||||
error: use of a blacklisted/placeholder name `quux`
|
||||
--> $DIR/blacklisted_name.rs:27:26
|
||||
|
|
||||
LL | (foo, Some(baz), quux @ Some(_)) => (),
|
||||
| ^^^^
|
||||
|
||||
error: use of a blacklisted/placeholder name `foo`
|
||||
--> $DIR/blacklisted_name.rs:32:19
|
||||
|
|
||||
LL | fn issue_1647(mut foo: u8) {
|
||||
| ^^^
|
||||
|
||||
error: use of a blacklisted/placeholder name `baz`
|
||||
--> $DIR/blacklisted_name.rs:33:13
|
||||
|
|
||||
LL | let mut baz = 0;
|
||||
| ^^^
|
||||
|
||||
error: use of a blacklisted/placeholder name `quux`
|
||||
--> $DIR/blacklisted_name.rs:34:21
|
||||
|
|
||||
LL | if let Some(mut quux) = Some(42) {}
|
||||
| ^^^^
|
||||
|
||||
error: use of a blacklisted/placeholder name `baz`
|
||||
--> $DIR/blacklisted_name.rs:38:13
|
||||
|
|
||||
LL | let ref baz = 0;
|
||||
| ^^^
|
||||
|
||||
error: use of a blacklisted/placeholder name `quux`
|
||||
--> $DIR/blacklisted_name.rs:39:21
|
||||
|
|
||||
LL | if let Some(ref quux) = Some(42) {}
|
||||
| ^^^^
|
||||
|
||||
error: use of a blacklisted/placeholder name `baz`
|
||||
--> $DIR/blacklisted_name.rs:43:17
|
||||
|
|
||||
LL | let ref mut baz = 0;
|
||||
| ^^^
|
||||
|
||||
error: use of a blacklisted/placeholder name `quux`
|
||||
--> $DIR/blacklisted_name.rs:44:25
|
||||
|
|
||||
LL | if let Some(ref mut quux) = Some(42) {}
|
||||
| ^^^^
|
||||
|
||||
error: aborting due to 14 previous errors
|
||||
|
@ -1,5 +1,5 @@
|
||||
#![deny(clippy::borrowed_box)]
|
||||
#![allow(clippy::blacklisted_name)]
|
||||
#![allow(clippy::disallowed_names)]
|
||||
#![allow(unused_variables)]
|
||||
#![allow(dead_code)]
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
#![allow(
|
||||
clippy::boxed_local,
|
||||
clippy::needless_pass_by_value,
|
||||
clippy::blacklisted_name,
|
||||
clippy::disallowed_names,
|
||||
unused
|
||||
)]
|
||||
|
||||
|
@ -26,4 +26,6 @@ fn main() {
|
||||
let _ = a.unsigned_abs() as u32;
|
||||
let _ = a.unsigned_abs() as u64;
|
||||
let _ = a.unsigned_abs() as u128;
|
||||
|
||||
let _ = (x as i64 - y as i64).unsigned_abs() as u32;
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user