Merge commit '2b2190cb5667cdd276a24ef8b9f3692209c54a89' into clippyup

This commit is contained in:
Philipp Krones 2022-08-11 19:42:16 +02:00
parent f719599c0f
commit dc29cfb8d5
182 changed files with 2240 additions and 827 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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, .. }),
if let Node::Expr(
closure_expr @ Expr {
kind: ExprKind::Closure(closure),
..
}) = cx.tcx.hir().get(owner_id)
},
) = 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 { .. } => (),
}
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View 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
}
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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(..)

View file

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

View file

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

View file

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

View file

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

View file

@ -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,15 +158,19 @@ 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());
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());
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<'_>) {
// If the method is an impl for a trait, don't doc.
@ -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());
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);
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);
if !is_from_proc_macro(cx, v) {
self.check_missing_docs_attrs(cx, attrs, v.span, "a", "variant");
}
}
}

View 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,
);
}
}
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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"),

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -496,12 +496,14 @@ impl<'tcx> LateLintPass<'tcx> for LintWithoutLintPass {
cx,
};
let body_id = cx.tcx.hir().body_owned_by(
cx.tcx.hir().local_def_id(
impl_item_refs
.iter()
.find(|iiref| iiref.ident.as_str() == "get_lints")
.expect("LintPass needs to implement get_lints")
.id
.hir_id(),
),
);
collector.visit_expr(&cx.tcx.hir().body(body_id).value);
}
@ -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 {

View file

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

View file

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

View file

@ -1,6 +1,6 @@
[package]
name = "clippy_utils"
version = "0.1.64"
version = "0.1.65"
edition = "2021"
publish = false

View 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("}"))
}

View file

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

View file

@ -223,8 +223,10 @@ 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);
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))
})
}

View file

@ -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"];

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1 +1 @@
blacklisted-names = 42
disallowed-names = 42

View file

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

View file

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

View file

@ -1 +0,0 @@
blacklisted-names = ["ducks", ".."]

View file

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

View file

@ -1 +0,0 @@
blacklisted-names = ["ducks"]

View file

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

View file

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

View file

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

View file

@ -0,0 +1 @@
disallowed-names = ["ducks", ".."]

View file

@ -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", "?"];

View file

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

View file

@ -0,0 +1 @@
disallowed-names = ["ducks"]

View file

@ -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", "?"];

View file

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

View file

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

View file

@ -0,0 +1 @@
fn main() {}

View file

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

View file

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

View file

@ -1 +0,0 @@
blacklisted-names = ["toto", "tata", "titi"]

View file

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

View file

@ -0,0 +1 @@
disallowed-names = ["toto", "tata", "titi"]

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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()`

View file

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

View file

@ -1,5 +1,5 @@
#![deny(clippy::borrowed_box)]
#![allow(clippy::blacklisted_name)]
#![allow(clippy::disallowed_names)]
#![allow(unused_variables)]
#![allow(dead_code)]

View file

@ -2,7 +2,7 @@
#![allow(
clippy::boxed_local,
clippy::needless_pass_by_value,
clippy::blacklisted_name,
clippy::disallowed_names,
unused
)]

View file

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